summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview
Print done when done.
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/English.lproj/InfoPlist.strings5
-rw-r--r--indra/newview/Info-SecondLife.plist39
-rw-r--r--indra/newview/Info-SecondLifeVorbis.plist28
-rw-r--r--indra/newview/SecondLife.nib/classes.nib4
-rw-r--r--indra/newview/SecondLife.nib/info.nib23
-rw-r--r--indra/newview/SecondLife.nib/objects.xib259
-rw-r--r--indra/newview/VertexCache.h87
-rw-r--r--indra/newview/VorbisFramework.h62
-rw-r--r--indra/newview/app_settings/CA.pem1811
-rw-r--r--indra/newview/app_settings/anim.ini87
-rw-r--r--indra/newview/app_settings/grass.xml47
-rw-r--r--indra/newview/app_settings/keys.ini310
-rw-r--r--indra/newview/app_settings/keywords.ini513
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl19
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl35
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl20
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl6
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl17
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/terrainF.glsl19
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/terrainV.glsl37
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/waterF.glsl22
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/waterV.glsl41
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/highlightF.glsl6
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/highlightV.glsl20
-rw-r--r--indra/newview/app_settings/shaders/class1/lighting/lightF.glsl31
-rw-r--r--indra/newview/app_settings/shaders/class1/lighting/lightV.glsl99
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/simpleF.glsl6
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/simpleV.glsl21
-rw-r--r--indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl23
-rw-r--r--indra/newview/app_settings/shaders/class2/environment/waterF.glsl136
-rw-r--r--indra/newview/app_settings/shaders/class2/lighting/lightF.glsl36
-rw-r--r--indra/newview/app_settings/shaders/class2/lighting/lightV.glsl126
-rw-r--r--indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl128
-rw-r--r--indra/newview/app_settings/std_bump.ini18
-rw-r--r--indra/newview/app_settings/trees.xml24
-rw-r--r--indra/newview/app_settings/viewerart.xml504
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_ARROW.tifbin0 -> 14336 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tifbin0 -> 14368 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tifbin0 -> 14376 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tifbin0 -> 14392 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_NO.tifbin0 -> 14380 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tifbin0 -> 14416 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tifbin0 -> 14336 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZENS.tifbin0 -> 14344 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tifbin0 -> 14340 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tifbin0 -> 14328 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tifbin0 -> 14776 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tifbin0 -> 14356 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tifbin0 -> 14368 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tifbin0 -> 14348 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tifbin0 -> 14364 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tifbin0 -> 14348 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tifbin0 -> 15144 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tifbin0 -> 14368 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tifbin0 -> 14392 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tifbin0 -> 14380 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tifbin0 -> 14384 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tifbin0 -> 15176 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tifbin0 -> 14388 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tifbin0 -> 14364 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_WORKING.tifbin0 -> 14392 bytes
-rw-r--r--indra/newview/featuretable.txt170
-rw-r--r--indra/newview/featuretable_mac.txt151
-rw-r--r--indra/newview/fmod_hidden_symbols.exp140
-rw-r--r--indra/newview/fmodwrapper.cpp19
-rw-r--r--indra/newview/gpu_table.txt118
-rw-r--r--indra/newview/licenses-linux.txt437
-rw-r--r--indra/newview/licenses-mac.txt388
-rw-r--r--indra/newview/licenses-win32.txt388
-rw-r--r--indra/newview/linux_tools/client-readme.txt202
-rwxr-xr-xindra/newview/linux_tools/launch_url.sh87
-rwxr-xr-xindra/newview/linux_tools/wrapper.sh50
-rw-r--r--indra/newview/llagent.cpp7233
-rw-r--r--indra/newview/llagent.h896
-rw-r--r--indra/newview/llagentdata.cpp15
-rw-r--r--indra/newview/llagentdata.h16
-rw-r--r--indra/newview/llagentpilot.cpp250
-rw-r--r--indra/newview/llagentpilot.h77
-rw-r--r--indra/newview/llappearance.h33
-rw-r--r--indra/newview/llassetuploadresponders.cpp197
-rw-r--r--indra/newview/llassetuploadresponders.h26
-rw-r--r--indra/newview/llaudiosourcevo.cpp129
-rw-r--r--indra/newview/llaudiosourcevo.h34
-rw-r--r--indra/newview/llbox.cpp105
-rw-r--r--indra/newview/llbox.h32
-rw-r--r--indra/newview/llcallbacklist.cpp173
-rw-r--r--indra/newview/llcallbacklist.h39
-rw-r--r--indra/newview/llcallingcard.cpp797
-rw-r--r--indra/newview/llcallingcard.h224
-rw-r--r--indra/newview/llchatbar.cpp706
-rw-r--r--indra/newview/llchatbar.h98
-rw-r--r--indra/newview/llclassifiedinfo.cpp49
-rw-r--r--indra/newview/llclassifiedinfo.h31
-rw-r--r--indra/newview/llcloud.cpp555
-rw-r--r--indra/newview/llcloud.h182
-rw-r--r--indra/newview/llcolorswatch.cpp386
-rw-r--r--indra/newview/llcolorswatch.h88
-rw-r--r--indra/newview/llcompilequeue.cpp756
-rw-r--r--indra/newview/llcompilequeue.h211
-rw-r--r--indra/newview/llconfirmationmanager.cpp100
-rw-r--r--indra/newview/llconfirmationmanager.h67
-rw-r--r--indra/newview/llcurrencyuimanager.cpp497
-rw-r--r--indra/newview/llcurrencyuimanager.h72
-rw-r--r--indra/newview/llcylinder.cpp308
-rw-r--r--indra/newview/llcylinder.h67
-rw-r--r--indra/newview/lldebugmessagebox.cpp226
-rw-r--r--indra/newview/lldebugmessagebox.h72
-rw-r--r--indra/newview/lldebugview.cpp133
-rw-r--r--indra/newview/lldebugview.h46
-rw-r--r--indra/newview/lldirpicker.cpp266
-rw-r--r--indra/newview/lldirpicker.h84
-rw-r--r--indra/newview/lldrawable.cpp1320
-rw-r--r--indra/newview/lldrawable.h311
-rw-r--r--indra/newview/lldrawpool.cpp1402
-rw-r--r--indra/newview/lldrawpool.h348
-rw-r--r--indra/newview/lldrawpoolalpha.cpp584
-rw-r--r--indra/newview/lldrawpoolalpha.h64
-rw-r--r--indra/newview/lldrawpoolavatar.cpp610
-rw-r--r--indra/newview/lldrawpoolavatar.h54
-rw-r--r--indra/newview/lldrawpoolbump.cpp1135
-rw-r--r--indra/newview/lldrawpoolbump.h141
-rw-r--r--indra/newview/lldrawpoolclouds.cpp87
-rw-r--r--indra/newview/lldrawpoolclouds.h28
-rw-r--r--indra/newview/lldrawpoolground.cpp85
-rw-r--r--indra/newview/lldrawpoolground.h28
-rw-r--r--indra/newview/lldrawpoolsimple.cpp227
-rw-r--r--indra/newview/lldrawpoolsimple.h74
-rw-r--r--indra/newview/lldrawpoolsky.cpp180
-rw-r--r--indra/newview/lldrawpoolsky.h42
-rw-r--r--indra/newview/lldrawpoolterrain.cpp1091
-rw-r--r--indra/newview/lldrawpoolterrain.h49
-rw-r--r--indra/newview/lldrawpooltree.cpp342
-rw-r--r--indra/newview/lldrawpooltree.h41
-rw-r--r--indra/newview/lldrawpoolwater.cpp729
-rw-r--r--indra/newview/lldrawpoolwater.h57
-rw-r--r--indra/newview/lldriverparam.cpp435
-rw-r--r--indra/newview/lldriverparam.h90
-rw-r--r--indra/newview/lldynamictexture.cpp215
-rw-r--r--indra/newview/lldynamictexture.h67
-rw-r--r--indra/newview/llemote.cpp126
-rw-r--r--indra/newview/llemote.h104
-rw-r--r--indra/newview/lleventinfo.cpp134
-rw-r--r--indra/newview/lleventinfo.h50
-rw-r--r--indra/newview/lleventnotifier.cpp308
-rw-r--r--indra/newview/lleventnotifier.h68
-rw-r--r--indra/newview/lleventpoll.cpp166
-rw-r--r--indra/newview/lleventpoll.h37
-rw-r--r--indra/newview/llface.cpp1939
-rw-r--r--indra/newview/llface.h250
-rw-r--r--indra/newview/llface.inl222
-rw-r--r--indra/newview/llfasttimerview.cpp1084
-rw-r--r--indra/newview/llfasttimerview.h52
-rw-r--r--indra/newview/llfeaturemanager.cpp842
-rw-r--r--indra/newview/llfeaturemanager.h98
-rw-r--r--indra/newview/llfilepicker.cpp1339
-rw-r--r--indra/newview/llfilepicker.h167
-rw-r--r--indra/newview/llfirstuse.cpp196
-rw-r--r--indra/newview/llfirstuse.h83
-rw-r--r--indra/newview/llflexibleobject.cpp832
-rw-r--r--indra/newview/llflexibleobject.h154
-rw-r--r--indra/newview/llfloaterabout.cpp167
-rw-r--r--indra/newview/llfloaterabout.h28
-rw-r--r--indra/newview/llfloateranimpreview.cpp1139
-rw-r--r--indra/newview/llfloateranimpreview.h108
-rw-r--r--indra/newview/llfloaterauction.cpp307
-rw-r--r--indra/newview/llfloaterauction.h53
-rw-r--r--indra/newview/llfloateravatarpicker.cpp374
-rw-r--r--indra/newview/llfloateravatarpicker.h78
-rw-r--r--indra/newview/llfloateravatartextures.cpp180
-rw-r--r--indra/newview/llfloateravatartextures.h59
-rw-r--r--indra/newview/llfloaterbuildoptions.cpp73
-rw-r--r--indra/newview/llfloaterbuildoptions.h36
-rw-r--r--indra/newview/llfloaterbump.cpp138
-rw-r--r--indra/newview/llfloaterbump.h33
-rw-r--r--indra/newview/llfloaterbuy.cpp288
-rw-r--r--indra/newview/llfloaterbuy.h53
-rw-r--r--indra/newview/llfloaterbuycontents.cpp274
-rw-r--r--indra/newview/llfloaterbuycontents.h49
-rw-r--r--indra/newview/llfloaterbuycurrency.cpp356
-rw-r--r--indra/newview/llfloaterbuycurrency.h28
-rw-r--r--indra/newview/llfloaterbuyland.cpp1342
-rw-r--r--indra/newview/llfloaterbuyland.h28
-rw-r--r--indra/newview/llfloaterchat.cpp420
-rw-r--r--indra/newview/llfloaterchat.h61
-rw-r--r--indra/newview/llfloatercolorpicker.cpp1259
-rw-r--r--indra/newview/llfloatercolorpicker.h179
-rw-r--r--indra/newview/llfloaterfriends.cpp736
-rw-r--r--indra/newview/llfloaterfriends.h124
-rw-r--r--indra/newview/llfloatergesture.cpp396
-rw-r--r--indra/newview/llfloatergesture.h64
-rw-r--r--indra/newview/llfloatergodtools.cpp1446
-rw-r--r--indra/newview/llfloatergodtools.h262
-rw-r--r--indra/newview/llfloatergroupinvite.cpp118
-rw-r--r--indra/newview/llfloatergroupinvite.h34
-rw-r--r--indra/newview/llfloatergroups.cpp454
-rw-r--r--indra/newview/llfloatergroups.h110
-rw-r--r--indra/newview/llfloaterimagepreview.cpp679
-rw-r--r--indra/newview/llfloaterimagepreview.h83
-rw-r--r--indra/newview/llfloaterland.cpp3208
-rw-r--r--indra/newview/llfloaterland.h472
-rw-r--r--indra/newview/llfloaterlandholdings.cpp305
-rw-r--r--indra/newview/llfloaterlandholdings.h62
-rw-r--r--indra/newview/llfloatermap.cpp199
-rw-r--r--indra/newview/llfloatermap.h50
-rw-r--r--indra/newview/llfloaternamedesc.cpp202
-rw-r--r--indra/newview/llfloaternamedesc.h42
-rw-r--r--indra/newview/llfloateropenobject.cpp196
-rw-r--r--indra/newview/llfloateropenobject.h56
-rw-r--r--indra/newview/llfloaterpostcard.cpp319
-rw-r--r--indra/newview/llfloaterpostcard.h58
-rw-r--r--indra/newview/llfloaterpreference.cpp365
-rw-r--r--indra/newview/llfloaterpreference.h106
-rw-r--r--indra/newview/llfloaterproperties.cpp927
-rw-r--r--indra/newview/llfloaterproperties.h82
-rw-r--r--indra/newview/llfloaterregioninfo.cpp2947
-rw-r--r--indra/newview/llfloaterregioninfo.h387
-rw-r--r--indra/newview/llfloaterreporter.cpp819
-rw-r--r--indra/newview/llfloaterreporter.h107
-rw-r--r--indra/newview/llfloaterscriptdebug.cpp230
-rw-r--r--indra/newview/llfloaterscriptdebug.h60
-rwxr-xr-xindra/newview/llfloatersellland.cpp486
-rwxr-xr-xindra/newview/llfloatersellland.h21
-rw-r--r--indra/newview/llfloatersnapshot.cpp1523
-rw-r--r--indra/newview/llfloatersnapshot.h53
-rw-r--r--indra/newview/llfloatertelehub.cpp289
-rw-r--r--indra/newview/llfloatertelehub.h57
-rw-r--r--indra/newview/llfloatertools.cpp931
-rw-r--r--indra/newview/llfloatertools.h164
-rw-r--r--indra/newview/llfloatertopobjects.cpp441
-rw-r--r--indra/newview/llfloatertopobjects.h86
-rw-r--r--indra/newview/llfloatertos.cpp269
-rw-r--r--indra/newview/llfloatertos.h64
-rw-r--r--indra/newview/llfloaterworldmap.cpp1579
-rw-r--r--indra/newview/llfloaterworldmap.h170
-rw-r--r--indra/newview/llfolderview.cpp4899
-rw-r--r--indra/newview/llfolderview.h864
-rw-r--r--indra/newview/llfollowcam.cpp882
-rw-r--r--indra/newview/llfollowcam.h216
-rw-r--r--indra/newview/llgesturemgr.cpp1067
-rw-r--r--indra/newview/llgesturemgr.h138
-rw-r--r--indra/newview/llglsandbox.cpp1222
-rw-r--r--indra/newview/llgroupmgr.cpp1816
-rw-r--r--indra/newview/llgroupmgr.h343
-rw-r--r--indra/newview/llhudeffect.cpp115
-rw-r--r--indra/newview/llhudeffect.h62
-rw-r--r--indra/newview/llhudeffectbeam.cpp371
-rw-r--r--indra/newview/llhudeffectbeam.h51
-rw-r--r--indra/newview/llhudeffectlookat.cpp501
-rw-r--r--indra/newview/llhudeffectlookat.h76
-rw-r--r--indra/newview/llhudeffectpointat.cpp432
-rw-r--r--indra/newview/llhudeffectpointat.h65
-rw-r--r--indra/newview/llhudeffecttrail.cpp271
-rw-r--r--indra/newview/llhudeffecttrail.h76
-rw-r--r--indra/newview/llhudicon.cpp253
-rw-r--r--indra/newview/llhudicon.h72
-rw-r--r--indra/newview/llhudmanager.cpp299
-rw-r--r--indra/newview/llhudmanager.h60
-rw-r--r--indra/newview/llhudobject.cpp277
-rw-r--r--indra/newview/llhudobject.h96
-rw-r--r--indra/newview/llhudrender.cpp113
-rw-r--r--indra/newview/llhudrender.h39
-rw-r--r--indra/newview/llhudtext.cpp991
-rw-r--r--indra/newview/llhudtext.h155
-rw-r--r--indra/newview/llhudview.cpp78
-rw-r--r--indra/newview/llhudview.h37
-rw-r--r--indra/newview/llimpanel.cpp793
-rw-r--r--indra/newview/llimpanel.h155
-rw-r--r--indra/newview/llimview.cpp717
-rw-r--r--indra/newview/llimview.h150
-rwxr-xr-xindra/newview/llinventorybridge.cpp4405
-rwxr-xr-xindra/newview/llinventorybridge.h579
-rw-r--r--indra/newview/llinventoryclipboard.cpp80
-rw-r--r--indra/newview/llinventoryclipboard.h65
-rw-r--r--indra/newview/llinventorymodel.cpp3493
-rw-r--r--indra/newview/llinventorymodel.h757
-rw-r--r--indra/newview/lljoystickbutton.cpp847
-rw-r--r--indra/newview/lljoystickbutton.h168
-rw-r--r--indra/newview/lllandmarklist.cpp116
-rw-r--r--indra/newview/lllandmarklist.h51
-rw-r--r--indra/newview/lllightconstants.h15
-rw-r--r--indra/newview/lllogchat.cpp82
-rw-r--r--indra/newview/lllogchat.h21
-rw-r--r--indra/newview/llmanip.cpp581
-rw-r--r--indra/newview/llmanip.h137
-rw-r--r--indra/newview/llmaniprotate.cpp1876
-rw-r--r--indra/newview/llmaniprotate.h100
-rw-r--r--indra/newview/llmanipscale.cpp2024
-rw-r--r--indra/newview/llmanipscale.h137
-rw-r--r--indra/newview/llmaniptranslate.cpp2126
-rw-r--r--indra/newview/llmaniptranslate.h97
-rw-r--r--indra/newview/llmemoryview.cpp223
-rw-r--r--indra/newview/llmemoryview.h31
-rw-r--r--indra/newview/llmenucommands.cpp135
-rw-r--r--indra/newview/llmenucommands.h27
-rw-r--r--indra/newview/llmorphview.cpp193
-rw-r--r--indra/newview/llmorphview.h68
-rw-r--r--indra/newview/llmoveview.cpp198
-rw-r--r--indra/newview/llmoveview.h68
-rw-r--r--indra/newview/llmutelist.cpp553
-rw-r--r--indra/newview/llmutelist.h128
-rw-r--r--indra/newview/llnamebox.cpp102
-rw-r--r--indra/newview/llnamebox.h46
-rw-r--r--indra/newview/llnameeditor.cpp184
-rw-r--r--indra/newview/llnameeditor.h70
-rw-r--r--indra/newview/llnamelistctrl.cpp409
-rw-r--r--indra/newview/llnamelistctrl.h71
-rw-r--r--indra/newview/llnetmap.cpp806
-rw-r--r--indra/newview/llnetmap.h104
-rw-r--r--indra/newview/lloverlaybar.cpp573
-rw-r--r--indra/newview/lloverlaybar.h77
-rw-r--r--indra/newview/llpanelavatar.cpp2347
-rw-r--r--indra/newview/llpanelavatar.h335
-rw-r--r--indra/newview/llpanelclassified.cpp852
-rw-r--r--indra/newview/llpanelclassified.h148
-rw-r--r--indra/newview/llpanelcontents.cpp189
-rw-r--r--indra/newview/llpanelcontents.h39
-rw-r--r--indra/newview/llpanelface.cpp956
-rw-r--r--indra/newview/llpanelface.h136
-rw-r--r--indra/newview/llpanelgroup.cpp651
-rw-r--r--indra/newview/llpanelgroup.h173
-rw-r--r--indra/newview/llpanelgroupgeneral.cpp771
-rw-r--r--indra/newview/llpanelgroupgeneral.h91
-rw-r--r--indra/newview/llpanelgroupinvite.cpp392
-rw-r--r--indra/newview/llpanelgroupinvite.h32
-rw-r--r--indra/newview/llpanelgrouplandmoney.cpp1387
-rw-r--r--indra/newview/llpanelgrouplandmoney.h127
-rw-r--r--indra/newview/llpanelgroupnotices.cpp565
-rw-r--r--indra/newview/llpanelgroupnotices.h98
-rw-r--r--indra/newview/llpanelgrouproles.cpp2642
-rw-r--r--indra/newview/llpanelgrouproles.h297
-rw-r--r--indra/newview/llpanelland.cpp236
-rw-r--r--indra/newview/llpanelland.h54
-rw-r--r--indra/newview/llpanellogin.cpp748
-rw-r--r--indra/newview/llpanellogin.h83
-rw-r--r--indra/newview/llpanelobject.cpp1679
-rw-r--r--indra/newview/llpanelobject.h147
-rw-r--r--indra/newview/llpanelpermissions.cpp1028
-rw-r--r--indra/newview/llpanelpermissions.h92
-rw-r--r--indra/newview/llpanelpick.cpp494
-rw-r--r--indra/newview/llpanelpick.h99
-rw-r--r--indra/newview/llpanelplace.cpp271
-rw-r--r--indra/newview/llpanelplace.h70
-rw-r--r--indra/newview/llpanelvolume.cpp499
-rw-r--r--indra/newview/llpanelvolume.h82
-rw-r--r--indra/newview/llpatchvertexarray.cpp231
-rw-r--r--indra/newview/llpatchvertexarray.h51
-rw-r--r--indra/newview/llpolymesh.cpp1117
-rw-r--r--indra/newview/llpolymesh.h412
-rw-r--r--indra/newview/llpolymorph.cpp639
-rw-r--r--indra/newview/llpolymorph.h163
-rw-r--r--indra/newview/llpreview.cpp489
-rw-r--r--indra/newview/llpreview.h139
-rw-r--r--indra/newview/llpreviewanim.cpp197
-rw-r--r--indra/newview/llpreviewanim.h39
-rw-r--r--indra/newview/llpreviewgesture.cpp1698
-rw-r--r--indra/newview/llpreviewgesture.h149
-rw-r--r--indra/newview/llpreviewnotecard.cpp579
-rw-r--r--indra/newview/llpreviewnotecard.h84
-rw-r--r--indra/newview/llpreviewscript.cpp1984
-rw-r--r--indra/newview/llpreviewscript.h212
-rw-r--r--indra/newview/llpreviewsound.cpp87
-rw-r--r--indra/newview/llpreviewsound.h26
-rw-r--r--indra/newview/llpreviewtexture.cpp365
-rw-r--r--indra/newview/llpreviewtexture.h78
-rw-r--r--indra/newview/llprogressview.cpp326
-rw-r--r--indra/newview/llprogressview.h54
-rw-r--r--indra/newview/llregionposition.cpp76
-rw-r--r--indra/newview/llregionposition.h43
-rw-r--r--indra/newview/llsavedsettingsglue.cpp51
-rw-r--r--indra/newview/llsavedsettingsglue.h28
-rw-r--r--indra/newview/llselectmgr.cpp6732
-rw-r--r--indra/newview/llselectmgr.h625
-rw-r--r--indra/newview/llsky.cpp427
-rw-r--r--indra/newview/llsky.h101
-rw-r--r--indra/newview/llspatialpartition.cpp2255
-rw-r--r--indra/newview/llspatialpartition.h209
-rw-r--r--indra/newview/llsprite.cpp296
-rw-r--r--indra/newview/llsprite.h89
-rw-r--r--indra/newview/llstartup.cpp3959
-rw-r--r--indra/newview/llstartup.h72
-rw-r--r--indra/newview/llstatusbar.cpp612
-rw-r--r--indra/newview/llstatusbar.h108
-rw-r--r--indra/newview/llsurface.cpp1337
-rw-r--r--indra/newview/llsurface.h249
-rw-r--r--indra/newview/llsurfacepatch.cpp974
-rw-r--r--indra/newview/llsurfacepatch.h158
-rw-r--r--indra/newview/lltable.h48
-rw-r--r--indra/newview/lltexlayer.cpp2839
-rw-r--r--indra/newview/lltexlayer.h566
-rw-r--r--indra/newview/lltexturectrl.cpp1368
-rw-r--r--indra/newview/lltexturectrl.h158
-rw-r--r--indra/newview/lltexturefetch.cpp744
-rw-r--r--indra/newview/lltexturefetch.h28
-rw-r--r--indra/newview/lltextureview.cpp304
-rw-r--r--indra/newview/lltextureview.h55
-rw-r--r--indra/newview/lltool.cpp151
-rw-r--r--indra/newview/lltool.h84
-rw-r--r--indra/newview/lltoolbar.cpp436
-rw-r--r--indra/newview/lltoolbar.h76
-rw-r--r--indra/newview/lltoolbrush.cpp585
-rw-r--r--indra/newview/lltoolbrush.h88
-rw-r--r--indra/newview/lltoolcomp.cpp673
-rw-r--r--indra/newview/lltoolcomp.h200
-rw-r--r--indra/newview/lltooldraganddrop.cpp2969
-rw-r--r--indra/newview/lltooldraganddrop.h239
-rw-r--r--indra/newview/lltoolface.cpp141
-rw-r--r--indra/newview/lltoolface.h34
-rw-r--r--indra/newview/lltoolfocus.cpp442
-rw-r--r--indra/newview/lltoolfocus.h58
-rw-r--r--indra/newview/lltoolgrab.cpp875
-rw-r--r--indra/newview/lltoolgrab.h112
-rw-r--r--indra/newview/lltoolgun.cpp122
-rw-r--r--indra/newview/lltoolgun.h35
-rw-r--r--indra/newview/lltoolindividual.cpp102
-rw-r--r--indra/newview/lltoolindividual.h41
-rw-r--r--indra/newview/lltoolmgr.cpp532
-rw-r--r--indra/newview/lltoolmgr.h99
-rw-r--r--indra/newview/lltoolmorph.cpp279
-rw-r--r--indra/newview/lltoolmorph.h88
-rw-r--r--indra/newview/lltoolobjpicker.cpp162
-rw-r--r--indra/newview/lltoolobjpicker.h45
-rw-r--r--indra/newview/lltoolpie.cpp635
-rw-r--r--indra/newview/lltoolpie.h59
-rwxr-xr-xindra/newview/lltoolpipette.cpp120
-rwxr-xr-xindra/newview/lltoolpipette.h47
-rw-r--r--indra/newview/lltoolplacer.cpp225
-rw-r--r--indra/newview/lltoolplacer.h79
-rw-r--r--indra/newview/lltoolselect.cpp244
-rw-r--r--indra/newview/lltoolselect.h39
-rw-r--r--indra/newview/lltoolselectland.cpp223
-rw-r--r--indra/newview/lltoolselectland.h56
-rw-r--r--indra/newview/lltoolselectrect.cpp182
-rw-r--r--indra/newview/lltoolselectrect.h44
-rw-r--r--indra/newview/lltoolview.cpp175
-rw-r--r--indra/newview/lltoolview.h69
-rw-r--r--indra/newview/lltracker.cpp801
-rw-r--r--indra/newview/lltracker.h135
-rw-r--r--indra/newview/lluiconstants.h32
-rw-r--r--indra/newview/lluploaddialog.cpp149
-rw-r--r--indra/newview/lluploaddialog.h38
-rw-r--r--indra/newview/llurl.cpp256
-rw-r--r--indra/newview/llurl.h76
-rw-r--r--indra/newview/llurlwhitelist.cpp221
-rw-r--r--indra/newview/llurlwhitelist.h48
-rw-r--r--indra/newview/llviewchildren.cpp99
-rw-r--r--indra/newview/llviewchildren.h49
-rw-r--r--indra/newview/llviewerassetstorage.cpp196
-rw-r--r--indra/newview/llviewerassetstorage.h47
-rw-r--r--indra/newview/llviewercamera.cpp611
-rw-r--r--indra/newview/llviewercamera.h95
-rw-r--r--indra/newview/llviewercontrol.cpp465
-rw-r--r--indra/newview/llviewercontrol.h60
-rw-r--r--indra/newview/llviewerdisplay.cpp832
-rw-r--r--indra/newview/llviewerdisplay.h17
-rw-r--r--indra/newview/llviewergesture.cpp260
-rw-r--r--indra/newview/llviewergesture.h71
-rw-r--r--indra/newview/llviewerinventory.cpp705
-rw-r--r--indra/newview/llviewerinventory.h253
-rw-r--r--indra/newview/llviewerjoint.cpp607
-rw-r--r--indra/newview/llviewerjoint.h137
-rw-r--r--indra/newview/llviewerjointattachment.cpp381
-rw-r--r--indra/newview/llviewerjointattachment.h92
-rw-r--r--indra/newview/llviewerjointmesh.cpp1601
-rw-r--r--indra/newview/llviewerjointmesh.h138
-rw-r--r--indra/newview/llviewerkeyboard.cpp833
-rw-r--r--indra/newview/llviewerkeyboard.h71
-rw-r--r--indra/newview/llviewerlayer.cpp82
-rw-r--r--indra/newview/llviewerlayer.h30
-rw-r--r--indra/newview/llviewermenu.cpp8810
-rw-r--r--indra/newview/llviewermenu.h174
-rw-r--r--indra/newview/llviewermessage.cpp5017
-rw-r--r--indra/newview/llviewermessage.h203
-rw-r--r--indra/newview/llviewernetwork.cpp70
-rw-r--r--indra/newview/llviewernetwork.h52
-rw-r--r--indra/newview/llviewerobject.cpp4683
-rw-r--r--indra/newview/llviewerobject.h626
-rw-r--r--indra/newview/llviewerobjectlist.cpp1484
-rw-r--r--indra/newview/llviewerobjectlist.h256
-rw-r--r--indra/newview/llviewerparcelmgr.cpp2568
-rw-r--r--indra/newview/llviewerparcelmgr.h327
-rw-r--r--indra/newview/llviewerparceloverlay.cpp850
-rw-r--r--indra/newview/llviewerparceloverlay.h97
-rw-r--r--indra/newview/llviewerpartsim.cpp626
-rw-r--r--indra/newview/llviewerpartsim.h141
-rw-r--r--indra/newview/llviewerpartsource.cpp719
-rw-r--r--indra/newview/llviewerpartsource.h188
-rw-r--r--indra/newview/llviewerprecompiledheaders.cpp16
-rw-r--r--indra/newview/llviewerprecompiledheaders.h218
-rw-r--r--indra/newview/llviewerregion.cpp1301
-rw-r--r--indra/newview/llviewerregion.h348
-rw-r--r--indra/newview/llviewerstats.cpp301
-rw-r--r--indra/newview/llviewerstats.h167
-rw-r--r--indra/newview/llviewertexteditor.cpp1475
-rw-r--r--indra/newview/llviewertexteditor.h102
-rw-r--r--indra/newview/llviewertextureanim.cpp191
-rw-r--r--indra/newview/llviewertextureanim.h33
-rw-r--r--indra/newview/llviewerthrottle.cpp313
-rw-r--r--indra/newview/llviewerthrottle.h72
-rw-r--r--indra/newview/llviewervisualparam.cpp132
-rw-r--r--indra/newview/llviewervisualparam.h83
-rw-r--r--indra/newview/llviewerwindow.cpp4816
-rw-r--r--indra/newview/llviewerwindow.h391
-rw-r--r--indra/newview/llvlcomposition.cpp456
-rw-r--r--indra/newview/llvlcomposition.h67
-rw-r--r--indra/newview/llvlmanager.cpp147
-rw-r--r--indra/newview/llvlmanager.h61
-rw-r--r--indra/newview/llvoavatar.cpp9426
-rw-r--r--indra/newview/llvoavatar.h897
-rw-r--r--indra/newview/llvocache.cpp134
-rw-r--r--indra/newview/llvocache.h51
-rw-r--r--indra/newview/llvoclouds.cpp188
-rw-r--r--indra/newview/llvoclouds.h48
-rw-r--r--indra/newview/llvograss.cpp499
-rw-r--r--indra/newview/llvograss.h84
-rw-r--r--indra/newview/llvoground.cpp165
-rw-r--r--indra/newview/llvoground.h36
-rw-r--r--indra/newview/llvoinventorylistener.cpp45
-rw-r--r--indra/newview/llvoinventorylistener.h43
-rw-r--r--indra/newview/llvopartgroup.cpp266
-rw-r--r--indra/newview/llvopartgroup.h42
-rw-r--r--indra/newview/llvosky.cpp2429
-rw-r--r--indra/newview/llvosky.h907
-rw-r--r--indra/newview/llvosurfacepatch.cpp924
-rw-r--r--indra/newview/llvosurfacepatch.h93
-rw-r--r--indra/newview/llvotextbubble.cpp204
-rw-r--r--indra/newview/llvotextbubble.h37
-rw-r--r--indra/newview/llvotree.cpp906
-rw-r--r--indra/newview/llvotree.h118
-rw-r--r--indra/newview/llvotreenew.h200
-rw-r--r--indra/newview/llvovolume.cpp2027
-rw-r--r--indra/newview/llvovolume.h213
-rw-r--r--indra/newview/llvowater.cpp1028
-rw-r--r--indra/newview/llvowater.h240
-rw-r--r--indra/newview/llwearable.cpp952
-rw-r--r--indra/newview/llwearable.h119
-rw-r--r--indra/newview/llwearablelist.cpp325
-rw-r--r--indra/newview/llwearablelist.h50
-rw-r--r--indra/newview/llweb.cpp69
-rw-r--r--indra/newview/llweb.h31
-rw-r--r--indra/newview/llwind.cpp340
-rw-r--r--indra/newview/llwind.h50
-rw-r--r--indra/newview/llwindebug.cpp254
-rw-r--r--indra/newview/llwindebug.h24
-rw-r--r--indra/newview/llworld.cpp1141
-rw-r--r--indra/newview/llworld.h178
-rw-r--r--indra/newview/llworldmap.cpp915
-rw-r--r--indra/newview/llworldmap.h192
-rw-r--r--indra/newview/llworldmapview.cpp1940
-rw-r--r--indra/newview/llworldmapview.h173
-rw-r--r--indra/newview/llxmlrpctransaction.cpp577
-rw-r--r--indra/newview/llxmlrpctransaction.h115
-rw-r--r--indra/newview/macmain.h26
-rw-r--r--indra/newview/macutil_Prefix.h18
-rw-r--r--indra/newview/macview.r123
-rw-r--r--indra/newview/macview_Prefix.h221
-rw-r--r--indra/newview/noise.cpp66
-rw-r--r--indra/newview/noise.h334
-rw-r--r--indra/newview/pipeline.cpp5316
-rw-r--r--indra/newview/pipeline.h659
-rw-r--r--indra/newview/res-sdl/arrow.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/arrowcop.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/arrowcopmulti.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/arrowdrag.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/circleandline.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/cross.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/hand.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/ibeam.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/ll_icon.BMPbin0 -> 5174 bytes
-rw-r--r--indra/newview/res-sdl/llarrow.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llarrowdrag.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llarrowdragmulti.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/llarrowlocked.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llgrablocked.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llno.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llnolocked.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolcamera.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolcreate.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolfocus.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolgrab.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolland.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolpan.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolpipette.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/lltoolrotate.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolscale.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltooltranslate.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolzoomin.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolzoomout.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizenesw.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizens.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizenwse.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizewe.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolbuy.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/toolopen.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/toolpickobject.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolpickobject2.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolpickobject3.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolsit.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/wait.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/working.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res/arrow.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/arrowcop.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/arrowcopmulti.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/arrowdrag.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/bitmap2.bmpbin0 -> 25118 bytes
-rw-r--r--indra/newview/res/circleandline.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/install_icon.BMPbin0 -> 262198 bytes
-rw-r--r--indra/newview/res/ll_icon.BMPbin0 -> 262198 bytes
-rw-r--r--indra/newview/res/ll_icon.icobin0 -> 364590 bytes
-rw-r--r--indra/newview/res/llarrow.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llarrowdrag.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llarrowdragmulti.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llarrowlocked.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llgrablocked.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llno.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llnolocked.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolcamera.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolcreate.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolfocus.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolgrab.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolland.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolpan.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolpipette.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolrotate.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolscale.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltooltranslate.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolzoomin.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolzoomout.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/loginbackground.bmpbin0 -> 336054 bytes
-rw-r--r--indra/newview/res/resource.h159
-rw-r--r--indra/newview/res/toolbuy.curbin0 -> 2238 bytes
-rw-r--r--indra/newview/res/toolopen.curbin0 -> 2238 bytes
-rw-r--r--indra/newview/res/toolpickobject.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolpickobject2.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolpickobject3.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolpipette.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolsit.curbin0 -> 2238 bytes
-rw-r--r--indra/newview/res/uninstall_icon.BMPbin0 -> 262198 bytes
-rw-r--r--indra/newview/secondlife.icnsbin0 -> 115923 bytes
-rw-r--r--indra/newview/skins/paths.xml4
641 files changed, 253714 insertions, 0 deletions
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..8cbe635155
--- /dev/null
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -0,0 +1,5 @@
+/* Localized versions of Info.plist keys */
+
+CFBundleName = "Second Life";
+CFBundleShortVersionString = "Second Life version 1.13.1.6";
+CFBundleGetInfoString = "Second Life version 1.13.1.6, Copyright 2004-2006 Linden Research, Inc.";
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
new file mode 100644
index 0000000000..c2ec0313af
--- /dev/null
+++ b/indra/newview/Info-SecondLife.plist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>Second Life</string>
+ <key>CFBundleIconFile</key>
+ <string>secondlife.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.secondlife.indra.viewer</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>Second Life</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleURLTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>Second Life URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>secondlife</string>
+ </array>
+ <key>LSIsAppleDefaultForScheme</key>
+ <true/>
+ </dict>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>1.13.1.6</string>
+ <key>CSResourcesFileMapped</key>
+ <true/>
+</dict>
+</plist>
diff --git a/indra/newview/Info-SecondLifeVorbis.plist b/indra/newview/Info-SecondLifeVorbis.plist
new file mode 100644
index 0000000000..9cb367eec1
--- /dev/null
+++ b/indra/newview/Info-SecondLifeVorbis.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>SecondLifeVorbis</string>
+ <key>CFBundleGetInfoString</key>
+ <string></string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.secondlife.indra.vorbis</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>SecondLifeVorbis</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string></string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>0.0.1d1</string>
+</dict>
+</plist>
diff --git a/indra/newview/SecondLife.nib/classes.nib b/indra/newview/SecondLife.nib/classes.nib
new file mode 100644
index 0000000000..ea58db1189
--- /dev/null
+++ b/indra/newview/SecondLife.nib/classes.nib
@@ -0,0 +1,4 @@
+{
+IBClasses = ();
+IBVersion = 1;
+}
diff --git a/indra/newview/SecondLife.nib/info.nib b/indra/newview/SecondLife.nib/info.nib
new file mode 100644
index 0000000000..1b531de104
--- /dev/null
+++ b/indra/newview/SecondLife.nib/info.nib
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>85 13 356 240 0 0 1280 1002 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>27 314 247 44 0 0 1280 1002 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>362.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>191</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>7D24</string>
+ <key>targetFramework</key>
+ <string>IBCarbonFramework</string>
+</dict>
+</plist>
diff --git a/indra/newview/SecondLife.nib/objects.xib b/indra/newview/SecondLife.nib/objects.xib
new file mode 100644
index 0000000000..b7ff30f2b2
--- /dev/null
+++ b/indra/newview/SecondLife.nib/objects.xib
@@ -0,0 +1,259 @@
+<?xml version="1.0" standalone="yes"?>
+<object class="NSIBObjectData">
+ <string name="targetFramework">IBCarbonFramework</string>
+ <object name="rootObject" class="NSCustomObject" id="1">
+ <string name="customClass">NSApplication</string>
+ </object>
+ <array count="31" name="allObjects">
+ <object class="IBCarbonMenu" id="29">
+ <string name="title">SecondLife</string>
+ <array count="4" name="items">
+ <object class="IBCarbonMenuItem" id="182">
+ <string name="title">Second Life</string>
+ <object name="submenu" class="IBCarbonMenu" id="181">
+ <string name="title">Second Life</string>
+ <array count="1" name="items">
+ <object class="IBCarbonMenuItem" id="183">
+ <boolean name="disabled">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">About Second Life</string>
+ <int name="keyEquivalentModifier">0</int>
+ <ostype name="command">abou</ostype>
+ </object>
+ </array>
+ <string name="name">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="IBCarbonMenuItem" id="127">
+ <string name="title">File</string>
+ <object name="submenu" class="IBCarbonMenu" id="131">
+ <string name="title">File</string>
+ </object>
+ </object>
+ <object class="IBCarbonMenuItem" id="152">
+ <string name="title">Edit</string>
+ <object name="submenu" class="IBCarbonMenu" id="147">
+ <string name="title">Edit</string>
+ <array count="10" name="items">
+ <object class="IBCarbonMenuItem" id="141">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Undo</string>
+ <string name="keyEquivalent">z</string>
+ <ostype name="command">undo</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="146">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Redo</string>
+ <string name="keyEquivalent">Z</string>
+ <ostype name="command">redo</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="142">
+ <boolean name="separator">TRUE</boolean>
+ </object>
+ <object class="IBCarbonMenuItem" id="143">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Cut</string>
+ <string name="keyEquivalent">x</string>
+ <ostype name="command">cut </ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="149">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Copy</string>
+ <string name="keyEquivalent">c</string>
+ <ostype name="command">copy</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="144">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Paste</string>
+ <string name="keyEquivalent">v</string>
+ <ostype name="command">past</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="151">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Delete</string>
+ <ostype name="command">clea</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="148">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Select All</string>
+ <string name="keyEquivalent">a</string>
+ <ostype name="command">sall</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="188">
+ <boolean name="separator">TRUE</boolean>
+ </object>
+ <object class="IBCarbonMenuItem" id="187">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Special Characters…</string>
+ <ostype name="command">chrp</ostype>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="IBCarbonMenuItem" id="153">
+ <string name="title">Window</string>
+ <object name="submenu" class="IBCarbonMenu" id="154">
+ <string name="title">Window</string>
+ <array count="6" name="items">
+ <object class="IBCarbonMenuItem" id="155">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Minimize Window</string>
+ <string name="keyEquivalent">m</string>
+ <ostype name="command">mini</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="184">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Minimize All Windows</string>
+ <string name="keyEquivalent">m</string>
+ <int name="keyEquivalentModifier">1572864</int>
+ <ostype name="command">mina</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="186">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Zoom</string>
+ <ostype name="command">zoom</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="156">
+ <boolean name="separator">TRUE</boolean>
+ </object>
+ <object class="IBCarbonMenuItem" id="157">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Bring All to Front</string>
+ <ostype name="command">bfrt</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="185">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Arrange in Front</string>
+ <int name="keyEquivalentModifier">1572864</int>
+ <ostype name="command">frnt</ostype>
+ </object>
+ </array>
+ <string name="name">_NSWindowsMenu</string>
+ </object>
+ </object>
+ </array>
+ <string name="name">_NSMainMenu</string>
+ </object>
+ <reference idRef="127"/>
+ <reference idRef="131"/>
+ <reference idRef="141"/>
+ <reference idRef="142"/>
+ <reference idRef="143"/>
+ <reference idRef="144"/>
+ <reference idRef="146"/>
+ <reference idRef="147"/>
+ <reference idRef="148"/>
+ <reference idRef="149"/>
+ <reference idRef="151"/>
+ <reference idRef="152"/>
+ <reference idRef="153"/>
+ <reference idRef="154"/>
+ <reference idRef="155"/>
+ <reference idRef="156"/>
+ <reference idRef="157"/>
+ <reference idRef="181"/>
+ <reference idRef="182"/>
+ <reference idRef="183"/>
+ <reference idRef="184"/>
+ <reference idRef="185"/>
+ <reference idRef="186"/>
+ <reference idRef="187"/>
+ <reference idRef="188"/>
+ <object class="IBCarbonRootControl" id="190">
+ <string name="bounds">0 0 482 694 </string>
+ <string name="viewFrame">0 0 694 482 </string>
+ <array count="2" name="subviews">
+ <object class="IBCarbonButton" id="192">
+ <string name="bounds">442 604 462 674 </string>
+ <string name="viewFrame">604 442 70 20 </string>
+ <string name="title">OK</string>
+ <ostype name="command">ok </ostype>
+ <object name="layoutInfo" class="IBCarbonHILayoutInfo">
+ <int name="bindingBottomKind">2</int>
+ <int name="bindingRightKind">2</int>
+ </object>
+ <int name="buttonType">1</int>
+ </object>
+ <object class="IBCarbonScrollView" id="201">
+ <string name="bounds">20 20 422 674 </string>
+ <string name="viewFrame">20 20 654 402 </string>
+ <array count="1" name="subviews">
+ <object class="IBCarbonTextView" id="200">
+ <string name="bounds">20 20 422 659 </string>
+ <string name="viewFrame">0 0 639 402 </string>
+ <ostype name="controlSignature">text</ostype>
+ <int name="fontStyle">5</int>
+ <boolean name="readOnly">TRUE</boolean>
+ </object>
+ </array>
+ <boolean name="scrollHorizontally">FALSE</boolean>
+ </object>
+ </array>
+ </object>
+ <object class="IBCarbonWindow" id="191">
+ <string name="windowRect">84 72 566 766 </string>
+ <string name="title">Release Notes</string>
+ <reference name="rootControl" idRef="190"/>
+ <boolean name="receiveUpdates">FALSE</boolean>
+ <boolean name="hasCloseBox">FALSE</boolean>
+ <boolean name="hasCollapseBox">FALSE</boolean>
+ <boolean name="hasHorizontalZoom">FALSE</boolean>
+ <boolean name="isResizable">FALSE</boolean>
+ <boolean name="hasVerticalZoom">FALSE</boolean>
+ <boolean name="liveResize">TRUE</boolean>
+ <boolean name="compositing">TRUE</boolean>
+ <int name="carbonWindowClass">4</int>
+ <int name="windowPosition">1</int>
+ <boolean name="isConstrained">FALSE</boolean>
+ </object>
+ <reference idRef="192"/>
+ <reference idRef="200"/>
+ <reference idRef="201"/>
+ </array>
+ <array count="31" name="allParents">
+ <reference idRef="1"/>
+ <reference idRef="29"/>
+ <reference idRef="127"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="152"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="29"/>
+ <reference idRef="29"/>
+ <reference idRef="153"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="182"/>
+ <reference idRef="29"/>
+ <reference idRef="181"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="191"/>
+ <reference idRef="1"/>
+ <reference idRef="190"/>
+ <reference idRef="201"/>
+ <reference idRef="190"/>
+ </array>
+ <dictionary count="3" name="nameTable">
+ <string>Files Owner</string>
+ <reference idRef="1"/>
+ <string>MenuBar</string>
+ <reference idRef="29"/>
+ <string>Release Notes</string>
+ <reference idRef="191"/>
+ </dictionary>
+ <unsigned_int name="nextObjectID">202</unsigned_int>
+</object>
diff --git a/indra/newview/VertexCache.h b/indra/newview/VertexCache.h
new file mode 100644
index 0000000000..e2b26ec5ee
--- /dev/null
+++ b/indra/newview/VertexCache.h
@@ -0,0 +1,87 @@
+/**
+ * @file VertexCache.h
+ * @brief VertexCache class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef VERTEX_CACHE_H
+
+#define VERTEX_CACHE_H
+
+class VertexCache
+{
+
+public:
+
+ VertexCache(int size)
+ {
+ numEntries = size;
+
+ entries = new int[numEntries];
+
+ for(int i = 0; i < numEntries; i++)
+ entries[i] = -1;
+ }
+
+ VertexCache() { VertexCache(16); }
+ ~VertexCache() { delete[] entries; entries = 0; }
+
+ bool InCache(int entry)
+ {
+ bool returnVal = false;
+ for(int i = 0; i < numEntries; i++)
+ {
+ if(entries[i] == entry)
+ {
+ returnVal = true;
+ break;
+ }
+ }
+
+ return returnVal;
+ }
+
+ int AddEntry(int entry)
+ {
+ int removed;
+
+ removed = entries[numEntries - 1];
+
+ //push everything right one
+ for(int i = numEntries - 2; i >= 0; i--)
+ {
+ entries[i + 1] = entries[i];
+ }
+
+ entries[0] = entry;
+
+ return removed;
+ }
+
+ void Clear()
+ {
+ memset(entries, -1, sizeof(int) * numEntries);
+ }
+
+ void Copy(VertexCache* inVcache)
+ {
+ for(int i = 0; i < numEntries; i++)
+ {
+ inVcache->Set(i, entries[i]);
+ }
+ }
+
+ int At(int index) { return entries[index]; }
+ void Set(int index, int value) { entries[index] = value; }
+
+private:
+
+ int *entries;
+ int numEntries;
+
+};
+
+#endif
diff --git a/indra/newview/VorbisFramework.h b/indra/newview/VorbisFramework.h
new file mode 100644
index 0000000000..8d98979d1b
--- /dev/null
+++ b/indra/newview/VorbisFramework.h
@@ -0,0 +1,62 @@
+/**
+ * @file VorbisFramework.h
+ * @author Dave Camp
+ * @date Fri Oct 10 2003
+ * @brief For the Macview project
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ogg/ogg.h"
+#include "vorbis/codec.h"
+#include "vorbis/vorbisenc.h"
+
+extern int mac_vorbis_analysis(vorbis_block *vb,ogg_packet *op);
+
+extern int mac_vorbis_analysis_headerout(vorbis_dsp_state *v,
+ vorbis_comment *vc,
+ ogg_packet *op,
+ ogg_packet *op_comm,
+ ogg_packet *op_code);
+
+extern int mac_vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi);
+
+extern int mac_vorbis_encode_ctl(vorbis_info *vi,int number,void *arg);
+
+extern int mac_vorbis_encode_setup_init(vorbis_info *vi);
+
+extern int mac_vorbis_encode_setup_managed(vorbis_info *vi,
+ long channels,
+ long rate,
+
+ long max_bitrate,
+ long nominal_bitrate,
+ long min_bitrate);
+
+extern void mac_vorbis_info_init(vorbis_info *vi);
+extern void mac_vorbis_info_clear(vorbis_info *vi);
+extern void mac_vorbis_comment_init(vorbis_comment *vc);
+extern void mac_vorbis_comment_clear(vorbis_comment *vc);
+extern int mac_vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb);
+extern int mac_vorbis_block_clear(vorbis_block *vb);
+extern void mac_vorbis_dsp_clear(vorbis_dsp_state *v);
+extern float **mac_vorbis_analysis_buffer(vorbis_dsp_state *v,int vals);
+extern int mac_vorbis_analysis_wrote(vorbis_dsp_state *v,int vals);
+extern int mac_vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb);
+
+extern int mac_ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op);
+extern int mac_ogg_stream_init(ogg_stream_state *os,int serialno);
+extern int mac_ogg_stream_flush(ogg_stream_state *os, ogg_page *og);
+extern int mac_ogg_stream_pageout(ogg_stream_state *os, ogg_page *og);
+extern int mac_ogg_page_eos(ogg_page *og);
+extern int mac_ogg_stream_clear(ogg_stream_state *os);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/indra/newview/app_settings/CA.pem b/indra/newview/app_settings/CA.pem
new file mode 100644
index 0000000000..b14e7346ad
--- /dev/null
+++ b/indra/newview/app_settings/CA.pem
@@ -0,0 +1,1811 @@
+-----BEGIN CERTIFICATE-----
+MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy
+MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG
+A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt
+YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD
+VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA
+isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj
+Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50
+QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt
+bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR
+yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID
+AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0
+cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f
+BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj
+cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1
+U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl
+YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos
+SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/
+t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u
+mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb
+K+9A46sd33oqK8n8
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIRANAeQJAAAEZSAAAAAQAAAAQwDQYJKoZIhvcNAQEF
+BQAwgYkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJEQzETMBEGA1UEBxMKV2Fz
+aGluZ3RvbjEXMBUGA1UEChMOQUJBLkVDT00sIElOQy4xGTAXBgNVBAMTEEFC
+QS5FQ09NIFJvb3QgQ0ExJDAiBgkqhkiG9w0BCQEWFWFkbWluQGRpZ3NpZ3Ry
+dXN0LmNvbTAeFw05OTA3MTIxNzMzNTNaFw0wOTA3MDkxNzMzNTNaMIGJMQsw
+CQYDVQQGEwJVUzELMAkGA1UECBMCREMxEzARBgNVBAcTCldhc2hpbmd0b24x
+FzAVBgNVBAoTDkFCQS5FQ09NLCBJTkMuMRkwFwYDVQQDExBBQkEuRUNPTSBS
+b290IENBMSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBkaWdzaWd0cnVzdC5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx0xHgeVVDBwhMywVC
+AOINg0Y95JO6tgbTDVm9PsHOQ2cBiiGo77zM0KLMsFWWU4RmBQDaREmA2FQK
+pSWGlO1jVv9wbKOhGdJ4vmgqRF4vz8wYXke8OrFGPR7wuSw0X4x8TAgpnUBV
+6zx9g9618PeKgw6hTLQ6pbNfWiKX7BmbwQVo/ea3qZGULOR4SCQaJRk665Wc
+OQqKz0Ky8BzVX/tr7WhWezkscjiw7pOp03t3POtxA6k4ShZsiSrK2jMTecJV
+jO2cu/LLWxD4LmE1xilMKtAqY9FlWbT4zfn0AIS2V0KFnTKo+SpU+/94Qby9
+cSj0u5C8/5Y0BONFnqFGKECBAgMBAAGjFjAUMBIGA1UdEwEB/wQIMAYBAf8C
+AQgwDQYJKoZIhvcNAQEFBQADggEBAARvJYbk5pYntNlCwNDJALF/VD6Hsm0k
+qS8Kfv2kRLD4VAe9G52dyntQJHsRW0mjpr8SdNWJt7cvmGQlFLdh6X9ggGvT
+ZOirvRrWUfrAtF13Gn9kCF55xgVM8XrdTX3O5kh7VNJhkoHWG9YA8A6eKHeg
+TYjHInYZw8eeG6Z3ePhfm1bR8PIXrI6dWeYf/le22V7hXZ9F7GFoGUHhsiAm
+/lowdiT/QHI8eZ98IkirRs3bs4Ysj78FQdPB4xTjQRcm0HyncUwZ6EoPclgx
+fexgeqMiKL0ZJGA/O4dzwGvky663qyVDslUte6sGDnVdNOVdc22esnVApVnJ
+TzFxiNmIf1Q=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
+VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
+bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyOTA2MDAw
+MFoXDTM3MTEyMDE1MDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
+T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
+SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAJnej8Mlo2k06AX3dLm/WpcZuS+U0pPlLYnKhHw/EEMbjIt8hFj4JHxI
+zyr9wBXZGH6EGhfT257XyuTZ16pYUYfw8ItITuLCxFlpMGK2MKKMCxGZYTVt
+fu/FsRkGIBKOQuHfD5YQUqjPnF+VFNivO3ULMSAfRC+iYkGzuxgh28pxPIzs
+trkNn+9R7017EvILDOGsQI93f7DKeHEMXRZxcKLXwjqFzQ6axOAAsNUl6twr
+5JQtOJyJQVdkKGUZHLZEtMgxa44Be3ZZJX8VHIQIfHNlIAqhBC4aMqiaILGc
+LCFZ5/vP7nAtCMpjPiybkxlqpMKX/7eGV4iFbJ4VFitNLLMCAwEAAaNjMGEw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoTYwFsuGkABFgFOxj8jYPXy+
+XxIwHwYDVR0jBBgwFoAUoTYwFsuGkABFgFOxj8jYPXy+XxIwDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQCKIBilvrMvtKaEAEAwKfq0FHNM
+eUWn9nDg6H5kHgqVfGphwu9OH77/yZkfB2FK4V1Mza3u0FIy2VkyvNp5ctZ7
+CegCgTXTCt8RHcl5oIBN/lrXVtbtDyqvpxh1MwzqwWEFT2qaifKNuZ8u77Bf
+WgDrvq2g+EQFZ7zLBO+eZMXpyD8Fv8YvBxzDNnGGyjhmSs3WuEvGbKeXO/oT
+LW4jYYehY0KswsuXn2Fozy1MBJ3XJU8KDk2QixhWqJNIV9xvrr2eZ1d3iVCz
+vhGbRWeDhhmH05i9CBoWH1iCC+GWaQVLjuyDUTEH1dSf/1l7qG6Fz9NLqUmw
+X7A5KGgOc90lmt4S
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
+VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
+bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw
+MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
+T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
+SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs
+RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs
+i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg
+/BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ
+2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0
+ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X
++Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml
+J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh
+EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo
+Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ
+Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex
+MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB
+Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA
+FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
+9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0
+cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF
+ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY
+vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/
+drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la
+5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks
+mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5
+O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD
+062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41
+xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H
+hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL
+Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4
+dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5h
+bCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzEL
+MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1B
+ZGRUcnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1
+c3QgRXh0ZXJuYWwgQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALf3GjPm8gAELTngTlvtH7xsD821+iO2zt6bETOXpClMfZOfvUq8
+k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfwTz/oMp50
+ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504
+B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDez
+eWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5
+aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0WicCAwEAAaOB
+3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0PBAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6
+xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdv
+cmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJ
+KoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
+j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5R
+xNKWt9x+Tu5w/Rw56wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjT
+K3rMUUKhemPR5ruhxSvCNr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1
+n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHx
+REzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49O
+hgQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw
+HhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwze
+xODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY654eyNAbFvAWlA3yCyykQruGI
+gb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWroulpOj0O
+M3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1Lc
+sRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5
+mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG
+9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0OBBYEFJWxtPCU
+tr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
+MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQsw
+CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
+ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAx
+IENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0
+MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
+iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9
+tTEv2dB8Xfjea4MYeDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL
+/bscVjby/rK25Xa71SJlpz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlV
+g3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6
+tkD9xOQ14R0WHNC8K47Wcdk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAe
+Fw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNF
+MRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
+IE5ldHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+
+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c
++OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1id9NEHif2
+P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKX
+C1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8R
+s3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9
+BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQWBBSBPjfYkrAf
+d59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zCB
+jgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkG
+A1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
+cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmu
+G7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbj
+PGsye/Kf8Lb93/AoGEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bY
+GozH7ZxOmuASu7VqTITh4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6
+NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9HEufOX1362Kqx
+My3ZdvJOOjMMK7MtkAY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9v
+dDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYT
+AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3Qg
+VFRQIE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBS
+b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoek
+n0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKk
+IhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3KP0q6p6z
+sLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1t
+UvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R
++Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvES
+a0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0GA1UdDgQWBBQ5
+lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkw
+ZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
+ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVh
+bGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2Vh
+lRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
+GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx9
+5dr6h+sNNVJn0J6XdgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKF
+Yqa0p9m9N5xotS1WfbC3P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVA
+wRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQw
+dOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l
+cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4X
+DTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMx
+HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp
+Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCa
+xlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CGv2BlnEtUiMJIxUo5vxTjWVXl
+GbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44zDyL9Hy7n
+BzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145Lcx
+VR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiE
+mf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCu
+JKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Zo/Z5
+9m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUA
+A4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
+Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOM
+IOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTI
+dGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g
+Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j
+8uB9Gr784N/Xx6dssPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l
+cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4X
+DTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMx
+HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp
+Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssN
+t79Hc9PwVU3dxgz6sWYFas14tNwC206B89enfHG8dWOgXeMHDEjsJcQDIPT/
+DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8f3SkWq7x
+uhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE
+18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxr
+kJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMD
+bi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8BPeraunzgWGcX
+uVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn6KVu
+Y8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9
+W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
+o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48
+ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124Hhn
+AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op
+aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNee
+MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypL
+M7PmG2tZTiLMubekJcmnxPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qf
+tIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjR
+Ywu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R
++FXgJXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr
++CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVM
+nNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMADjMSW7yV5TKQqLPGbIOt
+d+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh1NolNscI
+WC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZ
+ZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y
+3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz
+2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
+RY8mkaKO/qk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG
+EwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0
+MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUx
+MjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNV
+BAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZ
+QmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+h
+Xe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gR
+QKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1
+pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNT
+Px8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkC
+AwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1BE3wMBIGA1Ud
+EwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA
+A4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkT
+I7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/
+oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67
+G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
+RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG
+EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
+DwYDVQQLEwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQw
+MjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy
+ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEB
+AQUAA4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlR
+EmlvMVW5SXIACH7TpWJENySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+Lth
+zfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2io74CTADKAqjuAQIxZA9SLRN0
+dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E
+YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg
+U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIx
+MDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5fpFpRhgTCgJ3
+pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAMBgNV
+HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3
+DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN
+QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomA
+sH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6
+w4pl
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGp
+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM
+YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv
+LjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDEx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDEx
+ODE4NTVaFw0wODExMjgxODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE
+CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp
+Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDEx
+FjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0BCQEWEmNhQGRp
+Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdfWvnTLnUv2Chi0ZMv/E3U
+q4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uKxBmd9LIO/BZsubEF
+koPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBEzUNKcI5YhZXh
+TizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F5X5yP4Wd
+lGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMvOnNn
+7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+
+LegzZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvV
+WlHG4VMElo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX
+8ngvYzZAOONGDx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn8
+6Oawde3uPclwx12qgUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsT
+F7ANUkz+/m9c4pFuHf2kYtdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG
+EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
+DwYDVQQLEwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3
+MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy
+ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEB
+AQUAA4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fB
+w18DW9Fvrn5C6mYjuGODVvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87e
+ZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd
+55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E
+YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg
+U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIw
+OTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6CTShlgDzJQW6s
+NS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAMBgNV
+HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3
+DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR
+xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLb
+dHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlih
+w6ID
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGp
+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM
+YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv
+LjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAy
+MjQ2MTZaFw0wODExMjcyMjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE
+CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp
+Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDIx
+FjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0BCQEWEmNhQGRp
+Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbVp9oaBBg5kkp4o4HC9Xd6
+ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWwBZoPFflrWXJW8vo5
+/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl5WJp3OXuAFK9
+MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi3sOP17ih
+YqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+QVCv
+bK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWog
+WxyQ2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6
+HE3K1GjNI3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV
+6YyDfFk/xPEL553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8
+PzGn0EdzMzkbzE5q10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30
+sPDst2yC7S8xmUJMqbINuBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEgzCCA+ygAwIBAgIEOJ725DANBgkqhkiG9w0BAQQFADCBtDEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9HQ0NB
+X0NQUyBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
+HChjKSAyMDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
+c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMDAy
+MDcxNjE2NDBaFw0yMDAyMDcxNjQ2NDBaMIG0MRQwEgYDVQQKEwtFbnRydXN0
+Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0dDQ0FfQ1BTIGluY29y
+cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDIwMDAg
+RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xp
+ZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCTdLS25MVL1qFof2LV7PdRV7NySpj10InJrWPNTTVRaoTU
+rcloeW+46xHbh65cJFET8VQlhK8pK5/jgOLZy93GRUk0iJBeAZfv6lOm3fzB
+3ksqJeTpNfpVBQbliXrqpBFXO/x8PTbNZzVtpKklWb1m9fkn5JVn1j+SgF7y
+NH0rhQIDAQABo4IBnjCCAZowEQYJYIZIAYb4QgEBBAQDAgAHMIHdBgNVHR8E
+gdUwgdIwgc+ggcyggcmkgcYwgcMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUAw
+PgYDVQQLFDd3d3cuZW50cnVzdC5uZXQvR0NDQV9DUFMgaW5jb3JwLiBieSBy
+ZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMjAwMCBFbnRydXN0
+Lm5ldCBMaW1pdGVkMTMwMQYDVQQDEypFbnRydXN0Lm5ldCBDbGllbnQgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
+IoAPMjAwMDAyMDcxNjE2NDBagQ8yMDIwMDIwNzE2NDY0MFowCwYDVR0PBAQD
+AgEGMB8GA1UdIwQYMBaAFISLdP3FjcD/J20gN0V8/i3OutN9MB0GA1UdDgQW
+BBSEi3T9xY3A/ydtIDdFfP4tzrrTfTAMBgNVHRMEBTADAQH/MB0GCSqGSIb2
+fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQQFAAOBgQBObzWA
+O9GK9Q6nIMstZVXQkvTnhLUGJoMShAusO7JE7r3PQNsgDrpuFOow4DtifH+L
+a3xKp9U1PL6oXOpLu5OOgGarDyn9TS2/GpsKkMWr2tGzhtQvJFJcem3G8v7l
+TRowjJDyutdKPkN+1MhQGof4T4HHdguEOnKdzmVml64mXg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIElTCCA/6gAwIBAgIEOJsRPDANBgkqhkiG9w0BAQQFADCBujEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxPzA9BgNVBAsUNnd3dy5lbnRydXN0Lm5ldC9TU0xf
+Q1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDIwMDAgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVz
+dC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw0wMDAyMDQxNzIwMDBaFw0yMDAyMDQxNzUwMDBaMIG6MRQwEgYDVQQKEwtF
+bnRydXN0Lm5ldDE/MD0GA1UECxQ2d3d3LmVudHJ1c3QubmV0L1NTTF9DUFMg
+aW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykg
+MjAwMCBFbnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5l
+dCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0G
+CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwV9OcfHO8GCGD9JYf9Mzly0XonUw
+tZZkJi9ow0SrqHXmAGc0V55lxyKbc+bT3QgON1WqJUaBbL3+qPZ1V1eMkGxK
+wz6LS0MKyRFWmponIpnPVZ5h2QLifLZ8OAfc439PmrkDQYC2dWcTC5/oVzbI
+XQA23mYU2m52H083jIITiQIDAQABo4IBpDCCAaAwEQYJYIZIAYb4QgEBBAQD
+AgAHMIHjBgNVHR8EgdswgdgwgdWggdKggc+kgcwwgckxFDASBgNVBAoTC0Vu
+dHJ1c3QubmV0MT8wPQYDVQQLFDZ3d3cuZW50cnVzdC5uZXQvU1NMX0NQUyBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAy
+MDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxOjA4BgNVBAMTMUVudHJ1c3QubmV0
+IFNlY3VyZSBTZXJ2ZXIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMDAyMDQxNzIwMDBagQ8yMDIwMDIw
+NDE3NTAwMFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFMtswGvjuz7L/CKc
+/vuLkpyw8m4iMB0GA1UdDgQWBBTLbMBr47s+y/winP77i5KcsPJuIjAMBgNV
+HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkq
+hkiG9w0BAQQFAAOBgQBi24GRzsiad0Iv7L0no1MPUBvqTpLwqa+poLpIYcvv
+yQbvH9X07t9WLebKahlzqlO+krNQAraFJnJj2HVQYnUUt7NQGj/KEQALhUVp
+bbalrlHhStyCP2yMNLJ3a9kC9n8O6mUE8c1UyrrJzOCE98g+EZfTYAkYvAX/
+bIkz8OwVDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNf
+MjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
+HChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
+c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEy
+MjQxNzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0
+Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29y
+cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkg
+RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4
+QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/EC
+DNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj
+/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLP
+KQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZd
+enoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
+4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB
+0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJ
+FrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B
+AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFh
+fGPjK50xA3B20qMooPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVU
+KcgF7bISKo30Axv/55IQh7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaoho
+wXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2
++z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof888
+6ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UE
+BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50
+cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBs
+aW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExp
+bWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBa
+MIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNV
+BAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3Jw
+LiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50
+cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GL
+ADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSv
+x1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHV
+iTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173
+iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkw
+ggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50
+cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0Ff
+SW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UE
+CxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50
+cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYD
+VQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9D
+bGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkx
+MDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW
+/O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwG
+A1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
+hvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQ
+OokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkU
+ODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapE
+PHayXOw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UE
+BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50
+cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UE
+AxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQsw
+CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3
+dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlh
+Yi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVkMTow
+OAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
+b24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0
+VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHIN
+iC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3wkrYKZImZNHk
+mGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcwggHT
+MBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHY
+pIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
+BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChs
+aW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBM
+aW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNo
+dHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAi
+gA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMC
+AQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYE
+FPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9
+B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKn
+CqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2Zcgx
+xufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd2cNgQ4xYDiKWL2KjLB+6
+rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG
+EwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1
+cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4
+MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgx
+LTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0
+eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2R
+FGiYCh7+2gRvE4RiIcPRfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO
+/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuv
+K9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAGA1UdHwRp
+MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEt
+MCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gjIBBPM5iQn9Qw
+HQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMBAf8w
+GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB
+AFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2u
+FHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1
+aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0
+MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoT
+E0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJl
+IEdsb2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAuucXkAJlsTRVPEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQy
+td4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORR
+OhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxnhcXIw2EC
+AwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8w
+HwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6o
+oHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf
+2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkAZ70Br83gcfxa
+z2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIYNMR1
+pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1
+aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcN
+MjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZh
+eCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2lu
+ZXNzIENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fe
+k6lfWg0XTzQaDJj0ItlZ1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5
+/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4aIZX5UkxVWsUPOE9G+m34LjXW
+HXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBkMBEGCWCG
+SAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4
+MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBq
+R3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnm
+JXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+WB5Hh1Q+WKG1
+tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+KpYr
+tWKmpj29f5JZzVoqgrI3eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG
+EwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlm
+YXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5
+MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXgg
+U2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0Et
+MjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF
+7Y6yEb3+6+e0dMKP/wXn2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKD
+pkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HM
+HMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAGA1UdHwRp
+MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBT
+ZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y
+MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBqy/3YIHqngnYw
+HQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMBAf8w
+GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB
+AAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy
+0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkt
+y3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRy
+dXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1
+MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYD
+VQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMT
+GkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4
+ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALn
+ZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8F
+LztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh3
+46B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq
+81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+d
+XIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB+jCCAWMCAgGjMA0GCSqGSIb3DQEBBAUAMEUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xHDAaBgNVBAMTE0dURSBDeWJlclRy
+dXN0IFJvb3QwHhcNOTYwMjIzMjMwMTAwWhcNMDYwMjIzMjM1OTAwWjBFMQsw
+CQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMRwwGgYDVQQD
+ExNHVEUgQ3liZXJUcnVzdCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQC45k+625h8cXyvRLfTD0bZZOWTwUKOx7pJjTUteueLveUFMVnGsS8K
+DPufpz+iCWaEVh43KRuH6X4MypqfpX/1FZSj1aJGgthoTNE3FQZor734sLPw
+KfWVWgkWYXcKIiXUT0Wqx73llt/51KiOQswkwB6RJ0q1bQaAYznEol44AwID
+AQABMA0GCSqGSIb3DQEBBAUAA4GBABKzdcZfHeFhVYAA1IFLezEPI2PnPfMD
++fQ2qLvZ46WXTeorKeDWanOB5sCJo9Px4KWlIjeaY8JIILTbcuPI9tl8vrGv
+U9oUtCG41tWW4/5ODFlitppK+ULdjG+BqXH/9ApybW1EDp3zdHSo1TRJ6V6e
+6bR64eVaH4QwnNOfpSXY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYT
+AlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVz
+dCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBC
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEH
+CIjaWC9mOSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlC
+GDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7
+csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAj
+Nvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdRe
+JivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQAB
+o1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9
+qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1luMrMTjANBgkq
+hkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Qzxpe
+R+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWV
+Yrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
+PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot
+2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeX
+xx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
+Mw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzEL
+MAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNV
+BAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05
+ODA5MDExMjAwMDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkw
+FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRsw
+GQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDaDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR
+4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc
+71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4
+bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgK
+OOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMW
+ea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DP
+AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQUYHtmGkUNl8qJ
+UC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOC
+AQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq75bCd
+PTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Q
+gkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT
+2iHRrH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlD
+NPYPhyk7ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBg
+Hcl5JLL2bP2oZg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NjAwMjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2f
+NUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChM
+MFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34
+t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs3x/b
+e0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0Wu
+PIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
+PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICXDCCAcWgAwIBAgIQCgEBAQAAAnwAAAALAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMTAyNCBWMzAeFw0wMTAyMjIyMTAxNDlaFw0yNjAyMjIyMDAx
+NDlaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAxMDI0IFYzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDV3f5mCc8kPD6ugU5OisRpgFtZO9+5TUzKtS3DJy08rwBCbbwoppbPf9dY
+rIMKo1W1exeQFYRMiu4mmdxY78c4pqqv0I5CyGLXq6yp+0p9v+r+Ek3d/yYt
+bzZUaMjShFbuklNhCbM/OZuoyZu9zp9+1BlqFikYvtc6adwlWzMaUQIDAQAB
+o2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSME
+GDAWgBTEwBykB5T9zU0B1FTapQxf3q4FWjAdBgNVHQ4EFgQUxMAcpAeU/c1N
+AdRU2qUMX96uBVowDQYJKoZIhvcNAQEFBQADgYEAPy1q4yZDlX2Jl2X7deRy
+HUZXxGFraZ8SmyzVWujAovBDleMf6XbN3Ou8k6BlCsdNT1+nr6JGFLkM88y9
+am63nd4lQtBU/55oc2PcJOsiv6hy8l4A4Q1OOkNumU4/iXgDmMrzVcydro7B
+qkWY+o8aoI2II/EVQQ2lRj6RP4vr93E=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5
+MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37
+RqtBaB4Y6lXIL5F4iSj7Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E
+0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J
+6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iHKrtjEAMq
+s6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzD
+uvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2Mw
+YTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAW
+gBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NRMKSq6UWuNST6
+/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmYv/3V
+EhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5g
+EydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
+f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJq
+aHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEk
+llgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
+pKnXwiJPZ9d37CAFYd4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT
+AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD
+VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3
+b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENB
+MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe
+Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE
+ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y
+a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTEp
+MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0yAClxgwENv4wB3NrGrTmk
+qYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDwTFXlay3HhQswHJJO
+gtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8/vhYnvgpjbB7
+zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+W
+LDO/jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xR
+T3h2oNmsGb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/Ac
+ASZ4smZHcFFk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT
+AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD
+VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3
+b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENB
+MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe
+Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE
+ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y
+a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTEp
+MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUFLg2N7KBAahwOJ6ZQkmtQ
+GwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGwDtf7pBc9r6tpepYn
+v68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDWw1Krj10nnGvA
+o+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4
+iJIETb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yC
+GdHHsbHD2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQS
+CdS7kjXvD9s0
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBl
+cnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNp
+Y0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVow
+gcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV
+BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAm
+BgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNV
+BAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZ
+cGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTK
+P1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQ
+fmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8j
+kyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOB
+gQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
+c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95
+B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBl
+cnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m
+cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIz
+NTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUx
+EjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRp
+bmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+JDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqG
+SIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0N
+j3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3Ef
+QDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqY
+x7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
+MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgC
+neSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr
+5PjRzneigQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBl
+cnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXBy
+ZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5
+NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIw
+EAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5n
+MSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMw
+IQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3
+DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw7
+7f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8j
+Hnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuq
+W/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
+b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVx
+eTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1
+KzGJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3Rl
+IFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1
+OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQ
+BgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcg
+Y2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+ITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3
+DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhI
+NTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPL
+lyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN
+9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZ
+a4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcU
+Qg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3Rl
+IFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkG
+A1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
+ZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQ
+VGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRz
+QHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I
+/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC
+6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnX
+TEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWD
+TSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdni
+TCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmls
+bGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmlj
+YXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcw
+MTAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTAT
+BgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN
+BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24x
+HzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBANYrWHhhRYZT6jR7UZztsOYuGA7+4F+oJ9O0yeB8
+WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQaWt9MevPZQx08EHp5JduQ/vBR
+5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL8vg7ij5FrHGSALSQQZj7
+X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC9RAIDb/LogWK
+0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQpgCed/r8
+zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZCayJ
+SdM=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUF
+ADCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
+HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVU
+Ti1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0
+ODM5WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
+AlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVT
+RVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVz
+dC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNh
+dGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZV
+hawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAb
+GHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZ
+NaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hAReYFmnjDRy7rh4
+xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwiP8vv
+/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7i
+gEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD
+AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf
+8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0
+LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0G
+CSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXh
+i6r/fWRRzwr/vH3YIWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUq
+f9FuVSTiuwL7MT++6LzsQCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAf
+hZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvP
+NximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+
+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjis
+H8SE
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NTIyMjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw
+8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m
++FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8YTfwggtFzVXSN
+dnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwGlN+V
+YH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8so
+gTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
+nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NjAwMTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc
+65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQ
+b7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcn
+wbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZSWI4
+OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZ
+oDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
+W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8x
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UE
+CxMuQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eTAeFw05NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNV
+BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh
+c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCB
+nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3
+noaACpEO+jglr0aIguVzqKCbJF0NH8xlbgyw0FaEGIeaBpsQoXPftFg5a27B
+9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR4k5FVmkfeAKA2txHkSm7NsljXMXg
+1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATANBgkqhkiG9w0BAQIFAAOBgQBM
+P7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZoEWx8QszznC7EBz8UsA9P
+/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5FvjqBUuUfx3CHMjj
+t/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89FxlA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq
+0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9
+Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSmFc/IReumXY6c
+PvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9Zr
+bWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul
+uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4i
+P/68DzFc6PLZ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRR
+ZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO8ESlV8dAWB6j
+Rx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJrKsh
+JlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7P
+oBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2
+6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHh
+v2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQ
+BfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/N
+y9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUf
+xJM8/XmPBNQ+T+r3ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFM
+DSZl4kSAHsef493oCtrspSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5
+SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXV
+OBRgmaNL3gaWcSzy27YfpO8/7g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAwegAwIBAgIQK2jUo0aexTsoCas4XX8nIDANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAx
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC57V56Ondfzl86UvzNZPdxtW9qlsZZklWUXS9bLsER
+6iaKy6eBPPZaRN56Ey/9WlHZezcmSsAnPwQDalbBgyzhb1upVFAkSsYuekyh
+WzdUJCExH6F4GHansXDaItBq/gdiQMb39pt9DAa4S8co5GYjhFHvRreT2IEz
+y+U2rMboBQIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0xMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTEuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF
+BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j
+b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG
+CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAHCQ3bjkvlMX
+fH8C6dX3i5mTMWCNfuZgayTvYKzSzpHegG0JpNO4OOVEynJeDS3Bd5y9LAN4
+KY2kpXeH9fErJq3MB2w6VFoo4AnzTQoEytRYaQuns/XdAaXn3PAfusFdkI2z
+6k/BEVmXarIrE7HarZehs7GgIFvKMquNzxPwHynD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL
+Ey5DbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZM
+JaLtVRKXxaeAufqDwSCg+i8VDXyhYGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvE
+erf4Zh+AVPy3wo5ZShRXRtGak75BkQO7FYCTXOvnzAhsPz6zSvz/S2wj1VCC
+JkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBAIob
+K/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxgJ8pFUs4W7z8GZOeUaHxg
+MxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Ncr6Pc5iaAIzy4RHT3
+Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHB
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNV
+BAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4g
+LSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24g
+VHJ1c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTla
+MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6
+BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNp
+Z24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+p4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkf
+rbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjwDqL7MWzJ5m+Z
+Jwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEAATAN
+BgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/
+7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX
+rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6x
+RnInjBJ7xUS0rg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcox
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UE
+CxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkg
+VmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMG
+A1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcx
+NjIzNTk1OVowgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwg
+SW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UE
+CxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1
+c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJp
+bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY8
+1nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDOJxOeBUebMXoT
+2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7C9UT
+AJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQ
+HgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN
+qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVC
+YQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekh
+ktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf
+0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydE
+p85EXdQbkJgNHkKUsQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377B
+MnMiIYtYgXsVkXq642RIsH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab
+5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//jGHyJizNdrDPX
+p/naOlXJWBD5qu9ats9LS98q
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAwegAwIBAgIQCUYX5h3Y1BygDKBi6HmKpzANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODAxMDAwMDAwWhcNMDQwNzMxMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAy
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDQymMxYX9ENHwFfQs9apDLeUt3Cj9LxyPlwGItfpx+
+PoiHkdCs6E1Jh6KWkIrdBKUCP4yb6Yn+YqDiWr3I3bR45qVCkwhnAcAgTddc
+9F3as+M3plIaLExlTYqH2aij8UlUuzxcgFFoxvtJ/wtVqxXd+5rBuR10DbKM
+RF2J/J/5gwIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0yMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTIuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF
+BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j
+b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG
+CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAB99CW4kRnUE
+nPMmm+M5bhfvvL2iG9IChIar0ECXLMRDiDcZayKoA3FQnSDcNmAgmnMtc1Vs
+WJsswrQ0LHozQsqR2elDr88e4PXEeqs/cmMeqTfhWzuIsxOGgpBXy1f/9Fa+
+It3jl6jhvCJDwt1N2/aBnpIUnjkPE1TegtjAXjSN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL
+Ey5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69q
+RUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3In
+zPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a
+/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtM
+EivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPw
+TtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzk
+uxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM
+XtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXX
+wc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg013gfqLptQ5GV
+j0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01U
+bSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
+F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo
+1KpYoJ2daZH9
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2
+R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2tKmFZpGcmTNDo
+vFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUccLwg
+TS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+V
+k7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
+Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJ
+OxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my
+/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
+j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoA
+Wii/gt/4uhMdUIaC/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8S
+GhJouPtmmRQURVyu565pF4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbb
+o27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh
+/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAwugAwIBAgIQLpaev7ZibOx76XPM42zBhDANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDx5AgOg7t140jluNum8Lmr6Txix141W9ACVBHYydFW
+uXZLuat65s269gwE1n7WsAplrE454/H3LaMlOe+wi8++2wxdbnD0B81w9zrA
+PjUW7XiMQ8/CJi5H1oZ9nPG+1mcMIiWkymXmH3p4KC8/BdsEIb/hRWb+PLeC
+7Vq4FhW5VQIDAQABo4IBFDCCARAwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0zMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTMuMS4xLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCTBCBggr
+BgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGmJhYkaHR0cDovL29jc3AudmVyaXNp
+Z24uY29tL29jc3Avc3RhdHVzMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw
+KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL1JQQTAJ
+BgNVHRMEAjAAMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQUFAAOBgQAC9lNj
+wKke8tCLMzCPSJtMsFa0g3FKvtxQ2PW24AvbvXhP6c8JNNopSZ0Bc1qRkYJU
+LBMK03cjzzf8Y96n4/a3tWlFKEnDkdyqRxypiJksBSqNjYr6YuJatwAgXTnE
+KMLL/J6oia5bPY4S6jKy/OsU1wkVGsDNG9W1FU5B1ZbjTg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6
+8OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDMHO0oW369atyzkSTK
+QWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtKqsGgtG7rL+VX
+xbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwjcSGI
+L4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y
+cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckzt
+ImRPT8qAkbYp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYl
+S+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ+mGuqPKljYXC
+KtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM8BDc
+VHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdL
+MEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
+ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDD
+Zq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1Wr
+IhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt
+mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csK
+vE+MW8VLADsfKoKmfjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluP
+QSjA1egtTaRezarZ7c7c2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kP
+mF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr
+9Xgn2uf3ZkPznoM+IKrDNWCRzg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMu
+MS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD
+VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGb
+MA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6O
+LDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZs
+iAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmC
+sZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw
+4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxr
+l0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1
+g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAwygAwIBAgIRAP9F1SddJPuzwjkkU1fhT94wDQYJKoZIhvcNAQEF
+BQAwXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5
+LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MB4XDTAwMDgwNDAwMDAwMFoXDTA0MDgwMzIzNTk1OVowgZ4x
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6
+Ly93d3cudmVyaXNpZ24uY29tL1JQQSAoYykwMDElMCMGA1UEAxMcU2VjdXJl
+IFNlcnZlciBPQ1NQIFJlc3BvbmRlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAuFGZZIUO7rMKaPC/Y3YdU/X8oXiMM+6f9L452psPTUepjyDoS0S9
+zs17kNEw6JDEJXuJKN699pMd/7n/krWpjeSuzOLDB4Nqo3IQASdiIqY1Jjkt
+ns9gDPxHpNfQQninHWzQy08VpykKtJVFxLHnWgnXOZXYHTWewr2zXcEMSx8C
+AwEAAaOCAR0wggEZMCAGA1UdEQQZMBekFTATMREwDwYDVQQDEwhPQ1NQIDEt
+NDA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vY3JsLnZlcmlzaWduLmNvbS9S
+U0FTZWN1cmVTZXJ2ZXItcC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwkwQgYI
+KwYBBQUHAQEENjA0MDIGCCsGAQUFBzABpiYWJGh0dHA6Ly9vY3NwLnZlcmlz
+aWduLmNvbS9vY3NwL3N0YXR1czBEBgNVHSAEPTA7MDkGC2CGSAGG+EUBBwEB
+MCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9SUEEw
+CQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQEFBQADfgAAsxBT
+ZpxJky4xoAJC0lhXfmah/huKYRhQQCweK0Gl1tv/rAgcWgVtAlwqtpZPR9u+
+TtvOzLqGuBjOsRKRX2P380g+zPFNE+RtCZR4AJLLoyCdBgtqoEMHztEZbI8Y
+dZqfFzP9qSa44+LewqjEWop/mNYHBmvMVp6GcM7U7w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDzTCCAzagAwIBAgIQU2GyYK7bcY6nlLMTM/QHCTANBgkqhkiG9w0BAQUF
+ADCBwTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTww
+OgYDVQQLEzNDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5IC0gRzIxOjA4BgNVBAsTMShjKSAxOTk4IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmswHhcNMDAwOTI2MDAwMDAwWhcNMTAwOTI1MjM1
+OTU5WjCBpTEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBh
+dCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTAwMSwwKgYDVQQD
+EyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIEF1dGhvcml0eSBDQTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA0hmdZ8IAIVlizrQJIkRpivglWtvtDbc2
+fk7gu5Q+kCWHwmFHKdm9VLhjzCx9abQzNvQ3B5rB3UBU/OB4naCTuQk9I1F/
+RMIUdNsKvsvJMDRAmD7Q1yUQgZS9B0+c1lQn3y6ov8uQjI11S7zi6ESHzeZB
+CiVu6PQkAsVSD27smHUCAwEAAaOB3zCB3DAPBgNVHRMECDAGAQH/AgEAMEUG
+A1UdIAQ+MDwwOgYMYIZIAYb4RQEHFwEDMCowKAYIKwYBBQUHAgEWHGh0dHBz
+Oi8vd3d3LnZlcmlzaWduLmNvbS9ycGEwMQYDVR0fBCowKDAmoCSgIoYgaHR0
+cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwCwYDVR0PBAQDAgEGMEIG
+CCsGAQUFBwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJp
+c2lnbi5jb20vb2NzcC9zdGF0dXMwDQYJKoZIhvcNAQEFBQADgYEAgnBold+2
+DcIBcBlK0lRWHqzyRUyHuPU163hLBanInTsZIS5wNEqi9YngFXVF5yg3ADQn
+Keg3S/LvRJdrF1Eaw1adPBqK9kpGRjeM+sv1ZFo4aC4cw+9wzrhGBha/937n
+tag+RaypJXUie28/sJyU58dzq6wf7iWbwBbtt8pb8BQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDgDCCAmigAwIBAgICAx4wDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMC
+VVMxDTALBgNVBAoTBFZJU0ExLzAtBgNVBAsTJlZpc2EgSW50ZXJuYXRpb25h
+bCBTZXJ2aWNlIEFzc29jaWF0aW9uMRIwEAYDVQQDEwlHUCBSb290IDIwHhcN
+MDAwODE2MjI1MTAwWhcNMjAwODE1MjM1OTAwWjBhMQswCQYDVQQGEwJVUzEN
+MAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNl
+cnZpY2UgQXNzb2NpYXRpb24xEjAQBgNVBAMTCUdQIFJvb3QgMjCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkBcLWqxEDwq2omYXkZAPy/mzdZ
+DK9vZBv42pWUJGkzEXDK41Z0ohdXZFwgBuHW73G3O/erwWnQSaSxBNf0V2KJ
+XLB1LRckaeNCYOTudNargFbYiCjh+20i/SN8RnNPflRzHqgsVVh1t0zzWkWl
+Ahr62p3DRcMiXvOL8WAp0sdftAw6UYPvMPjU58fy+pmjIlC++QU3o63tmsPm
+7IgbthknGziLgE3sucfFicv8GjLtI/C1AVj59o/ghalMCXI5Etuz9c9OYmTa
+xhkVOmMd6RdVoUwiPDQyRvhlV7or7zaMavrZ2UT0qt2E1w0cslSsMoW0ZA3e
+QbuxNMYBhjJk1Z8CAwEAAaNCMEAwHQYDVR0OBBYEFJ59SzS/ca3CBfYDdYDO
+qU8axCRMMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqG
+SIb3DQEBBQUAA4IBAQAhpXYUVfmtJ3CPPPTVbMjMCqujmAuKBiPFyWHbmQdp
+NSYx/scuhMKZYdQN6X0uEyt8joW2hcdLzzW2LEc9zikv2G+fiRxkk78IvXbQ
+kIqUs38oW26sTTMs7WXcFsziza6kPWKSBpUmv9+55CCmc2rBvveURNZNbyoL
+axhNdBA2aGpawWqn3TYpjLgwi08hPwAuVDAHOrqK5MOeyti12HvOdUVmB/Rt
+Ldh6yumJivIj2C/LbgA2T/vwLwHMD8AiZfSr4k5hLQOCfZEWtTDVFN5ex5D8
+ofyrEK9ca3CnB+8phuiyJccg/ybdd+95RBTEvd07xQObdyPsoOy7Wjm1zK0G
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUF
+ADBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlz
+YSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMT
+E1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0
+MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UE
+CxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAa
+BgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh
+28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8bRaVK7362
+rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81
+q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtF
+Wsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0
+lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaLdXe6YJ2E5/4t
+AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOC
+AQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
+zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKht
+cbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGI
+xHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
+YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/
+hC3euiInlhBx6yLt398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFajCCBFKgAwIBAgIEPLU9RjANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK
+EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG
+A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EtQmFsdGltb3JlIEltcGxlbWVudGF0
+aW9uMB4XDTAyMDQxMTA3Mzg1MVoXDTIyMDQxMTA3Mzg1MVowZjESMBAGA1UE
+ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx
+BgNVBAMTKmJlVFJVU1RlZCBSb290IENBLUJhbHRpbW9yZSBJbXBsZW1lbnRh
+dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALx+xDmcjOPW
+HIb/ymKt4H8wRXqOGrO4x/nRNv8i805qX4QQ+2aBw5R5MdKR4XeOGCrDFN5R
+9U+jK7wYFuK13XneIviCfsuBH/0nLI/6l2Qijvj/YaOcGx6Sj8CoCd8JEey3
+fTGaGuqDIQY8n7pc/5TqarjDa1U0Tz0yH92BFODEPM2dMPgwqZfT7syj0B9f
+HBOB1BirlNFjw55/NZKeX0Tq7PQiXLfoPX2k+YmpkbIq2eszh+6l/ePazIjm
+iSZuxyuC0F6dWdsU7JGDBcNeDsYq0ATdcT0gTlgn/FP7eHgZFLL8kFKJOGJg
+B7Sg7KxrUNb9uShr71ItOrL/8QFArDcCAwEAAaOCAh4wggIaMA8GA1UdEwEB
+/wQFMAMBAf8wggG1BgNVHSAEggGsMIIBqDCCAaQGDysGAQQBsT4AAAEJKIOR
+MTCCAY8wggFIBggrBgEFBQcCAjCCAToaggE2UmVsaWFuY2Ugb24gb3IgdXNl
+IG9mIHRoaXMgQ2VydGlmaWNhdGUgY3JlYXRlcyBhbiBhY2tub3dsZWRnbWVu
+dCBhbmQgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5k
+YXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgdGhlIENlcnRpZmlj
+YXRpb24gUHJhY3RpY2UgU3RhdGVtZW50IGFuZCB0aGUgUmVseWluZyBQYXJ0
+eSBBZ3JlZW1lbnQsIHdoaWNoIGNhbiBiZSBmb3VuZCBhdCB0aGUgYmVUUlVT
+VGVkIHdlYiBzaXRlLCBodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20vcHJvZHVj
+dHNfc2VydmljZXMvaW5kZXguaHRtbDBBBggrBgEFBQcCARY1aHR0cDovL3d3
+dy5iZXRydXN0ZWQuY29tL3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWww
+HQYDVR0OBBYEFEU9w6nR3D8kVpgccxiIav+DR+22MB8GA1UdIwQYMBaAFEU9
+w6nR3D8kVpgccxiIav+DR+22MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0B
+AQUFAAOCAQEASZK8o+6svfoNyYt5hhwjdrCAWXf82n+0S9/DZEtqTg6t8n1Z
+dwWtColzsPq8y9yNAIiPpqCy6qxSJ7+hSHyXEHu67RMdmgduyzFiEuhjA6p9
+beP4G3YheBufS0OM00mG9htc9i5gFdPp43t1P9ACg9AYgkHNZTfqjjJ+vWuZ
+XTARyNtIVBw74acT02pIk/c9jH8F6M7ziCpjBLjqflh8AXtb4cV97yHgjQ5d
+UX2xZ/2jvTg2xvI4hocalmhgRvsoFEdV4aeADGvi6t9NfJBIoDa9CReJf8Py
+05yc493EG931t3GzUwWJBtDLSoDByFOQtTwxiBdQn8nEDovYqAJjDQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFLDCCBBSgAwIBAgIEOU99hzANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG
+EwJXVzESMBAGA1UEChMJYmVUUlVTVGVkMRswGQYDVQQDExJiZVRSVVNUZWQg
+Um9vdCBDQXMxGjAYBgNVBAMTEWJlVFJVU1RlZCBSb290IENBMB4XDTAwMDYy
+MDE0MjEwNFoXDTEwMDYyMDEzMjEwNFowWjELMAkGA1UEBhMCV1cxEjAQBgNV
+BAoTCWJlVFJVU1RlZDEbMBkGA1UEAxMSYmVUUlVTVGVkIFJvb3QgQ0FzMRow
+GAYDVQQDExFiZVRSVVNUZWQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBANS0c3oTCjhVAb6JVuGUntS+WutKNHUbYSnE4a0IYCF4
+SP+00PpeQY1hRIfo7clY+vyTmt9P6j41ffgzeubx181vSUs9Ty1uDoM6GHh3
+o8/n9E1z2Jo7Gh2+lVPPIJfCzz4kUmwMjmVZxXH/YgmPqsWPzGCgc0rXOD8V
+cr+il7dw6K/ifhYGTPWqZCZyByWtNfwYsSbX2P8ZDoMbjNx4RWc0PfSvHI3k
+bWvtILNnmrRhyxdviTX/507AMhLn7uzf/5cwdO2NR47rtMNE5qdMf1ZD6Li8
+tr76g5fmu/vEtpO+GRg+jIG5c4gW9JZDnGdzF5DYCW5jrEq2I8QBoa2k5MUC
+AwEAAaOCAfgwggH0MA8GA1UdEwEB/wQFMAMBAf8wggFZBgNVHSAEggFQMIIB
+TDCCAUgGCisGAQQBsT4BAAAwggE4MIIBAQYIKwYBBQUHAgIwgfQagfFSZWxp
+YW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz
+IGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0
+ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGFuZCBjZXJ0aWZpY2F0aW9u
+IHByYWN0aWNlIHN0YXRlbWVudCwgd2hpY2ggY2FuIGJlIGZvdW5kIGF0IGJl
+VFJVU1RlZCdzIHdlYiBzaXRlLCBodHRwczovL3d3dy5iZVRSVVNUZWQuY29t
+L3ZhdWx0L3Rlcm1zMDEGCCsGAQUFBwIBFiVodHRwczovL3d3dy5iZVRSVVNU
+ZWQuY29tL3ZhdWx0L3Rlcm1zMDQGA1UdHwQtMCswKaAnoCWkIzAhMRIwEAYD
+VQQKEwliZVRSVVNUZWQxCzAJBgNVBAYTAldXMB0GA1UdDgQWBBQquZtpLjub
+2M3eKjEENGvKBxirZzAfBgNVHSMEGDAWgBQquZtpLjub2M3eKjEENGvKBxir
+ZzAOBgNVHQ8BAf8EBAMCAf4wDQYJKoZIhvcNAQEFBQADggEBAHlh26Nebhax
+6nZR+csVm8tpvuaBa58oH2U+3RGFktToQb9+M70j5/Egv6S0phkBxoyNNXxl
+pE8JpNbYIxUFE6dDea/bow6be3ga8wSGWsb2jCBHOElQBp1yZzrwmAOtlmdE
+/D8QDYZN5AA7KXvOOzuZhmElQITcE2K3+spZ1gMe1lMBzW1MaFVA4e5rxyoA
+AEiCswoBw2AqDPeCNe5IhpbkdNQ96gFxugR1QKepfzk5mlWXKWWuGVUlBXJH
+0+gY3Ljpr0NzARJ0o+FcXxVdJPP55PS2Z2cS52QiivalQaYctmBjRYoQtLpG
+EK5BV2VsPyMQPyEQWbfkQN0mDCP2qq4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGUTCCBTmgAwIBAgIEPLVPQDANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK
+EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG
+A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EgLSBFbnRydXN0IEltcGxlbWVudGF0
+aW9uMB4XDTAyMDQxMTA4MjQyN1oXDTIyMDQxMTA4NTQyN1owZjESMBAGA1UE
+ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx
+BgNVBAMTKmJlVFJVU1RlZCBSb290IENBIC0gRW50cnVzdCBJbXBsZW1lbnRh
+dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr0RAOqEmq1
+Q+xVkrYwfTVXDNvzDSduTPdQqJtOK2/b9a0cS12zqcH+e0TrW6MFDR/FNCsw
+ACnxeECypP869AGIF37m1CbTukzqMvtDd5eHI8XbQ6P1KqNRXuE70mVpflUV
+m3rnafdE4Fe1FehmYA8NA/uCjqPoEXtsvsdjDheT389Lrm5zdeDzqrmkwAkb
+hepxKYhBMvnwKg5sCfJ0a2ZsUhMfGLzUPvfYbiCeyv78IZTuEyhL11xeDGbu
+6bsPwTSxfwh28z0mcMmLJR1iJAzqHHVOwBLkuhMdMCktVjMFu5dZfsZJT4nX
+LySotohAtWSSU1Yk5KKghbNekLQSM80CAwEAAaOCAwUwggMBMIIBtwYDVR0g
+BIIBrjCCAaowggGmBg8rBgEEAbE+AAACCSiDkTEwggGRMIIBSQYIKwYBBQUH
+AgIwggE7GoIBN1JlbGlhbmNlIG9uIG9yIHVzZSBvZiB0aGlzIENlcnRpZmlj
+YXRlIGNyZWF0ZXMgYW4gYWNrbm93bGVkZ21lbnQgYW5kIGFjY2VwdGFuY2Ug
+b2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29u
+ZGl0aW9ucyBvZiB1c2UsIHRoZSBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0
+YXRlbWVudCBhbmQgdGhlIFJlbHlpbmcgUGFydHkgQWdyZWVtZW50LCB3aGlj
+aCBjYW4gYmUgZm91bmQgYXQgdGhlIGJlVFJVU1RlZCB3ZWIgc2l0ZSwgaHR0
+cHM6Ly93d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRl
+eC5odG1sMEIGCCsGAQUFBwIBFjZodHRwczovL3d3dy5iZXRydXN0ZWQuY29t
+L3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWwwEQYJYIZIAYb4QgEBBAQD
+AgAHMIGJBgNVHR8EgYEwfzB9oHugeaR3MHUxEjAQBgNVBAoTCWJlVFJVU1Rl
+ZDEbMBkGA1UECxMSYmVUUlVTVGVkIFJvb3QgQ0FzMTMwMQYDVQQDEypiZVRS
+VVNUZWQgUm9vdCBDQSAtIEVudHJ1c3QgSW1wbGVtZW50YXRpb24xDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMjA0MTEwODI0MjdagQ8yMDIyMDQx
+MTA4NTQyN1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFH1w5a44iwY/qhwa
+j/nPJDCqhIQWMB0GA1UdDgQWBBR9cOWuOIsGP6ocGo/5zyQwqoSEFjAMBgNV
+HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkq
+hkiG9w0BAQUFAAOCAQEAKrgXzh8QlOu4mre5X+za95IkrNySO8cgjfKZ5V04
+ocI07cUTWVwFtStPYZuR+0H8/NU8TZh2BvWBfevdkObRVlTa4y0MnxEylCIB
+evZsLHRnBMylj44ss0O1lKLQfelifwa+JwGDnjr9iu6YQ0pr17WXOzq/T220
+Y/ozADQuLW2WyXvKmWO6vvT2MKAtmJbpVkQFqUSjYRDrgqFnXbxdJ3Wqiig2
+KjiS2d2kXgClzMx8KSreKJCrt+G2/30lC0DYqjSjLd4H61/OCt3Kfjp9JsFi
+aDrmLzfzgYYhxKlkqu9FNtEaZnz46TfW1mG+oq1I59/mdP7TbX3SJdysYlep
+9w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFaDCCBFCgAwIBAgIQO1nHe81bV569N1KsdrSqGjANBgkqhkiG9w0BAQUF
+ADBiMRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBS
+b290IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1w
+bGVtZW50YXRpb24wHhcNMDIwNDExMTExODEzWhcNMjIwNDEyMTEwNzI1WjBi
+MRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290
+IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1wbGVt
+ZW50YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkujQw
+CY5X0LkGLG9uJIAiv11DpvpPrILnHGhwhRujbrWqeNluB0s/6d/16uhUoWGK
+Di9pdRi3DOUUjXFumLhV/AyV0Jtu4S2I1DpAa5LxmZZk3tv/ePTulh1HiXzU
+vrmIdyM6CeYEnm2qXtLIvZpOGd+J6lsOfsPktPDgaTuID0GQ+NRxQyTBjyZL
+O1bp/4xsN+lFrYWMU8NghpBKlsmzVLC7F/AcRdnUGxlkVgoZ98zh/4avflhe
+rHqQH8koOUV7orbHnB/ahdQhhlkwk75TMzf270HPM8ercmsl9fNTGwxMLvF1
+S++gh/f+ihXQbNXL+WhTuXAVE8L1LvtDNXUtAgMBAAGjggIYMIICFDAMBgNV
+HRMEBTADAQH/MIIBtQYDVR0gBIIBrDCCAagwggGkBg8rBgEEAbE+AAADCSiD
+kTEwggGPMEEGCCsGAQUFBwIBFjVodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20v
+cHJvZHVjdHNfc2VydmljZXMvaW5kZXguaHRtbDCCAUgGCCsGAQUFBwICMIIB
+OhqCATZSZWxpYW5jZSBvbiBvciB1c2Ugb2YgdGhpcyBDZXJ0aWZpY2F0ZSBj
+cmVhdGVzIGFuIGFja25vd2xlZGdtZW50IGFuZCBhY2NlcHRhbmNlIG9mIHRo
+ZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlv
+bnMgb2YgdXNlLCB0aGUgQ2VydGlmaWNhdGlvbiBQcmFjdGljZSBTdGF0ZW1l
+bnQgYW5kIHRoZSBSZWx5aW5nIFBhcnR5IEFncmVlbWVudCwgd2hpY2ggY2Fu
+IGJlIGZvdW5kIGF0IHRoZSBiZVRSVVNUZWQgd2ViIHNpdGUsIGh0dHA6Ly93
+d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRleC5odG1s
+MAsGA1UdDwQEAwIBBjAfBgNVHSMEGDAWgBSp7BR++dlDzFMrFK3P9/BZiUHN
+GTAdBgNVHQ4EFgQUqewUfvnZQ8xTKxStz/fwWYlBzRkwDQYJKoZIhvcNAQEF
+BQADggEBANuXsHXqDMTBmMpWBcCorSZIry0g6IHHtt9DwSwddUvUQo3neqh0
+3GZCWYez9Wlt2ames30cMcH1VOJZJEnl7r05pmuKmET7m9cqg5c0Lcd9NUwt
+NLg+DcTsiCevnpL9UGGCqGAHFFPMZRPB9kdEadIxyKbdLrML3kqNWz2rDcI1
+UqJWN8wyiyiFQpyRQHpwKzg21eFzGh/l+n5f3NacOzDq28BbJ1zTcwfBwvNM
+m2+fG8oeqqg4MwlYsq78B+g23FW6L09A/nq9BqaBwZMifIYRCgZ3SK41ty8y
+mmFei74pnykkiFY5LKjSq5YDWtRIn7lAhAuYaPsBQ9Yb4gmxlxw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIETzCCAzegAwIBAgIEO63vKTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIzMTQxODE3WhcNMTEw
+OTIzMTMxODE3WjB1MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWdu
+ZXQgLSBDQSBLbGFzYSAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4SRW9Q58g5DY1Hw7h
+gCRKBEdPdGn0MFHsfw7rlu/oQm7IChI/uWd9q5wwo77YojtTDjRnpgZsjqBeynX8T90vFILqsY2K
+5CF1OESalwvVr3sZiQX79lisuFKat92u6hBFikFIVxfHHB67Af+g7u0dEHdDW7lwy81MwFYxBTRy
+9wIDAQABo4IBbTCCAWkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwggEEBgNVHSAE
+gfwwgfkwgfYGDSsGAQQBvj8CAQoBAQAwgeQwgZoGCCsGAQUFBwICMIGNGoGKQ2VydHlmaWthdCB3
+eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtOiAiUG9saXR5a2EgQ2VydHlmaWthY2ppIGRs
+YSBSb290Q0EiLiBDZXJ0eWZpa2F0IHd5c3Rhd2lvbnkgcHJ6ZXogUm9vdENBIHcgaGllcmFyY2hp
+aSBDQyBTaWduZXQuMEUGCCsGAQUFBwIBFjlodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0b3Jp
+dW0vZG9rdW1lbnR5L3BjX3Jvb3RjYS50eHQwHwYDVR0jBBgwFoAUwJvFIw0C4aZOSGsfAOnjmhQb
+sa8wHQYDVR0OBBYEFMODHtVZd1T7TftXR/nEI1zR54njMA0GCSqGSIb3DQEBBQUAA4IBAQBRIHQB
+FIGh8Jpxt87AgSLwIEEk4+oGy769u3NtoaR0R3WNMdmt7fXTi0tyTQ9V4AIszxVjhnUPaKnF1KYy
+f8Tl+YTzk9ZfFkZ3kCdSaILZAOIrmqWNLPmjUQ5/JiMGho0e1YmWUcMci84+pIisTsytFzVP32/W
++sz2H4FQAvOIMmxB7EJX9AdbnXn9EXZ+4nCqi0ft5z96ZqOJJiCB3vSaoYg+wdkcvb6souMJzuc2
+uptXtR1Xf3ihlHaGW+hmnpcwFA6AoNrom6Vgzk6U1ienx0Cw28BhRSKqzKkyXkuK8gRflZUx84uf
+tXncwKJrMiE3lvgOOBITRzcahirLer4c
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE9zCCA9+gAwIBAgIEPL/xoTANBgkqhkiG9w0BAQUFADB2MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MSAwHgYDVQQDExdDQyBTaWduZXQgLSBQQ0EgS2xhc2EgMjAeFw0wMjA0MTkxMDI5NTNa
+Fw0xNzA0MTgxMjUzMDdaMHUxCzAJBgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4g
+eiBvLm8uMSQwIgYDVQQLExtDZW50cnVtIENlcnR5ZmlrYWNqaSBTaWduZXQxHzAdBgNVBAMTFkND
+IFNpZ25ldCAtIENBIEtsYXNhIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqgLJu
+QqY4yavbSgHg8CyfKTx4BokNSDOVz4eD9vptUr11Kqd06ED1hlH7Sg0goBFAfntNU/QTKwSBaNui
+me7C4sSEdgsKrPoAhGb4Mq8y7Ty7RqZz7mkzNMqzL2L2U4yQ2QjvpH8MH0IBqOWEcpSkpwnrCDIm
+RoTfd+YlZWKi2JceQixUUYIQ45Ox8+x8hHbvvZdgqtcvo8PW27qoHkp/7hMuJ44kDAGrmxffBXl/
+OBRZp0uO1CSLcMcVJzyr2phKhy406MYdWrtNPEluGs0GFDzd0nrIctiWAO4cmct4S72S9Q6e//0G
+O9f3/Ca5Kb2I1xYLj/xE+HgjHX9aD2MhAgMBAAGjggGMMIIBiDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjCB4wYDVR0gBIHbMIHYMIHVBg0rBgEEAb4/AhQKAQEAMIHDMHUGCCsGAQUF
+BwICMGkaZ0NlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBQQ0EyIC0gQ2VydHlmaWthdHkgVXJ6ZWRvdyBLbGFzeSAyIi4wSgYI
+KwYBBQUHAgEWPmh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9kb2t1bWVudHkva2xh
+c2EyL3BjX3BjYTIudHh0MD8GA1UdHwQ4MDYwNKAyoDCGLmh0dHA6Ly93d3cuc2lnbmV0LnBsL3Jl
+cG96eXRvcml1bS9jcmwvcGNhMi5jcmwwHwYDVR0jBBgwFoAUwGxGyl2CfpYHRonE82AVXO08kMIw
+HQYDVR0OBBYEFLtFBlILy4HNKVSzvHxBTM0HDowlMA0GCSqGSIb3DQEBBQUAA4IBAQBWTsCbqXrX
+hBBev5v5cIuc6gJM8ww7oR0uMQRZoFSqvQUPWBYM2/TLI/f8UM9hSShUVj3zEsSj/vFHagUVmzuV
+Xo5u0WK8iaqATSyEVBhADHrPG6wYcLKJlagge/ILA0m+SieyP2sjYD9MUB9KZIEyBKv0429UuDTw
+6P7pslxMWJBSNyQxaLIs0SRKsqZZWkc7ZYAj2apSkBMX2Is1oHA+PwkF6jQMwCao/+CndXPUzfCF
+6caa9WwW31W26MlXCvSmJgfiTPwGvm4PkPmOnmWZ3CczzhHl4q7ztHFzshJH3sZWDnrWwBFjzz5e
+Pr3WHV1wA7EY6oT4zBx+2gT9XBTB
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEUzCCAzugAwIBAgIEPq+qjzANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJQTDE3MDUGA1UE
+ChMuQ1ppQyBDZW50cmFzdCBTQSB3IGltaWVuaXUgTWluaXN0cmEgR29zcG9kYXJraTEZMBcGA1UE
+AxMQQ1ppQyBDZW50cmFzdCBTQTAeFw0wMzA0MzAxMDUwNTVaFw0wODA0MjgxMDUwNTVaMGgxCzAJ
+BgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4geiBvLm8uMR8wHQYDVQQDExZDQyBT
+aWduZXQgLSBDQSBLbGFzYSAzMRcwFQYDVQQFEw5OdW1lciB3cGlzdTogNDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALVdeOM62cPH2NERFxbS5FIp/HSv3fgesdVsTUFxZbGtE+/E0RMl
+KZQJHH9emx7vRYubsi4EOLCjYsCOTFvgGRIpZzx7R7T5c0Di5XFkRU4gjBl7aHJoKb5SLzGlWdoX
+GsekVtl6keEACrizV2EafqjI8cnBWY7OxQ1ooLQp5AeFjXg+5PT0lO6TUZAubqjFbhVbxSWjqvdj
+93RGfyYE76MnNn4c2xWySD07n7uno06TC0IJe6+3WSX1h+76VsIFouWBXOoM7cxxiLjoqdBVu24+
+P8e81SukE7qEvOwDPmk9ZJFtt1nBNg8a1kaixcljrA/43XwOPz6qnJ+cIj/xywECAwEAAaOCAQow
+ggEGMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMDMGA1UdIAEB/wQpMCcwJQYEVR0g
+ADAdMBsGCCsGAQUFBwIBFg93d3cuY2VudHJhc3QucGwwgY4GA1UdIwSBhjCBg4AU2a7r85Cp1iJN
+W0Ca1LR6VG3996ShZaRjMGExCzAJBgNVBAYTAlBMMTcwNQYDVQQKEy5DWmlDIENlbnRyYXN0IFNB
+IHcgaW1pZW5pdSBNaW5pc3RyYSBHb3Nwb2RhcmtpMRkwFwYDVQQDExBDWmlDIENlbnRyYXN0IFNB
+ggQ9/0sQMB0GA1UdDgQWBBR7Y8wZkHq0zrY7nn1tFSdQ0PlJuTANBgkqhkiG9w0BAQUFAAOCAQEA
+ldt/svO5c1MU08FKgrOXCGEbEPbQxhpM0xcd6Iv3dCo6qugEgjEs9Qm5CwUNKMnFsvR27cJWUvZb
+MVcvwlwCwclOdwF6u/QRS8bC2HYErhYo9bp9yuxxzuow2A94c5fPqfVrjXy+vDouchAm6+A5Wjzv
+J8wxVFDCs+9iGACmyUWr/JGXCYiQIbQkwlkRKHHlan9ymKf1NvIej/3EpeT8fKr6ywxGuhAfqofW
+pg3WJY/RCB4lTzD8vZGNwfMFGkWhJkypad3i9w3lGmDVpsHaWtCgGfd0H7tUtWPkP+t7EjIRCD9J
+HYnTR+wbbewc5vOI+UobR15ynGfFIaSIiMTVtQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEejCCA2KgAwIBAgIEP4vk6TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2Vu
+dHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBD
+QSBLbGFzYSAyMB4XDTAzMTAxNDExNTgyMloXDTE3MDQxODEyNTMwN1owdzELMAkG
+A1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6IG8uby4xJDAiBgNV
+BAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEhMB8GA1UEAxMYQ0MgU2ln
+bmV0IC0gT0NTUCBLbGFzYSAyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo
+VCsaBStblXQYVNthe3dvaCrfvKpPXngh4almm988iIlEv9CVTaAdCfaJNihvA+Vs
+Qw8++ix1VqteMQE474/MV/YaXigP0Zr0QB+g+/7PWVlv+5U9Gzp9+Xx4DJay8AoI
+iB7Iy5Qf9iZiHm5BiPRIuUXT4ZRbZRYPh0/76vgRsQIDAQABo4IBkjCCAY4wDgYD
+VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEEGA1UdHwQ6MDgwNqA0
+oDKGMGh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9jcmwva2xhc2Ey
+LmNybDCB2AYDVR0gBIHQMIHNMIHKBg4rBgEEAb4/AoFICgwBADCBtzBsBggrBgEF
+BQcCAjBgGl5DZXJ0eWZpa2F0IHd5ZGFueSB6Z29kbmllIHogZG9rdW1lbnRlbSAi
+UG9saXR5a2EgQ2VydHlmaWthY2ppIC0gQ2VydHlmaWthdHkgcmVzcG9uZGVyb3cg
+T0NTUCIuMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0
+b3JpdW0vZG9rdW1lbnR5L3BjX29jc3BfMV8wLnBkZjAfBgNVHSMEGDAWgBS7RQZS
+C8uBzSlUs7x8QUzNBw6MJTAdBgNVHQ4EFgQUKEVrOY7cEHvsVgvoyZdytlbtgwEw
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEAQrRg5MV6dxr0HU2IsLInxhvt
+iUVmSFkIUsBCjzLoewOXA16d2oDyHhI/eE+VgAsp+2ANjZu4xRteHIHoYMsN218M
+eD2MLRsYS0U9xxAFK9gDj/KscPbrrdoqLvtPSMhUb4adJS9HLhvUe6BicvBf3A71
+iCNe431axGNDWKnpuj2KUpj4CFHYsWCXky847YtTXDjri9NIwJJauazsrSjK+oXp
+ngRS506mdQ7vWrtApkh8zhhWp7duCkjcCo1O8JxqYr2qEW1fXmgOISe010v2mmuv
+hHxPyVwoAU4KkOw0nbXZn53yak0is5+XmAjh0wWue44AssHrjC9nUh3mkLt6eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEezCCA2OgAwIBAgIEP4vnLzANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEfMB0GA1UEAxMWQ0Mg
+U2lnbmV0IC0gQ0EgS2xhc2EgMzEXMBUGA1UEBRMOTnVtZXIgd3Bpc3U6IDQwHhcN
+MDMxMDE0MTIwODAwWhcNMDgwNDI4MTA1MDU1WjB3MQswCQYDVQQGEwJQTDEfMB0G
+A1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBD
+ZXJ0eWZpa2FjamkgU2lnbmV0MSEwHwYDVQQDExhDQyBTaWduZXQgLSBPQ1NQIEts
+YXNhIDMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM/9GwvARNuCVN+PqZmO
+4FqH8vTqhenUyqRkmAVT4YhLu0a9AXeLAYVDu+NTkYzsAUMAfu55rIKHNLlm6WbF
+KvLiKKz4p4pbUr+ToPcwl/TDotidloUdBAxDg0SL+PmQqACZDe3seJho2IYf2vDL
+/G4TLMbKmNB0mlWFuN0f4fJNAgMBAAGjggGgMIIBnDAOBgNVHQ8BAf8EBAMCB4Aw
+EwYDVR0lBAwwCgYIKwYBBQUHAwkwTwYDVR0fBEgwRjBEoEKgQIY+aHR0cDovL3d3
+dy5zaWduZXQucGwva3dhbGlmaWtvd2FuZS9yZXBvenl0b3JpdW0vY3JsL2tsYXNh
+My5jcmwwgdgGA1UdIASB0DCBzTCBygYOKwYBBAG+PwKCLAoCAQAwgbcwbAYIKwYB
+BQUHAgIwYBpeQ2VydHlmaWthdCB3eWRhbnkgemdvZG5pZSB6IGRva3VtZW50ZW0g
+IlBvbGl0eWthIENlcnR5ZmlrYWNqaSAtIENlcnR5ZmlrYXR5IHJlc3BvbmRlcm93
+IE9DU1AiLjBHBggrBgEFBQcCARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5
+dG9yaXVtL2Rva3VtZW50eS9wY19vY3NwXzFfMC5wZGYwHwYDVR0jBBgwFoAUe2PM
+GZB6tM62O559bRUnUND5SbkwHQYDVR0OBBYEFG4jnCMvBALRQXtmDn9TyXQ/EKP+
+MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBACXrKG5Def5lpRwmZom3UEDq
+bl7y4U3qomG4B+ok2FVZGgPZti+ZgvrenPj7PtbYCUBPsCSTNrznKinoT3gD9lQQ
+xkEHwdc6VD1GlFp+qI64u0+wS9Epatrdf7aBnizrOIB4LJd4E2TWQ6trspetjMIU
+upyWls1BmYUxB91R7QkTiAUSNZ87s3auhZuG4f0V0JLVCcg2rn7AN1rfMkgxCbHk
+GxiQbYWFljl6aatxR3odnnzVUe1I8uoY2JXpmmUcOG4dNGuQYziyKG3mtXCQWvug
+5qi9Mf3KUh1oSTKx6HfLjjNl1+wMB5Mdb8LF0XyZLdJM9yIZh7SBRsYm9QiXevY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPL7eEDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwNDE4MTQ1NDA4WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7BrBlbN5ma
+M5eg0BOTqoZ+9NBDvU8Lm5rTdrMswFTCathzpVVLK/JD4K3+4oCZ9SRAspEXE4gvwb08ASY6w5s+
+HpRkeJw8YzMFR5kDZD5adgnCAy4vDfIXYZgppXPaTQ8wnfUZ7BZ7Zfa7QBemUIcJIzJBB0UqgtxW
+Ceol9IekpBRVmuuSA6QG0Jkm+pGDJ05yj2eQG8jTcBENM7sVA8rGRMyFA4skSZ+D0OG6FS2xC1i9
+JyN0ag1yII/LPx8HK5J4W9MaPRNjAEeaa2qI9EpchwrOxnyVbQfSedCG1VRJfAsE/9tT9CMUPZ3x
+W20QjQcSZJqVcmGW9gVsXKQOVLsCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQEBMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUwGxGyl2CfpYHRonE
+82AVXO08kMIwDQYJKoZIhvcNAQEFBQADggEBABp1TAUsa+BeVWg4cjowc8yTJ5XN3GvN96GObMkx
+UGY7U9kVrLI71xBgoNVyzXTiMNDBvjh7vdPWjpl5SDiRpnnKiOFXA43HvNWzUaOkTu1mxjJsZsan
+ot1Xt6j0ZDC+03FjLHdYMyM9kSWp6afb4980EPYZCcSzgM5TOGfJmNii5Tq468VFKrX+52Aou1G2
+2Ohu+EEOlOrG7ylKv1hHUJJCjwN0ZVEIn1nDbrU9FeGCz8J9ihVUvnENEBbBkU37PWqWuHitKQDV
+tcwTwJJdR8cmKq3NmkwAm9fPacidQLpaw0WkuGrS+fEDhu1Nhy9xELP6NA9GRTCNxm/dXlcwnmY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPV0tNDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwODE2MTY0OTU2WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALN3LanJtdue
+Ne6geWUTFENa+lEuzqELcoqhYB+a/tJcPEkc6TX/bYPzalRRjqs+quMP6KZTU0DixOrV+K7iWaqA
+iQ913HX5IBLmKDCrTVW/ZvSDpiBKbxlHfSNuJxAuVT6HdbzK7yAW38ssX+yS2tZYHZ5FhZcfqzPE
+OpO94mAKcBUhk6T/ki0evXX/ZvvktwmF3hKattzwtM4JMLurAEl8SInyEYULw5JdlfcBez2Tg6Db
+w34hA1A+ckTwhxzecrB8TUe2BnQKOs9vr2cCACpFFcOmPkM0Drtjctr1QHm1tYSqRFRf9VcV5tfC
+3P8QqoK4ONjtLPHc9x5NE1uK/FMCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQECMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUXvthcPHlH5BgGhlM
+ErJNXWlhlgAwDQYJKoZIhvcNAQEFBQADggEBACIce95Mvn710KCAISA0CuHD4aznTU6pLoCDShW4
+7OR+GTpJUm1coTcUqlBHV9mra4VFrBcBuOkHZoBLq/jmE0QJWnpSEULDcH9J3mF0nqO9SM+mWyJG
+dsJF/XU/7smummgjMNQXwzQTtWORF+6v5KUbWX85anO2wR+M6YTBWC55zWpWi4RG3vkHFs5Ze2oF
+JTlpuxw9ZgxTnWlwI9QR2MvEhYIUMKMOWxw1nt0kKj+5TCNQQGh/VJJ1dsiroGh/io1DOcePEhKz
+1Ag52y6Wf0nJJB9yk0sFakqZH18F7eQecQImgZyyeRtsG95leNugB3BXWCW+KxwiBrtQTXv4dTE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEzzCCA7egAwIBAgIEO6ocGTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIwMTY0MjE5WhcNMjYw
+OTIxMTU0MjE5WjBxMQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MRswGQYDVQQDExJDQyBTaWdu
+ZXQgLSBSb290Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrr2vydnNpELfGW3Ks
+ARiDhJvwDtUe4AbWev+OfMc3+vA29nX8ZmIwno3gmItjo5DbUCCRiCMq5c9epcGu+kg4a3BJChVX
+REl8gVh0ST15rr3RKrSc4VgsvQzl0ZUraeQLl8JoRT5PLsUj3qwF78jUCQVckiiLVcnGfZtFCm+D
+CJXliQBDMB9XFAUEiO/DtEBs0B7wJGx7lgJeJpQUcGiaOPjcJDYOk7rNAYmmD2gWeSlepufO8luU
+YG/YDxTC4mqhRqfa4MnVO5dqy+ICj2UvUpHbZDB0KfGRibgBYeQP1kuqgIzJN4UqknVAJb0aMBSP
+l+9k2fAUdchx1njlbdcbAgMBAAGjggFtMIIBaTAPBgNVHRMBAf8EBTADAQH/MIIBBAYDVR0gBIH8
+MIH5MIH2Bg0rBgEEAb4/AgEKAQEAMIHkMIGaBggrBgEFBQcCAjCBjRqBikNlcnR5ZmlrYXQgd3lz
+dGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0eWthIENlcnR5ZmlrYWNqaSBkbGEg
+Um9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6IFJvb3RDQSB3IGhpZXJhcmNoaWkg
+Q0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVt
+L2Rva3VtZW50eS9wY19yb290Y2EudHh0MB0GA1UdDgQWBBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAf
+BgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN
+AQEFBQADggEBAGnY5QmYqnnO9OqFOWZxxb25UHRnaRF6IV9aaGit5BZufZj2Tq3v8L3SgE34GOoI
+cdRMMG5JEpEU4mN/Ef3oY6Eo+7HfqaPHI4KFmbDSPiK5s+wmf+bQSm0Yq5/h4ZOdcAESlLQeLSt1
+CQk2JoKQJ6pyAf6xJBgWEIlm4RXE4J3324PUiOp83kW6MDvaa1xY976WyInr4rwoLgxVl11LZeKW
+ha0RJJxJgw/NyWpKG7LWCm1fglF8JH51vZNndGYq1iKtfnrIOvLZq6bzaCiZm1EurD8HE6P7pmAB
+KK6o3C2OXlNfNIgwkDN/cDqk5TYsTkrpfriJPdxXBH8hQOkW89g=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/TCCA2agAwIBAgIEP4/gkTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBDQSBLbGFzYSAxMB4XDTAzMTAxNzEyMjkwMloX
+DTExMDkyMzExMTgxN1owdjELMAkGA1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6
+IG8uby4xJDAiBgNVBAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEgMB4GA1UEAxMXQ0Mg
+U2lnbmV0IC0gVFNBIEtsYXNhIDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOJYrISEtSsd
+uHajROh5/n7NGrkpYTT9NEaPe9+ucuQ37KxIbfJwXJjgUc1dw4wCkcQ12FJarD1X6mSQ4cfN/60v
+LfKI5ZD4nhJTMKlAj1pX9ScQ/MuyvKStCbn5WTkjPhjRAM0tdwXSnzuTEunfw0Oup559y3Iqxg1c
+ExflB6cfAgMBAAGjggGXMIIBkzBBBgNVHR8EOjA4MDagNKAyhjBodHRwOi8vd3d3LnNpZ25ldC5w
+bC9yZXBvenl0b3JpdW0vY3JsL2tsYXNhMS5jcmwwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM
+MAoGCCsGAQUFBwMIMIHaBgNVHSAEgdIwgc8wgcwGDSsGAQQBvj8CZAoRAgEwgbowbwYIKwYBBQUH
+AgIwYxphQ2VydHlmaWthdCB3eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtICJQb2xpdHlr
+YSBDZXJ0eWZpa2FjamkgQ0MgU2lnbmV0IC0gWm5ha293YW5pZSBjemFzZW0iLjBHBggrBgEFBQcC
+ARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY190c2ExXzJf
+MS5wZGYwHwYDVR0jBBgwFoAUw4Me1Vl3VPtN+1dH+cQjXNHnieMwHQYDVR0OBBYEFJdDwEqtcavO
+Yd9u9tej53vWXwNBMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADgYEAnpiQkqLCJQYXUrqMHUEz
++z3rOqS0XzSFnVVLhkVssvXc8S3FkJIiQTUrkScjI4CToCzujj3EyfNxH6yiLlMbskF8I31JxIeB
+vueqV+s+o76CZm3ycu9hb0I4lswuxoT+q5ZzPR8Irrb51rZXlolR+7KtwMg4sFDJZ8RNgOf7tbA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEFTCCA36gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBvjELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UE
+ChMfU29mdHdhcmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9z
+dG1hc3RlcjEgMB4GA1UEAxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkq
+hkiG9w0BCQEWFmhvc3RtYXN0ZXJAc3BpLWluYy5vcmcwHhcNMDMwMTE1MTYyOTE3
+WhcNMDcwMTE0MTYyOTE3WjCBvjELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0luZGlh
+bmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMfU29mdHdhcmUgaW4g
+dGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9zdG1hc3RlcjEgMB4GA1UE
+AxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkqhkiG9w0BCQEWFmhvc3Rt
+YXN0ZXJAc3BpLWluYy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPB6
+rdoiLR3RodtM22LMcfwfqb5OrJNl7fwmvskgF7yP6sdD2bOfDIXhg9852jhY8/kL
+VOFe1ELAL2OyN4RAxk0rliZQVgeTgqvgkOVIBbNwgnjN6mqtuWzFiPL+NXQExq40
+I3whM+4lEiwSHaV+MYxWanMdhc+kImT50LKfkxcdAgMBAAGjggEfMIIBGzAdBgNV
+HQ4EFgQUB63oQR1/vda/G4F6P4xLiN4E0vowgesGA1UdIwSB4zCB4IAUB63oQR1/
+vda/G4F6P4xLiN4E0vqhgcSkgcEwgb4xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdJ
+bmRpYW5hMRUwEwYDVQQHEwxJbmRpYW5hcG9saXMxKDAmBgNVBAoTH1NvZnR3YXJl
+IGluIHRoZSBQdWJsaWMgSW50ZXJlc3QxEzARBgNVBAsTCmhvc3RtYXN0ZXIxIDAe
+BgNVBAMTF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MSUwIwYJKoZIhvcNAQkBFhZo
+b3N0bWFzdGVyQHNwaS1pbmMub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAm/Abn8c2y1nO3fgpAIslxvi9iNBZDhQtJ0VQZY6wgSfANyDOR4DW
+iexO/AlorB49KnkFS7TjCAoLOZhcg5FaNiKnlstMI5krQmau1Qnb/vGSNsE/UGms
+1ts+QYPUs0KmGEAFUri2XzLy+aQo9Kw74VBvqnxvaaMeY5yMcKNOieY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEUDCCA7mgAwIBAgIJAN4ppNGwj6yIMA0GCSqGSIb3DQEBBAUAMIHMMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j
+aXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5jLjEpMCcGA1UECxMgTGluZGVu
+IExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIExpbmRlbiBMYWIg
+Q2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZIhvcNAQkBFhBjYUBsaW5kZW5s
+YWIuY29tMB4XDTA1MDQyMTAyNDAzMVoXDTI1MDQxNjAyNDAzMVowgcwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp
+c2NvMRkwFwYDVQQKExBMaW5kZW4gTGFiLCBJbmMuMSkwJwYDVQQLEyBMaW5kZW4g
+TGFiIENlcnRpZmljYXRlIEF1dGhvcml0eTEpMCcGA1UEAxMgTGluZGVuIExhYiBD
+ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgkqhkiG9w0BCQEWEGNhQGxpbmRlbmxh
+Yi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKXh1MThucdTbMg9bYBO
+rAm8yWns32YojB0PRfbq8rUjepEhTm3/13s0u399Uc202v4ejcGhkIDWJZd2NZMF
+oKrhmRfxGHSKPCuFaXC3jh0lRECj7k8FoPkcmaPjSyodrDFDUUuv+C06oYJoI+rk
+8REyal9NwgHvqCzOrZtiTXAdAgMBAAGjggE2MIIBMjAdBgNVHQ4EFgQUO1zK2e1f
+1wO1fHAjq6DTJobKDrcwggEBBgNVHSMEgfkwgfaAFDtcytntX9cDtXxwI6ug0yaG
+yg63oYHSpIHPMIHMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
+MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5j
+LjEpMCcGA1UECxMgTGluZGVuIExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAn
+BgNVBAMTIExpbmRlbiBMYWIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZI
+hvcNAQkBFhBjYUBsaW5kZW5sYWIuY29tggkA3imk0bCPrIgwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQQFAAOBgQA/ZkgfvwHYqk1UIAKZS3kMCxz0HvYuEQtviwnu
+xA39CIJ65Zozs28Eg1aV9/Y+Of7TnWhW+U3J3/wD/GghaAGiKK6vMn9gJBIdBX/9
+e6ef37VGyiOEFFjnUIbuk0RWty0orN76q/lI/xjCi15XSA/VSq2j4vmnwfZcPTDu
+glmQ1A==
+-----END CERTIFICATE-----
diff --git a/indra/newview/app_settings/anim.ini b/indra/newview/app_settings/anim.ini
new file mode 100644
index 0000000000..63c84e544d
--- /dev/null
+++ b/indra/newview/app_settings/anim.ini
@@ -0,0 +1,87 @@
+Translations 1.0
+
+[hip]
+ relativepos = firstkey
+ relativerot = firstkey
+ outname = mPelvis
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[abdomen]
+ outname = mTorso
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[chest]
+ outname = mChest
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[neckDummy]
+ ignore = true
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[neck]
+ outname = mNeck
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[head]
+ outname = mHead
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[figureHair]
+ ignore = true
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lCollar]
+ outname = mCollarLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lShldr]
+ outname = mShoulderLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lForeArm]
+ outname = mElbowLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lHand]
+ outname = mWristLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rCollar]
+ outname = mCollarRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rShldr]
+ outname = mShoulderRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rForeArm]
+ outname = mElbowRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rHand]
+ outname = mWristRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lThigh]
+ outname = mHipLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lShin]
+ outname = mKneeLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lFoot]
+ outname = mAnkleLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rThigh]
+ outname = mHipRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rShin]
+ outname = mKneeRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rFoot]
+ outname = mAnkleRight
+ frame = 0 1 0, 0 0 1, 1 0 0 \ No newline at end of file
diff --git a/indra/newview/app_settings/grass.xml b/indra/newview/app_settings/grass.xml
new file mode 100644
index 0000000000..7cc29fa771
--- /dev/null
+++ b/indra/newview/app_settings/grass.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<grass_defs>
+ <grass
+ name="Grass 0"
+ species_id="0"
+ texture_name="grass_texture_2.tga"
+ blade_size_x="1.35"
+ blade_size_y="1.35"
+ />
+ <grass
+ name="Grass 1"
+ species_id="1"
+ texture_name="grass_texture_1.tga"
+ blade_size_x="1.0"
+ blade_size_y="0.66"
+ />
+ <grass
+ name="Grass 2"
+ species_id="2"
+ texture_name="grass_texture_2.tga"
+ blade_size_x="1.8"
+ blade_size_y="1.8"
+ />
+ <grass
+ name="Grass 3"
+ species_id="3"
+ texture_name="grass_texture_3.tga"
+ blade_size_x="1.0"
+ blade_size_y="1.0"
+ />
+ <grass
+ name="Grass 4"
+ species_id="4"
+ texture_name="grass_texture_4.tga"
+ blade_size_x="2.25"
+ blade_size_y="2.25"
+ />
+ <grass
+ name="undergrowth_1"
+ species_id="5"
+ texture_id="8f458549-173b-23ff-d4ff-bfaa5ea2371b"
+ blade_size_x="2.0"
+ blade_size_y="2.0"
+ />
+
+
+</grass_defs>
diff --git a/indra/newview/app_settings/keys.ini b/indra/newview/app_settings/keys.ini
new file mode 100644
index 0000000000..f6232abb47
--- /dev/null
+++ b/indra/newview/app_settings/keys.ini
@@ -0,0 +1,310 @@
+# keys.ini
+#
+# keyboard binding initialization
+#
+# comments must have # in the first column
+# blank lines OK
+#
+# Format:
+# mode key mask function
+#
+# mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR, or CONVERSATION
+# key must be upper case, or SPACE, HOME, END, PGUP, PGDN, LEFT, RIGHT, UP, DOWN,
+# or one of ,.;'[]
+# mask must be NONE, SHIFT, ALT, ALT_SHIFT.
+# Control is reserved for user commands.
+# function must be a function named in llkeyboard.cpp
+
+FIRST_PERSON A NONE slide_left
+FIRST_PERSON D NONE slide_right
+FIRST_PERSON W NONE push_forward
+FIRST_PERSON S NONE push_backward
+FIRST_PERSON E NONE jump
+FIRST_PERSON C NONE push_down
+FIRST_PERSON F NONE toggle_fly
+
+FIRST_PERSON LEFT NONE slide_left
+FIRST_PERSON RIGHT NONE slide_right
+FIRST_PERSON UP NONE push_forward
+FIRST_PERSON DOWN NONE push_backward
+FIRST_PERSON PGUP NONE jump
+FIRST_PERSON PGDN NONE push_down
+FIRST_PERSON HOME NONE toggle_fly
+
+FIRST_PERSON PAD_LEFT NONE slide_left
+FIRST_PERSON PAD_RIGHT NONE slide_right
+FIRST_PERSON PAD_UP NONE push_forward
+FIRST_PERSON PAD_DOWN NONE push_backward
+FIRST_PERSON PAD_PGUP NONE jump
+FIRST_PERSON PAD_PGDN NONE push_down
+FIRST_PERSON PAD_HOME NONE toggle_fly
+FIRST_PERSON PAD_CENTER NONE stop_moving
+FIRST_PERSON PAD_ENTER NONE start_chat
+
+FIRST_PERSON A SHIFT slide_left
+FIRST_PERSON D SHIFT slide_right
+FIRST_PERSON W SHIFT push_forward
+FIRST_PERSON S SHIFT push_backward
+FIRST_PERSON E SHIFT jump
+FIRST_PERSON C SHIFT push_down
+FIRST_PERSON F SHIFT toggle_fly
+
+FIRST_PERSON SPACE NONE stop_moving
+FIRST_PERSON ENTER NONE start_chat
+
+FIRST_PERSON LEFT SHIFT slide_left
+FIRST_PERSON RIGHT SHIFT slide_right
+FIRST_PERSON UP SHIFT push_forward
+FIRST_PERSON DOWN SHIFT push_backward
+FIRST_PERSON PGUP SHIFT jump
+FIRST_PERSON PGDN SHIFT push_down
+
+FIRST_PERSON PAD_LEFT SHIFT slide_left
+FIRST_PERSON PAD_RIGHT SHIFT slide_right
+FIRST_PERSON PAD_UP SHIFT push_forward
+FIRST_PERSON PAD_DOWN SHIFT push_backward
+FIRST_PERSON PAD_PGUP SHIFT jump
+FIRST_PERSON PAD_PGDN SHIFT push_down
+FIRST_PERSON PAD_HOME SHIFT toggle_fly
+FIRST_PERSON PAD_ENTER SHIFT start_chat
+
+THIRD_PERSON A NONE turn_left
+THIRD_PERSON D NONE turn_right
+THIRD_PERSON A SHIFT slide_left
+THIRD_PERSON D SHIFT slide_right
+THIRD_PERSON W NONE push_forward
+THIRD_PERSON S NONE push_backward
+THIRD_PERSON W SHIFT push_forward
+THIRD_PERSON S SHIFT push_backward
+THIRD_PERSON E NONE jump
+THIRD_PERSON C NONE push_down
+THIRD_PERSON E SHIFT jump
+THIRD_PERSON C SHIFT push_down
+
+THIRD_PERSON F NONE toggle_fly
+THIRD_PERSON F SHIFT toggle_fly
+
+THIRD_PERSON SPACE NONE stop_moving
+THIRD_PERSON ENTER NONE start_chat
+
+THIRD_PERSON LEFT NONE turn_left
+THIRD_PERSON LEFT SHIFT slide_left
+THIRD_PERSON RIGHT NONE turn_right
+THIRD_PERSON RIGHT SHIFT slide_right
+THIRD_PERSON UP NONE push_forward
+THIRD_PERSON DOWN NONE push_backward
+THIRD_PERSON UP SHIFT push_forward
+THIRD_PERSON DOWN SHIFT push_backward
+THIRD_PERSON PGUP NONE jump
+THIRD_PERSON PGDN NONE push_down
+THIRD_PERSON PGUP SHIFT jump
+THIRD_PERSON PGDN SHIFT push_down
+THIRD_PERSON HOME SHIFT toggle_fly
+THIRD_PERSON HOME NONE toggle_fly
+
+THIRD_PERSON PAD_LEFT NONE turn_left
+THIRD_PERSON PAD_LEFT SHIFT slide_left
+THIRD_PERSON PAD_RIGHT NONE turn_right
+THIRD_PERSON PAD_RIGHT SHIFT slide_right
+THIRD_PERSON PAD_UP NONE push_forward
+THIRD_PERSON PAD_DOWN NONE push_backward
+THIRD_PERSON PAD_UP SHIFT push_forward
+THIRD_PERSON PAD_DOWN SHIFT push_backward
+THIRD_PERSON PAD_PGUP NONE jump
+THIRD_PERSON PAD_PGDN NONE push_down
+THIRD_PERSON PAD_PGUP SHIFT jump
+THIRD_PERSON PAD_PGDN SHIFT push_down
+THIRD_PERSON PAD_HOME NONE toggle_fly
+THIRD_PERSON PAD_HOME SHIFT toggle_fly
+THIRD_PERSON PAD_CENTER NONE stop_moving
+THIRD_PERSON PAD_CENTER SHIFT stop_moving
+THIRD_PERSON PAD_ENTER NONE start_chat
+THIRD_PERSON PAD_ENTER SHIFT start_chat
+
+# Camera controls in third person on Alt
+THIRD_PERSON LEFT ALT spin_around_cw
+THIRD_PERSON RIGHT ALT spin_around_ccw
+THIRD_PERSON UP ALT move_forward
+THIRD_PERSON DOWN ALT move_backward
+THIRD_PERSON PGUP ALT spin_over
+THIRD_PERSON PGDN ALT spin_under
+
+THIRD_PERSON PAD_LEFT ALT spin_around_cw
+THIRD_PERSON PAD_RIGHT ALT spin_around_ccw
+THIRD_PERSON PAD_UP ALT move_forward
+THIRD_PERSON PAD_DOWN ALT move_backward
+THIRD_PERSON PAD_PGUP ALT spin_over
+THIRD_PERSON PAD_PGDN ALT spin_under
+THIRD_PERSON PAD_ENTER ALT start_chat
+
+# mimic alt zoom behavior with keyboard only
+THIRD_PERSON A CTL_ALT spin_around_cw
+THIRD_PERSON D CTL_ALT spin_around_ccw
+THIRD_PERSON W CTL_ALT spin_over
+THIRD_PERSON S CTL_ALT spin_under
+THIRD_PERSON E CTL_ALT spin_over
+THIRD_PERSON C CTL_ALT spin_under
+
+THIRD_PERSON LEFT CTL_ALT spin_around_cw
+THIRD_PERSON RIGHT CTL_ALT spin_around_ccw
+THIRD_PERSON UP CTL_ALT spin_over
+THIRD_PERSON DOWN CTL_ALT spin_under
+THIRD_PERSON PGUP CTL_ALT spin_over
+THIRD_PERSON PGDN CTL_ALT spin_under
+
+THIRD_PERSON PAD_LEFT CTL_ALT spin_around_cw
+THIRD_PERSON PAD_RIGHT CTL_ALT spin_around_ccw
+THIRD_PERSON PAD_UP CTL_ALT spin_over
+THIRD_PERSON PAD_DOWN CTL_ALT spin_under
+THIRD_PERSON PAD_PGUP CTL_ALT spin_over
+THIRD_PERSON PAD_PGDN CTL_ALT spin_under
+THIRD_PERSON PAD_ENTER CTL_ALT start_chat
+
+# Therefore pan on Alt-Shift
+THIRD_PERSON A CTL_ALT_SHIFT pan_left
+THIRD_PERSON D CTL_ALT_SHIFT pan_right
+THIRD_PERSON W CTL_ALT_SHIFT pan_up
+THIRD_PERSON S CTL_ALT_SHIFT pan_down
+
+THIRD_PERSON LEFT CTL_ALT_SHIFT pan_left
+THIRD_PERSON RIGHT CTL_ALT_SHIFT pan_right
+THIRD_PERSON UP CTL_ALT_SHIFT pan_up
+THIRD_PERSON DOWN CTL_ALT_SHIFT pan_down
+
+THIRD_PERSON PAD_LEFT CTL_ALT_SHIFT pan_left
+THIRD_PERSON PAD_RIGHT CTL_ALT_SHIFT pan_right
+THIRD_PERSON PAD_UP CTL_ALT_SHIFT pan_up
+THIRD_PERSON PAD_DOWN CTL_ALT_SHIFT pan_down
+THIRD_PERSON PAD_ENTER CTL_ALT_SHIFT start_chat
+
+# Basic editing camera control
+EDIT A NONE spin_around_cw
+EDIT D NONE spin_around_ccw
+EDIT W NONE move_forward
+EDIT S NONE move_backward
+EDIT E NONE spin_over
+EDIT C NONE spin_under
+EDIT ENTER NONE start_chat
+EDIT PAD_ENTER NONE start_chat
+
+EDIT LEFT NONE spin_around_cw
+EDIT RIGHT NONE spin_around_ccw
+EDIT UP NONE move_forward
+EDIT DOWN NONE move_backward
+EDIT PGUP NONE spin_over
+EDIT PGDN NONE spin_under
+
+EDIT A SHIFT pan_left
+EDIT D SHIFT pan_right
+EDIT W SHIFT pan_up
+EDIT S SHIFT pan_down
+
+EDIT LEFT SHIFT pan_left
+EDIT RIGHT SHIFT pan_right
+EDIT UP SHIFT pan_up
+EDIT DOWN SHIFT pan_down
+
+# Walking works with ALT held down.
+EDIT A ALT slide_left
+EDIT D ALT slide_right
+EDIT W ALT push_forward
+EDIT S ALT push_backward
+EDIT E ALT jump
+EDIT C ALT push_down
+
+EDIT LEFT ALT slide_left
+EDIT RIGHT ALT slide_right
+EDIT UP ALT push_forward
+EDIT DOWN ALT push_backward
+EDIT PGUP ALT jump
+EDIT PGDN ALT push_down
+EDIT HOME ALT toggle_fly
+
+EDIT PAD_LEFT ALT slide_left
+EDIT PAD_RIGHT ALT slide_right
+EDIT PAD_UP ALT push_forward
+EDIT PAD_DOWN ALT push_backward
+EDIT PAD_PGUP ALT jump
+EDIT PAD_PGDN ALT push_down
+EDIT PAD_ENTER ALT start_chat
+
+SITTING LEFT ALT spin_around_cw
+SITTING RIGHT ALT spin_around_ccw
+SITTING UP ALT move_forward
+SITTING DOWN ALT move_backward
+SITTING PGUP ALT spin_over
+SITTING PGDN ALT spin_under
+
+SITTING A NONE spin_around_cw_sitting
+SITTING D NONE spin_around_ccw_sitting
+SITTING W NONE move_forward_sitting
+SITTING S NONE move_backward_sitting
+SITTING E NONE spin_over_sitting
+SITTING C NONE spin_under_sitting
+
+SITTING LEFT NONE spin_around_cw_sitting
+SITTING RIGHT NONE spin_around_ccw_sitting
+SITTING UP NONE move_forward_sitting
+SITTING DOWN NONE move_backward_sitting
+SITTING PGUP NONE spin_over_sitting
+SITTING PGDN NONE spin_under_sitting
+
+SITTING PAD_LEFT NONE spin_around_cw_sitting
+SITTING PAD_RIGHT NONE spin_around_ccw_sitting
+SITTING PAD_UP NONE move_forward_sitting
+SITTING PAD_DOWN NONE move_backward_sitting
+SITTING PAD_PGUP NONE spin_over_sitting
+SITTING PAD_PGDN NONE spin_under_sitting
+SITTING PAD_CENTER NONE stop_moving
+SITTING PAD_ENTER NONE start_chat
+
+# these are for passing controls when sitting on vehicles
+SITTING A SHIFT slide_left
+SITTING D SHIFT slide_right
+SITTING LEFT SHIFT slide_left
+SITTING RIGHT SHIFT slide_right
+
+SITTING PAD_LEFT SHIFT slide_left
+SITTING PAD_RIGHT SHIFT slide_right
+SITTING PAD_ENTER SHIFT start_chat
+
+# pan on Alt-Shift
+SITTING A CTL_ALT_SHIFT pan_left
+SITTING D CTL_ALT_SHIFT pan_right
+SITTING W CTL_ALT_SHIFT pan_up
+SITTING S CTL_ALT_SHIFT pan_down
+
+SITTING LEFT CTL_ALT_SHIFT pan_left
+SITTING RIGHT CTL_ALT_SHIFT pan_right
+SITTING UP CTL_ALT_SHIFT pan_up
+SITTING DOWN CTL_ALT_SHIFT pan_down
+
+SITTING PAD_LEFT CTL_ALT_SHIFT pan_left
+SITTING PAD_RIGHT CTL_ALT_SHIFT pan_right
+SITTING PAD_UP CTL_ALT_SHIFT pan_up
+SITTING PAD_DOWN CTL_ALT_SHIFT pan_down
+SITTING PAD_ENTER CTL_ALT_SHIFT start_chat
+
+SITTING ENTER NONE start_chat
+
+# Avatar editing camera controls
+EDIT_AVATAR A NONE edit_avatar_spin_cw
+EDIT_AVATAR D NONE edit_avatar_spin_ccw
+EDIT_AVATAR W NONE edit_avatar_move_forward
+EDIT_AVATAR S NONE edit_avatar_move_backward
+EDIT_AVATAR E NONE edit_avatar_spin_over
+EDIT_AVATAR C NONE edit_avatar_spin_under
+EDIT_AVATAR LEFT NONE edit_avatar_spin_cw
+EDIT_AVATAR RIGHT NONE edit_avatar_spin_ccw
+EDIT_AVATAR UP NONE edit_avatar_move_forward
+EDIT_AVATAR DOWN NONE edit_avatar_move_backward
+EDIT_AVATAR PGUP NONE edit_avatar_spin_over
+EDIT_AVATAR PGDN NONE edit_avatar_spin_under
+EDIT_AVATAR ENTER NONE start_chat
+EDIT_AVATAR PAD_LEFT NONE edit_avatar_spin_cw
+EDIT_AVATAR PAD_RIGHT NONE edit_avatar_spin_ccw
+EDIT_AVATAR PAD_UP NONE edit_avatar_move_forward
+EDIT_AVATAR PAD_DOWN NONE edit_avatar_move_backward
+EDIT_AVATAR PAD_PGUP NONE edit_avatar_spin_over
+EDIT_AVATAR PAD_PGDN NONE edit_avatar_spin_under
+EDIT_AVATAR PAD_ENTER NONE start_chat
diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini
new file mode 100644
index 0000000000..0545c38959
--- /dev/null
+++ b/indra/newview/app_settings/keywords.ini
@@ -0,0 +1,513 @@
+llkeywords version 2
+
+# sections
+[word .5, .1, .3]
+default Name of default state that all scripts must have
+state Keyword to indicate state block or state transition
+
+# data types
+[word .1, .3, .1]
+integer Integer type
+float Floating-point type
+string String type
+key Key type. Use NULL_KEY to test for empty keys.
+vector Vector type of 3 floats. Used to represent 3D motion, Euler angles, and color.:Access components by .x, .y. or .z
+rotation Rotation type of 4 floats. Used to represent rotation.:Access components by .x, .y., .z, or .w
+list List of various data types
+
+# events
+[word 0, .3, .5]
+state_entry state_entry():Triggered on any state transition and startup
+state_exit state_exit():Triggered on any state transition
+touch_start touch_start(integer num_detected):Triggered by the start of agent clicking on task
+touch touch(integer num_detected):Triggered while agent is clicking on task
+touch_end touch_end(integer num_detected):Triggered when agent stops clicking on task
+collision_start collision_start(integer num_detected):Triggered when task starts colliding with another task
+collision collision(integer num_detected):Triggered while task is colliding with another task
+collision_end collision_end(integer num_detected):Triggered when task stops colliding with another task
+land_collision_start land_collision_start(vector pos):Triggered when task starts colliding with land
+land_collision land_collision(vector pos):Triggered when task is colliding with land
+land_collision_end land_collision_end(vector pos):Triggered when task stops colliding with land
+timer timer():Result of the llSetTimerEvent library function call.
+listen listen(integer channel, string name, key id, string message):Result of the llListen library function call
+sensor sensor(integer num_detected):Result of the llSensor library function call
+no_sensor no_sensor():Result of the llSensor library function call
+control control(key id, integer level, integer edge):Result of llTakeControls library function call
+at_target at_target(integer tnum, vector targetpos, vector ourpos):Result of llTarget library function call
+not_at_target not_at_target():Result of llTarget library function call
+at_rot_target at_rot_target(integer tnum, rotation targetrot, rotation ourrot):Result of LLRotTarget library function call
+not_at_rot_target not_at_rot_target():Result of LLRotTarget library function call
+money money(key id, integer amount):Triggered when money is given to task
+email email(string time, string address, string subj, string message, integer num_left):Triggered when task receives email
+run_time_permissions run_time_permissions(integer perm):Triggered when an agent grants run time permissions to task
+attach attach(key id):Triggered when an agent attaches or detaches from agent
+dataserver dataserver(key queryid, string data):Triggered when task receives asynchronous data
+moving_start moving_start():Triggered when task begins moving
+moving_end moving_end():Triggered when task stops moving
+on_rez on_rez(integer start_param):Triggered when task is rezed in from inventory or another task
+object_rez object_rez(key id):Triggered when task rezes in another task
+link_message link_message(integer sender_num, integer num, string str, key id):Triggered when task receives a link message via LLMessageLinked library function call
+changed changed( integer change ):Triggered various event change the task:(test change with CHANGED_INVENTORY, CHANGED_COLOR, CHANGED_SHAPE, CHANGED_SCALE, CHANGED_TEXTURE, CHANGED_LINK, CHANGED_ALLOWED_DROP, CHANGED_OWNER, CHANGED_REGION, CHANGED_TELEPORT)
+remote_data remote_data(integer event_type, key channel, key message_id, string sender,integer idata, string sdata):Triggered by various XML-RPC calls (event_type will be one of REMOTE_DATA_CHANNEL, REMOTE_DATA_REQUEST, REMOTE_DATA_REPLY)
+http_response http_response(key request_id, integer status, list metadata, string body):Triggered when task receives a response to one of its llHTTPRequests
+
+# integer constants
+[word .1, .1, .5]
+TRUE Integer constant for Boolean operations
+FALSE Integer constant for Boolean operations
+STATUS_PHYSICS Passed in the llSetStatus library function. If TRUE, object moves physically
+STATUS_PHANTOM Passed in the llSetStatus library function. If TRUE, object doesn't collide with other objects
+STATUS_ROTATE_X Passed in the llSetStatus library function. If FALSE, object doesn't rotate around local X axis
+STATUS_ROTATE_Y Passed in the llSetStatus library function. If FALSE, object doesn't rotate around local Y axis
+STATUS_ROTATE_Z Passed in the llSetStatus library function. If FALSE, object doesn't rotate around local Z axis
+STATUS_SANDBOX Passed in the llSetStatus library function. If TRUE, object can't cross region boundaries or move more than 10 meters from its start location
+STATUS_BLOCK_GRAB Passed in the llSetStatus library function. If TRUE, object can't be grabbed and physically dragged
+STATUS_DIE_AT_EDGE Passed in the llSetStatus library function. If TRUE, objects that reach the edge of the world just die:rather than teleporting back to the owner
+STATUS_RETURN_AT_EDGE Passed in the llSetStatus library function. If TRUE, script rezzed objects that reach the edge of the world:are returned rather than killed:STATUS_RETURN_AT_EDGE trumps STATUS_DIE_AT_EDGE if both are set
+STATUS_CAST_SHADOWS Passed in the llSetStatus library function. If TRUE, object casts shadows on other objects
+AGENT Passed in llSensor library function to look for other Agents
+ACTIVE Passed in llSensor library function to look for moving objects
+PASSIVE Passed in llSensor library function to look for objects that aren't moving
+SCRIPTED Passed in llSensor library function to look for scripted objects
+CONTROL_FWD Passed to llTakeControls library function and used control event handler to test for agent forward control
+CONTROL_BACK Passed to llTakeControls library function and used control event handler to test for agent back control
+CONTROL_LEFT Passed to llTakeControls library function and used control event handler to test for agent left control
+CONTROL_RIGHT Passed to llTakeControls library function and used control event handler to test for agent right control
+CONTROL_ROT_LEFT Passed to llTakeControls library function and used control event handler to test for agent rotate left control
+CONTROL_ROT_RIGHT Passed to llTakeControls library function and used control event handler to test for agent rotate right control
+CONTROL_UP Passed to llTakeControls library function and used control event handler to test for agent up control
+CONTROL_DOWN Passed to llTakeControls library function and used control event handler to test for agent down control
+CONTROL_LBUTTON Passed to llTakeControls library function and used control event handler to test for agent left button control
+CONTROL_ML_LBUTTON Passed to llTakeControls library function and used control event handler to test for agent left button control with the agent in mouse look
+PERMISSION_DEBIT Passed to llRequestPermissions library function to request permission to take money from agent's account
+PERMISSION_TAKE_CONTROLS Passed to llRequestPermissions library function to request permission to take agent's controls
+# PERMISSION_REMAP_CONTROLS Passed to llRequestPermissions library function to request permission to remap agent's controls (not implemented yet)
+PERMISSION_TRIGGER_ANIMATION Passed to llRequestPermissions library function to request permission to trigger animation on agent
+PERMISSION_ATTACH Passed to llRequestPermissions library function to request permission to attach/detach from agent
+# PERMISSION_RELEASE_OWNERSHIP Passed to llRequestPermissions library function to request permission to release ownership (not implemented)
+PERMISSION_CHANGE_LINKS Passed to llRequestPermissions library function to request permission to change links
+# PERMISSION_CHANGE_JOINTS Passed to llRequestPermissions library function to request permission to change joints (not implemented)
+# PERMISSION_CHANGE_PERMISSIONS Passed to llRequestPermissions library function to request permission to change permissions
+PERMISSION_TRACK_CAMERA Passed to llRequestPermissions library function to request permission to track agent's camera
+PERMISSION_CONTROL_CAMERA Passed to llRequestPermissions library function to request permission to change agent's camera
+
+DEBUG_CHANNEL Chat channel reserved for debug and error messages from scripts
+PUBLIC_CHANNEL Chat channel that broadcasts to all nearby users
+
+AGENT_FLYING Returned by llGetAgentInfo if the Agent is flying
+AGENT_ATTACHMENTS Returned by llGetAgentInfo if the Agent has attachments
+AGENT_SCRIPTED Returned by llGetAgentInfo if the Agent has scripted attachments
+AGENT_SITTING Returned by llGetAgentInfo if the Agent is sitting
+AGENT_ON_OBJECT Returned by llGetAgentInfo if the Agent is sitting on an object
+AGENT_MOUSELOOK Returned by llGetAgentInfo if the Agent is in mouselook
+AGENT_AWAY Returned by llGetAgentInfo if the Agent is in away mode
+AGENT_WALKING Returned by llGetAgentInfo if the Agent is walking
+AGENT_IN_AIR Returned by llGetAgentInfo if the Agent is in the air
+AGENT_TYPING Returned by llGetAgentInfo if the Agent is typing
+AGENT_CROUCHING Returned by llGetAgentInfo if the Agent is crouching
+AGENT_BUSY Returned by llGetAgentInfo if the Agent is busy
+AGENT_ALWAYS_RUN Returned by llGetAgentInfo if the Agent has 'Always Run' enabled
+
+PSYS_PART_FLAGS
+PSYS_PART_START_COLOR
+PSYS_PART_START_ALPHA
+PSYS_PART_START_SCALE
+PSYS_PART_END_COLOR
+PSYS_PART_END_ALPHA
+PSYS_PART_END_SCALE
+PSYS_PART_MAX_AGE
+
+PSYS_PART_BOUNCE_MASK
+PSYS_PART_WIND_MASK
+PSYS_PART_INTERP_COLOR_MASK
+PSYS_PART_INTERP_SCALE_MASK
+PSYS_PART_FOLLOW_SRC_MASK
+PSYS_PART_FOLLOW_VELOCITY_MASK
+PSYS_PART_TARGET_POS_MASK
+PSYS_PART_EMISSIVE_MASK
+PSYS_PART_TARGET_LINEAR_MASK
+
+PSYS_SRC_PATTERN
+PSYS_SRC_INNERANGLE Deprecated -- Use PSYS_SRC_ANGLE_BEGIN
+PSYS_SRC_OUTERANGLE Deprecated -- Use PSYS_SRC_ANGLE_END
+PSYS_SRC_ANGLE_BEGIN
+PSYS_SRC_ANGLE_END
+PSYS_SRC_BURST_RATE
+PSYS_SRC_BURST_PART_COUNT
+PSYS_SRC_BURST_RADIUS
+PSYS_SRC_BURST_SPEED_MIN
+PSYS_SRC_BURST_SPEED_MAX
+PSYS_SRC_MAX_AGE
+PSYS_SRC_ACCEL
+PSYS_SRC_TEXTURE
+PSYS_SRC_TARGET_KEY
+PSYS_SRC_OMEGA
+
+PSYS_SRC_PATTERN_DROP
+PSYS_SRC_PATTERN_EXPLODE
+PSYS_SRC_PATTERN_ANGLE
+PSYS_SRC_PATTERN_ANGLE_CONE
+PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY
+
+# some vehicle params
+VEHICLE_TYPE_NONE
+VEHICLE_TYPE_SLED
+VEHICLE_TYPE_CAR
+VEHICLE_TYPE_BOAT
+VEHICLE_TYPE_AIRPLANE
+VEHICLE_TYPE_BALLOON
+
+VEHICLE_REFERENCE_FRAME Rotation of vehicle axes relative to local frame
+
+VEHICLE_LINEAR_FRICTION_TIMESCALE A vector of timescales for exponential decay of linear velocity along the three vehicle axes
+VEHICLE_ANGULAR_FRICTION_TIMESCALE A vector of timescales for exponential decay of angular velocity about the three vehicle axes
+VEHICLE_LINEAR_MOTOR_DIRECTION The linear velocity that the vehicle will try to achieve
+VEHICLE_LINEAR_MOTOR_OFFSET An offset from the center of mass of the vehicle where the linear motor is applied
+VEHICLE_ANGULAR_MOTOR_DIRECTION The angular velocity that the vehicle will try to achieve
+
+VEHICLE_HOVER_HEIGHT The height the vehicle will try to hover
+VEHICLE_HOVER_EFFICIENCY A slider between 0 (bouncy) and 1 (critically damped) hover behavior
+VEHICLE_HOVER_TIMESCALE The period of time for the vehicle to achieve its hover height
+VEHICLE_BUOYANCY A slider between 0 (no anti-gravity) and 1 (full anti-gravity)
+
+VEHICLE_LINEAR_DEFLECTION_EFFICIENCY A slider between 0 (no deflection) and 1 (maximum strength)
+VEHICLE_LINEAR_DEFLECTION_TIMESCALE The exponential timescale for the vehicle to redirect its velocity to be along its x-axis
+
+VEHICLE_LINEAR_MOTOR_TIMESCALE The exponential timescale for the vehicle to achive its full linear motor velocity
+VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE The exponential timescale for the linear motor's effectiveness to decay toward zero
+
+VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY A slider between 0 (no deflection) and 1 (maximum strength)
+VEHICLE_ANGULAR_DEFLECTION_TIMESCALE The exponential timescale for the vehicle to achieve full angular deflection
+
+VEHICLE_ANGULAR_MOTOR_TIMESCALE The exponential timescale for the vehicle to achive its full angular motor velocity
+VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE The exponential timescale for the angular motor's effectiveness to decay toward zero
+
+VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY A slider between 0 (bouncy) and 1 (critically damped) attraction of vehicle z-axis to world z-axis (vertical)
+VEHICLE_VERTICAL_ATTRACTION_TIMESCALE The exponential timescale for the vehicle to align its z-axis to the world z-axis (vertical)
+
+VEHICLE_BANKING_EFFICIENCY A slider between -1 (leans out of turns), 0 (no banking), and +1 (leans into turns)
+VEHICLE_BANKING_MIX A slider betwen 0 (static banking) and 1 (dynamic banking)
+VEHICLE_BANKING_TIMESCALE The exponential timescale for the banking behavior to take full effect
+
+VEHICLE_FLAG_NO_DEFLECTION_UP Prevents linear deflection along world-z axis
+VEHICLE_FLAG_LIMIT_ROLL_ONLY Removes vertical attraction for changes in vehicle pitch
+VEHICLE_FLAG_HOVER_WATER_ONLY Hover only pays attention to water level
+VEHICLE_FLAG_HOVER_TERRAIN_ONLY Hover only pays attention to terrain height
+VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT Hover only pays attention to global height
+VEHICLE_FLAG_HOVER_UP_ONLY Hover only pushes up
+VEHICLE_FLAG_LIMIT_MOTOR_UP Prevents ground vehicles from motoring into the sky
+VEHICLE_FLAG_MOUSELOOK_STEER Makes vehicle try to turn toward mouselook direction.
+VEHICLE_FLAG_MOUSELOOK_BANK Makes vehicle try to turn toward mouselook direction assuming banking is enabled.
+VEHICLE_FLAG_CAMERA_DECOUPLED Causes the camera look-at axis to NOT move when the vehicle rotates.
+
+CAMERA_PITCH (-45 to 80) (Adjusts the angular amount that the camera aims straight ahead vs. straight down, maintaining the same distance. Analogous to 'incidence'.")
+CAMERA_FOCUS_OFFSET (-10 to 10) A vector that adjusts the position of the camera focus position relative to the subject
+CAMERA_POSITION_LAG (0.0 to 3.0) How much the camera lags as it tries to move towards its 'ideal' position
+CAMERA_FOCUS_LAG (0.0 to 3.0) How much the camera lags as it tries to aim towards the subject
+CAMERA_DISTANCE (0.5 to 10) Sets how far away the camera wants to be from its subject
+CAMERA_BEHINDNESS_ANGLE (0 to 180) Sets the angle in degrees within which the camera is not constrained by changes in subject rotation
+CAMERA_BEHINDNESS_LAG (0.0 to 3.0) Sets how strongly the camera is forced to stay behind the target if outside of behindness angle
+CAMERA_POSITION_THRESHOLD (0.0 to 4.0) Sets the radius of a sphere around the camera's ideal position within which it is not affected by subject motion
+CAMERA_FOCUS_THRESHOLD (0.0 to 4.0) Sets the radius of a sphere around the camera's subject position within which its focus is not affected by subject motion
+CAMERA_ACTIVE (0 or 1) Turns on or off scripted control of the camera
+CAMERA_POSITION Sets the position of the camera
+CAMERA_FOCUS Sets the focus (target position) of the camera
+CAMERA_POSITION_LOCKED (0 or 1) Locks the camera position so it will not move
+CAMERA_FOCUS_LOCKED (0 or 1) Locks the camera focus so it will not move
+
+INVENTORY_TEXTURE Passed to task inventory library functions to reference textures
+INVENTORY_SOUND Passed to task inventory library functions to reference sounds
+INVENTORY_OBJECT Passed to task inventory library functions to reference objects
+INVENTORY_SCRIPT Passed to task inventory library functions to reference scripts
+INVENTORY_LANDMARK Passed to task inventory library functions to reference landmarks
+INVENTORY_CLOTHING Passed to task inventory library functions to reference clothing
+INVENTORY_NOTECARD Passed to task inventory library functions to reference notecards
+INVENTORY_BODYPART Passed to task inventory library functions to reference body parts
+INVENTORY_ANIMATION Passed to task inventory library functions to reference animations
+INVENTORY_GESTURE Passed to task inventory library functions to reference gestures
+INVENTORY_ALL Passed to task inventory library functions to reference all inventory items
+INVENTORY_NONE Returned by llGetInventoryType when no item is found.
+
+ATTACH_CHEST Passed to llAttachToAvatar to attach task to chest
+ATTACH_HEAD Passed to llAttachToAvatar to attach task to head
+ATTACH_LSHOULDER Passed to llAttachToAvatar to attach task to left shoulder
+ATTACH_RSHOULDER Passed to llAttachToAvatar to attach task to right shoulder
+ATTACH_LHAND Passed to llAttachToAvatar to attach task to left hand
+ATTACH_RHAND Passed to llAttachToAvatar to attach task to right hand
+ATTACH_LFOOT Passed to llAttachToAvatar to attach task to left foot
+ATTACH_RFOOT Passed to llAttachToAvatar to attach task to right foot
+ATTACH_BACK Passed to llAttachToAvatar to attach task to back
+ATTACH_PELVIS Passed to llAttachToAvatar to attach task to pelvis
+ATTACH_MOUTH Passed to llAttachToAvatar to attach task to mouth
+ATTACH_CHIN Passed to llAttachToAvatar to attach task to chin
+ATTACH_LEAR Passed to llAttachToAvatar to attach task to left ear
+ATTACH_REAR Passed to llAttachToAvatar to attach task to right ear
+ATTACH_LEYE Passed to llAttachToAvatar to attach task to left eye
+ATTACH_REYE Passed to llAttachToAvatar to attach task to right eye
+ATTACH_NOSE Passed to llAttachToAvatar to attach task to noce
+ATTACH_RUARM Passed to llAttachToAvatar to attach task to right upper arm
+ATTACH_RLARM Passed to llAttachToAvatar to attach task to right lower arm
+ATTACH_LUARM Passed to llAttachToAvatar to attach task to left upper arm
+ATTACH_LLARM Passed to llAttachToAvatar to attach task to left lower arm
+ATTACH_RHIP Passed to llAttachToAvatar to attach task to right hip
+ATTACH_RULEG Passed to llAttachToAvatar to attach task to right upper leg
+ATTACH_RLLEG Passed to llAttachToAvatar to attach task to right lower leg
+ATTACH_LHIP Passed to llAttachToAvatar to attach task to left hip
+ATTACH_LULEG Passed to llAttachToAvatar to attach task to left upper leg
+ATTACH_LLLEG Passed to llAttachToAvatar to attach task to left lower leg
+ATTACH_BELLY Passed to llAttachToAvatar to attach task to belly
+ATTACH_RPEC Passed to llAttachToAvatar to attach task to right pectoral
+ATTACH_LPEC Passed to llAttachToAvatar to attach task to left pectoral
+
+LAND_LEVEL Passed to llModifyLand to level terrain
+LAND_RAISE Passed to llModifyLand to raise terrain
+LAND_LOWER Passed to llModifyLand to lower terrain
+LAND_SMOOTH Passed to llModifyLand to smooth terrain
+LAND_NOISE Passed to llModifyLand to randomize terrain
+LAND_REVERT Passed to llModifyLand to revert terrain toward original state
+LAND_SMALL_BRUSH Passed to llModifyLand to modify small land areas
+LAND_MEDIUM_BRUSH Passed to llModifyLand to modify medium land areas
+LAND_LARGE_BRUSH Passed to llModifyLand to modify large land areas
+
+DATA_PAYINFO Passed to llRequestAgentData to get payment status of an agent
+DATA_ONLINE Passed to llRequestAgentData to determine if agent is online
+DATA_NAME Passed to llRequestAgentData to get full agent name
+DATA_BORN Passed to llRequestAgentData to get born on date as a string
+DATA_RATING Passed to llRequestAgentData to get a comma separated sting of integer ratings
+DATA_SIM_POS Passed to llRequestSimulatorData to get a string (cast to vector) of a simulator's global position
+DATA_SIM_STATUS Passed to llRequestSimulatorData to get the status of a simulator
+DATA_SIM_RATING Passed to llRequestSimulatorData to get the rating of a simulator
+
+PAYMENT_INFO_ON_FILE Used with llRequestAgentData to tell if Agent is of "Payment Info On File" status
+PAYMENT_INFO_USED Used with llRequestAgentData to tell if Agent is of "Payment Info Used" status
+
+ANIM_ON Enable texture animation
+LOOP Loop when animating textures
+REVERSE Animate in the reverse direction
+PING_PONG Animate forward, then reverse.
+SMOOTH Textures slides, instead of stepping
+ROTATE Rotates the texture, instead of using frames
+SCALE Scales the texture, instead of using frames
+
+ALL_SIDES Passed to various texture and color library functions to modify all sides
+
+LINK_SET Passed to various link functions to modify all blocks in the object
+LINK_ROOT Passed to various link functions to modify only the root block (no effect on single block objects)
+LINK_ALL_OTHERS Passed to various link functions to modify all other blocks in the object
+LINK_ALL_CHILDREN Passed to various link functions to modify all child blocks in the object
+LINK_THIS Passed to various link functions to modify only the calling block
+
+CHANGED_INVENTORY Parameter of changed event handler used to indicate change to task's inventory
+CHANGED_COLOR Parameter of changed event handler used to indicate change to task's color
+CHANGED_SHAPE Parameter of changed event handler used to indicate change to task's shape parameters
+CHANGED_SCALE Parameter of changed event handler used to indicate change to task's scale
+CHANGED_TEXTURE Parameter of changed event handler used to indicate change to task's texture
+CHANGED_LINK Parameter of changed event handler used to indicate change to task's link status
+CHANGED_ALLOWED_DROP Parameter of changed event handler used to indicate a user dropped an inventory item:onto task that was allowed only by llAllowInventoryDrop function call
+CHANGED_OWNER Parameter of changed event handler used to indicate change to task's owner ONLY when an object is sold as original or deeded to group
+CHANGED_REGION Parameter of changed event handler used to indicate the region has changed
+CHANGED_TELEPORT Parameter of changed event handler used to indicate teleport has completed
+
+TYPE_INTEGER Indicates that the list entry is holding an integer
+TYPE_FLOAT Indicates that the list entry is holding an float
+TYPE_STRING Indicates that the list entry is holding an string
+TYPE_KEY Indicates that the list entry is holding an key
+TYPE_VECTOR Indicates that the list entry is holding an vector
+TYPE_ROTATION Indicates that the list entry is holding an rotation
+TYPE_INVALID Indicates that this wasn't a valid list entry
+
+
+REMOTE_DATA_CHANNEL Value of event_type in remote_event after successful llOpenRemoteDataChannel
+REMOTE_DATA_REQUEST Value of event_type in remote_event if XML-RPC request is received
+REMOTE_DATA_REPLY Value of event_type in remote_event if XML-RPC reply is received
+
+
+PRIM_TYPE Followed by PRIM_TYPE_BOX, PRIM_TYPE_CYLINDER, PRIM_TYPE_PRISM, PRIM_TYPE_SPHERE,:PRIM_TYPE_TORUS, or PRIM_TYPE_TUBE and their arguments
+PRIM_MATERIAL Sets material to PRIM_MATERIAL_STONE, PRIM_MATERIAL_METAL, PRIM_MATERIAL_GLASS,:PRIM_MATERIAL_WOOD, PRIM_MATERIAL_FLESH, PRIM_MATERIAL_PLASTIC,:PRIM_MATERIAL_RUBBER, or PRIM_MATERIAL_LIGHT
+PRIM_PHYSICS Sets physics to TRUE or FALSE
+PRIM_FLEXIBLE Sets primitive flexibility to TRUE or FALSE
+PRIM_POINT_LIGHT Sets light emission to TRUE or FALSE
+PRIM_TEMP_ON_REZ Sets temporay on rez to TRUE or FALSE
+PRIM_PHANTOM Sets phantom to TRUE or FALSE
+PRIM_CAST_SHADOWS Enables or disables shadow casting for the primitive
+PRIM_POSITION Sets primitive position to a vector position
+PRIM_SIZE Sets primitive size to a vector size
+PRIM_ROTATION Sets primitive rotation
+PRIM_TEXTURE Followed by an integer face, key id, vector repeats, vector offsets,:and float rotation in radians
+PRIM_COLOR Followed by an integer face, vector color, and float alpha
+PRIM_BUMP_SHINY Followed by an integer face, one of PRIM_SHINY_NONE, PRIM_SHINY_LOW,:PRIM_SHINY_MEDIUM, or PRIM_SHINY_HIGH,:and one of PRIM_BUMP_NONE, PRIM_BUMP_BRIGHT, PRIM_BUMP_DARK, etc
+PRIM_FULLBRIGHT Followed by an integer face, and TRUE or FALSE
+PRIM_TEXGEN Followed by an integer face, and one of PRIM_TEXGEN_DEFAULT or PRIM_TEXGEN_PLANAR
+
+PRIM_TYPE_BOX Followed by integer hole shape, vector cut, float hollow, vector twist,:vector top size, and vector top shear
+PRIM_TYPE_CYLINDER Followed by integer hole shape, vector cut, float hollow, vector twist,:vector top size, and vector top shear
+PRIM_TYPE_PRISM Followed by integer hole shape, vector cut, float hollow, vector twist,:vector top size, and vector top shear
+PRIM_TYPE_SPHERE Followed by integer hole shape, vector cut, float hollow, vector twist,:and vector dimple
+PRIM_TYPE_TORUS Followed by integer hole shape, vector cut, float hollow, vector twist,:vector hole size, vector top shear, vector advanced cut, vector taper,:float revolutions, float radius offset, and float skew
+PRIM_TYPE_TUBE Followed by integer hole shape, vector cut, float hollow, vector twist,:vector hole size, vector top shear, vector advanced cut, vector taper,:float revolutions, float radius offset, and float skew
+PRIM_TYPE_RING Followed by integer hole shape, vector cut, float hollow, vector twist,:vector hole size, vector top shear, vector advanced cut, vector taper,:float revolutions, float radius offset, and float skew
+
+PRIM_HOLE_DEFAULT Sets hole type to match the prim type.
+PRIM_HOLE_SQUARE Sets hole type to square.
+PRIM_HOLE_CIRCLE Sets hole type to circle.
+PRIM_HOLE_TRIANGLE Sets hole type to triangle.
+
+PRIM_MATERIAL_STONE Sets material to stone
+PRIM_MATERIAL_METAL Sets material to metal
+PRIM_MATERIAL_GLASS Sets material to glass
+PRIM_MATERIAL_WOOD Sets material to wood
+PRIM_MATERIAL_FLESH Sets material to flesh
+PRIM_MATERIAL_PLASTIC Sets material to plastic
+PRIM_MATERIAL_RUBBER Sets material to rubber
+PRIM_MATERIAL_LIGHT Sets material to light
+
+PRIM_SHINY_NONE No shininess
+PRIM_SHINY_LOW Low shininess
+PRIM_SHINY_MEDIUM Medium shininess
+PRIM_SHINY_HIGH High shininess
+
+PRIM_BUMP_NONE No bump map
+PRIM_BUMP_BRIGHT Generate bump map from highlights
+PRIM_BUMP_DARK Generate bump map from lowlights
+PRIM_BUMP_WOOD Wood bump map
+PRIM_BUMP_BARK Bark bump map
+PRIM_BUMP_BRICKS Brick bump map
+PRIM_BUMP_CHECKER Checker bump map
+PRIM_BUMP_CONCRETE Concrete bump map
+PRIM_BUMP_TILE Tile bump map
+PRIM_BUMP_STONE Stone bump map
+PRIM_BUMP_DISKS Disk bump map
+PRIM_BUMP_GRAVEL Gravel bump map
+PRIM_BUMP_BLOBS Blob bump map
+PRIM_BUMP_SIDING Siding bump map
+PRIM_BUMP_LARGETILE Large tile bump map
+PRIM_BUMP_STUCCO Stucco bump map
+PRIM_BUMP_SUCTION Suction cup bump map
+PRIM_BUMP_WEAVE Weave bump map
+
+PRIM_TEXGEN_DEFAULT Default texture mapping
+PRIM_TEXGEN_PLANAR Planar texture mapping
+
+MASK_BASE Base permissions
+MASK_OWNER Owner permissions
+MASK_GROUP Group permissions
+MASK_EVERYONE Everyone permissions
+MASK_NEXT Next owner permissions
+
+PERM_TRANSFER Transfer permission
+PERM_MODIFY Modify permission
+PERM_COPY Copy permission
+PERM_MOVE Move permission
+PERM_ALL Move/Modify/Copy/Transfer permissions
+
+PARCEL_MEDIA_COMMAND_STOP Stop media stream
+PARCEL_MEDIA_COMMAND_PAUSE Pause media stream
+PARCEL_MEDIA_COMMAND_PLAY Play media stream
+PARCEL_MEDIA_COMMAND_LOOP Loop media stream
+PARCEL_MEDIA_COMMAND_TEXTURE Get or set the parcel's media texture
+PARCEL_MEDIA_COMMAND_URL Get or set the parcel's media url
+PARCEL_MEDIA_COMMAND_TIME Set media stream to specific time
+PARCEL_MEDIA_COMMAND_AGENT Allows media stream commands to apply to only one agent
+PARCEL_MEDIA_COMMAND_UNLOAD Unloads the media stream
+PARCEL_MEDIA_COMMAND_AUTO_ALIGN Auto aligns the media stream to the texture size. May cause a performance hit and loss of some visual quality.
+
+PAY_HIDE Used with llSetPayPrice to hide a button
+PAY_DEFAULT Used with llSetPayPrice to use the default price for a button
+
+LIST_STAT_MAX Used with llListStatistics to find the largest number in a list
+LIST_STAT_MIN Used with llListStatistics to find the smallest number in a list
+LIST_STAT_MEAN Used with llListStatistics to find the mean of the numbers in a list
+LIST_STAT_MEDIAN Used with llListStatistics to find the median of the numbers in a list
+LIST_STAT_STD_DEV Used with llListStatistics to find the standard deviation of the numbers in a list
+LIST_STAT_SUM Used with llListStatistics to find the sum of the numbers in a list
+LIST_STAT_SUM_SQUARES Used with llListStatistics to find the sum of the squares of the numbers in a list
+LIST_STAT_NUM_COUNT Used with llListStatistics to find how many numbers are in a list
+LIST_STAT_GEOMETRIC_MEAN Used with llListStatistics to find the geometric mean of the numbers in a list (all numbers must be > 0)
+LIST_STAT_RANGE Used with llListStatistics to find the range of the numbers in a list
+
+PARCEL_FLAG_ALLOW_FLY Used with llGetParcelFlags to find if a parcel allows flying
+PARCEL_FLAG_ALLOW_GROUP_SCRIPTS Used with llGetParcelFlags to find if a parcel allows group scripts
+PARCEL_FLAG_ALLOW_SCRIPTS Used with llGetParcelFlags to find if a parcel allows outside scripts
+PARCEL_FLAG_ALLOW_LANDMARK Used with llGetParcelFlags to find if a parcel allows landmarks to be created
+PARCEL_FLAG_ALLOW_TERRAFORM Used with llGetParcelFlags to find if a parcel allows anyone to terraform the land
+PARCEL_FLAG_ALLOW_DAMAGE Used with llGetParcelFlags to find if a parcel allows damage
+PARCEL_FLAG_ALLOW_CREATE_OBJECTS Used with llGetParcelFlags to find if a parcel allows anyone to create objects
+PARCEL_FLAG_USE_ACCESS_GROUP Used with llGetParcelFlags to find if a parcel limits access to a group
+PARCEL_FLAG_USE_ACCESS_LIST Used with llGetParcelFlags to find if a parcel limits access to a list of residents
+PARCEL_FLAG_USE_BAN_LIST Used with llGetParcelFlags to find if a parcel uses a ban list
+PARCEL_FLAG_USE_LAND_PASS_LIST Used with llGetParcelFlags to find if a parcel allows passes to be purchased
+PARCEL_FLAG_LOCAL_SOUND_ONLY Used with llGetParcelFlags to find if a parcel restricts spacialized sound to the parcel
+PARCEL_FLAG_RESTRICT_PUSHOBJECT Used with llGetParcelFlags to find if a parcel restricts llPushObject() calls
+
+REGION_FLAG_ALLOW_DAMAGE Used with llGetRegionFlags to find if a region is entirely damage enabled
+REGION_FLAG_FIXED_SUN Used with llGetRegionFlags to find if a region has a fixed sun position
+REGION_FLAG_BLOCK_TERRAFORM Used with llGetRegionFlags to find if a region terraforming disabled
+REGION_FLAG_SANDBOX Used with llGetRegionFlags to find if a region is a sandbox
+REGION_FLAG_DISABLE_COLLISIONS Used with llGetRegionFlags to find if a region has disabled collisions
+REGION_FLAG_DISABLE_PHYSICS Used with llGetRegionFlags to find if a region has disabled physics
+REGION_FLAG_BLOCK_FLY Used with llGetRegionFlags to find if a region blocks flying
+REGION_FLAG_ALLOW_DIRECT_TELEPORT Used with llGetRegionFlags to find if a region allows direct teleports
+REGION_FLAG_RESTRICT_PUSHOBJECT Used with llGetRegionFlags to find if a region restricts llPushObject() calls
+
+HTTP_METHOD Used with llHTTPRequest to specify the method, such as "GET" or "POST"
+HTTP_MIMETYPE Used with llHTTPRequest to specify the MIME type, defaults to "text/plain"
+HTTP_BODY_MAXLENGTH Used with llHTTPRequest to specify the maxium reponse body to return
+HTTP_VERIFY_CERT Used with llHTTPRequest to specify SSL certificate verification
+HTTP_BODY_TRUNCATED Used with http_response to indicate truncation point in bytes
+
+PARCEL_COUNT_TOTAL Used with llGetParcelPrimCount to get the total number of prims on the parcel
+PARCEL_COUNT_OWNER Used with llGetParcelPrimCount to get the number of prims on the parcel owned by the owner
+PARCEL_COUNT_GROUP Used with llGetParcelPrimCount to get the number of prims on the parcel owned by the group
+PARCEL_COUNT_OTHER Used with llGetParcelPrimCount to get the number of prims on the parcel owned by others
+PARCEL_COUNT_SELECTED Used with llGetParcelPrimCount to get the number of prims on the parcel currently selected or sat upon
+PARCEL_COUNT_TEMP Used with llGetParcelPrimCount to get the number of prims on the parcel that are temp on rez
+
+PARCEL_DETAILS_NAME Used with llGetParcelDetails to get the parcel name.
+PARCEL_DETAILS_DESC Used with llGetParcelDetails to get the parcel description.
+PARCEL_DETAILS_OWNER Used with llGetParcelDetails to get the parcel owner id.
+PARCEL_DETAILS_GROUP Used with llGetParcelDetails to get the parcel group id.
+PARCEL_DETAILS_AREA Used with llGetParcelDetails to get the parcel area in square meters.
+
+# string constants
+[word .1, .3, .5]
+NULL_KEY Indicates an empty key
+EOF Indicates the last line of a notecard was read
+
+# float constants
+[word .3, .1, .5]
+PI 3.1415926535897932384626433832795
+TWO_PI 6.283185307179586476925286766559
+PI_BY_TWO 1.5707963267948966192313216916398
+DEG_TO_RAD To convert from degrees to radians
+RAD_TO_DEG To convert from radians to degrees
+SQRT2 1.4142135623730950488016887242097
+
+# compound constants
+[word .4, .2, .4]
+ZERO_VECTOR <0.0, 0.0, 0.0>
+ZERO_ROTATION <0.0, 0.0, 0.0, 1.0>
+
+
+# flow control keywords
+[word 0, 0, .8]
+for for loop:for (initializer; test; iteration):{: statements:}
+do do loop:do:{: statements:} while (test);
+while while loop:while (test):{ statements:}
+if if statement:if (test):{ statements:}
+else else clause:if (test):{ statements:}:else:{ statements:}
+jump jump statement:jump label;:
+return Leave current function or event handler
+
+# flow control label
+[line 0, 0, .8]
+@ Label:Target for jump statement
+
+# Comment
+[one_sided_delimiter .8, .3, .15]
+// Comment:Non-functional commentary or disabled code
+
+# String literals
+[two_sided_delimiter 0, .2, 0]
+" String literal
+
+#functions are supplied by the program now.
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl
new file mode 100644
index 0000000000..5731add4d5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl
@@ -0,0 +1,7 @@
+void default_lighting();
+
+void main()
+{
+ default_lighting();
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl
new file mode 100644
index 0000000000..1fcc001911
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl
@@ -0,0 +1,19 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color);
+
+attribute vec4 weight; //1
+
+uniform vec4 matrixPalette[45];
+
+mat4 getSkinnedTransform()
+{
+ mat4 ret;
+ int i = int(floor(weight.x));
+ float x = fract(weight.x);
+
+ ret[0] = mix(matrixPalette[i+0], matrixPalette[i+1], x);
+ ret[1] = mix(matrixPalette[i+15],matrixPalette[i+16], x);
+ ret[2] = mix(matrixPalette[i+30],matrixPalette[i+31], x);
+ ret[3] = vec4(0,0,0,1);
+
+ return ret;
+}
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl
new file mode 100644
index 0000000000..50f9b0192e
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl
@@ -0,0 +1,35 @@
+void default_scatter(vec3 viewVec, vec3 lightDir);
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+mat4 getSkinnedTransform();
+vec2 getScatterCoord(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+void main()
+{
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+
+ vec4 pos;
+ vec3 norm;
+
+ mat4 trans = getSkinnedTransform();
+ pos.x = dot(trans[0], gl_Vertex);
+ pos.y = dot(trans[1], gl_Vertex);
+ pos.z = dot(trans[2], gl_Vertex);
+ pos.w = 1.0;
+
+ norm.x = dot(trans[0].xyz, gl_Normal);
+ norm.y = dot(trans[1].xyz, gl_Normal);
+ norm.z = dot(trans[2].xyz, gl_Normal);
+ norm = normalize(norm);
+
+ gl_Position = gl_ProjectionMatrix * pos;
+
+ //gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+
+ vec4 color = calcLighting(pos.xyz, norm, materialColor, gl_Color);
+ gl_FrontColor = color;
+
+}
diff --git a/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl b/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl
new file mode 100644
index 0000000000..5731add4d5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl
@@ -0,0 +1,7 @@
+void default_lighting();
+
+void main()
+{
+ default_lighting();
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl b/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl
new file mode 100644
index 0000000000..d436b4e00a
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl
@@ -0,0 +1,20 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ vec4 color = calcLighting(pos, norm, materialColor, gl_Color.rgb);
+ default_scatter(pos, gl_LightSource[0].position.xyz);
+
+ gl_FrontColor = color;
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl
new file mode 100644
index 0000000000..b311afb59c
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl
@@ -0,0 +1,6 @@
+uniform sampler2D diffuseMap;
+
+void main()
+{
+ gl_FragColor = vec4(gl_Color.rgb, texture2D(diffuseMap, gl_TexCoord[0].xy).a);
+}
diff --git a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl
new file mode 100644
index 0000000000..b6dcbe1693
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl
@@ -0,0 +1,17 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color);
+mat4 getSkinnedTransform();
+
+void main()
+{
+ vec4 pos;
+
+ mat4 trans = getSkinnedTransform();
+ pos.x = dot(trans[0], gl_Vertex);
+ pos.y = dot(trans[1], gl_Vertex);
+ pos.z = dot(trans[2], gl_Vertex);
+ pos.w = 1.0;
+
+ gl_FrontColor = gl_Color;
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+ gl_Position = gl_ProjectionMatrix * pos;
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl b/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl
new file mode 100644
index 0000000000..fde370155d
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl
@@ -0,0 +1,19 @@
+void terrain_lighting(inout vec3 color);
+
+uniform sampler2D detail0; //0
+uniform sampler2D detail1; //2
+uniform sampler2D alphaRamp; //1
+
+
+void main()
+{
+ float a = texture2D(alphaRamp, gl_TexCoord[1].xy).a;
+ vec3 color = mix(texture2D(detail1, gl_TexCoord[2].xy).rgb,
+ texture2D(detail0, gl_TexCoord[0].xy).rgb,
+ a);
+
+ terrain_lighting(color);
+
+ gl_FragColor.rgb = color;
+ gl_FragColor.a = texture2D(alphaRamp, gl_TexCoord[3].xy).a;
+}
diff --git a/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl b/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl
new file mode 100644
index 0000000000..3153a80e93
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl
@@ -0,0 +1,37 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+vec4 texgen_object(vec4 vpos, vec4 tc, mat4 mat, vec4 tp0, vec4 tp1)
+{
+ vec4 tcoord;
+
+ tcoord.x = dot(vpos, tp0);
+ tcoord.y = dot(vpos, tp1);
+ tcoord.z = tc.z;
+ tcoord.w = tc.w;
+
+ tcoord = mat * tcoord;
+
+ return tcoord;
+}
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+
+ vec4 pos = gl_ModelViewMatrix * gl_Vertex;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ vec4 color = calcLighting(pos.xyz, norm, materialColor, gl_Color);
+
+ gl_FrontColor = color;
+
+ gl_TexCoord[0] = texgen_object(gl_Vertex,gl_MultiTexCoord0,gl_TextureMatrix[0],gl_ObjectPlaneS[0],gl_ObjectPlaneT[0]);
+ gl_TexCoord[1] = gl_TextureMatrix[1]*gl_MultiTexCoord1;
+ gl_TexCoord[2] = texgen_object(gl_Vertex,gl_MultiTexCoord2,gl_TextureMatrix[2],gl_ObjectPlaneS[2],gl_ObjectPlaneT[2]);
+ gl_TexCoord[3] = gl_TextureMatrix[3]*gl_MultiTexCoord3;
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+}
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterF.glsl b/indra/newview/app_settings/shaders/class1/environment/waterF.glsl
new file mode 100644
index 0000000000..f8b8031ce6
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/waterF.glsl
@@ -0,0 +1,22 @@
+void water_lighting(inout vec3 diff);
+
+uniform samplerCube environmentMap;
+uniform sampler2D diffuseMap;
+uniform sampler2D bumpMap;
+
+varying vec4 specular;
+
+void main()
+{
+ vec4 depth = texture2D(diffuseMap, gl_TexCoord[0].xy);
+ vec4 diff = texture2D(bumpMap, gl_TexCoord[1].xy);
+ vec3 ref = textureCube(environmentMap, gl_TexCoord[2].xyz).rgb;
+
+ diff.rgb *= depth.rgb;
+
+ vec3 col = mix(diff.rgb, ref, specular.a)+specular.rgb*diff.rgb;
+
+ water_lighting(col.rgb);
+ gl_FragColor.rgb = col.rgb;
+ gl_FragColor.a = (gl_Color.a+depth.a)*0.5;
+}
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterV.glsl b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
new file mode 100644
index 0000000000..873a6fcb34
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
@@ -0,0 +1,41 @@
+void default_scatter(vec3 viewVec, vec3 lightDir);
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol);
+vec2 getScatterCoord(vec3 viewVec, vec3 lightDir);
+
+varying vec4 specular;
+
+vec4 texgen_object(vec4 vpos, vec4 tc, mat4 mat, vec4 tp0, vec4 tp1)
+{
+ vec4 tcoord;
+
+ tcoord.x = dot(vpos, tp0);
+ tcoord.y = dot(vpos, tp1);
+ tcoord.z = tc.z;
+ tcoord.w = tc.w;
+
+ tcoord = mat * tcoord;
+
+ return tcoord;
+}
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+ gl_TexCoord[1] = texgen_object(gl_Vertex, gl_MultiTexCoord1, gl_TextureMatrix[1], gl_ObjectPlaneS[1],gl_ObjectPlaneT[1]);
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+ vec4 spec = gl_Color;
+ gl_FrontColor.rgb = calcLightingSpecular(pos, norm, gl_Color, spec, vec3(0.0, 0.0, 0.0)).rgb;
+ gl_FrontColor.a = gl_Color.a;
+ specular = spec;
+ specular.a = gl_Color.a*0.5;
+ vec3 ref = reflect(pos,norm);
+
+ gl_TexCoord[2] = gl_TextureMatrix[2]*vec4(ref,1);
+
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl b/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl
new file mode 100644
index 0000000000..1e342fb51b
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl
@@ -0,0 +1,6 @@
+uniform sampler2D diffuseMap;
+
+void main()
+{
+ gl_FragColor = gl_Color*texture2D(diffuseMap, gl_TexCoord[0].xy);
+}
diff --git a/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl b/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl
new file mode 100644
index 0000000000..bb6707b2a9
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl
@@ -0,0 +1,20 @@
+attribute vec4 materialColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ pos = normalize(pos);
+ float d = dot(pos, normalize(gl_NormalMatrix * gl_Normal));
+ d *= d;
+ d = 1.0 - d;
+ d *= d;
+
+ d = min(d, materialColor.a*2.0);
+
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ gl_FrontColor.rgb = materialColor.rgb;
+ gl_FrontColor.a = max(d, materialColor.a);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
new file mode 100644
index 0000000000..b2a6d67621
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
@@ -0,0 +1,31 @@
+void applyScatter(inout vec3 color);
+
+uniform sampler2D diffuseMap;
+
+void default_lighting()
+{
+ vec4 color = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy);
+ applyScatter(color.rgb);
+ gl_FragColor = color;
+}
+
+void alpha_lighting()
+{
+ default_lighting();
+}
+
+void water_lighting(inout vec3 diff)
+{
+ applyScatter(diff);
+}
+
+void terrain_lighting(inout vec3 color)
+{
+ color.rgb *= gl_Color.rgb;
+ applyScatter(color);
+}
+
+vec4 getLightColor()
+{
+ return gl_Color;
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl
new file mode 100644
index 0000000000..e3816318a1
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl
@@ -0,0 +1,99 @@
+
+float calcDirectionalLight(vec3 n, vec3 l)
+{
+ float a = max(dot(n,l),0.0);
+ return a;
+}
+
+float calcPointLight(vec3 v, vec3 n, vec3 l, float r, float pw)
+{
+ //get light vector
+ vec3 lv = l-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = max((r-d)/r, 0.0);
+
+ //da = pow(da, pw);
+
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+
+ return da;
+}
+
+float calcDirectionalSpecular(vec3 view, vec3 n, vec3 l)
+{
+ float a = max(dot(n,l),0.0);
+ return a;
+}
+
+float calcDirectionalLightSpecular(inout vec4 specular, vec3 view, vec3 n, vec3 l, vec3 lightCol, float da)
+{
+
+ specular.rgb += calcDirectionalSpecular(view,n,l)*lightCol*da;
+ return calcDirectionalLight(n,l);
+}
+
+vec3 calcPointLightSpecular(inout vec4 specular, vec3 view, vec3 v, vec3 n, vec3 l, float r, float pw, vec3 lightCol)
+{
+ //get light vector
+ vec3 lv = l-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = clamp((r-d)/r, 0.0, 1.0);
+
+ //da = pow(da, pw);
+
+ //angular attenuation
+ da *= calcDirectionalLightSpecular(specular, view, n, lv, lightCol, da);
+
+ return da*lightCol;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseLight)
+{
+ vec4 col;
+ col.a = color.a;
+
+ col.rgb = gl_LightModel.ambient.rgb + baseLight.rgb;
+
+ col.rgb += gl_LightSource[0].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[0].position.xyz);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
+
+ col.rgb = min(col.rgb*color.rgb, 1.0);
+
+ return col;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseLight)
+{
+ return calcLighting(pos, norm, color, vec4(baseLight, 1.0));
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color)
+{
+ return calcLighting(pos, norm, color, vec3(0.0,0.0,0.0));
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec4 baseCol)
+{
+ specularColor.rgb = vec3(0.0, 0.0, 0.0);
+ return calcLighting(pos, norm, color, baseCol);
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol)
+{
+ return calcLightingSpecular(pos, norm, color, specularColor, vec4(baseCol, 1.0));
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl
new file mode 100644
index 0000000000..ce5ab12b74
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl
@@ -0,0 +1,6 @@
+void default_lighting();
+
+void main()
+{
+ default_lighting();
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
new file mode 100644
index 0000000000..2aa3521931
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
@@ -0,0 +1,21 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ default_scatter(pos, gl_LightSource[0].position.xyz);
+
+ vec4 color = calcLighting(pos, norm, materialColor, gl_Color);
+ gl_FrontColor = color;
+
+ gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl b/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl
new file mode 100644
index 0000000000..7957eddb31
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl
@@ -0,0 +1,23 @@
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+attribute vec4 specularColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+ vec4 specular = specularColor;
+ vec4 color = calcLightingSpecular(pos, norm, materialColor, specular, gl_Color.rgb);
+
+ gl_FrontColor = color;
+ gl_FogFragCoord = pos.z;
+}
+
diff --git a/indra/newview/app_settings/shaders/class2/environment/waterF.glsl b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
new file mode 100644
index 0000000000..e0e79e95ba
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
@@ -0,0 +1,136 @@
+void applyScatter(inout vec3 color);
+
+uniform sampler2D diffuseMap;
+uniform sampler2D bumpMap;
+uniform samplerCube environmentMap; //: TEXUNIT4, // Environment map texture
+uniform sampler2D screenTex; // : TEXUNIT5
+
+uniform vec3 lightDir;
+uniform vec3 specular;
+uniform float lightExp;
+uniform vec2 fbScale;
+uniform float refScale;
+
+float msin(float x) {
+ float k = sin(x)+1.0;
+ k *= 0.5;
+ k *= k;
+ return 2.0 * k;
+}
+
+float mcos(float x) {
+ float k = cos(x)+1.0;
+ k *= 0.5;
+ k *= k;
+ return 2.0 * k;
+}
+
+float waveS(vec2 v, float t, float a, float f, vec2 d, float s, sampler1D sinMap)
+{
+ return texture1D(sinMap, (dot(d, v)*f + t*s)*f).r*a;
+}
+
+float waveC(vec2 v, float t, float a, float f, vec2 d, float s, sampler1D sinMap)
+{
+ return texture1D(sinMap, (dot(d, v)*f + t*s)*f).g*a*2.0-1.0;
+}
+
+float magnitude(vec3 vec) {
+ return sqrt(dot(vec,vec));
+}
+
+vec3 mreflect(vec3 i, vec3 n) {
+ return i + n * 2.0 * abs(dot(n,i))+vec3(0.0,0.0,0.5);
+}
+
+void main()
+{
+ vec2 texCoord = gl_TexCoord[0].xy; // Texture coordinates
+ vec2 littleWave1 = gl_TexCoord[0].zw;
+ vec2 littleWave2 = gl_TexCoord[1].xy;
+ vec2 bigWave = gl_TexCoord[1].zw;
+ vec3 viewVec = gl_TexCoord[2].xyz;
+ vec4 refCoord = gl_TexCoord[3];
+ vec4 col = gl_Color;
+ vec4 color;
+
+ //get color from alpha map (alpha denotes water depth), rgb denotes water color
+ vec4 wcol = texture2D(diffuseMap, texCoord.xy);
+
+ //store texture alpha
+ float da = wcol.a;
+
+ //modulate by incoming water color
+ //wcol.a *= refCoord.w;
+
+ //scale wcol.a (water depth) for steep transition
+ wcol.a *= wcol.a;
+
+ //normalize view vector
+ viewVec = normalize(viewVec);
+
+ //get bigwave normal
+ vec3 wavef = texture2D(bumpMap, bigWave).xyz*2.0;
+
+ vec3 view = vec3(viewVec.x, viewVec.y, viewVec.z);
+
+ float dx = 1.0-(dot(wavef*2.0-vec3(1.0), view))*da;
+ dx *= 0.274;
+
+ //get detail normals
+ vec3 dcol = texture2D(bumpMap, littleWave1+dx*view.xy).rgb*0.75;
+ dcol += texture2D(bumpMap, littleWave2+view.xy*dx*0.1).rgb*1.25;
+
+ //interpolate between big waves and little waves (big waves in deep water)
+ wavef = wavef*wcol.a + dcol*(1.0-wcol.a);
+
+ //crunch normal to range [-1,1]
+ wavef -= vec3(1,1,1);
+
+ //get base fresnel component
+ float df = dot(viewVec,wavef);
+ //reposition fresnel to latter half of [0,1]
+ df = 1.0-clamp(df,0.0,1.0);
+
+ //set output alpha based on fresnel
+ color.a = clamp((df+da)*0.5,0.0,1.0);
+
+ //calculate reflection vector
+ vec3 ref = reflect(viewVec.xyz, wavef);
+
+ //get specular component
+ float spec = clamp(dot(lightDir, normalize(ref)),0.0,1.0);
+
+ //fudge reflection to be more noisy at good angles
+ ref.z = ref.z*ref.z+df*df*0.5;
+
+ //get diffuse component
+ float diff = clamp((abs(dot(ref, wavef))),0.0,1.0)*0.9;
+
+ //fudge diffuse for extra contrast and ambience
+ diff *= diff;
+ diff += 0.4;
+
+ //set diffuse color contribution
+ color.rgb = textureCube(environmentMap, ref).rgb*diff;
+
+ //harden specular
+ spec = pow(spec, lightExp);
+
+ //add specular color contribution
+ color.rgb += spec * specular;
+
+ //figure out distortion vector (ripply)
+ vec2 distort = clamp(((refCoord.xy/refCoord.z) * 0.5 + 0.5 + wavef.xy*refScale),0.0,0.99);
+
+ //read from framebuffer (offset)
+ vec4 fb = texture2D(screenTex, distort*fbScale);
+
+ //tint by framebuffer
+ color.rgb = color.a*color.rgb + (1.0-color.a)*fb.rgb;
+
+ //apply fog
+ applyScatter(color.rgb);
+
+ gl_FragColor = color;
+}
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
new file mode 100644
index 0000000000..0ef1129253
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
@@ -0,0 +1,36 @@
+void applyScatter(inout vec3 color);
+
+uniform sampler2D diffuseMap;
+
+void default_lighting()
+{
+ vec4 color = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy);
+ applyScatter(color.rgb);
+ gl_FragColor = color;
+}
+
+void alpha_lighting()
+{
+ vec4 diff = texture2D(diffuseMap, gl_TexCoord[0].xy);
+ vec3 color = gl_Color.rgb * diff.rgb;
+ applyScatter(color);
+ gl_FragColor.rgb = color;
+ gl_FragColor.a = diff.a * gl_Color.a;
+}
+
+void water_lighting(inout vec3 diff)
+{
+ diff = (diff*0.9 + gl_Color.rgb*0.1);
+ applyScatter(diff);
+}
+
+void terrain_lighting(inout vec3 color)
+{
+ color.rgb *= gl_Color.rgb;
+ applyScatter(color);
+}
+
+vec4 getLightColor()
+{
+ return gl_Color;
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl
new file mode 100644
index 0000000000..b15960dea2
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl
@@ -0,0 +1,126 @@
+// All lights, no specular highlights
+
+float calcDirectionalLight(vec3 n, vec3 l)
+{
+ float a = max(dot(n,l),0.0);
+ return a;
+}
+
+float calcPointLight(vec3 v, vec3 n, vec4 lp, float la)
+{
+ //get light vector
+ vec3 lv = lp.xyz-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = clamp(1.0/(la * d), 0.0, 1.0);
+
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+
+ return da;
+}
+
+float calcDirectionalSpecular(vec3 view, vec3 n, vec3 l)
+{
+ return pow(max(dot(reflect(view, n),l), 0.0),8.0);
+}
+
+float calcDirectionalLightSpecular(inout vec4 specular, vec3 view, vec3 n, vec3 l, vec3 lightCol, float da)
+{
+
+ specular.rgb += calcDirectionalSpecular(view,n,l)*lightCol*da;
+ return calcDirectionalLight(n,l);
+}
+
+vec3 calcPointLightSpecular(inout vec4 specular, vec3 view, vec3 v, vec3 n, vec3 l, float r, float pw, vec3 lightCol)
+{
+ //get light vector
+ vec3 lv = l-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = clamp(1.0/(r * d), 0.0, 1.0);
+
+ //angular attenuation
+
+ da *= calcDirectionalLightSpecular(specular, view, n, lv, lightCol, da);
+
+ return da*lightCol;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseLight)
+{
+ vec4 col;
+ col.a = color.a;
+
+ col.rgb = gl_LightModel.ambient.rgb + baseLight.rgb;
+
+ col.rgb += gl_LightSource[0].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[0].position.xyz);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
+ col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation);
+ col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation);
+ col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation);
+ col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[5].position, gl_LightSource[5].linearAttenuation);
+ col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[6].position, gl_LightSource[6].linearAttenuation);
+ col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[7].position, gl_LightSource[7].linearAttenuation);
+
+ col.rgb = min(col.rgb*color.rgb, 1.0);
+
+ gl_FrontColor = vec4(col.rgb, col.a);
+ return col;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseLight)
+{
+ return calcLighting(pos, norm, color, vec4(baseLight, 1.0));
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color)
+{
+ return calcLighting(pos, norm, color, vec3(0.0,0.0,0.0));
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec4 baseCol)
+{
+ vec4 col;
+ col.a = color.a;
+
+ col.rgb = gl_LightModel.ambient.rgb;
+
+ vec3 view = normalize(pos);
+
+ vec4 specular = specularColor;
+ specularColor.rgb = vec3(0.0, 0.0, 0.0);
+
+ col.rgb += baseCol.a*gl_LightSource[0].diffuse.rgb*calcDirectionalLightSpecular(specularColor, view, norm, gl_LightSource[0].position.xyz,gl_LightSource[0].diffuse.rgb*baseCol.a, 1.0);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLightSpecular(specularColor, view, norm, gl_LightSource[1].position.xyz,gl_LightSource[1].diffuse.rgb, 1.0);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[2].position.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation,gl_LightSource[2].diffuse.rgb);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[3].position.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation,gl_LightSource[3].diffuse.rgb);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[4].position.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation,gl_LightSource[4].diffuse.rgb);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[5].position.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation,gl_LightSource[5].diffuse.rgb);
+ //col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[6].position.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation,gl_LightSource[6].diffuse.rgb);
+ //col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[7].position.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation,gl_LightSource[7].diffuse.rgb);
+ col.rgb += baseCol.rgb;
+
+ col.rgb = min(col.rgb*color.rgb, 1.0);
+ specularColor.rgb = min(specularColor.rgb*specular.rgb, 1.0);
+
+ gl_FrontColor = vec4(col.rgb+specularColor.rgb,col.a);
+ return col;
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol)
+{
+ return calcLightingSpecular(pos, norm, color, specularColor, vec4(baseCol, 1.0));
+}
diff --git a/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl b/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl
new file mode 100644
index 0000000000..2505afea65
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl
@@ -0,0 +1,128 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseCol);
+mat4 getSkinnedTransform();
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor; //2
+
+attribute vec4 binormal; //6
+attribute vec4 clothing; //4
+
+attribute vec4 gWindDir; //7
+attribute vec4 gSinWaveParams; //3
+attribute vec4 gGravity; //5
+
+const vec4 gMinMaxConstants = vec4(1.0, 0.166666, 0.0083143, .00018542); // #minimax-generated coefficients
+const vec4 gPiConstants = vec4(0.159154943, 6.28318530, 3.141592653, 1.5707963); // # {1/2PI, 2PI, PI, PI/2}
+
+void main()
+{
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+
+ vec4 pos;
+ mat4 trans = getSkinnedTransform();
+
+ vec3 norm;
+ norm.x = dot(trans[0].xyz, gl_Normal);
+ norm.y = dot(trans[1].xyz, gl_Normal);
+ norm.z = dot(trans[2].xyz, gl_Normal);
+ norm = normalize(norm);
+
+ vec3 binorm;
+ binorm.x = dot(trans[0].xyz, binormal.xyz);
+ binorm.y = dot(trans[1].xyz, binormal.xyz);
+ binorm.z = dot(trans[2].xyz, binormal.xyz);
+ norm = normalize(norm);
+
+ //wind
+ vec4 windEffect;
+ windEffect = vec4(dot(norm, gWindDir.xyz)); // DP3 windEffect, blendNorm, gWindDir;
+ pos.x = dot(trans[2].xyz, gl_Vertex.xyz); // DP3 blendPos.x, blendMatZ, iPos;
+ windEffect.xyz = pos.x * vec3(0.015, 0.015, 0.015)
+ + windEffect.xyz; // MAD windEffect.xyz, blendPos.x, {0.015, 0.015, 0.015, 0}, windEffect;
+ windEffect.w = windEffect.w * 2.0 + 1.0; // MAD windEffect.w, windEffect, {0, 0, 0, 2}, {0, 0, 0, 1}; # move wind offset value to [-1, 3]
+ windEffect.w = windEffect.w*gWindDir.w; // MUL windEffect.w, windEffect, gWindDir; # modulate wind strength
+
+ windEffect.xyz = windEffect.xyz*gSinWaveParams.xyz
+ +vec3(gSinWaveParams.w); // MAD windEffect.xyz, windEffect, gSinWaveParams, gSinWaveParams.w; # use sin wave params to scale and offset input
+
+
+ //reduce to period of 2 PI
+ vec4 temp1, temp0, temp2, offsetPos;
+ temp1.xyz = windEffect.xyz * gPiConstants.x; // MUL temp1.xyz, windEffect, gPiConstants.x; # change input as multiple of [0-2PI] to [0-1]
+ temp0.y = mod(temp1.x,1.0); // EXP temp0, temp1.x; # find mod(x, 1)
+ windEffect.x = temp0.y * gPiConstants.y; // MUL windEffect.x, temp0.y, gPiConstants.y; # scale from [0,1] to [0, 2PI]
+ temp1.z = temp1.z - gPiConstants.w; // ADD temp1.z, temp1.z, -gPiConstants.w; # shift normal oscillation by PI/2
+ temp0.y = mod(temp1.z,1.0); // EXP temp0, temp1.z; # find mod(x, 1)
+
+ windEffect.z = temp0.y * gPiConstants.y; // MUL windEffect.z, temp0.y, gPiConstants.y; # scale from [0,1] to [0, 2PI]
+ windEffect.xyz = windEffect.xyz + vec3(-3.141592); // # offset to [-PI, PI]
+ // ADD windEffect.xyz, windEffect, {-3.141592, -3.141592, -3.141592, -3.141592};
+
+ //calculate sinusoid
+ vec4 sinWave;
+ temp1 = windEffect*windEffect; // MUL temp1, windEffect, windEffect; # x^2
+ sinWave = -temp1 * gMinMaxConstants.w
+ + vec4(gMinMaxConstants.z); // MAD sinWave, -temp1, gMinMaxConstants.w, gMinMaxConstants.z; # y = -(x^2)/7! + 1/5!
+ sinWave = sinWave * -temp1 + vec4(gMinMaxConstants.y); // MAD sinWave, sinWave, -temp1, gMinMaxConstants.y; # y = -(x^2) * (-(x^2)/7! + 1/5!) + 1/3!
+ sinWave = sinWave * -temp1 + vec4(gMinMaxConstants.x); // MAD sinWave, sinWave, -temp1, gMinMaxConstants.x; # y = -(x^2) * (-(x^2) * (-(x^2)/7! + 1/5!) + 1/3!) + 1
+ sinWave = sinWave * windEffect; // MUL sinWave, sinWave, windEffect; # y = x * (-(x^2) * (-(x^2) * (-(x^2)/7! + 1/5!) + 1/3!) + 1)
+
+ // sinWave.x holds sin(norm . wind_direction) with primary frequency
+ // sinWave.y holds sin(norm . wind_direction) with secondary frequency
+ // sinWave.z hold cos(norm . wind_direction) with primary frequency
+ sinWave.xyz = sinWave.xyz * gWindDir.w
+ + vec3(windEffect.w); // MAD sinWave.xyz, sinWave, gWindDir.w, windEffect.w; # multiply by wind strength in gWindDir.w [-wind, wind]
+
+ // add normal facing bias offset [-wind,wind] -> [-wind - .25, wind + 1]
+ temp1 = vec4(dot(norm, gGravity.xyz)); // DP3 temp1, blendNorm, gGravity; # how much is this normal facing in direction of gGravity?
+ temp1 = min(temp1, vec4(0.2,0.0,0.0,0.0)); // MIN temp1, temp1, {0.2, 0, 0, 0}; # clamp [-1, 1] to [-1, 0.2]
+ temp1 = temp1*vec4(1.5,0.0,0.0,0.0); // MUL temp1, temp1, {1.5, 0, 0, 0}; # scale from [-1,0.2] to [-1.5, 0.3]
+ sinWave.x = sinWave.x + temp1.x; // ADD sinWave.x, sinWave, temp1; # add gGravity effect to sinwave (only primary frequency)
+ sinWave.xyz = sinWave.xyz * clothing.w; // MUL sinWave.xyz, sinWave, iClothing.w; # modulate by clothing coverage
+
+ sinWave.xyz = max(sinWave.xyz, vec3(-1.0, -1.0, -1.0)); // MAX sinWave.xyz, sinWave, {-1, -1, -1, -1}; # clamp to underlying body shape
+ offsetPos = clothing * sinWave.x; // MUL offsetPos, iClothing, sinWave.x; # multiply wind effect times clothing displacement
+ temp2 = gWindDir*sinWave.z + vec4(norm,0); // MAD temp2, gWindDir, sinWave.z, blendNorm; # calculate normal offset due to wind oscillation
+ offsetPos = vec4(1.0,1.0,1.0,0.0)*offsetPos+gl_Vertex; // MAD offsetPos, {1.0, 1.0, 1.0, 0.0}, offsetPos, iPos; # add to offset vertex position, and zero out effect from w
+ norm += temp2.xyz*2.0; // MAD blendNorm, temp2, {2, 2, 2, 2}, blendNorm; # add sin wave effect on normals (exaggerated)
+
+ //add "backlighting" effect
+ float colorAcc;
+ colorAcc = 1.0 - clothing.w; // SUB colorAcc, {1, 1, 1, 1}, iClothing;
+ norm.z -= colorAcc * 0.2; // MAD blendNorm, colorAcc.w, {0, 0, -0.2, 0}, blendNorm;
+
+ //renormalize normal (again)
+ norm = normalize(norm); // DP3 divisor.w, blendNorm, blendNorm;
+ // RSQ divisor.xyz, divisor.w;
+ // MUL blendNorm.xyz, blendNorm, divisor;
+
+ //project binormal to normal plane to ensure orthogonality
+ temp2 = vec4(dot(norm, binorm)); // DP3 temp2, blendNorm, blendBinorm;
+ binorm = binorm - temp2.xyz; // SUB blendBinorm, blendBinorm, temp2;
+
+ //renormalize binormal
+ binorm = normalize(binorm); // DP3 divisor.w, blendBinorm, blendBinorm;
+ // RSQ divisor.xyz, divisor.w;
+ // MUL blendBinorm.xyz, blendBinorm, divisor;
+
+ pos.x = dot(trans[0], offsetPos);
+ pos.y = dot(trans[1], offsetPos);
+ pos.z = dot(trans[2], offsetPos);
+ pos.w = 1.0;
+
+ vec4 color = calcLighting(pos.xyz, norm, materialColor, gl_Color.rgb);
+ gl_FrontColor = color;
+
+ gl_Position = gl_ProjectionMatrix * pos;
+
+ vec3 N = norm;
+ vec3 B = binorm;
+ vec3 T = cross(N,B);
+
+ //gl_TexCoord[1].xy = gl_MultiTexCoord0.xy + 1.0/512.0 * vec2(dot(T,gl_LightSource[0].position.xyz),
+ // dot(B,gl_LightSource[0].position.xyz));
+
+ gl_TexCoord[2] = vec4(pos.xyz, 1.0);
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/std_bump.ini b/indra/newview/app_settings/std_bump.ini
new file mode 100644
index 0000000000..9509c357ff
--- /dev/null
+++ b/indra/newview/app_settings/std_bump.ini
@@ -0,0 +1,18 @@
+LLStandardBumpmap version 1
+
+
+woodgrain bump_woodgrain.tga
+bark bump_bark.tga
+bricks bump_bricks.tga
+checker bump_checker.tga
+concrete bump_concrete.tga
+crustytile bump_crustytile.tga
+cutstone bump_cutstone.tga
+discs bump_discs.tga
+gravel bump_gravel.tga
+petridish bump_petridish.tga
+siding bump_siding.tga
+stonetile bump_stonetile.tga
+stucco bump_stucco.tga
+suction bump_suction.tga
+weave bump_weave.tga
diff --git a/indra/newview/app_settings/trees.xml b/indra/newview/app_settings/trees.xml
new file mode 100644
index 0000000000..213ee85fc7
--- /dev/null
+++ b/indra/newview/app_settings/trees.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<tree_defs>
+ <tree name="Pine 1" species_id="0" texture_id="0187babf-6c0d-5891-ebed-4ecab1426683" droop="60.0" twist="5.0" branches="5.0" depth="1" scale_step="0.7" trunk_depth="6" branch_length="8.0" trunk_length="11.5" leaf_scale="22.0" billboard_scale="39.5" billboard_ratio="1.1" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="20.0" noise_mag="0.5" noise_scale="2.5" taper="0.8" repeat_z="3" />
+ <tree name="Oak" species_id="1" texture_id="8a515889-eac9-fb55-8eba-d2dc09eb32c8" droop="35.0" twist="3.0" branches="4.0" depth="3" scale_step="0.7" trunk_depth="0" branch_length="3.0" trunk_length="3.8" leaf_scale="7.0" billboard_scale="10.25" billboard_ratio="1.0" trunk_aspect="0.15" branch_aspect="0.07" leaf_rotate="0.0" noise_mag="1.2" noise_scale="4.0" taper="0.3" repeat_z="4" />
+ <tree name="Tropical Bush 1" species_id="2" texture_id="5bc11cd6-2f40-071e-a8da-0903394204f9" droop="10.0" twist="0.0" branches="6.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="7.5" billboard_scale="5.0" billboard_ratio="1.25" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Palm 1" species_id="3" texture_id="ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c" droop="0.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="0" branch_length="0.7" trunk_length="9.0" leaf_scale="10.0" billboard_scale="13.25" billboard_ratio="1.0" trunk_aspect="0.035" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="0.2" noise_scale="6.0" taper="0.7" repeat_z="10" />
+ <tree name="Dogwood" species_id="4" texture_id="64367bd1-697e-b3e6-0b65-3f862a577366" droop="30.0" twist="0.0" branches="3.0" depth="2" scale_step="0.7" trunk_depth="1" branch_length="2.75" trunk_length="4.0" leaf_scale="5.5" billboard_scale="10.0" billboard_ratio="1.0" trunk_aspect="0.06" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.5" noise_scale="2.0" taper="0.8" repeat_z="3" />
+ <tree name="Tropical Bush 2" species_id="5" texture_id="cdd9a9fc-6d0b-f90d-8416-c72b6019bca8" droop="10.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="6.0" billboard_scale="4.5" billboard_ratio="0.9" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Palm 2" species_id="6" texture_id="2d784476-d0db-9979-0cff-9408745a7cf3" droop="0.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="0" branch_length="0.7" trunk_length="10.0" leaf_scale="7.5" billboard_scale="13.5" billboard_ratio="1.0" trunk_aspect="0.035" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="0.2" noise_scale="6.0" taper="0.6" repeat_z="12" />
+ <tree name="Cypress 1" species_id="7" texture_id="fb2ae204-3fd1-df33-594f-c9f882830e66" droop="30.0" twist="0.0" branches="3.0" depth="4" scale_step="0.5" trunk_depth="0" branch_length="10.0" trunk_length="10.0" leaf_scale="70.0" billboard_scale="22.5" billboard_ratio="1.0" trunk_aspect="0.05" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="1.2" noise_scale="1.0" taper="0.5" repeat_z="6" />
+ <tree name="Cypress 2" species_id="8" texture_id="30047cec-269d-408e-0c30-b2603b887268" droop="30.0" twist="0.0" branches="3.0" depth="4" scale_step="0.6" trunk_depth="3.0" branch_length="7.5" trunk_length="10.0" leaf_scale="35.0" billboard_scale="25.0" billboard_ratio="0.8" trunk_aspect="0.05" branch_aspect="0.04" leaf_rotate="0.0" noise_mag="1.2" noise_scale="1.0" taper="0.5" repeat_z="5" />
+ <tree name="Pine 2" species_id="9" texture_id="d691a01c-13b7-578d-57c0-5caef0b4e7e1" droop="50.0" twist="7.5" branches="4.0" depth="2" scale_step="0.7" trunk_depth="6.0" branch_length="6.0" trunk_length="10.0" leaf_scale="15.5" billboard_scale="33.0" billboard_ratio="1.35" trunk_aspect="0.1" branch_aspect="0.08" leaf_rotate="5.0" noise_mag="0.5" noise_scale="2.5" taper="0.7" repeat_z="3" />
+ <tree name="Plumeria" species_id="10" texture_id="6de37e4e-7029-61f5-54b8-f5e63f983f58" droop="8.0" twist="7.0" branches="3.0" depth="2" scale_step="0.6" trunk_depth="0" branch_length="3.0" trunk_length="0.1" leaf_scale="20" billboard_scale="10.0" billboard_ratio="1.35" trunk_aspect="0.10" branch_aspect="0.075" leaf_rotate="0.0" noise_mag="0" noise_scale="0" taper="0.85" repeat_z="2" />
+ <tree name="Winter Pine 1" species_id="11" texture_id="10d2a01a-0818-84b9-4b96-c2eb63256519" droop="90.0" twist="2.5" branches="6.0" depth="1" scale_step="0.66" trunk_depth="8" branch_length="0.0" trunk_length="4" leaf_scale="6.75" billboard_scale="12.5" billboard_ratio="0.6" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.0" noise_scale="2.5" taper="0.85" repeat_z="2" />
+ <tree name="Winter Aspen" species_id="12" texture_id="7c0cf89b-44b1-1ce2-dd74-07102a98ac2a" droop="85.0" twist="3.0" branches="5" depth="1" scale_step="0.6" trunk_depth="8" branch_length="3.0" trunk_length="4.5" leaf_scale="8" billboard_scale="12" billboard_ratio=".675" trunk_aspect="0.06" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.75" noise_scale="2.5" taper="0.8" repeat_z="2" />
+ <tree name="Winter Pine 2" species_id="13" texture_id="67931331-0c02-4876-1255-28770896c6a2" droop="140.0" twist="5.0" branches="6.0" depth="1" scale_step="0.6" trunk_depth="7" branch_length="0.0" trunk_length="3" leaf_scale="5.0" billboard_scale="7.5" billboard_ratio="0.5" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.750" noise_scale="2.5" taper="0.5" repeat_z="2" />
+ <tree name="Eucalyptus" species_id="14" texture_id="a6162133-724b-54df-a12f-51cd070ad6f3" droop="20.0" twist="5.0" branches="3.6" depth="4" scale_step="0.6" trunk_depth="0" branch_length="12.0" trunk_length="8.0" leaf_scale="33.0" billboard_scale="24" billboard_ratio="1.3" trunk_aspect="0.15" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="0" noise_scale="0" taper="0.675" repeat_z="3" />
+ <tree name="Fern" species_id="15" texture_id="8872f2b8-31db-42d8-580a-b3e4a91262de" droop="12.0" twist="0.0" branches="7.0" depth="1" scale_step="0.5" trunk_depth="0.1" branch_length="0.01" trunk_length="0.0" leaf_scale="4" billboard_scale="3.5" billboard_ratio="0.85" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Eelgrass" species_id="16" texture_id="96b4de31-f4fa-337d-ec78-451e3609769e" droop="0.0" twist="0.0" branches="5.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="5.0" billboard_scale="3.0" billboard_ratio="1.0" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Sea Sword" species_id="17" texture_id="5894e2e7-ab8d-edfa-e61c-18cf16854ba3" droop="0.0" twist="0.0" branches="6.0" depth="1" scale_step="0.7" trunk_depth="1" branch_length="0.0" trunk_length="0.0" leaf_scale="2.0" billboard_scale="2.0" billboard_ratio="1.0" trunk_aspect="1.0" branch_aspect="1.0" leaf_rotate="0.0" noise_mag="0.5" noise_scale="0.0" taper="0.0" repeat_z="1" />
+ <tree name="Kelp 1" species_id="18" texture_id="2caf1179-7861-6ff3-4b7d-46e17780bdfa" droop="-15.0" twist="0.0" branches="1.0" depth="1" scale_step="1.0" trunk_depth="3" branch_length="2.5" trunk_length="0.75" leaf_scale="1.85" billboard_scale="4.9" billboard_ratio="1.0" trunk_aspect="0.04" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.0" noise_scale="2.0" taper="0.8" repeat_z="2" />
+ <tree name="Beach Grass 1" species_id="19" texture_id="18fb888b-e8f1-dce7-7da7-321d651ea6b0" droop="0.0" twist="0.0" branches="4.0" depth="1" scale_step="0.7" trunk_depth="1" branch_length="0.0" trunk_length="0.0" leaf_scale="4.0" billboard_scale="2.5" billboard_ratio="1.2" trunk_aspect="1.0" branch_aspect="1.0" leaf_rotate="0.0" noise_mag="0.5" noise_scale="0.0" taper="0.0" repeat_z="1" />
+ <tree name="Kelp 2" species_id="20" texture_id="2a4880b6-b7a3-690a-2049-bfbe38eafb9f" droop="-15.0" twist="0.0" branches="1.0" depth="1" scale_step="1.0" trunk_depth="3" branch_length="2.5" trunk_length="1.35" leaf_scale="2.0" billboard_scale="4.9" billboard_ratio="1.0" trunk_aspect="0.025" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.0" noise_scale="2.0" taper="0.8" repeat_z="2" />
+</tree_defs>
diff --git a/indra/newview/app_settings/viewerart.xml b/indra/newview/app_settings/viewerart.xml
new file mode 100644
index 0000000000..dbd22278a7
--- /dev/null
+++ b/indra/newview/app_settings/viewerart.xml
@@ -0,0 +1,504 @@
+<settings version = "101">
+ <closebox.tga value="47a8c844-cd2a-4b1a-be01-df8b1612fe5d"/>
+ <close_in_blue.tga value="e5821134-23c0-4bd0-af06-7fa95b9fb01a"/>
+ <tearoffbox.tga value="74e1a96f-4833-a24d-a1bb-1bce1468b0e7"/>
+ <tearoff_pressed.tga value="d2524c13-4ba6-af7c-e305-8ac6cc18d86a"/>
+ <resize_handle_bottom_right_blue.tga value="e3690e25-9690-4f6c-a745-e7dcd885285a"/>
+ <scrollbutton_up_out_blue.tga value="dad084d7-9a46-452a-b0ff-4b9f1cefdde9"/>
+ <scrollbutton_up_in_blue.tga value="a93abdf3-27b5-4e22-a8fa-c48216cd2e3a"/>
+ <scrollbutton_down_out_blue.tga value="b4ecdecf-5c8d-44e7-b882-17a77e88ed55"/>
+ <scrollbutton_down_in_blue.tga value="d2421bab-2eaf-4863-b8f6-5e4c52519247"/>
+ <scrollbutton_left_out_blue.tga value="43773e8d-49aa-48e0-80f3-a04715f4677a"/>
+ <scrollbutton_left_in_blue.tga value="ea137a32-6718-4d05-9c22-7d570d27b2cd"/>
+ <scrollbutton_right_out_blue.tga value="3d700d19-e708-465d-87f2-46c8c0ee7938"/>
+ <scrollbutton_right_in_blue.tga value="b749de64-e903-4c3c-ac0b-25fb6fa39cb5"/>
+ <spin_up_out_blue.tga value="56576e6e-6710-4e66-89f9-471b59122794"/>
+ <spin_up_in_blue.tga value="c8450082-96a0-4319-8090-d3ff900b4954"/>
+ <spin_down_out_blue.tga value="b6d240dd-5602-426f-b606-bbb49a30726d"/>
+ <spin_down_in_blue.tga value="a985ac71-052f-48e6-9c33-d931c813ac92"/>
+ <radio_active_false.tga value="7a1ba9b8-1047-4d1e-9cfc-bc478c80b63f"/>
+ <radio_active_true.tga value="52f09e07-5816-4052-953c-94c6c10479b7"/>
+ <radio_inactive_false.tga value="90688481-67ff-4af0-be69-4aa084bcad1e"/>
+ <radio_inactive_true.tga value="1975db39-aa29-4251-aea0-409ac09d414d"/>
+ <checkbox_enabled_false.tga value="05bb64ee-96fd-4243-b74e-f40a41bc53ba"/>
+ <checkbox_enabled_true.tga value="cf4a2ed7-1533-4686-9dde-df9a37ddca55"/>
+ <checkbox_disabled_false.tga value="7d94cb59-32a2-49bf-a516-9e5a2045f9d9"/>
+ <checkbox_disabled_true.tga value="c817c642-9abd-4236-9287-ae0513fe7d2b"/>
+ <tab_top_blue.tga value="1ed83f57-41cf-4052-a3b4-2e8bb78d8191"/>
+ <tab_top_selected_blue.tga value="16d032e8-817b-4368-8a4e-b7b947ae3889"/>
+ <tab_bottom_blue.tga value="bf0a8779-689b-48c3-bb9a-6af546366ef4"/>
+ <tab_bottom_selected_blue.tga value="c001d8fd-a869-4b6f-86a1-fdcb106df9c7"/>
+ <tab_left.tga value="1097dcb3-aef9-8152-f471-431d840ea89e"/>
+ <tab_left_selected.tga value="bea77041-5835-1661-f298-47e2d32b7a70"/>
+ <crosshairs.tga value="6e1a3980-bf2d-4274-8970-91e60d85fb52"/>
+ <move_backward_in.tga value="db11d956-5e7d-4aa5-b39d-7774d339fc5c"/>
+ <move_backward_out.tga value="3ae8bb18-ed97-4cd3-ae5c-d54bc8479fe7"/>
+ <move_down_in.tga value="b92a70b9-c841-4c94-b4b3-cee9eb460d48"/>
+ <move_down_out.tga value="b5abc9fa-9e62-4e03-bc33-82c4c1b6b689"/>
+ <move_forward_in.tga value="54197a61-f5d1-4c29-95d2-c071d08849cb"/>
+ <move_forward_out.tga value="a0eb4021-1b20-4a53-892d-8faa9265a6f5"/>
+ <move_left_in.tga value="724996f5-b956-46f6-9844-4fcfce1d5e83"/>
+ <move_left_out.tga value="82476321-0374-4c26-9567-521535ab4cd7"/>
+ <move_right_in.tga value="7eeb57d2-3f37-454d-a729-8b217b8be443"/>
+ <move_right_out.tga value="1fbe4e60-0607-44d1-a50a-032eff56ae75"/>
+ <move_turn_left_in.tga value="95463c78-aaa6-464d-892d-3a805b6bb7bf"/>
+ <move_turn_left_out.tga value="13a93910-6b44-45eb-ad3a-4d1324c59bac"/>
+ <move_turn_right_in.tga value="5e616d0d-4335-476f-9977-560bccd009da"/>
+ <move_turn_right_out.tga value="5a44fd04-f52b-4c30-8b00-4a31e27614bd"/>
+ <move_up_out.tga value="f887146d-829f-4e39-9211-cf872b78f97c"/>
+ <move_up_in.tga value="49b4b357-e430-4b56-b9e0-05b8759c3c82"/>
+ <cam_rotate_out.tga value="88745b46-da05-11d5-8ac0-0003477c4611"/>
+ <cam_rotate_in.tga value="70bf2262-3eed-4996-88ac-076907e8921d"/>
+ <cam_zoom_out.tga value="bb02e941-cb3b-4dd3-892a-6841b5de6e45"/>
+ <cam_zoom_plus_in.tga value="c7aefd32-ce13-4242-82cc-2631d44ff9d3"/>
+ <cam_zoom_minus_in.tga value="deed3f4b-93e9-4183-a3b0-a5a98a6de1bb"/>
+ <cam_tracking_out.tga value="95c4ea0e-e3c2-4904-b847-7d7676139ebb"/>
+ <cam_tracking_in.tga value="fe2fc73b-5a64-4a8e-aacc-46fa81faf96a"/>
+ <direction_arrow.tga value="586383e8-4d9b-4fba-9196-2b5938e79c2c"/>
+ <minimize.tga value="34c9398d-bb78-4643-9633-46a2fa3e9637"/>
+ <minimize_inactive.tga value="6e72abba-1378-437f-bf7a-f0c15f3e99a3"/>
+ <minimize_pressed.tga value="39801651-26cb-4926-af57-7af9352c273c"/>
+ <restore.tga value="111b39de-8928-4690-b7b2-e17d5c960277"/>
+ <restore_inactive.tga value="0eafa471-70af-4882-b8c1-40a310929744"/>
+ <restore_pressed.tga value="90a0ed5c-2e7b-4845-9958-a64a1b30f312"/>
+ <combobox_arrow.tga value="b31c1335-0e9c-4927-bc90-53277777d9c1"/>
+ <white.tga value="5748decc-f629-461c-9a36-a35a221fe21f"/>
+ <darkgray.tga value="267e26d3-e0e1-41b8-91b1-3b337102928d"/>
+ <lightgray.tga value="c520bf46-cc5d-412b-a60b-9f1bd245189f"/>
+ <eyes.tga value="6522e74d-1660-4e7f-b601-6f48c1659a77"/>
+ <hair.tga value="7ca39b4c-bd19-4699-aff7-f93fd03d3e7b"/>
+ <black.tga value="e2244626-f22f-4839-8123-1e7baddeb659"/>
+ <close_inactive_blue.tga value="779e4fa3-9b13-f74a-fba9-3886fe9c86ba"/>
+ <button_disabled_32x128.tga value="f8124d60-2875-c358-7847-2acb63e5400c"/>
+ <button_enabled_32x128.tga value="d8faf8cb-ee6e-b0b5-abd9-bde873ad3461"/>
+ <button_enabled_selected_32x128.tga value="1eddba75-b682-110a-104e-6cdcce616a25"/>
+ <button_anim_play_selected.tga value="119c37bb-24af-45fe-ae11-3a6bc3c85138"/>
+ <button_anim_pause_selected.tga value="ad65d67a-777b-fbfa-693d-4bdcfca2acca"/>
+ <button_anim_pause.tga value="db2d9c2d-0bbd-21e2-e83a-103ea2def7a8"/>
+ <button_anim_play.tga value="2a7f6738-5d82-2ff3-d419-30ed09cbb72b"/>
+ <button_anim_stop.tga value="e10c9e36-d9f6-c8b4-de96-557dccce9205"/>
+ <button_anim_stop_selected.tga value="b8c0e0aa-2771-439e-c919-d2f5dad69a1c"/>
+ <rounded_square.tga value="38ce8b3c-fb30-5c59-9926-bd643613f606"/>
+ <rounded_square_soft.tga value="4c95e6bc-fe77-9cb4-b58a-909848042c1e"/>
+ <badge_ok.tga value="211035a7-c313-378d-478c-e80bbd0fde63"/>
+ <badge_note.tga value="13f6e639-b3f9-28da-a1e6-e990a43052b6"/>
+ <badge_warn.tga value="0992d4bc-7af8-4a1f-f2e6-e6c4083b066e"/>
+ <badge_error.tga value="00c50485-8491-ab70-2ea8-43f26fd028e2"/>
+ <status_money.tga value="5863eb7a-1546-6501-533a-6061f73a36b7"/>
+ <status_health.tga value="4330e8ce-b39b-1eb8-c2ec-a97c0b3947b5"/>
+ <status_fly.tga value="0e058115-5b8f-c3d7-dcaa-9623d92885d1"/>
+ <status_build.tga value="175a6b75-45c9-c2c2-4765-bf37a3909b53"/>
+ <status_busy.tga value="beb0d821-6725-abdf-032d-1f70cdabde82"/>
+ <status_scripts.tga value="4cc1afcd-04dd-178f-e074-0f9dc730ab45"/>
+ <status_buy_currency.tga value="f43a535a-59ac-26e3-84bc-c786735fabe4"/>
+ <status_buy_currency_pressed.tga value="bfa5be70-37c7-8126-fecd-df55390954d5"/>
+ <status_buy_land.tga value="1a0edac5-3e50-fc9b-2752-70c1f69cb959"/>
+ <status_buy_land_pressed.tga value="257647b7-199f-99ff-8be9-f6753289a3aa"/>
+ <terrain_dirt.tga value="b8d3965a-ad78-bf43-699b-bff8eca6c975"/>
+ <terrain_grass.tga value="abb783e6-3e93-26c0-248a-247666855da3"/>
+ <terrain_mountain.tga value="179cdabd-398a-9b6b-1391-4dc333ba321f"/>
+ <terrain_rock.tga value="beb169c7-11ea-fff2-efe5-0f24dc881df2"/>
+ <terrain_dirt_detail.tga value="0bc58228-74a0-7e83-89bc-5c23464bcec5"/>
+ <terrain_grass_detail.tga value="63338ede-0037-c4fd-855b-015d77112fc8"/>
+ <terrain_mountain_detail.tga value="303cd381-8560-7579-23f1-f0a880799740"/>
+ <terrain_rock_detail.tga value="53a2f406-4895-1d13-d541-d2e3b86bc19c"/>
+ <square_btn_32x128.tga value="b28df901-6b8d-d31c-7903-4eb9676d4bfc"/>
+ <square_btn_selected_32x128.tga value="c48c9e95-191b-96d3-08b2-6e8ada58b651"/>
+ <tree_pine_1.tga value="0187babf-6c0d-5891-ebed-4ecab1426683"/>
+ <tree_oak.tga value="8a515889-eac9-fb55-8eba-d2dc09eb32c8"/>
+ <tree_tropical_1.tga value="5bc11cd6-2f40-071e-a8da-0903394204f9"/>
+ <tree_palm_1.tga value="ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c"/>
+ <tree_dogwood.tga value="64367bd1-697e-b3e6-0b65-3f862a577366"/>
+ <tree_tropical_2.tga value="cdd9a9fc-6d0b-f90d-8416-c72b6019bca8"/>
+ <tree_palm_2.tga value="2d784476-d0db-9979-0cff-9408745a7cf3"/>
+ <tree_cypress_1.tga value="fb2ae204-3fd1-df33-594f-c9f882830e66"/>
+ <tree_cypress_2.tga value="30047cec-269d-408e-0c30-b2603b887268"/>
+ <tree_pine_2.tga value="d691a01c-13b7-578d-57c0-5caef0b4e7e1"/>
+ <tree_plumeria.tga value="6de37e4e-7029-61f5-54b8-f5e63f983f58"/>
+ <winter_tree_aspen.tga value="7c0cf89b-44b1-1ce2-dd74-07102a98ac2a"/>
+ <winter_tree_pine_1.tga value="10d2a01a-0818-84b9-4b96-c2eb63256519"/>
+ <winter_tree_pine_2.tga value="67931331-0c02-4876-1255-28770896c6a2"/>
+ <tree_eucalyptus.tga value="a6162133-724b-54df-a12f-51cd070ad6f3"/>
+ <tree_fern.tga value="8872f2b8-31db-42d8-580a-b3e4a91262de"/>
+ <tree_eelgrass.tga value="96b4de31-f4fa-337d-ec78-451e3609769e"/>
+ <tree_sea_sword.tga value="5894e2e7-ab8d-edfa-e61c-18cf16854ba3"/>
+ <tree_kelp_1.tga value="2caf1179-7861-6ff3-4b7d-46e17780bdfa"/>
+ <tree_kelp_2.tga value="2a4880b6-b7a3-690a-2049-bfbe38eafb9f"/>
+ <tree_beach_grass_1.tga value="18fb888b-e8f1-dce7-7da7-321d651ea6b0"/>
+ <tool_dozer.tga value="d2a0d4d4-54eb-4d16-be4b-4eae43845c74"/>
+ <tool_dozer_active.tga value="d4afdbbe-1550-4b7d-91de-95731f47e8e3"/>
+ <tool_land.tga value="86fe4df4-0ecb-4382-b9ae-475925a92388"/>
+ <tool_land_active.tga value="34e60587-0791-4a07-8918-f5995fcc22a3"/>
+ <tool_zoom.tga value="27eb8829-fe65-45ed-a49a-73aac42f4b38"/>
+ <tool_zoom_active.tga value="69445f58-5c8e-44e0-9d2e-47408bb43b39"/>
+ <tool_orbit.tga value="06964fe4-033f-448a-95c9-30dc41d1be8b"/>
+ <tool_orbit_active.tga value="ee4e07db-3f72-4098-bd4c-aef34515a7bc"/>
+ <tool_pan.tga value="a32aa302-0a15-48d2-b2b1-4d69f1161173"/>
+ <tool_pan_active.tga value="24d9ad33-0b42-4eb5-99a3-659d838bc5c0"/>
+ <inv_folder_texture.tga value="743f035b-a049-43f4-16c7-7ec8daa2c481"/>
+ <inv_folder_sound.tga value="e10cb910-1e71-da47-bd12-8c53f7793714"/>
+ <inv_folder_callingcard.tga value="a3735971-e2b2-d78a-580d-d265cd8f2484"/>
+ <inv_folder_landmark.tga value="9f921155-7c8c-e276-d5ec-03ac9340584d"/>
+ <inv_folder_script.tga value="baa5c310-6a6d-cc48-51eb-65196ba31d77"/>
+ <inv_folder_object.tga value="113e5133-fd0d-ee51-4a59-9d67ca10e8a7"/>
+ <inv_folder_notecard.tga value="a9e75d84-5073-9cb7-10a9-1ca68ef5c7ba"/>
+ <inv_folder_clothing.tga value="f1427d3d-b2e8-97c4-69ab-1f36d4c0e8f0"/>
+ <inv_folder_bodypart.tga value="1fe05580-1d2f-0345-b28b-52b6e3a20e5d"/>
+ <inv_folder_trash.tga value="88ad072e-ea0b-aabd-5ac0-b37862a6eb66"/>
+ <inv_folder_plain_closed.tga value="86f00960-c3e9-9680-145d-3beffd743e9c"/>
+ <inv_folder_plain_open.tga value="d15dc243-2d0b-47af-0ce1-ec376464bdc8"/>
+ <inv_folder_snapshot.tga value="6efe85e7-800f-1843-296c-a5b7adffe091"/>
+ <inv_folder_lostandfound.tga value="9a371a04-297d-bacf-0d16-5f49753efe1d"/>
+ <inv_folder_animation.tga value="4d59b3ee-f29d-b912-2bcc-9bb1f8a07ec6"/>
+ <inv_folder_gesture.tga value="4de9129a-9fc1-d759-d739-364293906ba2"/>
+ <inv_item_texture.tga value="19f452d7-4eee-9f46-76cc-5497d17f1dd9"/>
+ <inv_item_sound.tga value="eb414d69-c77d-d4e7-66e6-6c2e6f6c1976"/>
+ <inv_item_callingcard_online.tga value="672cc53e-8dc0-ba91-2a4e-574104cf071c"/>
+ <inv_item_callingcard_offline.tga value="d0afe86b-2489-7600-55b7-6abb0a63d9f9"/>
+ <inv_item_landmark.tga value="bf25a2a0-85da-7fa0-0993-e461768d0221"/>
+ <inv_item_landmark_visited.tga value="229fac85-5428-4ab7-adeb-eb8389e91092"/>
+ <inv_item_script.tga value="59a3df81-ed76-06c9-7264-6dada535e7a3"/>
+ <inv_item_clothing.tga value="34dfe476-8e26-0e3a-11cf-76cc4a7126ce"/>
+ <inv_item_object.tga value="0f0780a0-89c4-742a-ef28-26405a41cf85"/>
+ <inv_item_notecard.tga value="23ce8a2c-9ea2-d863-6572-806f0645b0c7"/>
+ <inv_item_bodypart.tga value="d2a5362d-5c55-57dd-a9e9-5c814d1ddc16"/>
+ <inv_item_attach.tga value="5bcae41e-aa5d-02f8-edf1-605ebdd875ab"/>
+ <inv_item_snapshot.tga value="3810d584-b092-7caa-57e0-010f192b9659"/>
+ <inv_item_eyes.tga value="eaa5fd96-5c25-06ef-2280-7ef20203e167"/>
+ <inv_item_gloves.tga value="117b11cb-c04e-5081-13da-1a8846070fd0"/>
+ <inv_item_hair.tga value="6bca3bf4-ed6d-d438-63a0-2a7066d03a0b"/>
+ <inv_item_jacket.tga value="8df59386-56e0-c811-0443-840da3acb3a5"/>
+ <inv_item_pants.tga value="a87a58ca-f857-63b1-0acf-072711ed1bdb"/>
+ <inv_item_shape.tga value="4463e433-4db5-79ef-c1b0-4821b03ddb07"/>
+ <inv_item_shirt.tga value="e2ffb62b-6abc-22d6-952d-764759b4d636"/>
+ <inv_item_shoes.tga value="cf384fa5-1edd-c37c-2134-283dd4fe3396"/>
+ <inv_item_skirt.tga value="0b43f826-2abc-2944-7d72-10777a51d19b"/>
+ <inv_item_socks.tga value="22137c6d-6ec5-6eee-9a2e-2d7a9e6cbcd4"/>
+ <inv_item_underpants.tga value="2f15dc09-4385-526c-aa5d-d9d516ec7d99"/>
+ <inv_item_undershirt.tga value="f72ab629-a3ab-de0c-35c0-5285e27478ce"/>
+ <inv_item_animation.tga value="b5cda0d6-d196-ce48-63db-d04323ef8931"/>
+ <inv_item_gesture.tga value="5579245d-d5bf-5f13-46b0-8624490de24c"/>
+ <pixiesmall.tga value="168e6813-096e-07ea-97ae-fd416826f627"/>
+ <legend.tga value="ca7609c6-6ec6-32d9-332e-0d8f437ef644"/>
+ <propertyline.tga value="e3548c46-8d5e-03da-fcab-4fc36ad818bb"/>
+ <startup_logo.tga value="66864f3c-e095-d9c8-058d-d6575e6ed1b8"/>
+ <grass_texture_1.tga value="79504bf5-c3ec-0763-6563-d843de66d0a1"/>
+ <grass_texture_2.tga value="6c4727b8-ac79-ba44-3b81-f9aa887b47eb"/>
+ <grass_texture_3.tga value="99bd60a2-3250-efc9-2e39-2fbcadefbecc"/>
+ <grass_texture_4.tga value="7a2b3a4a-53c2-53ac-5716-aac7d743c020"/>
+ <undergrowth_1.tga value="8f458549-173b-23ff-d4ff-bfaa5ea2371b"/>
+ <silhouette.tga value="da5d4079-7819-6b53-d2a4-dc9929381d7d"/>
+ <avatar_thumb_bkgrnd.tga value="3a7f4f0d-be14-ee78-29e3-fc8b0b2a68d3"/>
+ <missing_asset.tga value="32dfd1c8-7ff6-5909-d983-6d4adfb4255d"/>
+ <alpha_gradient.tga value="e97cf410-8e61-7005-ec06-629eba4cd1fb"/>
+ <alpha_gradient_2d.tga value="38b86f85-2575-52a9-a531-23108d8da837"/>
+ <alpha_noise.tga value="b9e1cf8a-9660-c020-0c69-18f1ea27268a"/>
+ <alpha_sizzle.tga value="e121e2fc-7573-740f-edfd-0d45a9ba486e"/>
+ <bump_woodgrain.tga value="058c75c0-a0d5-f2f8-43f3-e9699a89c2fc"/>
+ <bump_bark.tga value="6c9fa78a-1c69-2168-325b-3e03ffa348ce"/>
+ <bump_bricks.tga value="b8eed5f0-64b7-6e12-b67f-43fa8e773440"/>
+ <bump_checker.tga value="9deab416-9c63-78d6-d558-9a156f12044c"/>
+ <bump_concrete.tga value="db9d39ec-a896-c287-1ced-64566217021e"/>
+ <bump_crustytile.tga value="f2d7b6f6-4200-1e9a-fd5b-96459e950f94"/>
+ <bump_cutstone.tga value="d9258671-868f-7511-c321-7baef9e948a4"/>
+ <bump_discs.tga value="d21e44ca-ff1c-a96e-b2ef-c0753426b7d9"/>
+ <bump_gravel.tga value="4726f13e-bd07-f2fb-feb0-bfa2ac58ab61"/>
+ <bump_petridish.tga value="e569711a-27c2-aad4-9246-0c910239a179"/>
+ <bump_siding.tga value="073c9723-540c-5449-cdd4-0e87fdc159e3"/>
+ <bump_stonetile.tga value="ae874d1a-93ef-54fb-5fd3-eb0cb156afc0"/>
+ <bump_stucco.tga value="92e66e00-f56f-598a-7997-048aa64cde18"/>
+ <bump_suction.tga value="83b77fc6-10b4-63ec-4de7-f40629f238c5"/>
+ <bump_weave.tga value="735198cf-6ea0-2550-e222-21d3c6a341ae"/>
+ <icon_avatar_online.tga value="529ed15b-3d41-dcc1-79de-90bf21770b5b"/>
+ <icon_avatar_offline.tga value="34648c67-5bfb-5790-e05e-8bd6600fd087"/>
+ <icon_event.tga value="be235ae0-53cf-1d68-b3ae-cf375ed1fb58"/>
+ <icon_event_mature.tga value="cc090999-1b3e-2e97-7a38-c9f4afd10297"/>
+ <icon_group.tga value="04237108-a879-5a95-9b0c-b18fd09bc447"/>
+ <icon_place.tga value="ba0bac4e-815e-14e1-2895-5065b8c703b3"/>
+ <icon_top_pick.tga value="77ca91a2-4431-aeaf-6249-3dd99c7dd86d"/>
+ <icon_popular.tga value="bdd47da5-5b5b-c906-37ad-16aaa64f096f"/>
+ <icon_for_sale.tga value="f20728fd-1670-3771-2293-e0dd3f0bcaab"/>
+ <icon_auction.tga value="96abf5b1-335c-6b76-61e3-74ada07f3cb8"/>
+ <icon_land_for_landless.tga value="c421ddf2-b9d7-b373-503c-f4c423f37f1c"/>
+ <icon_day_cycle.tga value="5b30a285-f1e3-92b1-dcd3-0d07366ced3e"/>
+ <icon_lock.tga value="9beb8cdd-3dce-53c2-b28e-e1f3bc2ec0a4"/>
+ <noentrylines.tga value="5d3e196b-fd4d-ada7-e4c1-99f8e9f1cfbf"/>
+ <noentrypasslines.tga value="ac8f8627-6a30-8da8-d4bd-958668eea7a0"/>
+ <notify_tip_icon.tga value="74ba3584-58ea-9984-5b76-62d37942ab77"/>
+ <notify_box_icon.tga value="b2ef2d31-9714-a07b-6ca7-31638166364b"/>
+ <notify_next.tga value="07d0ea4c-af0c-aad1-dbbf-c24020ff2b80"/>
+ <map_avatar_you_8.tga value="02fbdc40-5e07-a6e1-228b-58e10f8335b7"/>
+ <map_avatar_8.tga value="0be58a91-8065-c02b-7a12-2cc14dddbc37"/>
+ <map_avatar_16.tga value="db0dadd5-026a-88cf-f5c1-523a0a2daa3e"/>
+ <map_telehub.tga value="bf1b2bb0-13b1-40ae-3354-b1b93761bdb4"/>
+ <map_infohub.tga value="85b1a79a-7f6c-9df3-4d6c-17b1a4efb55a"/>
+ <map_home.tga value="ae9b8f5f-03a1-2e71-db77-6eb27a1ba181"/>
+ <map_event.tga value="6008be5e-9267-2a3a-9798-e81b076c22ca"/>
+ <map_event_mature.tga value="f9cdba28-a227-d613-2f16-ce06209314ae"/>
+ <map_track_8.tga value="bfdc7bf6-e2ee-1754-f4df-cc25887714ad"/>
+ <map_track_16.tga value="a3878395-ef00-a0e6-ee9a-f45ed6b9ce59"/>
+ <object_cone.tga value="c2b8c90a-7dca-26e3-1a63-7aa4a0389cf9"/>
+ <object_cone_active.tga value="cf69c64b-f19e-e1f3-a586-42fef31a23be"/>
+ <object_cube.tga value="70c747ac-1de3-a8b9-514d-101753ca6ccb"/>
+ <object_cube_active.tga value="f9c5e213-1076-7a7d-7889-52388aad2c1a"/>
+ <object_cylinder.tga value="13e35d95-5f6c-9a91-1766-49dedf9b1267"/>
+ <object_cylinder_active.tga value="3f3e4932-8412-e2a7-cfe9-92caf5978b1b"/>
+ <object_grass.tga value="7ca8e672-920b-4653-3970-1abc91abef58"/>
+ <object_grass_active.tga value="d0fc7cc9-646a-6860-cf7c-1d9e58cd6dab"/>
+ <object_hemi_cone.tga value="69d5e60c-739a-40b1-b526-84072121e394"/>
+ <object_hemi_cone_active.tga value="2e0c5435-95bb-1c0d-5da1-42336fb1cfc0"/>
+ <object_hemi_cylinder.tga value="f4be3e06-24a8-f86e-acc7-7daefc0572b7"/>
+ <object_hemi_cylinder_active.tga value="67279486-cfc1-3633-de42-85db65db373c"/>
+ <object_hemi_sphere.tga value="b67251ab-1716-b9fb-f911-967ba3fe027b"/>
+ <object_hemi_sphere_active.tga value="6c489466-3058-6475-6b1b-e5fc1d49f1f3"/>
+ <object_pyramid.tga value="9dde8b56-2cc4-a932-b63a-38c3a83221ad"/>
+ <object_pyramid_active.tga value="e7217b1a-e3d8-e339-d28a-d7714d0b5bee"/>
+ <object_sphere.tga value="7fa122c0-b994-460e-8636-cdc451d67268"/>
+ <object_sphere_active.tga value="f2c3bcbc-2904-41a5-1c22-688f176fd1ee"/>
+ <object_tetrahedron.tga value="e17db404-9fc5-9534-1038-777c82b2771f"/>
+ <object_tetrahedron_active.tga value="2792ea3b-c052-85fe-d168-a62b2f4e9d7c"/>
+ <object_tree.tga value="710d1bec-fb33-28f1-e77e-ddbb5b51f5ed"/>
+ <object_tree_active.tga value="da4835c7-b12a-41dd-11db-dae452f040c2"/>
+ <object_prism.tga value="02935f3a-dcda-3b42-1874-da89d4c12870"/>
+ <object_prism_active.tga value="223aac97-bd2f-ec2e-ad45-5641b77c78f9"/>
+ <object_torus.tga value="19e1f4c9-6aa6-4414-981d-59a1343a6472"/>
+ <object_torus_active.tga value="ef2bca77-5004-4547-b00a-3b96e463f89f"/>
+ <object_tube.tga value="7ce81316-a478-480f-961c-435fcbdecaf0"/>
+ <object_tube_active.tga value="55c3e4d1-cfdc-48a8-af32-a34844b91832"/>
+ <object_ring_active.tga value="2c955a73-fa31-237b-a4a1-5c8ede3bae50"/>
+ <object_ring.tga value="a7610e41-4647-16d8-0e0e-85a1211c1596"/>
+ <container_animation.tga value="c4e657a1-4c86-0159-2da0-32ff948484e6"/>
+ <container_bodypart.tga value="770cb2df-758d-34d5-36c7-e3de06db5b5d"/>
+ <container_clothing.tga value="dd90406f-4c8f-a3f9-41df-d562f94f09e0"/>
+ <container_gesture.tga value="59cd31c0-2791-3c48-f740-f0a36c68653e"/>
+ <container_landmark.tga value="24c63386-04f7-ce6f-4ff2-dfb215d2e21f"/>
+ <container_many_things.tga value="849d3292-d9fa-7186-5465-dd7b5fc1ec48"/>
+ <container_object.tga value="ad887ae1-2bee-f2c9-6786-5599de3c95c4"/>
+ <container_script.tga value="b93bd494-c4bd-bcdf-4a59-35a9497d03f3"/>
+ <container_sound.tga value="5ddea031-cfa3-2776-43e3-c7146c1b4cd6"/>
+ <container_texture.tga value="b3f95caf-bd62-bef3-0ded-dea752920629"/>
+ <avatar_aim_l_bow.bvh value="46bb4359-de38-4ed8-6a22-f1f52fe8f506"/>
+ <avatar_aim_r_bazooka.bvh value="b5b4a67d-0aee-30d2-72cd-77b333e932ef"/>
+ <avatar_aim_r_handgun.bvh value="3147d815-6338-b932-f011-16b56d9ac18b"/>
+ <avatar_aim_r_rifle.bvh value="ea633413-8006-180a-c3ba-96dd1d756720"/>
+ <avatar_angry_fingerwag.bvh value="c1bc7f36-3ba0-d844-f93c-93be945d644f"/>
+ <avatar_angry_tantrum.bvh value="11000694-3f41-adc2-606b-eee1d66f3724"/>
+ <avatar_away.bvh value="fd037134-85d4-f241-72c6-4f42164fedee"/>
+ <avatar_backflip.bvh value="c4ca6188-9127-4f31-0158-23c4e2f93304"/>
+ <avatar_blowkiss.bvh value="db84829b-462c-ee83-1e27-9bbee66bd624"/>
+ <avatar_bow.bvh value="82e99230-c906-1403-4d9c-3889dd98daba"/>
+ <avatar_brush.bvh value="349a3801-54f9-bf2c-3bd0-1ac89772af01"/>
+ <avatar_clap.bvh value="9b0c1c4e-8ac7-7969-1494-28c874c4f668"/>
+ <avatar_courtbow.bvh value="9ba1c942-08be-e43a-fb29-16ad440efc50"/>
+ <avatar_crouch.bvh value="201f3fdf-cb1f-dbec-201f-7333e328ae7c"/>
+ <avatar_crouchwalk.bvh value="47f5f6fb-22e5-ae44-f871-73aaaf4a6022"/>
+ <avatar_dance1.bvh value="b68a3d7c-de9e-fc87-eec8-543d787e5b0d"/>
+ <avatar_dance2.bvh value="928cae18-e31d-76fd-9cc9-2f55160ff818"/>
+ <avatar_dance3.bvh value="30047778-10ea-1af7-6881-4db7a3a5a114"/>
+ <avatar_dance4.bvh value="951469f4-c7b2-c818-9dee-ad7eea8c30b7"/>
+ <avatar_dance5.bvh value="4bd69a1d-1114-a0b4-625f-84e0a5237155"/>
+ <avatar_dance6.bvh value="cd28b69b-9c95-bb78-3f94-8d605ff1bb12"/>
+ <avatar_dance7.bvh value="a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6"/>
+ <avatar_dance8.bvh value="b0dc417c-1f11-af36-2e80-7e7489fa7cdc"/>
+ <avatar_dead.bvh value="57abaae6-1d17-7b1b-5f98-6d11a6411276"/>
+ <avatar_drink.bvh value="0f86e355-dd31-a61c-fdb0-3a96b9aad05f"/>
+ <avatar_express_afraid.bvh value="6b61c8e8-4747-0d75-12d7-e49ff207a4ca"/>
+ <avatar_express_anger.bvh value="5747a48e-073e-c331-f6f3-7c2149613d3e"/>
+ <avatar_express_bored.bvh value="b906c4ba-703b-1940-32a3-0c7f7d791510"/>
+ <avatar_express_cry.bvh value="92624d3e-1068-f1aa-a5ec-8244585193ed"/>
+ <avatar_express_embarrased.bvh value="514af488-9051-044a-b3fc-d4dbf76377c6"/>
+ <avatar_express_laugh.bvh value="18b3a4b5-b463-bd48-e4b6-71eaac76c515"/>
+ <avatar_express_repulsed.bvh value="36f81a92-f076-5893-dc4b-7c3795e487cf"/>
+ <avatar_express_sad.bvh value="0eb702e2-cc5a-9a88-56a5-661a55c0676a"/>
+ <avatar_express_shrug.bvh value="70ea714f-3a97-d742-1b01-590a8fcd1db5"/>
+ <avatar_express_surprise.bvh value="313b9881-4302-73c0-c7d0-0e7a36b6c224"/>
+ <avatar_express_wink.bvh value="869ecdad-a44b-671e-3266-56aef2e3ac2e"/>
+ <avatar_express_worry.bvh value="9f496bd2-589a-709f-16cc-69bf7df1d36c"/>
+ <avatar_falldown.bvh value="666307d9-a860-572d-6fd4-c3ab8865c094"/>
+ <avatar_female_walk.bvh value="f5fc7433-043d-e819-8298-f519a119b688"/>
+ <avatar_fist_pump.bvh value="7db00ccd-f380-f3ee-439d-61968ec69c8a"/>
+ <avatar_fly.bvh value="aec4610c-757f-bc4e-c092-c6e9caf18daf"/>
+ <avatar_flyslow.bvh value="2b5a38b2-5e00-3a97-a495-4c826bc443e6"/>
+ <avatar_hello.bvh value="9b29cd61-c45b-5689-ded2-91756b8d76a9"/>
+ <avatar_hold_l_bow.bvh value="8b102617-bcba-037b-86c1-b76219f90c88"/>
+ <avatar_hold_r_bazooka.bvh value="ef62d355-c815-4816-2474-b1acc21094a6"/>
+ <avatar_hold_r_handgun.bvh value="efdc1727-8b8a-c800-4077-975fc27ee2f2"/>
+ <avatar_hold_r_rifle.bvh value="3d94bad0-c55b-7dcc-8763-033c59405d33"/>
+ <avatar_hover.bvh value="4ae8016b-31b9-03bb-c401-b1ea941db41d"/>
+ <avatar_hover_down.bvh value="20f063ea-8306-2562-0b07-5c853b37b31e"/>
+ <avatar_hover_up.bvh value="62c5de58-cb33-5743-3d07-9e4cd4352864"/>
+ <avatar_impatient.bvh value="5ea3991f-c293-392e-6860-91dfa01278a3"/>
+ <avatar_jump.bvh value="2305bd75-1ca9-b03b-1faa-b176b8a8c49e"/>
+ <avatar_jumpforjoy.bvh value="709ea28e-1573-c023-8bf8-520c8bc637fa"/>
+ <avatar_kick_roundhouse_r.bvh value="49aea43b-5ac3-8a44-b595-96100af0beda"/>
+ <avatar_kissmybutt.bvh value="19999406-3a3a-d58c-a2ac-d72e555dcf51"/>
+ <avatar_land.bvh value="7a17b059-12b2-41b1-570a-186368b6aa6f"/>
+ <avatar_laugh_short.bvh value="ca5b3f14-3194-7a2b-c894-aa699b718d1f"/>
+ <avatar_motorcycle_sit.bvh value="08464f78-3a8e-2944-cba5-0c94aff3af29"/>
+ <avatar_musclebeach.bvh value="315c3a41-a5f3-0ba4-27da-f893f769e69b"/>
+ <avatar_no_head.bvh value="5a977ed9-7f72-44e9-4c4c-6e913df8ae74"/>
+ <avatar_no_unhappy.bvh value="d83fa0e5-97ed-7eb2-e798-7bd006215cb4"/>
+ <avatar_nyanya.bvh value="f061723d-0a18-754f-66ee-29a44795a32f"/>
+ <avatar_peace.bvh value="b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9"/>
+ <avatar_point_me.bvh value="17c024cc-eef2-f6a0-3527-9869876d7752"/>
+ <avatar_point_you.bvh value="ec952cca-61ef-aa3b-2789-4d1344f016de"/>
+ <avatar_prejump.bvh value="7a4e87fe-de39-6fcb-6223-024b00893244"/>
+ <avatar_punch_l.bvh value="f3300ad9-3462-1d07-2044-0fef80062da0"/>
+ <avatar_punch_onetwo.bvh value="eefc79be-daae-a239-8c04-890f5d23654a"/>
+ <avatar_punch_r.bvh value="c8e42d32-7310-6906-c903-cab5d4a34656"/>
+ <avatar_rps_countdown.bvh value="35db4f7e-28c2-6679-cea9-3ee108f7fc7f"/>
+ <avatar_rps_paper.bvh value="0836b67f-7f7b-f37b-c00a-460dc1521f5a"/>
+ <avatar_rps_rock.bvh value="42dd95d5-0bc6-6392-f650-777304946c0f"/>
+ <avatar_rps_scissors.bvh value="16803a9f-5140-e042-4d7b-d28ba247c325"/>
+ <avatar_run.bvh value="05ddbff8-aaa9-92a1-2b74-8fe77a29b445"/>
+ <avatar_salute.bvh value="cd7668a6-7011-d7e2-ead8-fc69eff1a104"/>
+ <avatar_shoot_l_bow.bvh value="e04d450d-fdb5-0432-fd68-818aaf5935f8"/>
+ <avatar_shout.bvh value="6bd01860-4ebd-127a-bb3d-d1427e8e0c42"/>
+ <avatar_sit.bvh value="1a5fe8ac-a804-8a5d-7cbd-56bd83184568"/>
+ <avatar_sit_female.bvh value="b1709c8d-ecd3-54a1-4f28-d55ac0840782"/>
+ <avatar_sit_generic.bvh value="245f3c54-f1c0-bf2e-811f-46d8eeb386e7"/>
+ <avatar_sit_ground.bvh value="1c7600d6-661f-b87b-efe2-d7421eb93c86"/>
+ <avatar_sit_ground_constrained.bvh value="1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e"/>
+ <avatar_sit_to_stand.bvh value="a8dee56f-2eae-9e7a-05a2-6fb92b97e21e"/>
+ <avatar_sleep.bvh value="f2bed5f9-9d44-39af-b0cd-257b2a17fe40"/>
+ <avatar_smoke_idle.bvh value="d2f2ee58-8ad1-06c9-d8d3-3827ba31567a"/>
+ <avatar_smoke_inhale.bvh value="6802d553-49da-0778-9f85-1599a2266526"/>
+ <avatar_smoke_throw_down.bvh value="0a9fb970-8b44-9114-d3a9-bf69cfe804d6"/>
+ <avatar_snapshot.bvh value="eae8905b-271a-99e2-4c0e-31106afd100c"/>
+ <avatar_soft_land.bvh value="f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57"/>
+ <avatar_stand.bvh value="2408fe9e-df1d-1d7d-f4ff-1384fa7b350f"/>
+ <avatar_stand_1.bvh value="15468e00-3400-bb66-cecc-646d7c14458e"/>
+ <avatar_stand_2.bvh value="370f3a20-6ca6-9971-848c-9a01bc42ae3c"/>
+ <avatar_stand_3.bvh value="42b46214-4b44-79ae-deb8-0df61424ff4b"/>
+ <avatar_stand_4.bvh value="f22fed8b-a5ed-2c93-64d5-bdd8b93c889f"/>
+ <avatar_standup.bvh value="3da1d753-028a-5446-24f3-9c9b856d9422"/>
+ <avatar_stretch.bvh value="80700431-74ec-a008-14f8-77575e73693f"/>
+ <avatar_stride.bvh value="1cb562b0-ba21-2202-efb3-30f82cdf9595"/>
+ <avatar_surf.bvh value="41426836-7437-7e89-025d-0aa4d10f1d69"/>
+ <avatar_sword_strike_r.bvh value="85428680-6bf9-3e64-b489-6f81087c24bd"/>
+ <avatar_talk.bvh value="5c682a95-6da4-a463-0bf6-0f5b7be129d1"/>
+ <avatar_throw_r.bvh value="aa134404-7dac-7aca-2cba-435f9db875ca"/>
+ <avatar_tryon_shirt.bvh value="83ff59fe-2346-f236-9009-4e3608af64c1"/>
+ <avatar_turn_180.bvh value="038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53"/>
+ <avatar_turnback_180.bvh value="6883a61a-b27b-5914-a61e-dda118a9ee2c"/>
+ <avatar_turnleft.bvh value="56e0ba0d-4a9f-7f27-6117-32f2ebbf6135"/>
+ <avatar_turnright.bvh value="2d6daa51-3192-6794-8e2e-a15f8338ec30"/>
+ <avatar_type.bvh value="c541c47f-e0c0-058b-ad1a-d6ae3a4584d9"/>
+ <avatar_walk.bvh value="6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0"/>
+ <avatar_whisper.bvh value="7693f268-06c7-ea71-fa21-2b30d6533f8f"/>
+ <avatar_whistle.bvh value="b1ed7982-c68e-a982-7561-52a88a5298c0"/>
+ <avatar_wink_hollywood.bvh value="c0c4030f-c02b-49de-24ba-2331f43fe41c"/>
+ <avatar_yes_happy.bvh value="b8c8b2a3-9008-1771-3bfc-90924955ab2d"/>
+ <avatar_yes_head.bvh value="15dd911d-be82-2856-26db-27659b142875"/>
+ <avatar_yoga_float.bvh value="42ecd00b-9947-a97c-400a-bbc9174c7aeb"/>
+ <fringe.tga value="8ac54e9d-ec09-d804-60ab-47404a9b4a36"/>
+ <foot_shadow.tga value="14e8a47d-1055-0a68-5d55-eafd9ad3da5b"/>
+ <img_smoke_poof.tga value="c734da52-f2ba-f0ba-d59e-15ea49f3d5e9"/>
+ <img_shot.tga value="173b05c7-53a9-4cf8-ce6b-5eec21c5c63f"/>
+ <folder_arrow.tga value="09a324a8-acc1-d9cd-2cbd-7465d90d3a98"/>
+ <color_swatch_alpha.tga value="f13db22f-c55c-8bdf-7b1c-221e56fde253"/>
+ <script_error.tga value="e5a0ec29-f59e-d29e-2c59-ed66c187c26c"/>
+ <status_script_debug.tga value="7775b5cc-93a5-6efd-0d9b-4e079afac217"/>
+ <water_normal.tga value="822ded49-9a6c-f61c-cb89-6df54f42cdf4"/>
+ <icon_groupnotice.tga value="21579c81-a85e-f11c-2d80-33a4c007d88c"/>
+ <icon_groupnoticeinventory.tga value="8fcca699-08e7-3d58-2f05-86c9d52bbe82"/>
+ <tab_background_lightgrey.tga value="c769e547-c307-43ca-2b6a-51cad6d1c527"/>
+ <tab_background_purple.tga value="0ae8a2e9-aff4-249c-fc4a-0f41f89847dd"/>
+ <tab_background_darkpurple.tga value="38ff4f7e-3078-a749-8302-d6cc94b404c4"/>
+ <smicon_warn.tga value="f47c17a3-8bfb-3c9f-22b8-77923de7eed9"/>
+ <uv_test1.tga value="f43b75f5-9aa5-18ec-d5d9-e6d1b8442613"/>
+ <uv_test2.tga value="300ce95f-3d3f-7c1a-3a22-3fc48f873fb9"/>
+ <eye_button_active.tga value="2b42b375-f9b4-788e-46c7-7ef38762d0ba"/>
+ <eye_button_inactive.tga value="be1b7225-98b5-eb2a-2c86-ddaae3328a6e"/>
+ <account_id_blue.tga value="6ab9179a-7308-58db-6c9d-893d3b52bece"/>
+ <account_id_orange.tga value="fbe89371-1251-4e77-d2d8-8eeccffe3ca8"/>
+ <account_id_green.tga value="3bf64d5a-38d3-b752-cf52-3d9f8fca353a"/>
+ <status_push.tga value="07d1f523-e327-4d10-20d6-8bc22a6e8f56"/>
+ <ff_visible_online.tga value="d609a41f-34c0-7aae-b2c6-2fc3ab26d916"/>
+ <ff_visible_map.tga value="20b52706-c1ab-414a-9dea-1cb788ad5689"/>
+ <ff_edit_mine.tga value="1baee0b9-4b89-39eb-8815-866d82300ab5"/>
+ <ff_edit_theirs.tga value="32e981cd-4700-da5a-7fc7-d573ec3742f4"/>
+ <inv_item_script_dangerous.tga value="0b502db8-6fcd-c442-ecfe-483a0dce875e"/>
+ <ff_visible_map_button.tga value="c1079bef-5cf9-90f3-6dcd-48989851c252"/>
+ <ff_visible_online_button.tga value="36749b47-93d6-2c5e-7ebd-d38d30311163"/>
+ <ff_edit_theirs_button.tga value="ca229f65-d7e0-133e-1bc2-674abc33f3d5"/>
+ <ff_edit_mine_button.tga value="57f05b46-63d8-c3d5-66d6-8b915746b956"/>
+ <ff_online_status_button.tga value="3b1b6a53-9c8c-568a-22c5-2a8f3e5286f5"/>
+ <oi_hud_cen_0_0.tga value="3c650257-9caf-7cad-b26c-84c9eca560f1"/>
+ <oi_hud_intro.tga value="7611fb3d-9ff2-abd3-d98f-805c1c87e757"/>
+ <oi_hud_underwater.tga value="cde61aea-83c2-3001-d598-6b348f7a8e0b"/>
+ <oi_hud_got_passport.tga value="1271838d-d777-b811-7c4c-2a00308bd80a"/>
+ <oi_hud_texture_off_edge.tga value="852be205-b1ea-6356-58c8-8c5ee5a841a6"/>
+ <oi_hud_texture_on_edge.tga value="ab11e6ff-a732-be70-67df-c43131274562"/>
+ <oi_hud_flyingabovewater.tga value="c9d150d6-2739-5f8b-cce6-3cf98242920a"/>
+ <oi_hud_walkingabovewater.tga value="78284eeb-05f3-ff25-11a0-3cc9dbb30f0c"/>
+ <oi_hud_landmark.tga value="6cd9c221-9d42-a283-256b-09a113a87271"/>
+ <oi_hud_cus_5_3.tga value="7c12f4fb-f502-26d1-a2f3-cdb6aff61663"/>
+ <oi_hud_cus_5_2.tga value="c52c9c94-adc0-0f4e-6658-ed33d6ea8829"/>
+ <oi_hud_cus_5_1.tga value="9f6d5d11-6ca9-608c-e8a6-b77989350292"/>
+ <oi_hud_cus_5_0.tga value="2000cff1-119f-2023-66c0-ac5630d2f96e"/>
+ <oi_hud_cus_4_5.tga value="f302a935-ccd1-e2f5-3a38-e185cc262f3a"/>
+ <oi_hud_cus_4_3.tga value="af8d5b3c-b40f-cea5-b0b2-440fbd84a11a"/>
+ <oi_hud_cus_4_2.tga value="11b26901-8207-12bc-5224-10a12ac4c651"/>
+ <oi_hud_cus_4_1.tga value="41baadb7-1b94-907e-9443-54e92bba77cd"/>
+ <oi_hud_cus_4_0.tga value="9d627f8e-092c-5d32-6c12-ef76ab81cedc"/>
+ <oi_hud_cus_3_4.tga value="b196486e-d0d2-4fd7-529a-c84b4495fc74"/>
+ <oi_hud_cus_3_2.tga value="0b81c4bb-de33-e493-7bcb-e7221d97e5e7"/>
+ <oi_hud_cus_3_1.tga value="436dab74-25ae-8b60-c648-50663b7faa1d"/>
+ <oi_hud_cus_3_0.tga value="6c1594de-1e66-273c-a2ab-8f0ffa8b4633"/>
+ <oi_hud_cus_2_4.tga value="bb31fe48-8566-eec0-e96b-64025f832b63"/>
+ <oi_hud_cus_2_2.tga value="c946959a-26ae-eb66-efa0-20154057789d"/>
+ <oi_hud_cus_2_1.tga value="c946959a-26ae-eb66-efa0-20154057789d"/>
+ <oi_hud_cus_2_0.tga value="d7833106-b4a8-7666-bde1-64886de289f9"/>
+ <oi_hud_cus_1_0.tga value="811ded22-5940-940c-4821-6fbbfb6611d6"/>
+ <oi_hud_cus_1_1.tga value="eda8513b-a343-5109-1fd6-f1c7ad89b703"/>
+ <oi_hud_cus_1_2.tga value="7a4ce18c-e715-34d4-dfee-704c270a8ac8"/>
+ <oi_hud_cus_1_4.tga value="d3771c15-ac03-b762-b992-d9fd2fedf38a"/>
+ <oi_hud_com_4_4.tga value="d9e1e90d-3cc3-6269-128e-67f7a2b32d26"/>
+ <oi_hud_com_4_2.tga value="0f649a26-6fdb-c73b-ffac-e50fc311d5ce"/>
+ <oi_hud_com_4_1.tga value="ae5b1ce6-a2d2-22d2-f532-6280b3bc6adb"/>
+ <oi_hud_com_4_0.tga value="12cda3a0-58c7-dfa8-7f9b-380e5bb8baf9"/>
+ <oi_hud_com_3_4.tga value="ff326257-0530-356a-e0f8-be535044e540"/>
+ <oi_hud_com_3_2.tga value="66740ddb-1d56-89f9-f0c9-ae5eb7bb9537"/>
+ <oi_hud_com_3_1.tga value="55d662f4-6a28-6388-7c75-af1c9fd33055"/>
+ <oi_hud_com_3_0.tga value="de9d318f-b69e-82f9-0c61-43b868c5ca6b"/>
+ <oi_hud_com_2_4.tga value="01d47e68-400a-d0e1-afb7-d6806d1d477e"/>
+ <oi_hud_com_2_0.tga value="09c98850-27d4-6a12-abae-4af4bba23b6b"/>
+ <oi_hud_com_1_3.tga value="5c2049b9-f797-6608-ca71-758f3716aa90"/>
+ <oi_hud_com_1_1.tga value="1116ff68-cdc4-1cfc-e137-30f8426afeda"/>
+ <oi_hud_com_1_0.tga value="bd847d31-f5af-95f7-2b9c-af47d8ba53bd"/>
+ <oi_hud_nav_4_5.tga value="66194280-b087-db94-35d9-41e8f7518515"/>
+ <oi_hud_nav_4_4.tga value="180c4241-e309-4c05-13ee-9080ab69498d"/>
+ <oi_hud_nav_4_3.tga value="e98a6ba6-99c6-fa15-84b6-9afadea6c467"/>
+ <oi_hud_nav_4_2.tga value="2e19f352-1893-59a9-949b-4d2cfd3a8222"/>
+ <oi_hud_nav_4_1.tga value="13a1675b-fb5a-19b3-b5a3-74b0a6765f7d"/>
+ <oi_hud_nav_4_0.tga value="e7526e8d-b085-b26c-b0ae-2708ec231401"/>
+ <oi_hud_nav_3_5.tga value="5e67b0d0-29a2-6a08-c85e-b12d59e53d6e"/>
+ <oi_hud_nav_3_4.tga value="2ed8fbc2-5c4d-53c2-b289-88baffceab1a"/>
+ <oi_hud_nav_3_3.tga value="e0a72f1a-282e-1c1a-2cb7-6423feb41759"/>
+ <oi_hud_nav_3_2.tga value="4bcebb23-da5e-47d9-eac1-e4453f762c8c"/>
+ <oi_hud_nav_3_1.tga value="6ac87575-330e-3a2d-3b80-a34e7b277e50"/>
+ <oi_hud_nav_3_0.tga value="f1451e8e-7310-9152-47d5-5d037c28fef3"/>
+ <oi_hud_nav_2_6.tga value="c60b42ff-ee60-98e4-e603-ca2470141d4b"/>
+ <oi_hud_nav_2_5.tga value="a02b5a1a-bbdb-5556-ae5b-a2e68494755a"/>
+ <oi_hud_nav_2_4.tga value="625535ab-8abf-b3e7-48fb-43f728b77c79"/>
+ <oi_hud_nav_2_3.tga value="00a609c3-5750-3b5a-3ce3-458bdf632203"/>
+ <oi_hud_nav_2_2.tga value="94903387-d37f-092c-e4d2-c190f68577b8"/>
+ <oi_hud_nav_2_1.tga value="ee0cd82c-6ce8-8e73-307b-6d0dc77b19e8"/>
+ <oi_hud_nav_2_0.tga value="3e10b379-ed2c-7424-1fe7-bef3558c7536"/>
+ <oi_hud_nav_1_4.tga value="bf8d0be8-2012-1664-3ea5-e69a71c206e9"/>
+ <oi_hud_nav_1_2.tga value="72100f87-18a7-fc4a-4793-de281e8b02cc"/>
+ <oi_hud_nav_1_1.tga value="b048faf3-60ce-c3a2-d034-36613449d377"/>
+ <oi_hud_nav_1_0.tga value="0ad45106-3b26-6448-0b90-feae8bd46c38"/>
+ <oi_hud_mov_4_5.tga value="7c4a45c2-37dd-312c-c6ab-20896dd0a5a6"/>
+ <oi_hud_mov_4_3.tga value="8a88da1c-3735-c71e-d48a-016df0798de4"/>
+ <oi_hud_mov_4_2.tga value="f55ae4d3-7d6a-e6ac-4cf7-03014ce14390"/>
+ <oi_hud_mov_4_1.tga value="1cc3fcf1-35c0-e222-27d2-6905cf5c4cee"/>
+ <oi_hud_mov_4_0.tga value="1ae592dc-46f4-616e-b7c6-0dff3e6f40e5"/>
+ <oi_hud_mov_3_4.tga value="831b39be-99fc-45bd-ba85-708f9dc93bfd"/>
+ <oi_hud_mov_3_2.tga value="9f7e7373-92a9-d66a-ad5a-afb55ca6ac1f"/>
+ <oi_hud_mov_3_1.tga value="ab37ed0d-7e66-1f77-3acf-b0fe4b74dbe8"/>
+ <oi_hud_mov_3_0.tga value="f5ff1f08-4c92-8606-1854-cc5b9d3e445c"/>
+ <oi_hud_mov_1_2.tga value="1e3abeed-e893-c44e-1f9d-5ecc76d21e5d"/>
+ <oi_hud_mov_1_0.tga value="e300fc95-aa94-8e31-c501-ce903cac8b7c"/>
+</settings> \ No newline at end of file
diff --git a/indra/newview/cursors_mac/UI_CURSOR_ARROW.tif b/indra/newview/cursors_mac/UI_CURSOR_ARROW.tif
new file mode 100644
index 0000000000..a20893b1ee
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_ARROW.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tif b/indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tif
new file mode 100644
index 0000000000..ab84bfbcc5
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tif b/indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tif
new file mode 100644
index 0000000000..400ae42943
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tif b/indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tif
new file mode 100644
index 0000000000..1cae801ee3
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_NO.tif b/indra/newview/cursors_mac/UI_CURSOR_NO.tif
new file mode 100644
index 0000000000..bc4a7a75e2
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_NO.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tif b/indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tif
new file mode 100644
index 0000000000..62332fc956
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tif
new file mode 100644
index 0000000000..de2c28bbbc
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZENS.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZENS.tif
new file mode 100644
index 0000000000..2676c78a4f
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZENS.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tif
new file mode 100644
index 0000000000..33df3392f3
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tif
new file mode 100644
index 0000000000..8952054c23
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tif
new file mode 100644
index 0000000000..39d89987aa
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tif
new file mode 100644
index 0000000000..bbf415a2f1
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tif
new file mode 100644
index 0000000000..2ab71c8af6
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tif
new file mode 100644
index 0000000000..db4ca17de9
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tif
new file mode 100644
index 0000000000..366630d6b2
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tif
new file mode 100644
index 0000000000..ad5a9ca06d
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tif
new file mode 100644
index 0000000000..864c8dee7a
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tif
new file mode 100644
index 0000000000..0a89f9bd91
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tif
new file mode 100644
index 0000000000..55317f19aa
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tif
new file mode 100644
index 0000000000..98881561cb
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tif
new file mode 100644
index 0000000000..316dd38c69
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tif
new file mode 100644
index 0000000000..d6d835a90b
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tif
new file mode 100644
index 0000000000..0cd06379e8
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tif
new file mode 100644
index 0000000000..65f3b6de28
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_WORKING.tif b/indra/newview/cursors_mac/UI_CURSOR_WORKING.tif
new file mode 100644
index 0000000000..c49f16a1c4
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_WORKING.tif
Binary files differ
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
new file mode 100644
index 0000000000..3675fc3618
--- /dev/null
+++ b/indra/newview/featuretable.txt
@@ -0,0 +1,170 @@
+version 9
+
+// NOTE: This is mostly identical to featuretable_mac.txt with a few differences
+// Should be combined into one table
+
+//
+// Generates lists of feature mask that can be applied on top of each other.
+//
+// // Begin comments
+// list <name>
+// Starts a feature list named <name>
+// <name> <available> <recommended>
+// <name> is the name of a feature
+// <available> is 0 or 1, whether the feature is available
+// <recommended> is an S32 which is the recommended value
+//
+// For now, the first list read sets up all of the default values
+//
+
+
+//
+// All contains everything at their default settings for high end machines
+// NOTE: All settings are set to the MIN of applied values, including 'all'!
+//
+list all
+RenderAGP 1 1
+RenderAniso 1 0
+RenderAvatarMode 1 2
+RenderAvatarVP 1 1
+RenderDistance 1 128
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderParticleCount 1 4096
+RenderRippleWater 1 1
+RenderTerrainDetail 1 2
+VertexShaderEnable 1 1
+
+//
+// Class 0 Hardware (Unknown or just old)
+//
+list Class0
+VertexShaderEnable 1 0
+RenderDistance 1 64
+RenderAvatarVP 1 0
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 1 Hardware
+//
+list Class1
+VertexShaderEnable 1 0
+RenderDistance 1 96
+RenderAvatarVP 1 1
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 2 Hardware (make it purty)
+//
+list Class2
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 1
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// Class 3 Hardware (make it purty)
+//
+list Class3
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 1
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// No Pixel Shaders available
+//
+list NoPixelShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// No Vertex Shaders available
+//
+list NoVertexShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// "Default" setups for safe, low, medium, high
+//
+list safe
+RenderAGP 1 0
+RenderAniso 1 0
+RenderAvatarVP 0 0
+RenderLighting 1 0
+RenderParticleCount 1 1024
+RenderTerrainDetail 1 0
+
+
+list low
+RenderAGP 1 1
+RenderAniso 1 0
+RenderLighting 1 0
+
+list medium
+RenderLighting 1 0
+
+
+//
+// CPU based feature masks
+//
+
+// 1Ghz or less (equiv)
+list CPUSlow
+RenderParticleCount 1 1024
+
+
+//
+// RAM based feature masks
+//
+list RAM256MB
+RenderObjectBump 0 0
+
+
+//
+// Graphics card based feature masks
+//
+list Brookdale
+RenderAniso 1 0
+RenderLighting 1 0
+RenderTerrainDetail 1 0
+
+list GeForce2
+RenderAniso 1 0
+RenderLighting 1 0
+RenderParticleCount 1 2048
+RenderTerrainDetail 1 0
+
+list GeForce3
+
+list ATI
+
+// Hacked to be paranoid "safe"
+// Disable AGP entirely, in Catalyst 4.3 it's at least 50% slower
+list Radeon8500
+RenderAGP 0 0
+RenderLighting 1 0
+RenderParticleCount 1 4096
+
+// Hacked to be paranoid "safe"
+list Radeon9700
+RenderParticleCount 1 4096
+
+// Hacked to be paranoid "safe"
+// Disable AGP entirely, in Catalyst 4.3 it's at least 50% slower
+list MobilityRadeon9000
+RenderLighting 1 0
+RenderParticleCount 1 4096
+
+list GeForceFX
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
new file mode 100644
index 0000000000..25228fc999
--- /dev/null
+++ b/indra/newview/featuretable_mac.txt
@@ -0,0 +1,151 @@
+version 9
+
+// NOTE: This is mostly identical to featuretable.txt with a few differences
+// Should be combined into one table
+
+//
+// Generates lists of feature mask that can be applied on top of each other.
+//
+// // Begin comments
+// list <name>
+// Starts a feature list named <name>
+// <name> <available> <recommended>
+// <name> is the name of a feature
+// <available> is 0 or 1, whether the feature is available
+// <recommended> is an S32 which is the recommended value
+//
+// For now, the first list read sets up all of the default values
+//
+
+
+//
+// All contains everything at their default settings for high end machines
+// NOTE: All settings are set to the MIN of applied values, including 'all'!
+//
+// Mac specific: RenderAvatarVP not enabled at all
+list all
+RenderAGP 1 1
+RenderAniso 1 0
+RenderAvatarMode 1 2
+RenderAvatarVP 1 0
+RenderDistance 1 128
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderParticleCount 1 4096
+RenderRippleWater 1 1
+RenderTerrainDetail 1 2
+VertexShaderEnable 1 1
+
+
+//
+// Class 0 Hardware (Unknown or just old)
+//
+list Class0
+VertexShaderEnable 1 0
+RenderAvatarVP 1 0
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 1 Hardware
+//
+list Class1
+VertexShaderEnable 1 0
+RenderAvatarVP 1 1
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 2 Hardware (make it purty)
+//
+list Class2
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 2
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// Class 3 Hardware (make it purty)
+//
+list Class3
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 2
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// No Pixel Shaders available
+//
+list NoPixelShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// No Vertex Shaders available
+//
+list NoVertexShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// "Default" setups for safe, low, medium, high
+//
+list safe
+RenderAGP 1 0
+RenderAniso 1 0
+RenderAvatarVP 0 0
+RenderDistance 1 64
+RenderLighting 1 0
+RenderParticleCount 1 1024
+RenderTerrainDetail 1 0
+
+
+list low
+RenderAGP 1 1
+RenderAniso 1 0
+RenderDistance 1 96
+RenderLighting 1 0
+
+list medium
+RenderLighting 1 0
+
+
+//
+// CPU based feature masks
+//
+
+// 1Ghz or less (equiv)
+list CPUSlow
+RenderDistance 1 96
+RenderParticleCount 1 1024
+
+
+//
+// RAM based feature masks
+//
+list RAM256MB
+RenderDistance 1 96
+RenderObjectBump 0 0
+
+// nVidia settings
+list NVIDIA
+
+list GeForce2
+RenderAniso 1 0
+RenderDistance 1 64
+RenderLighting 1 0
+RenderParticleCount 1 2048
+RenderTerrainDetail 1 0
+
+//
+// ATI and AGP now work okay.
+//
+list ATI
diff --git a/indra/newview/fmod_hidden_symbols.exp b/indra/newview/fmod_hidden_symbols.exp
new file mode 100644
index 0000000000..c0d5bed859
--- /dev/null
+++ b/indra/newview/fmod_hidden_symbols.exp
@@ -0,0 +1,140 @@
+__book_maptype1_quantvals
+__book_unquantize
+__float32_pack
+__float32_unpack
+__ilog
+__make_words
+_lpc_clear
+_lpc_init
+__vorbis_block_alloc
+__vorbis_block_ripcord
+__vorbis_apply_window
+__vorbis_window_get
+_vorbis_analysis_blockout
+_vorbis_analysis_buffer
+_vorbis_analysis_wrote
+_vorbis_block_clear
+_vorbis_block_init
+_vorbis_dsp_clear
+_vorbis_synthesis_blockin
+_vorbis_synthesis_init
+_vorbis_synthesis_pcmout
+_vorbis_synthesis_read
+_vorbis_packet_blocksize
+_vorbis_synthesis
+_vorbis_book_clear
+_vorbis_book_decode
+_vorbis_book_decodev_add
+_vorbis_book_decodev_set
+_vorbis_book_decodevs_add
+_vorbis_book_decodevv_add
+_vorbis_book_init_decode
+_vorbis_comment_add
+_vorbis_comment_add_tag
+_vorbis_comment_clear
+_vorbis_comment_init
+_vorbis_comment_query
+_vorbis_comment_query_count
+_vorbis_coslook
+_vorbis_fromdBlook
+_vorbis_info_blocksize
+_vorbis_info_clear
+_vorbis_info_init
+_vorbis_invsq2explook
+_vorbis_invsqlook
+_vorbis_lpc_from_curve
+_vorbis_lpc_from_data
+_vorbis_lpc_predict
+_vorbis_lsp_to_curve
+_vorbis_staticbook_clear
+_vorbis_staticbook_destroy
+_vorbis_staticbook_unpack
+_vorbis_synthesis_headerin
+_vorbis_synthesis_lapout
+_vorbis_synthesis_restart
+_vorbis_synthesis_trackonly
+_vorbis_window
+_ogg_packet_clear
+_ogg_page_bos
+_ogg_page_checksum_set
+_ogg_page_continued
+_ogg_page_eos
+_ogg_page_granulepos
+_ogg_page_packets
+_ogg_page_pageno
+_ogg_page_serialno
+_ogg_page_version
+_ogg_stream_reset_serialno
+_ogg_stream_clear
+_ogg_stream_destroy
+_ogg_stream_eos
+_ogg_stream_flush
+_ogg_stream_init
+_ogg_stream_packetout
+_ogg_stream_packetpeek
+_ogg_stream_pagein
+_ogg_stream_pageout
+_ogg_stream_reset
+_ogg_sync_buffer
+_ogg_sync_clear
+_ogg_sync_destroy
+_ogg_sync_init
+_ogg_sync_pageout
+_ogg_sync_pageseek
+_ogg_sync_reset
+_ogg_sync_wrote
+_ov_bitrate
+_ov_bitrate_instant
+_ov_clear
+_ov_comment
+_ov_info
+_ov_open
+_ov_open_callbacks
+_ov_pcm_seek
+_ov_pcm_seek_page
+_ov_pcm_tell
+_ov_pcm_total
+_ov_raw_seek
+_ov_raw_tell
+_ov_raw_total
+_ov_read
+_ov_read_float
+_ov_seekable
+_ov_serialnumber
+_ov_streams
+_ov_test
+_ov_test_callbacks
+_ov_test_open
+_ov_time_seek
+_ov_time_seek_page
+_ov_time_tell
+_ov_time_total
+_ogg_toupper
+_oggpackB_adv
+_oggpackB_adv1
+_oggpackB_bits
+_oggpackB_bytes
+_oggpackB_get_buffer
+_oggpackB_look
+_oggpackB_look1
+_oggpackB_read
+_oggpackB_read1
+_oggpackB_readinit
+_oggpackB_reset
+_oggpack_adv
+_oggpack_adv1
+_oggpack_bits
+_oggpack_bytes
+_oggpack_get_buffer
+_oggpack_look
+_oggpack_look1
+_oggpack_read
+_oggpack_read1
+_oggpack_readinit
+_oggpack_reset
+_ov_crosslap
+_ov_pcm_seek_lap
+_ov_pcm_seek_page_lap
+_ov_raw_seek_lap
+_ov_time_seek_lap
+_ov_time_seek_page_lap
diff --git a/indra/newview/fmodwrapper.cpp b/indra/newview/fmodwrapper.cpp
new file mode 100644
index 0000000000..99fd95434f
--- /dev/null
+++ b/indra/newview/fmodwrapper.cpp
@@ -0,0 +1,19 @@
+/**
+ * @file fmodwrapper.cpp
+ * @brief dummy source file for building a shared library to wrap libfmod.a
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+extern "C"
+{
+ void FSOUND_Init(void);
+}
+
+void* fmodwrapper(void)
+{
+ // When building the fmodwrapper library, the linker doesn't seem to want to bring in libfmod.a unless I explicitly
+ // reference at least one symbol in the library. This seemed like the simplest way.
+ return (void*)&FSOUND_Init;
+} \ No newline at end of file
diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt
new file mode 100644
index 0000000000..1af40f8932
--- /dev/null
+++ b/indra/newview/gpu_table.txt
@@ -0,0 +1,118 @@
+//
+// Categorizes graphics chips into various classes by name
+//
+// The table contains chip names regular expressions to match
+// against driver strings and a class number.
+//
+// Class Numbers:
+// 0 - Compatibility rendering only
+// 1 - Shaders available off by default
+// 2 - Shaders enabled by default
+// 3 - Everything on.
+//
+// Format:
+// <chip name> <regexp> <class>
+//
+
+3Dfx .*3Dfx.* 0
+3Dlabs .*3Dlabs.* 0
+ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1
+ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3
+ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3
+ATI ASUS X1300 .*ATI.*ASUS X13.* 3
+ATI ASUS X1600 .*ATI.*ASUS X16.* 3
+ATI Diamond X1300 .*ATI.*Diamond X13.* 3
+ATI FireGL .*ATI.*Fire.*GL.* 0
+ATI FireMV .*ATI.*FireMV.* 0
+ATI Generic .*ATI.*Generic.* 0
+ATI Radeon 7000 .*ATI.*Radeon 7.* 0
+ATI Radeon 8000 .*ATI.*Radeon 8.* 0
+ATI Radeon 9000 .*ATI.*Radeon 90.* 1
+ATI Radeon 9100 .*ATI.*Radeon 91.* 1
+ATI Radeon 9200 .*ATI.*Radeon 92.* 1
+ATI Radeon 9500 .*ATI.*Radeon 95.* 1
+ATI Radeon 9600 .*ATI.*Radeon 96.* 1
+ATI Radeon 9700 .*ATI.*Radeon 97.* 1
+ATI Radeon 9800 .*ATI.*Radeon 98.* 1
+ATI Radeon X1300 .*ATI.*Radeon X13.* 3
+ATI Radeon X1600 .*ATI.*Radeon X16.* 3
+ATI Radeon X1800 .*ATI.*Radeon X18.* 3
+ATI Radeon X1900 .*ATI.*Radeon X19.* 3
+ATI Radeon X300 .*ATI.*Radeon X3.* 2
+ATI Radeon X500 .*ATI.*Radeon X5.* 2
+ATI Radeon X600 .*ATI.*Radeon X6.* 2
+ATI Radeon X700 .*ATI.*Radeon X7.* 2
+ATI Radeon X800 .*ATI.*Radeon X8.* 2
+ATI Radeon Xpress .*ATI.*Radeon Xpress.* 1
+ATI Rage 128 .*ATI.*Rage 128.* 0
+Intel 830M .*Intel.*830M 0
+Intel 845G .*Intel.*845G 0
+Intel 855GM .*Intel.*855GM 0
+Intel 865G .*Intel.*865G 0
+Intel 900 .*Intel.*900.*900 0
+Intel 915G .*Intel.*915G 0
+Intel 915GM .*Intel.*915GM 0
+Intel 945G .*Intel.*945G 0
+Intel 945GM .*Intel.*945GM 0
+Intel 950 .*Intel.*950.*950 0
+Intel Brookdale .*Intel.*Brookdale.* 0
+Intel Montara .*Intel.*Montara.* 0
+Intel Springdale .*Intel.*Springdale.* 0
+Matrox .*Matrox.* 0
+NVIDIA GeForce .*GeForce 256.* 0
+NVIDIA GeForce 2 .*GeForce2.* 0
+NVIDIA GeForce 3 .*GeForce3.* 0
+NVIDIA GeForce 4 Go .*NVIDIA.*GeForce4.*Go.* 0
+NVIDIA GeForce 4 MX .*NVIDIA.*GeForce4 MX.* 0
+NVIDIA GeForce 4 Ti .*NVIDIA.*GeForce4 Ti.* 0
+NVIDIA GeForce 6100 .*NVIDIA.*GeForce 61.* 2
+NVIDIA GeForce 6200 .*NVIDIA.*GeForce 62.* 2
+NVIDIA GeForce 6500 .*NVIDIA.*GeForce 65.* 2
+NVIDIA GeForce 6600 .*NVIDIA.*GeForce 66.* 2
+NVIDIA GeForce 6700 .*NVIDIA.*GeForce 67.* 2
+NVIDIA GeForce 6800 .*NVIDIA.*GeForce 68.* 2
+NVIDIA GeForce 7300 .*NVIDIA.*GeForce 73.* 3
+NVIDIA GeForce 7600 .*NVIDIA.*GeForce 76.* 3
+NVIDIA GeForce 7800 .*NVIDIA.*GeForce 78.* 3
+NVIDIA GeForce 7900 .*NVIDIA.*GeForce 79.* 3
+NVIDIA GeForce FX 5100 .*NVIDIA.*GeForce FX 51.* 1
+NVIDIA GeForce FX 5200 .*NVIDIA.*GeForce FX 52.* 1
+NVIDIA GeForce FX 5500 .*NVIDIA.*GeForce FX 55.* 1
+NVIDIA GeForce FX 5600 .*NVIDIA.*GeForce FX 56.* 1
+NVIDIA GeForce FX 5700 .*NVIDIA.*GeForce FX 57.* 1
+NVIDIA GeForce FX 5800 .*NVIDIA.*GeForce FX 58.* 1
+NVIDIA GeForce FX 5900 .*NVIDIA.*GeForce FX 59.* 1
+NVIDIA GeForce FX Go5100 .*NVIDIA.*GeForce FX Go51.* 1
+NVIDIA GeForce FX Go5200 .*NVIDIA.*GeForce FX Go52.* 1
+NVIDIA GeForce FX Go5300 .*NVIDIA.*GeForce FX Go53.* 1
+NVIDIA GeForce FX Go5500 .*NVIDIA.*GeForce FX Go55.* 1
+NVIDIA GeForce FX Go5600 .*NVIDIA.*GeForce FX Go56.* 1
+NVIDIA GeForce FX Go5700 .*NVIDIA.*GeForce FX Go57.* 1
+NVIDIA GeForce FX Go5800 .*NVIDIA.*GeForce FX Go58.* 1
+NVIDIA GeForce FX Go5900 .*NVIDIA.*GeForce FX Go59.* 1
+NVIDIA GeForce Go 6 .*GeForce Go 6.* 2
+NVIDIA GeForce Go 6100 .*NVIDIA.*GeForce Go 61.* 2
+NVIDIA GeForce Go 6200 .*NVIDIA.*GeForce Go 62.* 2
+NVIDIA GeForce Go 6500 .*NVIDIA.*GeForce Go 65.* 2
+NVIDIA GeForce Go 6600 .*NVIDIA.*GeForce Go 66.* 2
+NVIDIA GeForce Go 6700 .*NVIDIA.*GeForce Go 67.* 2
+NVIDIA GeForce Go 6800 .*NVIDIA.*GeForce Go 68.* 2
+NVIDIA GeForce Go 7300 .*NVIDIA.*GeForce Go 73.* 3
+NVIDIA GeForce Go 7400 .*NVIDIA.*GeForce Go 74.* 3
+NVIDIA GeForce Go 7600 .*NVIDIA.*GeForce Go 76.* 3
+NVIDIA GeForce Go 7800 .*NVIDIA.*GeForce Go 78.* 3
+NVIDIA GeForce Go 7900 .*NVIDIA.*GeForce Go 79.* 3
+NVIDIA GeForce PCX .*GeForce PCX.* 1
+NVIDIA Generic .*NVIDIA.*NV.* 0
+NVIDIA Generic .*NVIDIA.*Unknown.* 0
+NVIDIA Quadro 2 .*Quadro2.* 0
+NVIDIA Quadro 4 .*Quadro4.* 0
+NVIDIA Quadro DCC .*Quadro DCC.* 0
+NVIDIA Quadro FX .*Quadro FX.* 1
+NVIDIA Quadro NVS .*Quadro NVS.* 0
+NVIDIA RIVA TNT .*RIVA TNT.* 0
+S3 .*S3 Graphics.* 0
+SiS SiS.* 0
+Trident Trident.* 0
+Tungsten Graphics Tungsten.* 0
+XGI XGI.* 0
diff --git a/indra/newview/licenses-linux.txt b/indra/newview/licenses-linux.txt
new file mode 100644
index 0000000000..f7ccf27a12
--- /dev/null
+++ b/indra/newview/licenses-linux.txt
@@ -0,0 +1,437 @@
+===========
+APR License
+===========
+
+Copyright 2000-2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+==========
+Cg License
+==========
+
+Copyright (c) 2002, NVIDIA Corporation.
+
+
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in consideration
+of your agreement to the following terms, and your use, installation,
+modification or redistribution of this NVIDIA software constitutes
+acceptance of these terms. If you do not agree with these terms, please do
+not use, install, modify or redistribute this NVIDIA software.
+
+
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive license,
+under NVIDIA's copyrights in this original NVIDIA software (the "NVIDIA
+Software"), to use, reproduce, modify and redistribute the NVIDIA
+Software, with or without modifications, in source and/or binary forms;
+provided that if you redistribute the NVIDIA Software, you must retain the
+copyright notice of NVIDIA, this notice and the following text and
+disclaimers in all such redistributions of the NVIDIA Software. Neither the
+name, trademarks, service marks nor logos of NVIDIA Corporation may be used
+to endorse or promote products derived from the NVIDIA Software without
+specific prior written permission from NVIDIA. Except as expressly stated
+in this notice, no other rights or licenses express or implied, are granted
+by NVIDIA herein, including but not limited to any patent rights that may be
+infringed by your derivative works or by other works in which the NVIDIA
+Software may be incorporated. No hardware is licensed hereunder.
+
+
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR ITS USE AND OPERATION
+EITHER ALONE OR IN COMBINATION WITH OTHER PRODUCTS.
+
+
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
+EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOST
+PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY OUT OF THE USE,
+REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE NVIDIA SOFTWARE,
+HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
+NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF NVIDIA HAS BEEN ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+============
+cURL License
+============
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2002, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+
+
+=============
+expat License
+=============
+
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================
+FreeType License
+================
+
+Portions of this software are copyright (c) 2003 The FreeType
+Project (www.freetype.org). All rights reserved.
+
+
+==========
+GL License
+==========
+
+Mesa 3-D graphics library
+Version: 6.2
+
+Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+=======================
+JPEG Library 6b License
+=======================
+
+This software is based in part on the work of the Independent JPEG Group
+
+================
+JPEG2000 License
+================
+
+Copyright 2001, David Taubman, The University of New South Wales (UNSW)
+The copyright owner is Unisearch Ltd, Australia (commercial arm of UNSW)
+Neither this copyright statement, nor the licensing details below
+may be removed from this file or dissociated from its contents.
+
+Licensee: Linden Research, Inc.
+License number: 00024
+The licensee has been granted a COMMERCIAL license to the contents of
+this source file. A brief summary of this license appears below. This
+summary is not to be relied upon in preference to the full text of the
+license agreement, accepted at purchase of the license.
+1. The Licensee has the right to Commercial Use of the Kakadu software,
+ including distribution of one or more Applications built using the
+ software.
+2. The Licensee has the right to Internal Use of the Kakadu software,
+ including use by employees of the Licensee or an Affiliate for the
+ purpose of performing services on behalf of the Licensee or Affiliate,
+ or in the performance of services for Third Parties who engage Licensee
+ or an Affiliate for such services.
+3. The Licensee has the right to distribute Reusable Code (including
+ source code and dynamically or statically linked libraries) to a Third
+ Party, provided the Third Party possesses a license to use the Kakadu
+ software.
+
+==================
+ogg/vorbis License
+==================
+
+Copyright (c) 2001, Xiphophorus
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiphophorus nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===========
+SDL License
+===========
+
+SDL - Simple DirectMedia Layer
+Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; if not, write to the Free
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Sam Lantinga
+slouken@libsdl.org
+
+The GNU Library GPL is available at http://www.gnu.org/copyleft/lesser.html
+
+=============
+ELFIO License
+=============
+
+ELFIO.h - ELF reader and producer.
+Copyright (C) 2001 Serge Lamikhov-Center <to_serge@users.sourceforge.net>
+
+This library 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.1 of the License, or (at your option) any later version.
+
+This library 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 library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+The GNU Library GPL is available at http://www.gnu.org/copyleft/lesser.html
+
+===============
+OpenSSL License
+===============
+
+Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+
+This product includes cryptographic software written by Eric Young
+(eay@cryptsoft.com). This product includes software written by Tim
+Hudson (tjh@cryptsoft.com).
+
+
+
+=======================
+Original SSLeay License
+=======================
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+
+==================
+xmlrpc-epi License
+==================
+
+Copyright 2000 Epinions, Inc.
+
+Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well.
+
+1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software.
+
+2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+
+3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+============
+zlib License
+============
+
+'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+
+Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the
+use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+The origin of this software must not be misrepresented; you must not claim
+that you wrote the original software. If you use this software in a product,
+an acknowledgment in the product documentation would be appreciated but is
+not required.
+
+Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+This notice may not be removed or altered from any source distribution.
+
+Jean-loup Gailly
+jloup@gzip.org
+
+Mark Adler
+madler@alumni.caltech.edu
diff --git a/indra/newview/licenses-mac.txt b/indra/newview/licenses-mac.txt
new file mode 100644
index 0000000000..f761ed6cbb
--- /dev/null
+++ b/indra/newview/licenses-mac.txt
@@ -0,0 +1,388 @@
+===========
+APR License
+===========
+
+Copyright 2000-2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+==========
+Cg License
+==========
+
+Copyright (c) 2002, NVIDIA Corporation.
+
+
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in consideration
+of your agreement to the following terms, and your use, installation,
+modification or redistribution of this NVIDIA software constitutes
+acceptance of these terms. If you do not agree with these terms, please do
+not use, install, modify or redistribute this NVIDIA software.
+
+
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive license,
+under NVIDIA's copyrights in this original NVIDIA software (the "NVIDIA
+Software"), to use, reproduce, modify and redistribute the NVIDIA
+Software, with or without modifications, in source and/or binary forms;
+provided that if you redistribute the NVIDIA Software, you must retain the
+copyright notice of NVIDIA, this notice and the following text and
+disclaimers in all such redistributions of the NVIDIA Software. Neither the
+name, trademarks, service marks nor logos of NVIDIA Corporation may be used
+to endorse or promote products derived from the NVIDIA Software without
+specific prior written permission from NVIDIA. Except as expressly stated
+in this notice, no other rights or licenses express or implied, are granted
+by NVIDIA herein, including but not limited to any patent rights that may be
+infringed by your derivative works or by other works in which the NVIDIA
+Software may be incorporated. No hardware is licensed hereunder.
+
+
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR ITS USE AND OPERATION
+EITHER ALONE OR IN COMBINATION WITH OTHER PRODUCTS.
+
+
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
+EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOST
+PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY OUT OF THE USE,
+REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE NVIDIA SOFTWARE,
+HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
+NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF NVIDIA HAS BEEN ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+============
+cURL License
+============
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2002, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+
+
+=============
+expat License
+=============
+
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================
+FreeType License
+================
+
+Portions of this software are copyright (c) 2003 The FreeType
+Project (www.freetype.org). All rights reserved.
+
+
+==========
+GL License
+==========
+
+Mesa 3-D graphics library
+Version: 6.2
+
+Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+=======================
+JPEG Library 6b License
+=======================
+
+This software is based in part on the work of the Independent JPEG Group
+
+================
+JPEG2000 License
+================
+
+Copyright 2001, David Taubman, The University of New South Wales (UNSW)
+The copyright owner is Unisearch Ltd, Australia (commercial arm of UNSW)
+Neither this copyright statement, nor the licensing details below
+may be removed from this file or dissociated from its contents.
+
+Licensee: Linden Research, Inc.
+License number: 00024
+The licensee has been granted a COMMERCIAL license to the contents of
+this source file. A brief summary of this license appears below. This
+summary is not to be relied upon in preference to the full text of the
+license agreement, accepted at purchase of the license.
+1. The Licensee has the right to Commercial Use of the Kakadu software,
+ including distribution of one or more Applications built using the
+ software.
+2. The Licensee has the right to Internal Use of the Kakadu software,
+ including use by employees of the Licensee or an Affiliate for the
+ purpose of performing services on behalf of the Licensee or Affiliate,
+ or in the performance of services for Third Parties who engage Licensee
+ or an Affiliate for such services.
+3. The Licensee has the right to distribute Reusable Code (including
+ source code and dynamically or statically linked libraries) to a Third
+ Party, provided the Third Party possesses a license to use the Kakadu
+ software.
+
+==================
+ogg/vorbis License
+==================
+
+Copyright (c) 2001, Xiphophorus
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiphophorus nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============
+OpenSSL License
+===============
+
+Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+
+This product includes cryptographic software written by Eric Young
+(eay@cryptsoft.com). This product includes software written by Tim
+Hudson (tjh@cryptsoft.com).
+
+
+
+=======================
+Original SSLeay License
+=======================
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+
+==================
+xmlrpc-epi License
+==================
+
+Copyright 2000 Epinions, Inc.
+
+Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well.
+
+1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software.
+
+2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+
+3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+============
+zlib License
+============
+
+'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+
+Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the
+use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+The origin of this software must not be misrepresented; you must not claim
+that you wrote the original software. If you use this software in a product,
+an acknowledgment in the product documentation would be appreciated but is
+not required.
+
+Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+This notice may not be removed or altered from any source distribution.
+
+Jean-loup Gailly
+jloup@gzip.org
+
+Mark Adler
+madler@alumni.caltech.edu
diff --git a/indra/newview/licenses-win32.txt b/indra/newview/licenses-win32.txt
new file mode 100644
index 0000000000..f761ed6cbb
--- /dev/null
+++ b/indra/newview/licenses-win32.txt
@@ -0,0 +1,388 @@
+===========
+APR License
+===========
+
+Copyright 2000-2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+==========
+Cg License
+==========
+
+Copyright (c) 2002, NVIDIA Corporation.
+
+
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in consideration
+of your agreement to the following terms, and your use, installation,
+modification or redistribution of this NVIDIA software constitutes
+acceptance of these terms. If you do not agree with these terms, please do
+not use, install, modify or redistribute this NVIDIA software.
+
+
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive license,
+under NVIDIA's copyrights in this original NVIDIA software (the "NVIDIA
+Software"), to use, reproduce, modify and redistribute the NVIDIA
+Software, with or without modifications, in source and/or binary forms;
+provided that if you redistribute the NVIDIA Software, you must retain the
+copyright notice of NVIDIA, this notice and the following text and
+disclaimers in all such redistributions of the NVIDIA Software. Neither the
+name, trademarks, service marks nor logos of NVIDIA Corporation may be used
+to endorse or promote products derived from the NVIDIA Software without
+specific prior written permission from NVIDIA. Except as expressly stated
+in this notice, no other rights or licenses express or implied, are granted
+by NVIDIA herein, including but not limited to any patent rights that may be
+infringed by your derivative works or by other works in which the NVIDIA
+Software may be incorporated. No hardware is licensed hereunder.
+
+
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR ITS USE AND OPERATION
+EITHER ALONE OR IN COMBINATION WITH OTHER PRODUCTS.
+
+
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
+EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOST
+PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY OUT OF THE USE,
+REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE NVIDIA SOFTWARE,
+HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
+NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF NVIDIA HAS BEEN ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+============
+cURL License
+============
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2002, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+
+
+=============
+expat License
+=============
+
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================
+FreeType License
+================
+
+Portions of this software are copyright (c) 2003 The FreeType
+Project (www.freetype.org). All rights reserved.
+
+
+==========
+GL License
+==========
+
+Mesa 3-D graphics library
+Version: 6.2
+
+Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+=======================
+JPEG Library 6b License
+=======================
+
+This software is based in part on the work of the Independent JPEG Group
+
+================
+JPEG2000 License
+================
+
+Copyright 2001, David Taubman, The University of New South Wales (UNSW)
+The copyright owner is Unisearch Ltd, Australia (commercial arm of UNSW)
+Neither this copyright statement, nor the licensing details below
+may be removed from this file or dissociated from its contents.
+
+Licensee: Linden Research, Inc.
+License number: 00024
+The licensee has been granted a COMMERCIAL license to the contents of
+this source file. A brief summary of this license appears below. This
+summary is not to be relied upon in preference to the full text of the
+license agreement, accepted at purchase of the license.
+1. The Licensee has the right to Commercial Use of the Kakadu software,
+ including distribution of one or more Applications built using the
+ software.
+2. The Licensee has the right to Internal Use of the Kakadu software,
+ including use by employees of the Licensee or an Affiliate for the
+ purpose of performing services on behalf of the Licensee or Affiliate,
+ or in the performance of services for Third Parties who engage Licensee
+ or an Affiliate for such services.
+3. The Licensee has the right to distribute Reusable Code (including
+ source code and dynamically or statically linked libraries) to a Third
+ Party, provided the Third Party possesses a license to use the Kakadu
+ software.
+
+==================
+ogg/vorbis License
+==================
+
+Copyright (c) 2001, Xiphophorus
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiphophorus nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============
+OpenSSL License
+===============
+
+Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+
+This product includes cryptographic software written by Eric Young
+(eay@cryptsoft.com). This product includes software written by Tim
+Hudson (tjh@cryptsoft.com).
+
+
+
+=======================
+Original SSLeay License
+=======================
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+
+==================
+xmlrpc-epi License
+==================
+
+Copyright 2000 Epinions, Inc.
+
+Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well.
+
+1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software.
+
+2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+
+3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+============
+zlib License
+============
+
+'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+
+Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the
+use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+The origin of this software must not be misrepresented; you must not claim
+that you wrote the original software. If you use this software in a product,
+an acknowledgment in the product documentation would be appreciated but is
+not required.
+
+Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+This notice may not be removed or altered from any source distribution.
+
+Jean-loup Gailly
+jloup@gzip.org
+
+Mark Adler
+madler@alumni.caltech.edu
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
new file mode 100644
index 0000000000..4748eaffbe
--- /dev/null
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -0,0 +1,202 @@
+Second Life - Linux Alpha README
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+This document contains information about the Second Life Linux
+client, and isn't meant to serve as an introduction to Second
+Life itself - please see <http://www.secondlife.com/whatis/>.
+
+1. Introduction
+2. System Requirements
+3. Installing & Running
+4. Known Issues
+5. Troubleshooting
+6. Advanced Troubleshooting
+7. Getting more help, and reporting problems
+
+
+1. INTRODUCTION
+-=-=-=-=-=-=-=-
+
+Hi! This is an ALPHA release of the Second Life client for Linux.
+The 'alpha' status means that not everything is implemented yet and
+we're still hard at work on this version of the client, but many
+residents find that it already works well 'out of the box' for accessing
+Second Life.
+
+We encourage you to try it out and let us know of its compatibility
+with your system. Be aware that although the client itself is provided
+for testing purposes, any changes you make within the Second Life world
+are permanent.
+
+Please enjoy!
+
+
+2. SYSTEM REQUIREMENTS
+-=-=-=-=-=-=-=-=-=-=-=
+
+Minimum requirements:
+ * Internet Connection: Cable or DSL
+ * Computer Processor: 800MHz Pentium III or Athlon, or better
+ * Computer Memory: 256MB or better
+ * Linux Operating System: A reasonably modern 32-bit Linux environment
+ is required. If you are running a 64-bit Linux distribution, you
+ may need a set of 32-bit compatibility libraries.
+ * Video/Graphics Card:
+ o nVidia GeForce 2, GeForce 4mx, or better
+ o OR ATI Radeon 8500, 9250, or better
+
+ **NOTE**: Second Life absolutely requires you to have recent, correctly-
+ configured OpenGL 3D drivers for your hardware - the graphics drivers
+ that came with your operating system may not be good enough! See the
+ TROUBLESHOOTING section if you encounter problems starting Second Life.
+
+For a more comfortable experience, the RECOMMENDED hardware for the Second
+Life Linux client is very similar to that for Windows, as detailed at:
+<https://secondlife.com/corporate/sysreqs.php>
+
+
+3. INSTALLING & RUNNING
+-=-=-=-=-=-=-=-=-=-=-=-
+
+The Second Life Linux client entirely runs out of the directory you have
+unpacked it into - no installation step is required.
+
+Run ./secondlife from the installation directory to start Second Life.
+
+User data is stored in the hidden directory ~/.secondlife by default; you may
+override this location with the SECONDLIFE_USER_DIR environment variable if
+you wish.
+
+
+4. KNOWN ISSUES
+-=-=-=-=-=-=-=-
+
+The following user-visible features are currently not fully implemented on
+the Linux client and are therefore known not to work properly:
+ * QuickTime movie playback and movie recording
+ * F1 Help
+ * Embedded Web Profiles and HTML Login Screen
+ * Video memory detection
+ * Full Unicode font rendering
+ * Auto-updater
+
+* UPDATING - when the client detects that a new version of Second Life
+ is available, it will ask you if you wish to download the new version.
+ This option is not implemented; to upgrade, you should manually download a
+ new version from the Second Life web site, <http://www.secondlife.com/>.
+
+* UPLOAD / SAVE / COLOR-PICKER DIALOGS - These only function when the client
+ is in 'windowed' mode, not 'fullscreen' mode.
+
+* GRAPHICAL SPEED/QUALITY - as many Linux graphics drivers are not as robust
+ as their counterparts for some other operating systems, advanced Second Life
+ graphical features have been disabled to aid stability. See PROBLEM 3 in the
+ TROUBLESHOOTING section if you wish to turn these on to enhance your
+ experience.
+
+
+5. TROUBLESHOOTING
+-=-=-=-=-=-=-=-=-=
+
+The client prints a lot of diagnostic information to the console it was
+run from. Most of this is also replicated in ~/.secondlife/logs/SecondLife.log
+- this is helpful to read when troubleshooting, especially 'WARNING' lines.
+
+PROBLEM 1:- Second Life fails to start up, with a warning on the console like:
+ 'Error creating window.' or
+ 'Unable to create window, be sure screen is set at 32-bit color' or
+ 'SDL: Couldn't find matching GLX visual.'
+SOLUTION:- Usually this indicates that your graphics card does not meet
+ the minimum requirements, or that your system's OpenGL 3D graphics driver is
+ not updated and configured correctly. If you believe that your graphics
+ card DOES meet the minimum requirements then you likely need to install the
+ official so-called 'non-free' nVidia or ATI (fglrx) graphics drivers; we
+ suggest one of the following options:
+ * Consult your Linux distribution's documentation for installing these
+ official drivers. For example, Ubuntu provides documentation here:
+ <https://help.ubuntu.com/community/BinaryDriverHowto>
+ * If your distribution does not make it easy, then you can download the
+ required Linux drivers straight from your graphics card manufacturer:
+ - nVidia cards: <http://www.nvidia.com/object/unix.html>
+ - ATI cards: <http://ati.amd.com/support/driver.html>
+
+PROBLEM 2:- My whole system seems to hang when running Second Life.
+SOLUTION:- This is typically a hardware/driver issue. The first thing to
+ do is to check that you have the most recent official drivers for your
+ graphics card.
+SOLUTION:- Some residents with ATI cards have reported that running
+ 'sudo aticonfig --locked-userpages=off' before running Second Life solves
+ their stability issues.
+SOLUTION:- As a last resort, you can disable most of Second Life's advanced
+ graphics features by editing the 'secondlife' script and removing the '#'
+ from the line which reads '#export LL_GL_NOEXT=x'
+
+PROBLEM 3:- Performance or graphical quality are not as high as I expect.
+PROBLEM:- I can't turn on Anisotropic Filtering, Ripple Water, or AGP.
+PROBLEM:- 'Shiny' doesn't work.
+SOLUTION:- Some graphics performance features in Second Life are disabled
+ by default for the Linux version due to stability issues with some common
+ Linux graphic drivers. You can re-enable these features at the slight
+ risk of decreasing system stability. To do so:
+ * Edit the 'secondlife' script. Comment-out these lines by putting a '#'
+ in front of them: 'export LL_GL_BASICEXT=x', 'export LL_GL_NOEXT=x',
+ 'export LL_GL_BLACKLIST=abcdefghijklmno'.
+ * Now start Second Life. Some advanced performance features will now be
+ automatically used, and some new options in Preferences will now be
+ available to you; there is no guarantee, however, that they will
+ positively affect performance!
+
+PROBLEM 4:- Sound effects seem to 'lag' a fraction of a second behind
+ actions.
+SOLUTION:- You may uncomment the 'LL_BAD_ESD' line in the 'secondlife' script
+ to get more responsive audio. However, if you do this then you may
+ encounter audio issues or a hang during login, so beware.
+
+
+6. ADVANCED TROUBLESHOOTING
+-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+The 'secondlife' script which launches Second Life contains some
+configuration options for advanced troubleshooters.
+
+* AUDIO - Edit the 'secondlife' script and you will see three audio
+ options: LL_BAD_ESD, LL_BAD_OSS, LL_BAD_ALSA. Second Life tries to
+ use ESD, OSS, then ALSA audio drivers in this order; you may uncomment
+ the corresponding LL_BAD_* option to skip an audio driver which you
+ believe may be causing you trouble.
+
+* OPENGL - For advanced troubleshooters, the LL_GL_BLACKLIST option lets
+ you disable specific GL extensions, each of which is represented by a
+ letter ("a"-"o"). If you can narrow down a stability problem on your system
+ to just one or two GL extensions then please post details of your hardware
+ (and drivers) to the Linux Client Alpha Testers forum (see link below) along
+ with the minimal LL_GL_BLACKLIST which solves your problems. This will help
+ us to improve stability for your hardware while minimally impacting
+ performance.
+ LL_GL_BASICEXT and LL_GL_NOEXT should be commented-out for this to be useful.
+
+
+7. GETTING MORE HELP AND REPORTING PROBLEMS
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+For general help and support with Second Life:
+<http://secondlife.com/community/support.php>
+
+In-world help: Please use the 'Help' menu in the client for general
+non-Linux-specific Second Life help including live support from the fabulous
+Live Help team.
+
+In-world discussion: There is a 'Linux Client Users' group
+inside Second Life which is free to join. You can find it by pressing
+the 'Search' button at the bottom of the window and then selecting the
+'Groups' tab and searching for 'Linux'. This group is useful for discussing
+Linux issues with fellow Linux client users who are online.
+
+Linux Client Alpha Testers forum:
+<http://forums.secondlife.com/forumdisplay.php?forumid=263>
+This is a good place for discussing Linux-specific Second Life problems
+if you find that the Troubleshooting section in this file hasn't helped.
+When reporting problems here, please include information about the
+Second Life version you are running, your graphics card, graphics driver,
+and Linux distribution.
+
diff --git a/indra/newview/linux_tools/launch_url.sh b/indra/newview/linux_tools/launch_url.sh
new file mode 100755
index 0000000000..564e834939
--- /dev/null
+++ b/indra/newview/linux_tools/launch_url.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+# bash v1.14+ expected
+
+# This script loads a web page in the 'default' graphical web browser.
+# It MUST return immediately (or soon), so the browser should be
+# launched in the background (thus no text-only browsers).
+# This script does not trust the URL to be well-escaped or shell-safe.
+#
+# On Unixoids we try, in order of decreasing priority:
+# - $BROWSER if set (preferred)
+# - kfmclient openURL
+# - x-www-browser
+# - opera
+# - firefox
+# - mozilla
+# - netscape
+
+URL="$1"
+
+if [ -z "$URL" ]; then
+ echo Usage: $0 URL
+ exit
+fi
+
+# if $BROWSER is defined, use it.
+XBROWSER=`echo "$BROWSER" |cut -f1 -d:`
+if [ ! -z "$XBROWSER" ]; then
+ XBROWSER_CMD=`echo "$XBROWSER" |cut -f1 -d' '`
+ # look for $XBROWSER_CMD either literally or in PATH
+ if [ -x "$XBROWSER_CMD" ] || which $XBROWSER_CMD >/dev/null; then
+ # check for %s string, avoiding bash2-ism of [[ ]]
+ if echo "$XBROWSER" | grep %s >/dev/null; then
+ # $XBROWSER has %s which needs substituting
+ echo "$URL" | xargs -r -i%s $XBROWSER &
+ exit
+ fi
+ # $XBROWSER has no %s, tack URL on the end instead
+ $XBROWSER "$URL" &
+ exit
+ fi
+ echo "Couldn't find the browser specified by \$BROWSER ($BROWSER)"
+ echo "Trying some others..."
+fi
+
+# else kfmclient
+# (embodies KDE concept of 'preferred browser')
+if which kfmclient >/dev/null; then
+ kfmclient openURL "$URL" &
+ exit
+fi
+
+# else x-www-browser
+# (Debianesque idea of a working X browser)
+if which x-www-browser >/dev/null; then
+ x-www-browser "$URL" &
+ exit
+fi
+
+# else opera
+# (if user has opera in their path, they probably went to the
+# trouble of installing it -> prefer it)
+if which opera >/dev/null; then
+ opera "$URL" &
+ exit
+fi
+
+# else firefox
+if which firefox >/dev/null; then
+ firefox "$URL" &
+ exit
+fi
+
+# else mozilla
+if which mozilla >/dev/null; then
+ mozilla "$URL" &
+ exit
+fi
+
+# else netscape
+if which netscape >/dev/null; then
+ netscape "$URL" &
+ exit
+fi
+
+echo 'Failed to find a known browser. Please consider setting the $BROWSER environment variable.'
+
+# end.
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
new file mode 100755
index 0000000000..222ce98c73
--- /dev/null
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+## Here are some configuration options for Linux Client Alpha Testers.
+## These options are for self-assisted troubleshooting during this alpha
+## testing phase; you should not usually need to touch them.
+
+## - Avoids using the ESD audio driver.
+#export LL_BAD_ESD=x
+
+## - Avoids using the OSS audio driver.
+#export LL_BAD_OSS=x
+
+## - Avoids using the ALSA audio driver.
+#export LL_BAD_ALSA=x
+
+## - Avoids the optional OpenGL extensions which have proven most problematic
+## on some hardware. Disabling this option may cause crashes and hangs on
+## some unstable combinations of drivers and hardware.
+export LL_GL_BASICEXT=x
+
+## - Avoids *all* optional OpenGL extensions. This is the safest and least-
+## exciting option. Enable this if you experience stability issues, and
+## report whether it helps in the Linux Client Alpha Testers forum.
+#export LL_GL_NOEXT=x
+
+## - For advanced troubleshooters, this lets you disable specific GL
+## extensions, each of which is represented by a letter a-o. If you can
+## narrow down a stability problem on your system to just one or two
+## extensions then please post details of your hardware (and drivers) to
+## the Linux Client Alpha Testers forum along with the minimal
+## LL_GL_BLACKLIST which solves your problems.
+#export LL_GL_BLACKLIST=abcdefghijklmno
+
+## - Avoids an often-buggy X feature that doesn't really benefit us anyway.
+export SDL_VIDEO_X11_DGAMOUSE=0
+
+## Nothing worth editing below this line.
+##-------------------------------------------------------------------
+
+RUN_PATH=`dirname "$0" || echo .`
+cd "${RUN_PATH}"
+LD_LIBRARY_PATH="`pwd`"/lib:"${LD_LIBRARY_PATH}" bin/do-not-directly-run-secondlife-bin `cat gridargs.dat` $@ | cat
+
+echo
+echo '*********************************************************'
+echo 'This is an ALPHA release of the Second Life linux client.'
+echo 'Thank you for testing!'
+echo 'You can visit the Linux Client Alpha Testers forum at:'
+echo 'http://forums.secondlife.com/forumdisplay.php?forumid=263'
+echo 'Please see README-linux.txt before reporting problems.'
+echo
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
new file mode 100644
index 0000000000..e46800fff1
--- /dev/null
+++ b/indra/newview/llagent.cpp
@@ -0,0 +1,7233 @@
+/**
+ * @file llagent.cpp
+ * @brief LLAgent class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "stdtypes.h"
+#include "stdenums.h"
+
+#include "llagent.h"
+
+#include "llcoordframe.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llcriticaldamp.h"
+#include "llfocusmgr.h"
+#include "llglheaders.h"
+#include "llparcel.h"
+#include "llpermissions.h"
+#include "llregionhandle.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "message.h"
+#include "llquaternion.h"
+#include "v3math.h"
+#include "v4math.h"
+//#include "vmath.h"
+
+#include "imageids.h"
+#include "llbox.h"
+#include "llbutton.h"
+#include "llcameraview.h"
+#include "llcallingcard.h"
+#include "llchatbar.h"
+#include "llconsole.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterdirectory.h"
+#include "llfloatergroupinfo.h"
+#include "llfloatergroups.h"
+#include "llfloatermap.h"
+#include "llfloatermute.h"
+#include "llfloatersnapshot.h"
+#include "llfloatertools.h"
+#include "llfloaterworldmap.h"
+#include "llgroupmgr.h"
+#include "llhudeffectlookat.h"
+#include "llhudmanager.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "lljoystickbutton.h"
+#include "llmenugl.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llnotify.h"
+#include "llquantize.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llsphere.h"
+#include "llstatusbar.h"
+#include "llimview.h"
+#include "lltool.h"
+#include "lltoolcomp.h" // for gToolGun
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolview.h"
+#include "llui.h" // for make_ui_sound
+#include "llviewercamera.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoground.h"
+#include "llvosky.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "pipeline.h"
+#include "roles_constants.h"
+#include "viewer.h"
+
+// Ventrella
+#include "llfollowcam.h"
+// end Ventrella
+
+extern LLMenuBarGL* gMenuBarView;
+extern F32 gMinObjectDistance;
+extern U8 gLastPickAlpha;
+extern F32 gFrameDTClamped;
+
+//drone wandering constants
+const F32 MAX_WANDER_TIME = 20.f; // seconds
+const F32 MAX_HEADING_HALF_ERROR = 0.2f; // radians
+const F32 WANDER_MAX_SLEW_RATE = 2.f * DEG_TO_RAD; // radians / frame
+const F32 WANDER_TARGET_MIN_DISTANCE = 10.f; // meters
+
+// Autopilot constants
+const F32 AUTOPILOT_HEADING_HALF_ERROR = 10.f * DEG_TO_RAD; // radians
+const F32 AUTOPILOT_MAX_SLEW_RATE = 1.f * DEG_TO_RAD; // radians / frame
+const F32 AUTOPILOT_STOP_DISTANCE = 2.f; // meters
+const F32 AUTOPILOT_HEIGHT_ADJUST_DISTANCE = 8.f; // meters
+const F32 AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND = 1.f; // meters
+const F32 AUTOPILOT_MAX_TIME_NO_PROGRESS = 1.5f; // seconds
+
+// face editing constants
+const LLVector3d FACE_EDIT_CAMERA_OFFSET(0.4f, -0.05f, 0.07f);
+const LLVector3d FACE_EDIT_TARGET_OFFSET(0.f, 0.f, 0.05f);
+
+// Mousewheel camera zoom
+const F32 MIN_ZOOM_FRACTION = 0.25f;
+const F32 INITIAL_ZOOM_FRACTION = 1.f;
+const F32 MAX_ZOOM_FRACTION = 8.f;
+const F32 METERS_PER_WHEEL_CLICK = 1.f;
+
+const F32 MAX_TIME_DELTA = 1.f;
+
+const F32 CAMERA_ZOOM_HALF_LIFE = 0.07f; // seconds
+const F32 FOV_ZOOM_HALF_LIFE = 0.07f; // seconds
+
+const F32 CAMERA_FOCUS_HALF_LIFE = 0.f;//0.02f;
+const F32 CAMERA_LAG_HALF_LIFE = 0.25f;
+const F32 MIN_CAMERA_LAG = 0.5f;
+const F32 MAX_CAMERA_LAG = 5.f;
+
+const F32 CAMERA_COLLIDE_EPSILON = 0.1f;
+const F32 MIN_CAMERA_DISTANCE = 0.1f;
+const F32 AVATAR_ZOOM_MIN_X_FACTOR = 0.55f;
+const F32 AVATAR_ZOOM_MIN_Y_FACTOR = 0.7f;
+const F32 AVATAR_ZOOM_MIN_Z_FACTOR = 1.15f;
+
+const F32 MAX_CAMERA_DISTANCE_FROM_AGENT = 50.f;
+
+const F32 HEAD_BUFFER_SIZE = 0.3f;
+const F32 CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP = 0.2f;
+
+const F32 LAND_MIN_ZOOM = 0.15f;
+const F32 AVATAR_MIN_ZOOM = 0.5f;
+const F32 OBJECT_MIN_ZOOM = 0.02f;
+
+const F32 APPEARANCE_MIN_ZOOM = 0.39f;
+const F32 APPEARANCE_MAX_ZOOM = 8.f;
+
+// fidget constants
+const F32 MIN_FIDGET_TIME = 8.f; // seconds
+const F32 MAX_FIDGET_TIME = 20.f; // seconds
+
+const S32 MAX_NUM_CHAT_POSITIONS = 10;
+const F32 GROUND_TO_AIR_CAMERA_TRANSITION_TIME = 0.5f;
+const F32 GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME = 0.5f;
+
+const F32 MAX_VELOCITY_AUTO_LAND_SQUARED = 4.f * 4.f;
+
+const F32 MAX_FOCUS_OFFSET = 20.f;
+
+const F32 OBJECT_EXTENTS_PADDING = 0.5f;
+
+const F32 MIN_RADIUS_ALPHA_SIZZLE = 0.5f;
+
+const F64 CHAT_AGE_FAST_RATE = 3.0;
+
+const S32 MAX_WEARABLES_PER_LAYERSET = 7;
+
+const EWearableType WEARABLE_BAKE_TEXTURE_MAP[BAKED_TEXTURE_COUNT][MAX_WEARABLES_PER_LAYERSET] =
+{
+ { WT_SHAPE, WT_SKIN, WT_HAIR, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID }, // TEX_HEAD_BAKED
+ { WT_SHAPE, WT_SKIN, WT_SHIRT, WT_JACKET, WT_GLOVES, WT_UNDERSHIRT, WT_INVALID }, // TEX_UPPER_BAKED
+ { WT_SHAPE, WT_SKIN, WT_PANTS, WT_SHOES, WT_SOCKS, WT_JACKET, WT_UNDERPANTS }, // TEX_LOWER_BAKED
+ { WT_EYES, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID }, // TEX_EYES_BAKED
+ { WT_SKIRT, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID } // TEX_SKIRT_BAKED
+};
+
+const LLUUID BAKED_TEXTURE_HASH[BAKED_TEXTURE_COUNT] =
+{
+ LLUUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6"),
+ LLUUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f"),
+ LLUUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f"),
+ LLUUID("b2cf28af-b840-1071-3c6a-78085d8128b5"),
+ LLUUID("ea800387-ea1a-14e0-56cb-24f2022f969a")
+};
+
+//
+// Statics
+//
+BOOL LLAgent::sDebugDisplayTarget = FALSE;
+
+const F32 LLAgent::TYPING_TIMEOUT_SECS = 5.f;
+
+class LLAgentFriendObserver : public LLFriendObserver
+{
+public:
+ LLAgentFriendObserver() {}
+ virtual ~LLAgentFriendObserver() {}
+ virtual void changed(U32 mask);
+};
+
+void LLAgentFriendObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLFriendObserver::POWERS)) != 0)
+ {
+ gAgent.friendsChanged();
+ }
+}
+
+// ************************************************************
+// Enabled this definition to compile a 'hacked' viewer that
+// locally believes the end user has godlike powers.
+// #define HACKED_GODLIKE_VIEWER
+// For a toggled version, see viewer.h for the
+// TOGGLE_HACKED_GODLIKE_VIEWER define, instead.
+// ************************************************************
+
+// Constructors and Destructors
+
+// JC - Please try to make this order match the order in the header
+// file. Otherwise it's hard to find variables that aren't initialized.
+//-----------------------------------------------------------------------------
+// LLAgent()
+//-----------------------------------------------------------------------------
+LLAgent::LLAgent()
+: mViewerPort(NET_USE_OS_ASSIGNED_PORT),
+ mDrawDistance( DEFAULT_FAR_PLANE ),
+ mAccess(SIM_ACCESS_PG),
+ mGroupPowers(0),
+ mGroupID(),
+ //mGroupInsigniaID(),
+ mMapOriginX(0),
+ mMapOriginY(0),
+ mMapWidth(0),
+ mMapHeight(0),
+ mLookAt(NULL),
+ mPointAt(NULL),
+ mInitialized(FALSE),
+ mNumPendingQueries(0),
+ mForceMouselook(FALSE),
+ mTeleportState( TELEPORT_NONE ),
+ mRegionp(NULL),
+
+ mAgentOriginGlobal(),
+ mPositionGlobal(),
+
+ mDistanceTraveled(0),
+ mLastPositionGlobal(LLVector3d::zero),
+
+ mAvatarObject(NULL),
+
+ mRenderState(0),
+ mTypingTimer(),
+
+ mCameraMode( CAMERA_MODE_THIRD_PERSON ),
+ mLastCameraMode( CAMERA_MODE_THIRD_PERSON ),
+ mViewsPushed(FALSE),
+
+ mbAlwaysRun(FALSE),
+ mShowAvatar(TRUE),
+
+ mCameraAnimating( FALSE ),
+ mAnimationCameraStartGlobal(),
+ mAnimationFocusStartGlobal(),
+ mAnimationTimer(),
+ mAnimationDuration(0.33f),
+ mCameraFOVZoomFactor(0.f),
+ mCameraCurrentFOVZoomFactor(0.f),
+ mCameraFocusOffset(),
+ mCameraOffsetDefault(),
+// mCameraOffsetNorm(),
+ mCameraCollidePlane(),
+ mCurrentCameraDistance(2.f), // meters, set in init()
+ mTargetCameraDistance(2.f),
+ mCameraZoomFraction(1.f), // deprecated
+ mThirdPersonHeadOffset(0.f, 0.f, 1.f),
+ mSitCameraEnabled(FALSE),
+ mFocusOnAvatar(TRUE),
+ mFocusGlobal(),
+ mFocusTargetGlobal(),
+ mFocusObject(NULL),
+ mFocusObjectOffset(),
+ mFocusDotRadius( 0.1f ), // meters
+ mTrackFocusObject(TRUE),
+
+ mFrameAgent(),
+
+ mCrouching(FALSE),
+ mIsBusy(FALSE),
+
+ // movement keys below
+
+ mControlFlags(0x00000000),
+ mbFlagsDirty(FALSE),
+ mbFlagsNeedReset(FALSE),
+
+ mbJump(FALSE),
+
+ mWanderTimer(),
+ mWanderTargetGlobal( LLVector3d::zero ),
+
+ mAutoPilot(FALSE),
+ mAutoPilotFlyOnStop(FALSE),
+ mAutoPilotTargetGlobal(),
+ mAutoPilotStopDistance(1.f),
+ mAutoPilotUseRotation(FALSE),
+ mAutoPilotTargetFacing(LLVector3::zero),
+ mAutoPilotTargetDist(0.f),
+ mAutoPilotFinishedCallback(NULL),
+ mAutoPilotCallbackData(NULL),
+
+
+ mEffectColor(0.f, 1.f, 1.f, 1.f),
+ mHaveHomePosition(FALSE),
+ mHomeRegionHandle( 0 ),
+ mNearChatRadius(10.f),
+ mGodLevel( GOD_NOT ),
+
+
+ mNextFidgetTime(0.f),
+ mCurrentFidget(0),
+ mFirstLogin(FALSE),
+ mGenderChosen(FALSE),
+ mAgentWearablesUpdateSerialNum(0),
+ mWearablesLoaded(FALSE),
+ mTextureCacheQueryID(0),
+ mAppearanceSerialNum(0)
+{
+
+ U32 i;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ mControlsTakenCount[i] = 0;
+ mControlsTakenPassedOnCount[i] = 0;
+ }
+
+ // Initialize movement keys
+ mAtKey = 0; // Either 1, 0, or -1... indicates that movement-key is pressed
+ mWalkKey = 0; // like AtKey, but causes less forward thrust
+ mLeftKey = 0;
+ mUpKey = 0;
+ mYawKey = 0.f;
+ mPitchKey = 0;
+
+ mOrbitLeftKey = 0.f;
+ mOrbitRightKey = 0.f;
+ mOrbitUpKey = 0.f;
+ mOrbitDownKey = 0.f;
+ mOrbitInKey = 0.f;
+ mOrbitOutKey = 0.f;
+
+ mPanUpKey = 0.f;
+ mPanDownKey = 0.f;
+ mPanLeftKey = 0.f;
+ mPanRightKey = 0.f;
+ mPanInKey = 0.f;
+ mPanOutKey = 0.f;
+
+ mActiveCacheQueries = new S32[BAKED_TEXTURE_COUNT];
+ for (i = 0; i < (U32)BAKED_TEXTURE_COUNT; i++)
+ {
+ mActiveCacheQueries[i] = 0;
+ }
+
+ //Ventrella
+ mCameraUpVector = LLVector3::z_axis;// default is straight up
+ mFollowCam.setMaxCameraDistantFromSubject( MAX_CAMERA_DISTANCE_FROM_AGENT );
+ //end ventrella
+}
+
+// Requires gSavedSettings to be initialized.
+//-----------------------------------------------------------------------------
+// init()
+//-----------------------------------------------------------------------------
+void LLAgent::init()
+{
+ mDrawDistance = gSavedSettings.getF32("RenderFarClip");
+
+ gCamera = new LLViewerCamera();
+
+ gCamera->setView(DEFAULT_FIELD_OF_VIEW);
+ // Leave at 0.1 meters until we have real near clip management
+ gCamera->setNear(0.1f);
+ gCamera->setFar(mDrawDistance); // if you want to change camera settings, do so in camera.h
+ gCamera->setAspect( gViewerWindow->getDisplayAspectRatio() ); // default, overridden in LLViewerWindow::reshape
+ gCamera->setViewHeightInPixels(768); // default, overridden in LLViewerWindow::reshape
+
+ setFlying( gSavedSettings.getBOOL("FlyingAtExit") );
+
+ mCameraFocusOffsetTarget = LLVector4(gSavedSettings.getVector3("CameraOffsetBuild"));
+ mCameraOffsetDefault = gSavedSettings.getVector3("CameraOffsetDefault");
+// mCameraOffsetNorm = mCameraOffsetDefault;
+// mCameraOffsetNorm.normVec();
+ mCameraCollidePlane.clearVec();
+ mCurrentCameraDistance = mCameraOffsetDefault.magVec();
+ mTargetCameraDistance = mCurrentCameraDistance;
+ mCameraZoomFraction = 1.f;
+ mTrackFocusObject = gSavedSettings.getBOOL("TrackFocusObject");
+
+// LLDebugVarMessageBox::show("Camera Lag", &CAMERA_FOCUS_HALF_LIFE, 0.5f, 0.01f);
+ gSavedSettings.getControl("RenderHideGroupTitle")->addListener(&mHideGroupTitleListener);
+ gSavedSettings.getControl("EffectColor")->addListener(&mEffectColorListener);
+
+ mEffectColor = gSavedSettings.getColor4("EffectColor");
+
+ mInitialized = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// cleanup()
+//-----------------------------------------------------------------------------
+void LLAgent::cleanup()
+{
+ setSitCamera(LLUUID::null);
+ mAvatarObject = NULL;
+ mLookAt = NULL;
+ mPointAt = NULL;
+ mRegionp = NULL;
+ setFocusObject(NULL);
+ mFadeObjects.clear();
+}
+
+//-----------------------------------------------------------------------------
+// LLAgent()
+//-----------------------------------------------------------------------------
+LLAgent::~LLAgent()
+{
+ cleanup();
+
+ delete [] mActiveCacheQueries;
+ mActiveCacheQueries = NULL;
+
+ delete gCamera;
+ gCamera = NULL;
+}
+
+// Change camera back to third person, stop the autopilot,
+// deselect stuff, etc.
+//-----------------------------------------------------------------------------
+// resetView()
+//-----------------------------------------------------------------------------
+void LLAgent::resetView(BOOL reset_camera)
+{
+ if (mAutoPilot)
+ {
+ stopAutoPilot(TRUE);
+ }
+
+ if (!gNoRender)
+ {
+ gSelectMgr->deselectAll();
+ gSelectMgr->unhighlightAll();
+
+ // By popular request, keep land selection while walking around. JC
+ // gParcelMgr->deselectLand();
+
+ // Hide all popup menus
+ gPieSelf->hide(FALSE);
+ gPieAvatar->hide(FALSE);
+ gPieObject->hide(FALSE);
+ gPieLand->hide(FALSE);
+ }
+
+ if (reset_camera && !gSavedSettings.getBOOL("FreezeTime"))
+ {
+ if (!gViewerWindow->getLeftMouseDown() && cameraThirdPerson())
+ {
+ // leaving mouse-steer mode
+ LLVector3 agent_at_axis = getAtAxis();
+ agent_at_axis -= projected_vec(agent_at_axis, getReferenceUpVector());
+ agent_at_axis.normVec();
+ gAgent.resetAxes(lerp(getAtAxis(), agent_at_axis, LLCriticalDamp::getInterpolant(0.3f)));
+ }
+
+ setFocusOnAvatar(TRUE, ANIMATE);
+ }
+
+ if (mAvatarObject.notNull())
+ {
+ mAvatarObject->mHUDTargetZoom = 1.f;
+ }
+}
+
+void LLAgent::ageChat()
+{
+ if (mAvatarObject)
+ {
+ // get amount of time since I last chatted
+ F64 elapsed_time = (F64)mAvatarObject->mChatTimer.getElapsedTimeF32();
+ // add in frame time * 3 (so it ages 4x)
+ mAvatarObject->mChatTimer.setAge(elapsed_time + (F64)gFrameDTClamped * (CHAT_AGE_FAST_RATE - 1.0));
+ }
+}
+
+// Allow camera to be moved somewhere other than behind avatar.
+//-----------------------------------------------------------------------------
+// unlockView()
+//-----------------------------------------------------------------------------
+void LLAgent::unlockView()
+{
+ if (getFocusOnAvatar())
+ {
+ if (mAvatarObject)
+ {
+ setFocusGlobal( LLVector3d::zero, mAvatarObject->mID );
+ }
+ setFocusOnAvatar(FALSE, FALSE); // no animation
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// moveAt()
+//-----------------------------------------------------------------------------
+void LLAgent::moveAt(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mAtKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_AT_POS | AGENT_CONTROL_FAST_AT);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_AT_NEG | AGENT_CONTROL_FAST_AT);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveAtNudge()
+//-----------------------------------------------------------------------------
+void LLAgent::moveAtNudge(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mWalkKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_AT_POS);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_AT_NEG);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveLeft()
+//-----------------------------------------------------------------------------
+void LLAgent::moveLeft(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mLeftKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_LEFT_POS | AGENT_CONTROL_FAST_LEFT);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_LEFT_NEG | AGENT_CONTROL_FAST_LEFT);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveLeftNudge()
+//-----------------------------------------------------------------------------
+void LLAgent::moveLeftNudge(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mLeftKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_LEFT_POS);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_LEFT_NEG);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveUp()
+//-----------------------------------------------------------------------------
+void LLAgent::moveUp(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mUpKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_UP_POS | AGENT_CONTROL_FAST_UP);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_UP_NEG | AGENT_CONTROL_FAST_UP);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveYaw()
+//-----------------------------------------------------------------------------
+void LLAgent::moveYaw(F32 mag)
+{
+ mYawKey = mag;
+
+ if (mag > 0)
+ {
+ setControlFlags(AGENT_CONTROL_YAW_POS);
+ }
+ else if (mag < 0)
+ {
+ setControlFlags(AGENT_CONTROL_YAW_NEG);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// movePitch()
+//-----------------------------------------------------------------------------
+void LLAgent::movePitch(S32 direction)
+{
+ setKey(direction, mPitchKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_PITCH_POS );
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_PITCH_NEG);
+ }
+}
+
+
+// Does this parcel allow you to fly?
+BOOL LLAgent::canFly()
+{
+ if (isGodlike()) return TRUE;
+
+ if (!gParcelMgr) return FALSE;
+
+ LLViewerRegion* regionp = getRegion();
+ if (regionp && regionp->getBlockFly()) return FALSE;
+
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (!parcel) return FALSE;
+
+ // Allow owners to fly on their own land.
+ if (LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_ALLOW_FLY))
+ {
+ return TRUE;
+ }
+
+ return parcel->getAllowFly();
+}
+
+
+//-----------------------------------------------------------------------------
+// setFlying()
+//-----------------------------------------------------------------------------
+void LLAgent::setFlying(BOOL fly)
+{
+ if (mAvatarObject.notNull())
+ {
+ if(mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_STANDUP) != mAvatarObject->mSignaledAnimations.end())
+ {
+ return;
+ }
+
+ // don't allow taking off while sitting
+ if (fly && mAvatarObject->mIsSitting)
+ {
+ return;
+ }
+ }
+
+ if (fly)
+ {
+ BOOL was_flying = getFlying();
+ if (gParcelMgr)
+ {
+ if (!canFly() && !was_flying)
+ {
+ // parcel doesn't let you start fly
+ // gods can always fly
+ // and it's OK if you're already flying
+ make_ui_sound("UISndBadKeystroke");
+ return;
+ }
+ }
+ if( !was_flying )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_FLY_COUNT);
+ }
+ setControlFlags(AGENT_CONTROL_FLY);
+ gSavedSettings.setBOOL("FlyBtnState", TRUE);
+ }
+ else
+ {
+ clearControlFlags(AGENT_CONTROL_FLY);
+ gSavedSettings.setBOOL("FlyBtnState", FALSE);
+ }
+ mbFlagsDirty = TRUE;
+}
+
+
+// UI based mechanism of setting fly state
+//-----------------------------------------------------------------------------
+// toggleFlying()
+//-----------------------------------------------------------------------------
+void LLAgent::toggleFlying()
+{
+ BOOL fly = !(mControlFlags & AGENT_CONTROL_FLY);
+
+ setFlying( fly );
+ resetView();
+}
+
+
+//-----------------------------------------------------------------------------
+// setRegion()
+//-----------------------------------------------------------------------------
+void LLAgent::setRegion(LLViewerRegion *regionp)
+{
+ llassert(regionp);
+ if (mRegionp != regionp)
+ {
+ // JC - Avoid this, causes out-of-bounds array write deep within
+ // Windows.
+ // char host_name[MAX_STRING];
+ // regionp->getHost().getHostName(host_name, MAX_STRING);
+
+ char ip[MAX_STRING];
+ regionp->getHost().getString(ip, MAX_STRING);
+ llinfos << "Moving agent into region: " << regionp->getName()
+ << " located at " << ip << llendl;
+ if (mRegionp)
+ {
+ // We've changed regions, we're now going to change our agent coordinate frame.
+ mAgentOriginGlobal = regionp->getOriginGlobal();
+ LLVector3d agent_offset_global = mRegionp->getOriginGlobal();
+
+ LLVector3 delta;
+ delta.setVec(regionp->getOriginGlobal() - mRegionp->getOriginGlobal());
+
+ setPositionAgent(getPositionAgent() - delta);
+ LLVector3 camera_position_agent = gCamera->getOrigin();
+ gCamera->setOrigin(camera_position_agent - delta);
+
+ // Update all of the regions.
+ gWorldPointer->updateAgentOffset(agent_offset_global);
+
+ // Hack to keep sky in the agent's region, otherwise it may get deleted - DJS 08/02/02
+ if (gSky.mVOSkyp)
+ {
+ gSky.mVOSkyp->setRegion(regionp);
+ }
+ if (gSky.mVOStarsp)
+ {
+ gSky.mVOStarsp->setRegion(regionp);
+ }
+ if (gSky.mVOGroundp)
+ {
+ gSky.mVOGroundp->setRegion(regionp);
+ }
+
+ }
+ else
+ {
+ // First time initialization.
+ // We've changed regions, we're now going to change our agent coordinate frame.
+ mAgentOriginGlobal = regionp->getOriginGlobal();
+
+ LLVector3 delta;
+ delta.setVec(regionp->getOriginGlobal());
+
+ setPositionAgent(getPositionAgent() - delta);
+ LLVector3 camera_position_agent = gCamera->getOrigin();
+ gCamera->setOrigin(camera_position_agent - delta);
+
+ // Update all of the regions.
+ gWorldPointer->updateAgentOffset(mAgentOriginGlobal);
+ }
+ }
+ mRegionp = regionp;
+
+ // Must shift hole-covering water object locations because local
+ // coordinate frame changed.
+ gWorldPointer->updateWaterObjects();
+
+ // keep a list of regions we've been too
+ // this is just an interesting stat, logged at the dataserver
+ // we could trake this at the dataserver side, but that's harder
+ U64 handle = regionp->getHandle();
+ mRegionsVisited.insert(handle);
+}
+
+
+//-----------------------------------------------------------------------------
+// getRegion()
+//-----------------------------------------------------------------------------
+LLViewerRegion *LLAgent::getRegion() const
+{
+ return mRegionp;
+}
+
+
+const LLHost& LLAgent::getRegionHost() const
+{
+ if (mRegionp)
+ {
+ return mRegionp->getHost();
+ }
+ else
+ {
+ return LLHost::invalid;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// inPrelude()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::inPrelude()
+{
+ return mRegionp && mRegionp->isPrelude();
+}
+
+
+//-----------------------------------------------------------------------------
+// canManageEstate()
+//-----------------------------------------------------------------------------
+
+BOOL LLAgent::canManageEstate() const
+{
+ return mRegionp && mRegionp->canManageEstate();
+}
+//-----------------------------------------------------------------------------
+// sendMessage()
+//-----------------------------------------------------------------------------
+void LLAgent::sendMessage()
+{
+ if (gDisconnected)
+ {
+ llwarns << "Trying to send message when disconnected!" << llendl;
+ return;
+ }
+ if (!mRegionp)
+ {
+ llerrs << "No region for agent yet!" << llendl;
+ }
+ gMessageSystem->sendMessage(mRegionp->getHost());
+}
+
+
+//-----------------------------------------------------------------------------
+// sendReliableMessage()
+//-----------------------------------------------------------------------------
+void LLAgent::sendReliableMessage()
+{
+ if (gDisconnected)
+ {
+ llwarns << "Trying to send message when disconnected!" << llendl;
+ return;
+ }
+ if (!mRegionp)
+ {
+ llwarns << "LLAgent::sendReliableMessage No region for agent yet, not sending message!" << llendl;
+ return;
+ }
+ gMessageSystem->sendReliable(mRegionp->getHost());
+}
+
+//-----------------------------------------------------------------------------
+// getVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLAgent::getVelocity() const
+{
+ if (mAvatarObject)
+ {
+ return mAvatarObject->getVelocity();
+ }
+ else
+ {
+ return LLVector3::zero;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setPositionAgent()
+//-----------------------------------------------------------------------------
+void LLAgent::setPositionAgent(const LLVector3 &pos_agent)
+{
+ if (!pos_agent.isFinite())
+ {
+ llerrs << "setPositionAgent is not a number" << llendl;
+ }
+
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLVector3 pos_agent_sitting;
+ LLVector3d pos_agent_d;
+ LLViewerObject *parent = (LLViewerObject*)mAvatarObject->getParent();
+
+ pos_agent_sitting = mAvatarObject->getPosition() * parent->getRotation() + parent->getPositionAgent();
+ pos_agent_d.setVec(pos_agent_sitting);
+
+ mFrameAgent.setOrigin(pos_agent_sitting);
+ mPositionGlobal = pos_agent_d + mAgentOriginGlobal;
+ }
+ else
+ {
+ mFrameAgent.setOrigin(pos_agent);
+
+ LLVector3d pos_agent_d;
+ pos_agent_d.setVec(pos_agent);
+ mPositionGlobal = pos_agent_d + mAgentOriginGlobal;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// slamLookAt()
+//-----------------------------------------------------------------------------
+void LLAgent::slamLookAt(const LLVector3 &look_at)
+{
+ LLVector3 look_at_norm = look_at;
+ look_at_norm.mV[VZ] = 0.f;
+ look_at_norm.normVec();
+ resetAxes(look_at_norm);
+}
+
+//-----------------------------------------------------------------------------
+// getPositionGlobal()
+//-----------------------------------------------------------------------------
+const LLVector3d &LLAgent::getPositionGlobal()
+{
+ if (!mAvatarObject.isNull() && !mAvatarObject->mDrawable.isNull())
+ {
+ mPositionGlobal = getPosGlobalFromAgent(mAvatarObject->getRenderPosition());
+ }
+ else
+ {
+ mPositionGlobal = getPosGlobalFromAgent(mFrameAgent.getOrigin());
+ }
+
+ return mPositionGlobal;
+}
+
+//-----------------------------------------------------------------------------
+// getPositionAgent()
+//-----------------------------------------------------------------------------
+const LLVector3 &LLAgent::getPositionAgent()
+{
+ if(!mAvatarObject.isNull() && !mAvatarObject->mDrawable.isNull())
+ {
+ mFrameAgent.setOrigin(mAvatarObject->getRenderPosition());
+ }
+
+ return mFrameAgent.getOrigin();
+}
+
+//-----------------------------------------------------------------------------
+// getRegionsVisited()
+//-----------------------------------------------------------------------------
+const S32 LLAgent::getRegionsVisited() const
+{
+ return mRegionsVisited.size();
+}
+
+//-----------------------------------------------------------------------------
+// getDistanceTraveled()
+//-----------------------------------------------------------------------------
+const F64 LLAgent::getDistanceTraveled() const
+{
+ return mDistanceTraveled;
+}
+
+
+//-----------------------------------------------------------------------------
+// getPosAgentFromGlobal()
+//-----------------------------------------------------------------------------
+LLVector3 LLAgent::getPosAgentFromGlobal(const LLVector3d &pos_global) const
+{
+ LLVector3 pos_agent;
+ pos_agent.setVec(pos_global - mAgentOriginGlobal);
+ return pos_agent;
+}
+
+
+//-----------------------------------------------------------------------------
+// getPosGlobalFromAgent()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::getPosGlobalFromAgent(const LLVector3 &pos_agent) const
+{
+ LLVector3d pos_agent_d;
+ pos_agent_d.setVec(pos_agent);
+ return pos_agent_d + mAgentOriginGlobal;
+}
+
+
+//-----------------------------------------------------------------------------
+// resetAxes()
+//-----------------------------------------------------------------------------
+void LLAgent::resetAxes()
+{
+ mFrameAgent.resetAxes();
+}
+
+
+// Copied from LLCamera::setOriginAndLookAt
+// Look_at must be unit vector
+//-----------------------------------------------------------------------------
+// resetAxes()
+//-----------------------------------------------------------------------------
+void LLAgent::resetAxes(const LLVector3 &look_at)
+{
+ LLVector3 skyward = getReferenceUpVector();
+
+ // if look_at has zero length, fail
+ // if look_at and skyward are parallel, fail
+ //
+ // Test both of these conditions with a cross product.
+ LLVector3 cross(look_at % skyward);
+ if (cross.isNull())
+ {
+ llinfos << "LLAgent::resetAxes cross-product is zero" << llendl;
+ return;
+ }
+
+ // Make sure look_at and skyward are not parallel
+ // and neither are zero length
+ LLVector3 left(skyward % look_at);
+ LLVector3 up(look_at % left);
+
+ mFrameAgent.setAxes(look_at, left, up);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(F32 angle, const LLVector3 &axis)
+{
+ mFrameAgent.rotate(angle, axis);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(F32 angle, F32 x, F32 y, F32 z)
+{
+ mFrameAgent.rotate(angle, x, y, z);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(const LLMatrix3 &matrix)
+{
+ mFrameAgent.rotate(matrix);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(const LLQuaternion &quaternion)
+{
+ mFrameAgent.rotate(quaternion);
+}
+
+
+//-----------------------------------------------------------------------------
+// getReferenceUpVector()
+//-----------------------------------------------------------------------------
+LLVector3 LLAgent::getReferenceUpVector()
+{
+ // this vector is in the coordinate frame of the avatar's parent object, or the world if none
+ LLVector3 up_vector = LLVector3::z_axis;
+ if (mAvatarObject.notNull() &&
+ mAvatarObject->getParent() &&
+ mAvatarObject->mDrawable.notNull())
+ {
+ U32 camera_mode = mCameraAnimating ? mLastCameraMode : mCameraMode;
+ // and in third person...
+ if (camera_mode == CAMERA_MODE_THIRD_PERSON)
+ {
+ // make the up vector point to the absolute +z axis
+ up_vector = up_vector * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ }
+ else if (camera_mode == CAMERA_MODE_MOUSELOOK)
+ {
+ // make the up vector point to the avatar's +z axis
+ up_vector = up_vector * mAvatarObject->mDrawable->getRotation();
+ }
+ }
+
+ return up_vector;
+}
+
+
+// Radians, positive is forward into ground
+//-----------------------------------------------------------------------------
+// pitch()
+//-----------------------------------------------------------------------------
+void LLAgent::pitch(F32 angle)
+{
+ // don't let user pitch if pointed almost all the way down or up
+
+ // A dot B = mag(A) * mag(B) * cos(angle between A and B)
+ // so... cos(angle between A and B) = A dot B / mag(A) / mag(B)
+ // = A dot B for unit vectors
+
+ LLVector3 skyward = getReferenceUpVector();
+
+ F32 look_down_limit;
+ F32 look_up_limit = 10.f * DEG_TO_RAD;
+
+ F32 angle_from_skyward = acos( mFrameAgent.getAtAxis() * skyward );
+
+ if (mAvatarObject.notNull() && mAvatarObject->mIsSitting)
+ {
+ look_down_limit = 130.f * DEG_TO_RAD;
+ }
+ else
+ {
+ look_down_limit = 170.f * DEG_TO_RAD;
+ }
+
+ // clamp pitch to limits
+ if ((angle >= 0.f) && (angle_from_skyward + angle > look_down_limit))
+ {
+ angle = look_down_limit - angle_from_skyward;
+ }
+ else if ((angle < 0.f) && (angle_from_skyward + angle < look_up_limit))
+ {
+ angle = look_up_limit - angle_from_skyward;
+ }
+
+ mFrameAgent.pitch(angle);
+}
+
+
+//-----------------------------------------------------------------------------
+// roll()
+//-----------------------------------------------------------------------------
+void LLAgent::roll(F32 angle)
+{
+ mFrameAgent.roll(angle);
+}
+
+
+//-----------------------------------------------------------------------------
+// yaw()
+//-----------------------------------------------------------------------------
+void LLAgent::yaw(F32 angle)
+{
+ if (!rotateGrabbed())
+ {
+ mFrameAgent.rotate(angle, getReferenceUpVector());
+ }
+}
+
+
+// Returns a quat that represents the rotation of the agent in the absolute frame
+//-----------------------------------------------------------------------------
+// getQuat()
+//-----------------------------------------------------------------------------
+LLQuaternion LLAgent::getQuat() const
+{
+ return mFrameAgent.getQuaternion();
+}
+
+
+//-----------------------------------------------------------------------------
+// calcFocusOffset()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::calcFocusOffset(LLViewerObject *object, S32 x, S32 y)
+{
+ // calculate offset based on view direction
+ BOOL is_avatar = object->isAvatar();
+ LLMatrix4 obj_matrix = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldMatrix() : object->getRenderMatrix();
+ LLQuaternion obj_rot = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldRotation() : object->getRenderRotation();
+ LLVector3 obj_pos = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldPosition() : object->getRenderPosition();
+ LLQuaternion inv_obj_rot = ~obj_rot;
+
+ LLVector3 obj_dir_abs = obj_pos - gCamera->getOrigin();
+ obj_dir_abs.rotVec(inv_obj_rot);
+ obj_dir_abs.normVec();
+ obj_dir_abs.abs();
+
+ LLVector3 object_extents = object->getScale();
+ // make sure they object extents are non-zero
+ object_extents.clamp(0.001f, F32_MAX);
+ LLVector3 object_half_extents = object_extents * 0.5f;
+
+ obj_dir_abs.mV[VX] = obj_dir_abs.mV[VX] / object_extents.mV[VX];
+ obj_dir_abs.mV[VY] = obj_dir_abs.mV[VY] / object_extents.mV[VY];
+ obj_dir_abs.mV[VZ] = obj_dir_abs.mV[VZ] / object_extents.mV[VZ];
+
+ LLVector3 normal;
+ if (obj_dir_abs.mV[VX] > obj_dir_abs.mV[VY] && obj_dir_abs.mV[VX] > obj_dir_abs.mV[VZ])
+ {
+ normal.setVec(obj_matrix.getFwdRow4());
+ }
+ else if (obj_dir_abs.mV[VY] > obj_dir_abs.mV[VZ])
+ {
+ normal.setVec(obj_matrix.getLeftRow4());
+ }
+ else
+ {
+ normal.setVec(obj_matrix.getUpRow4());
+ }
+ normal.normVec();
+
+ LLVector3d focus_pt_global;
+ // RN: should we check return value for valid pick?
+ gViewerWindow->mousePointOnPlaneGlobal(focus_pt_global, x, y, gAgent.getPosGlobalFromAgent(obj_pos), normal);
+ LLVector3 focus_pt = gAgent.getPosAgentFromGlobal(focus_pt_global);
+ // find vector from camera to focus point in object coordinates
+ LLVector3 camera_focus_vec = focus_pt - gCamera->getOrigin();
+ // convert to object-local space
+ camera_focus_vec.rotVec(inv_obj_rot);
+
+ // find vector from object origin to focus point in object coordinates
+ LLVector3 focus_delta = focus_pt - obj_pos;
+ // convert to object-local space
+ focus_delta.rotVec(inv_obj_rot);
+
+ // calculate clip percentage needed to get focus offset back in bounds along the camera_focus axis
+ LLVector3 clip_fraction;
+
+ for (U32 axis = VX; axis <= VZ; axis++)
+ {
+ F32 clip_amt;
+ if (focus_delta.mV[axis] > 0.f)
+ {
+ clip_amt = llmax(0.f, focus_delta.mV[axis] - object_half_extents.mV[axis]);
+ }
+ else
+ {
+ clip_amt = llmin(0.f, focus_delta.mV[axis] + object_half_extents.mV[axis]);
+ }
+
+ // don't divide by very small nunber
+ if (llabs(camera_focus_vec.mV[axis]) < 0.0001f)
+ {
+ clip_fraction.mV[axis] = 0.f;
+ }
+ else
+ {
+ clip_fraction.mV[axis] = clip_amt / camera_focus_vec.mV[axis];
+ }
+ }
+
+ LLVector3 abs_clip_fraction = clip_fraction;
+ abs_clip_fraction.abs();
+
+ // find greatest shrinkage factor and
+ // rescale focus offset to inside object extents
+ if (abs_clip_fraction.mV[VX] > abs_clip_fraction.mV[VY] &&
+ abs_clip_fraction.mV[VX] > abs_clip_fraction.mV[VZ])
+ {
+ focus_delta -= clip_fraction.mV[VX] * camera_focus_vec;
+ }
+ else if (abs_clip_fraction.mV[VY] > abs_clip_fraction.mV[VZ])
+ {
+ focus_delta -= clip_fraction.mV[VY] * camera_focus_vec;
+ }
+ else
+ {
+ focus_delta -= clip_fraction.mV[VZ] * camera_focus_vec;
+ }
+
+ // convert back to world space
+ focus_delta.rotVec(obj_rot);
+
+ if (!is_avatar)
+ {
+ //unproject relative clicked coordinate from window coordinate using GL
+ glPushMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf((const GLfloat*) gCamera->getProjection().mMatrix);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf((const GLfloat*) gCamera->getModelview().mMatrix);
+ glMultMatrixf((const GLfloat*) obj_matrix.mMatrix);
+
+ GLint viewport[4];
+ GLdouble modelview[16];
+ GLdouble projection[16];
+ GLfloat winX, winY, winZ;
+ GLdouble posX, posY, posZ;
+
+ glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
+ glGetDoublev( GL_PROJECTION_MATRIX, projection );
+ glGetIntegerv( GL_VIEWPORT, viewport );
+
+ winX = ((F32)x) * gViewerWindow->getDisplayScale().mV[VX];
+ winY = ((F32)y) * gViewerWindow->getDisplayScale().mV[VY];
+ glReadPixels( llfloor(winX), llfloor(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
+
+ gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
+
+ glPopMatrix();
+
+ LLVector3 obj_rel = LLVector3((F32)posX, (F32)posY, (F32)posZ);
+ LLVector3 obj_center = LLVector3(0, 0, 0);
+ obj_rel = obj_rel * object->getRenderMatrix(); //mDrawable->getWorldMatrix();
+ obj_center = obj_center * object->getRenderMatrix();//mDrawable->getWorldMatrix();
+ obj_rel -= object->getRenderPosition();//mDrawable->getWorldPosition();
+
+ //now that we have the object relative position, we should bias toward the center of the object
+ //based on the distance of the camera to the focus point vs. the distance of the camera to the focus
+
+ F32 relDist = llabs(obj_rel * gCamera->getAtAxis());
+ F32 viewDist = dist_vec(obj_center + obj_rel, gCamera->getOrigin());
+
+
+ LLBBox obj_bbox = object->getBoundingBoxAgent();
+ F32 bias = 0.f;
+
+ LLVector3 virtual_camera_pos = gAgent.getPosAgentFromGlobal(mFocusTargetGlobal + (getCameraPositionGlobal() - mFocusTargetGlobal) / (1.f + mCameraFOVZoomFactor));
+
+ if(obj_bbox.containsPointAgent(virtual_camera_pos))
+ {
+ // if the camera is inside the object (large, hollow objects, for example)
+ // force focus point all the way to destination depth, away from object center
+ bias = 1.f;
+ }
+ else
+ {
+ // perform magic number biasing of focus point towards surface vs. planar center
+ bias = clamp_rescale(relDist/viewDist, 0.1f, 0.7f, 0.0f, 1.0f);
+ }
+
+ obj_rel = lerp(focus_delta, obj_rel, bias);
+
+ return LLVector3d(obj_rel);
+ }
+
+ return LLVector3d(focus_delta.mV[VX], focus_delta.mV[VY], focus_delta.mV[VZ]);
+}
+
+//-----------------------------------------------------------------------------
+// calcCameraMinDistance()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::calcCameraMinDistance(F32 &obj_min_distance)
+{
+ BOOL soft_limit = FALSE; // is the bounding box to be treated literally (volumes) or as an approximation (avatars)
+
+ if (!mFocusObject || mFocusObject->isDead())
+ {
+ obj_min_distance = 0.f;
+ return TRUE;
+ }
+
+ if (mFocusObject->mDrawable.isNull())
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Focus object with no drawable!" << llendl;
+#else
+ mFocusObject->dump();
+ llerrs << "Focus object with no drawable!" << llendl;
+#endif
+ obj_min_distance = 0.f;
+ return TRUE;
+ }
+
+ LLQuaternion inv_object_rot = ~mFocusObject->getRenderRotation();
+ LLVector3 target_offset_origin = mFocusObjectOffset;
+ LLVector3 camera_offset_target(getCameraPositionAgent() - getPosAgentFromGlobal(mFocusTargetGlobal));
+
+ // convert offsets into object local space
+ camera_offset_target.rotVec(inv_object_rot);
+ target_offset_origin.rotVec(inv_object_rot);
+
+ // push around object extents based on target offset
+ LLVector3 object_extents = mFocusObject->getScale();
+ if (mFocusObject->isAvatar())
+ {
+ // fudge factors that lets you zoom in on avatars a bit more (which don't do FOV zoom)
+ object_extents.mV[VX] *= AVATAR_ZOOM_MIN_X_FACTOR;
+ object_extents.mV[VY] *= AVATAR_ZOOM_MIN_Y_FACTOR;
+ object_extents.mV[VZ] *= AVATAR_ZOOM_MIN_Z_FACTOR;
+ soft_limit = TRUE;
+ }
+ LLVector3 abs_target_offset = target_offset_origin;
+ abs_target_offset.abs();
+
+ LLVector3 target_offset_dir = target_offset_origin;
+ F32 object_radius = mFocusObject->getVObjRadius();
+
+ BOOL target_outside_object_extents = FALSE;
+
+ for (U32 i = VX; i <= VZ; i++)
+ {
+ if (abs_target_offset.mV[i] * 2.f > object_extents.mV[i] + OBJECT_EXTENTS_PADDING)
+ {
+ target_outside_object_extents = TRUE;
+ }
+ if (camera_offset_target.mV[i] > 0.f)
+ {
+ object_extents.mV[i] -= target_offset_origin.mV[i] * 2.f;
+ }
+ else
+ {
+ object_extents.mV[i] += target_offset_origin.mV[i] * 2.f;
+ }
+ }
+
+ // don't shrink the object extents so far that the object inverts
+ object_extents.clamp(0.001f, F32_MAX);
+
+ // move into first octant
+ LLVector3 camera_offset_target_abs_norm = camera_offset_target;
+ camera_offset_target_abs_norm.abs();
+ // make sure offset is non-zero
+ camera_offset_target_abs_norm.clamp(0.001f, F32_MAX);
+ camera_offset_target_abs_norm.normVec();
+
+ // find camera position relative to normalized object extents
+ LLVector3 camera_offset_target_scaled = camera_offset_target_abs_norm;
+ camera_offset_target_scaled.mV[VX] /= object_extents.mV[VX];
+ camera_offset_target_scaled.mV[VY] /= object_extents.mV[VY];
+ camera_offset_target_scaled.mV[VZ] /= object_extents.mV[VZ];
+
+ if (camera_offset_target_scaled.mV[VX] > camera_offset_target_scaled.mV[VY] &&
+ camera_offset_target_scaled.mV[VX] > camera_offset_target_scaled.mV[VZ])
+ {
+ if (camera_offset_target_abs_norm.mV[VX] < 0.001f)
+ {
+ obj_min_distance = object_extents.mV[VX] * 0.5f;
+ }
+ else
+ {
+ obj_min_distance = object_extents.mV[VX] * 0.5f / camera_offset_target_abs_norm.mV[VX];
+ }
+ }
+ else if (camera_offset_target_scaled.mV[VY] > camera_offset_target_scaled.mV[VZ])
+ {
+ if (camera_offset_target_abs_norm.mV[VY] < 0.001f)
+ {
+ obj_min_distance = object_extents.mV[VY] * 0.5f;
+ }
+ else
+ {
+ obj_min_distance = object_extents.mV[VY] * 0.5f / camera_offset_target_abs_norm.mV[VY];
+ }
+ }
+ else
+ {
+ if (camera_offset_target_abs_norm.mV[VZ] < 0.001f)
+ {
+ obj_min_distance = object_extents.mV[VZ] * 0.5f;
+ }
+ else
+ {
+ obj_min_distance = object_extents.mV[VZ] * 0.5f / camera_offset_target_abs_norm.mV[VZ];
+ }
+ }
+
+ LLVector3 object_split_axis;
+ LLVector3 target_offset_scaled = target_offset_origin;
+ target_offset_scaled.abs();
+ target_offset_scaled.normVec();
+ target_offset_scaled.mV[VX] /= object_extents.mV[VX];
+ target_offset_scaled.mV[VY] /= object_extents.mV[VY];
+ target_offset_scaled.mV[VZ] /= object_extents.mV[VZ];
+
+ if (target_offset_scaled.mV[VX] > target_offset_scaled.mV[VY] &&
+ target_offset_scaled.mV[VX] > target_offset_scaled.mV[VZ])
+ {
+ object_split_axis = LLVector3::x_axis;
+ }
+ else if (target_offset_scaled.mV[VY] > target_offset_scaled.mV[VZ])
+ {
+ object_split_axis = LLVector3::y_axis;
+ }
+ else
+ {
+ object_split_axis = LLVector3::z_axis;
+ }
+
+ LLVector3 camera_offset_object(getCameraPositionAgent() - mFocusObject->getPositionAgent());
+
+ // length projected orthogonal to target offset
+ F32 camera_offset_dist = (camera_offset_object - target_offset_dir * (camera_offset_object * target_offset_dir)).magVec();
+
+ // calculate whether the target point would be "visible" if it were outside the bounding box
+ // on the opposite of the splitting plane defined by object_split_axis;
+ BOOL exterior_target_visible = FALSE;
+ if (camera_offset_dist > object_radius)
+ {
+ // target is visible from camera, so turn off fov zoom
+ exterior_target_visible = TRUE;
+ }
+
+ F32 camera_offset_clip = camera_offset_object * object_split_axis;
+ F32 target_offset_clip = target_offset_dir * object_split_axis;
+
+ // target has moved outside of object extents
+ // check to see if camera and target are on same side
+ if (target_outside_object_extents)
+ {
+ if (camera_offset_clip > 0.f && target_offset_clip > 0.f)
+ {
+ return FALSE;
+ }
+ else if (camera_offset_clip < 0.f && target_offset_clip < 0.f)
+ {
+ return FALSE;
+ }
+ }
+
+ // clamp obj distance to diagonal of 10 by 10 cube
+ obj_min_distance = llmin(obj_min_distance, 10.f * F_SQRT3);
+
+ obj_min_distance += gCamera->getNear() + (soft_limit ? 0.1f : 0.2f);
+
+ return TRUE;
+}
+
+F32 LLAgent::getCameraZoomFraction()
+{
+ // 0.f -> camera zoomed all the way out
+ // 1.f -> camera zoomed all the way in
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // already [0,1]
+ return mAvatarObject->mHUDTargetZoom;
+ }
+ else if (mFocusOnAvatar && cameraThirdPerson())
+ {
+ return clamp_rescale(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION, 1.f, 0.f);
+ }
+ else if (cameraCustomizeAvatar())
+ {
+ F32 distance = (F32)mCameraFocusOffsetTarget.magVec();
+ return clamp_rescale(distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM, 1.f, 0.f );
+ }
+ else
+ {
+ F32 min_zoom;
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_zoom = gWorldPointer ? llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE,
+ MAX_CAMERA_DISTANCE_FROM_AGENT) : MAX_CAMERA_DISTANCE_FROM_AGENT;
+
+ F32 distance = (F32)mCameraFocusOffsetTarget.magVec();
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject->isAvatar())
+ {
+ min_zoom = AVATAR_MIN_ZOOM;
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+ else
+ {
+ min_zoom = LAND_MIN_ZOOM;
+ }
+
+ return clamp_rescale(distance, min_zoom, max_zoom, 1.f, 0.f);
+ }
+}
+
+void LLAgent::setCameraZoomFraction(F32 fraction)
+{
+ // 0.f -> camera zoomed all the way out
+ // 1.f -> camera zoomed all the way in
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mAvatarObject->mHUDTargetZoom = fraction;
+ }
+ else if (mFocusOnAvatar && cameraThirdPerson())
+ {
+ mCameraZoomFraction = rescale(fraction, 0.f, 1.f, MAX_ZOOM_FRACTION, MIN_ZOOM_FRACTION);
+ }
+ else if (cameraCustomizeAvatar())
+ {
+ LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
+ camera_offset_dir.normVec();
+ mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, APPEARANCE_MAX_ZOOM, APPEARANCE_MIN_ZOOM);
+ }
+ else
+ {
+ F32 min_zoom = LAND_MIN_ZOOM;
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_zoom = llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE,
+ MAX_CAMERA_DISTANCE_FROM_AGENT);
+
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject->isAvatar())
+ {
+ min_zoom = AVATAR_MIN_ZOOM;
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+ }
+
+ LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
+ camera_offset_dir.normVec();
+ mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
+ }
+ startCameraAnimation();
+}
+
+
+//-----------------------------------------------------------------------------
+// cameraOrbitAround()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraOrbitAround(const F32 radians)
+{
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // do nothing for hud selection
+ }
+ else if (mFocusOnAvatar && (mCameraMode == CAMERA_MODE_THIRD_PERSON || mCameraMode == CAMERA_MODE_FOLLOW))
+ {
+ mFrameAgent.rotate(radians, getReferenceUpVector());
+ }
+ else
+ {
+ mCameraFocusOffsetTarget.rotVec(radians, 0.f, 0.f, 1.f);
+
+ cameraZoomIn(1.f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// cameraOrbitOver()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraOrbitOver(const F32 angle)
+{
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // do nothing for hud selection
+ }
+ else if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ pitch(angle);
+ }
+ else
+ {
+ LLVector3 camera_offset_unit(mCameraFocusOffsetTarget);
+ camera_offset_unit.normVec();
+
+ F32 angle_from_up = acos( camera_offset_unit * getReferenceUpVector() );
+
+ LLVector3d left_axis;
+ left_axis.setVec(gCamera->getLeftAxis());
+ F32 new_angle = llclamp(angle_from_up - angle, 1.f * DEG_TO_RAD, 179.f * DEG_TO_RAD);
+ mCameraFocusOffsetTarget.rotVec(angle_from_up - new_angle, left_axis);
+
+ cameraZoomIn(1.f);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// cameraZoomIn()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraZoomIn(const F32 fraction)
+{
+ if (gDisconnected)
+ {
+ return;
+ }
+
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // just update hud zoom level
+ mAvatarObject->mHUDTargetZoom /= fraction;
+ return;
+ }
+
+
+ LLVector3d camera_offset(mCameraFocusOffsetTarget);
+ LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
+ F32 min_zoom = LAND_MIN_ZOOM;
+ F32 current_distance = (F32)camera_offset_unit.normVec();
+ F32 new_distance = current_distance * fraction;
+
+ // Don't move through focus point
+ if (mFocusObject)
+ {
+ LLVector3 camera_offset_dir((F32)camera_offset_unit.mdV[VX], (F32)camera_offset_unit.mdV[VY], (F32)camera_offset_unit.mdV[VZ]);
+
+ if (mFocusObject->isAvatar())
+ {
+ calcCameraMinDistance(min_zoom);
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+
+ new_distance = llmax(new_distance, min_zoom);
+
+ // Don't zoom too far back
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_distance = llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE );
+
+ if (new_distance > max_distance)
+ {
+ new_distance = max_distance;
+
+ /*
+ // Unless camera is unlocked
+ if (!LLViewerCamera::sDisableCameraConstraints)
+ {
+ return;
+ }
+ */
+ }
+
+ if( cameraCustomizeAvatar() )
+ {
+ new_distance = llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
+ }
+
+ mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
+}
+
+//-----------------------------------------------------------------------------
+// cameraOrbitIn()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraOrbitIn(const F32 meters)
+{
+ if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ F32 camera_offset_dist = llmax(0.001f, mCameraOffsetDefault.magVec());
+
+ mCameraZoomFraction = (mTargetCameraDistance - meters) / camera_offset_dist;
+
+ if (!gSavedSettings.getBOOL("FreezeTime") && mCameraZoomFraction < MIN_ZOOM_FRACTION && meters > 0.f)
+ {
+ // No need to animate, camera is already there.
+ changeCameraToMouselook(FALSE);
+ }
+
+ mCameraZoomFraction = llclamp(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION);
+ }
+ else
+ {
+ LLVector3d camera_offset(mCameraFocusOffsetTarget);
+ LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
+ F32 current_distance = (F32)camera_offset_unit.normVec();
+ F32 new_distance = current_distance - meters;
+ F32 min_zoom = LAND_MIN_ZOOM;
+
+ // Don't move through focus point
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject->isAvatar())
+ {
+ min_zoom = AVATAR_MIN_ZOOM;
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+
+ new_distance = llmax(new_distance, min_zoom);
+
+ // Don't zoom too far back
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_distance = llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE );
+
+ if (new_distance > max_distance)
+ {
+ // Unless camera is unlocked
+ if (!LLViewerCamera::sDisableCameraConstraints)
+ {
+ return;
+ }
+ }
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode() )
+ {
+ llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
+ }
+
+ // Compute new camera offset
+ mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
+ cameraZoomIn(1.f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// cameraPanIn()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraPanIn(F32 meters)
+{
+ LLVector3d at_axis;
+ at_axis.setVec(gCamera->getAtAxis());
+
+ mFocusTargetGlobal += meters * at_axis;
+ mFocusGlobal = mFocusTargetGlobal;
+ // don't enforce zoom constraints as this is the only way for users to get past them easily
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// cameraPanLeft()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraPanLeft(F32 meters)
+{
+ LLVector3d left_axis;
+ left_axis.setVec(gCamera->getLeftAxis());
+
+ mFocusTargetGlobal += meters * left_axis;
+ mFocusGlobal = mFocusTargetGlobal;
+ cameraZoomIn(1.f);
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// cameraPanUp()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraPanUp(F32 meters)
+{
+ LLVector3d up_axis;
+ up_axis.setVec(gCamera->getUpAxis());
+
+ mFocusTargetGlobal += meters * up_axis;
+ mFocusGlobal = mFocusTargetGlobal;
+ cameraZoomIn(1.f);
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// setKey()
+//-----------------------------------------------------------------------------
+void LLAgent::setKey(const S32 direction, S32 &key)
+{
+ if (direction > 0)
+ {
+ key = 1;
+ }
+ else if (direction < 0)
+ {
+ key = -1;
+ }
+ else
+ {
+ key = 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getControlFlags()
+//-----------------------------------------------------------------------------
+U32 LLAgent::getControlFlags()
+{
+/*
+ // HACK -- avoids maintenance of control flags when camera mode is turned on or off,
+ // only worries about it when the flags are measured
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ if ( !(mControlFlags & AGENT_CONTROL_MOUSELOOK) )
+ {
+ mControlFlags |= AGENT_CONTROL_MOUSELOOK;
+ }
+ }
+*/
+ return mControlFlags;
+}
+
+//-----------------------------------------------------------------------------
+// setControlFlags()
+//-----------------------------------------------------------------------------
+void LLAgent::setControlFlags(U32 mask)
+{
+ mControlFlags |= mask;
+ mbFlagsDirty = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// clearControlFlags()
+//-----------------------------------------------------------------------------
+void LLAgent::clearControlFlags(U32 mask)
+{
+ U32 old_flags = mControlFlags;
+ mControlFlags &= ~mask;
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// controlFlagsDirty()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::controlFlagsDirty() const
+{
+ return mbFlagsDirty;
+}
+
+//-----------------------------------------------------------------------------
+// enableControlFlagReset()
+//-----------------------------------------------------------------------------
+void LLAgent::enableControlFlagReset()
+{
+ mbFlagsNeedReset = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// resetControlFlags()
+//-----------------------------------------------------------------------------
+void LLAgent::resetControlFlags()
+{
+ if (mbFlagsNeedReset)
+ {
+ mbFlagsNeedReset = FALSE;
+ mbFlagsDirty = FALSE;
+ // reset all of the ephemeral flags
+ // some flags are managed elsewhere
+ mControlFlags &= AGENT_CONTROL_AWAY | AGENT_CONTROL_FLY | AGENT_CONTROL_MOUSELOOK;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setAFK()
+//-----------------------------------------------------------------------------
+void LLAgent::setAFK()
+{
+ // Drones can't go AFK
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (!gAllowAFK)
+ {
+ return;
+ }
+
+ if (!gAgent.getRegion())
+ {
+ // Don't set AFK if we're not talking to a region yet.
+ return;
+ }
+
+ if (!(mControlFlags & AGENT_CONTROL_AWAY))
+ {
+ sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_START);
+ setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP);
+ gAwayTimer.start();
+ if (gAFKMenu)
+ {
+ gAFKMenu->setLabel("Set Not Away");
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearAFK()
+//-----------------------------------------------------------------------------
+void LLAgent::clearAFK()
+{
+ gAwayTriggerTimer.reset();
+
+ // Gods can sometimes get into away state (via gestures)
+ // without setting the appropriate control flag. JC
+ LLVOAvatar* av = mAvatarObject;
+ if (mControlFlags & AGENT_CONTROL_AWAY
+ || (av
+ && (av->mSignaledAnimations.find(ANIM_AGENT_AWAY) != av->mSignaledAnimations.end())))
+ {
+ sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP);
+ clearControlFlags(AGENT_CONTROL_AWAY);
+ if (gAFKMenu)
+ {
+ gAFKMenu->setLabel("Set Away");
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getAFK()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::getAFK() const
+{
+ return (mControlFlags & AGENT_CONTROL_AWAY) != 0;
+}
+
+//-----------------------------------------------------------------------------
+// setBusy()
+//-----------------------------------------------------------------------------
+void LLAgent::setBusy()
+{
+ sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_START);
+ mIsBusy = TRUE;
+ if (gBusyMenu)
+ {
+ gBusyMenu->setLabel("Set Not Busy");
+ }
+ if (gFloaterMute)
+ {
+ gFloaterMute->updateButtons();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearBusy()
+//-----------------------------------------------------------------------------
+void LLAgent::clearBusy()
+{
+ mIsBusy = FALSE;
+ sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_STOP);
+ if (gBusyMenu)
+ {
+ gBusyMenu->setLabel("Set Busy");
+ }
+ if (gFloaterMute)
+ {
+ gFloaterMute->updateButtons();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getBusy()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::getBusy() const
+{
+ return mIsBusy;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateWanderTarget()
+//-----------------------------------------------------------------------------
+void LLAgent::updateWanderTarget()
+{
+ S32 num_regions;
+ LLViewerRegion* rand_region;
+ F32 rand_x;
+ F32 rand_y;
+
+ if (mWanderTimer.checkExpirationAndReset(frand(MAX_WANDER_TIME)))
+ {
+ // Pick a random spot to wander towards
+ num_regions = gWorldPointer->mActiveRegionList.getLength();
+ S32 region_num = llround(frand(1.f) * num_regions);
+ rand_region = gWorldPointer->mActiveRegionList.getFirstData();
+ S32 i = 0;
+ while (i < region_num)
+ {
+ rand_region = gWorldPointer->mActiveRegionList.getNextData();
+ i++;
+ }
+ rand_x = frand(rand_region->getWidth());
+ rand_y = frand(rand_region->getWidth());
+
+ stopAutoPilot();
+ startAutoPilotGlobal(rand_region->getPosGlobalFromRegion(LLVector3(rand_x, rand_y, 0.f)));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// startAutoPilotGlobal()
+//-----------------------------------------------------------------------------
+void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::string& behavior_name, const LLQuaternion *target_rotation, void (*finish_callback)(BOOL, void *), void *callback_data, F32 stop_distance, F32 rot_threshold)
+{
+ if (!gAgent.getAvatarObject() || !gWorldPointer)
+ {
+ return;
+ }
+
+ mAutoPilotFinishedCallback = finish_callback;
+ mAutoPilotCallbackData = callback_data;
+ mAutoPilotRotationThreshold = rot_threshold;
+ mAutoPilotBehaviorName = behavior_name;
+
+ LLVector3d delta_pos( target_global );
+ delta_pos -= getPositionGlobal();
+ F64 distance = delta_pos.magVec();
+ LLVector3d trace_target = target_global;
+
+ trace_target.mdV[VZ] -= 10.f;
+
+ LLVector3d intersection;
+ LLVector3 normal;
+ LLViewerObject *hit_obj;
+ F32 heightDelta = gWorldPointer->resolveStepHeightGlobal(NULL, target_global, trace_target, intersection, normal, &hit_obj);
+
+ if (stop_distance > 0.f)
+ {
+ mAutoPilotStopDistance = stop_distance;
+ }
+ else
+ {
+ // Guess at a reasonable stop distance.
+ mAutoPilotStopDistance = fsqrtf( distance );
+ if (mAutoPilotStopDistance < 0.5f)
+ {
+ mAutoPilotStopDistance = 0.5f;
+ }
+ }
+
+ mAutoPilotFlyOnStop = getFlying();
+
+ if (distance > 30.0)
+ {
+ setFlying(TRUE);
+ }
+
+ if ( distance > 1.f && heightDelta > (sqrtf(mAutoPilotStopDistance) + 1.f))
+ {
+ setFlying(TRUE);
+ mAutoPilotFlyOnStop = TRUE;
+ }
+
+ mAutoPilot = TRUE;
+ mAutoPilotTargetGlobal = target_global;
+
+ // trace ray down to find height of destination from ground
+ LLVector3d traceEndPt = target_global;
+ traceEndPt.mdV[VZ] -= 20.f;
+
+ LLVector3d targetOnGround;
+ LLVector3 groundNorm;
+ LLViewerObject *obj;
+
+ gWorldPointer->resolveStepHeightGlobal(NULL, target_global, traceEndPt, targetOnGround, groundNorm, &obj);
+ F64 target_height = llmax((F64)gAgent.getAvatarObject()->getPelvisToFoot(), target_global.mdV[VZ] - targetOnGround.mdV[VZ]);
+
+ // clamp z value of target to minimum height above ground
+ mAutoPilotTargetGlobal.mdV[VZ] = targetOnGround.mdV[VZ] + target_height;
+ mAutoPilotTargetDist = (F32)dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal);
+ if (target_rotation)
+ {
+ mAutoPilotUseRotation = TRUE;
+ mAutoPilotTargetFacing = LLVector3::x_axis * *target_rotation;
+ mAutoPilotTargetFacing.mV[VZ] = 0.f;
+ mAutoPilotTargetFacing.normVec();
+ }
+ else
+ {
+ mAutoPilotUseRotation = FALSE;
+ }
+
+ mAutoPilotNoProgressFrameCount = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// startFollowPilot()
+//-----------------------------------------------------------------------------
+void LLAgent::startFollowPilot(const LLUUID &leader_id)
+{
+ if (!mAutoPilot) return;
+
+ mLeaderID = leader_id;
+ if ( mLeaderID.isNull() ) return;
+
+ LLViewerObject* object = gObjectList.findObject(mLeaderID);
+ if (!object)
+ {
+ mLeaderID = LLUUID::null;
+ return;
+ }
+
+ startAutoPilotGlobal(object->getPositionGlobal());
+}
+
+
+//-----------------------------------------------------------------------------
+// stopAutoPilot()
+//-----------------------------------------------------------------------------
+void LLAgent::stopAutoPilot(BOOL user_cancel)
+{
+ if (mAutoPilot)
+ {
+ mAutoPilot = FALSE;
+ if (mAutoPilotUseRotation && !user_cancel)
+ {
+ resetAxes(mAutoPilotTargetFacing);
+ }
+ //NB: auto pilot can terminate for a reason other than reaching the destination
+ //TODO: enforce rotation constraint here as well
+ if (mAutoPilotFinishedCallback &&
+ ((mAutoPilotTargetDist < mAutoPilotStopDistance) || (mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped)))
+ {
+ mAutoPilotFinishedCallback(!user_cancel && dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < mAutoPilotTargetDist, mAutoPilotCallbackData);
+ }
+ mLeaderID = LLUUID::null;
+
+ // If the user cancelled, don't change the fly state
+ if (!user_cancel)
+ {
+ setFlying(mAutoPilotFlyOnStop);
+ }
+ setControlFlags(AGENT_CONTROL_STOP);
+
+ if (user_cancel && !mAutoPilotBehaviorName.empty())
+ {
+ if (mAutoPilotBehaviorName == "Sit")
+ LLNotifyBox::showXml("CancelledSit");
+ else if (mAutoPilotBehaviorName == "Attach")
+ LLNotifyBox::showXml("CancelledAttach");
+ else
+ LLNotifyBox::showXml("Cancelled");
+ }
+ }
+}
+
+
+// Returns necessary agent pitch and yaw changes, radians.
+//-----------------------------------------------------------------------------
+// autoPilot()
+//-----------------------------------------------------------------------------
+void LLAgent::autoPilot(F32 *delta_yaw)
+{
+ if (mAutoPilot)
+ {
+ if (!mLeaderID.isNull())
+ {
+ LLViewerObject* object = gObjectList.findObject(mLeaderID);
+ if (!object)
+ {
+ stopAutoPilot();
+ return;
+ }
+ mAutoPilotTargetGlobal = object->getPositionGlobal();
+ }
+
+ if (!mAvatarObject)
+ {
+ return;
+ }
+
+ if (mAvatarObject->mInAir)
+ {
+ setFlying(TRUE);
+ }
+
+ LLVector3 at;
+ at.setVec(mFrameAgent.getAtAxis());
+ LLVector3 target_agent = getPosAgentFromGlobal(mAutoPilotTargetGlobal);
+ LLVector3 direction = target_agent - getPositionAgent();
+
+ F32 target_dist = direction.magVec();
+
+ if (target_dist >= mAutoPilotTargetDist)
+ {
+ mAutoPilotNoProgressFrameCount++;
+ if (mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped)
+ {
+ stopAutoPilot();
+ return;
+ }
+ }
+
+ mAutoPilotTargetDist = target_dist;
+
+ // Make this a two-dimensional solution
+ at.mV[VZ] = 0.f;
+ direction.mV[VZ] = 0.f;
+
+ at.normVec();
+ F32 xy_distance = direction.normVec();
+
+ F32 yaw = 0.f;
+ if (mAutoPilotTargetDist > mAutoPilotStopDistance)
+ {
+ yaw = angle_between(mFrameAgent.getAtAxis(), direction);
+ }
+ else if (mAutoPilotUseRotation)
+ {
+ // we're close now just aim at target facing
+ yaw = angle_between(at, mAutoPilotTargetFacing);
+ direction = mAutoPilotTargetFacing;
+ }
+
+ yaw = 4.f * yaw / gFPSClamped;
+
+ // figure out which direction to turn
+ LLVector3 scratch(at % direction);
+
+ if (scratch.mV[VZ] > 0.f)
+ {
+ setControlFlags(AGENT_CONTROL_YAW_POS);
+ }
+ else
+ {
+ yaw = -yaw;
+ setControlFlags(AGENT_CONTROL_YAW_NEG);
+ }
+
+ *delta_yaw = yaw;
+
+ // Compute when to start slowing down and when to stop
+ F32 stop_distance = mAutoPilotStopDistance;
+ F32 slow_distance;
+ if (getFlying())
+ {
+ slow_distance = llmax(6.f, mAutoPilotStopDistance + 5.f);
+ stop_distance = llmax(2.f, mAutoPilotStopDistance);
+ }
+ else
+ {
+ slow_distance = llmax(3.f, mAutoPilotStopDistance + 2.f);
+ }
+
+ // If we're flying, handle autopilot points above or below you.
+ if (getFlying() && xy_distance < AUTOPILOT_HEIGHT_ADJUST_DISTANCE)
+ {
+ if (mAvatarObject)
+ {
+ F64 current_height = mAvatarObject->getPositionGlobal().mdV[VZ];
+ F32 delta_z = (F32)(mAutoPilotTargetGlobal.mdV[VZ] - current_height);
+ F32 slope = delta_z / xy_distance;
+ if (slope > 0.45f && delta_z > 6.f)
+ {
+ setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_POS);
+ }
+ else if (slope > 0.002f && delta_z > 0.5f)
+ {
+ setControlFlags(AGENT_CONTROL_UP_POS);
+ }
+ else if (slope < -0.45f && delta_z < -6.f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND)
+ {
+ setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_NEG);
+ }
+ else if (slope < -0.002f && delta_z < -0.5f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND)
+ {
+ setControlFlags(AGENT_CONTROL_UP_NEG);
+ }
+ }
+ }
+
+ // calculate delta rotation to target heading
+ F32 delta_target_heading = angle_between(mFrameAgent.getAtAxis(), mAutoPilotTargetFacing);
+
+ if (xy_distance > slow_distance && yaw < (F_PI / 10.f))
+ {
+ // walking/flying fast
+ setControlFlags(AGENT_CONTROL_FAST_AT | AGENT_CONTROL_AT_POS);
+ }
+ else if (mAutoPilotTargetDist > mAutoPilotStopDistance)
+ {
+ // walking/flying slow
+ if (at * direction > 0.9f)
+ {
+ setControlFlags(AGENT_CONTROL_AT_POS);
+ }
+ else if (at * direction < -0.9f)
+ {
+ setControlFlags(AGENT_CONTROL_AT_NEG);
+ }
+ }
+
+ // check to see if we need to keep rotating to target orientation
+ if (mAutoPilotTargetDist < mAutoPilotStopDistance)
+ {
+ setControlFlags(AGENT_CONTROL_STOP);
+ if(!mAutoPilotUseRotation || (delta_target_heading < mAutoPilotRotationThreshold))
+ {
+ stopAutoPilot();
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// propagate()
+//-----------------------------------------------------------------------------
+void LLAgent::propagate(const F32 dt)
+{
+ // Update UI based on agent motion
+ LLFloaterMove *floater_move = LLFloaterMove::getInstance();
+ if (floater_move)
+ {
+ floater_move->mForwardButton ->setToggleState( mAtKey > 0 || mWalkKey > 0 );
+ floater_move->mBackwardButton ->setToggleState( mAtKey < 0 || mWalkKey < 0 );
+ floater_move->mSlideLeftButton ->setToggleState( mLeftKey > 0 );
+ floater_move->mSlideRightButton->setToggleState( mLeftKey < 0 );
+ floater_move->mTurnLeftButton ->setToggleState( mYawKey > 0.f );
+ floater_move->mTurnRightButton ->setToggleState( mYawKey < 0.f );
+ floater_move->mMoveUpButton ->setToggleState( mUpKey > 0 );
+ floater_move->mMoveDownButton ->setToggleState( mUpKey < 0 );
+ }
+
+ // handle rotation based on keyboard levels
+ const F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second
+ yaw( YAW_RATE * mYawKey * dt );
+
+ const F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second
+ pitch(PITCH_RATE * (F32) mPitchKey * dt);
+
+ // handle auto-land behavior
+ if (mAvatarObject)
+ {
+ BOOL in_air = mAvatarObject->mInAir;
+ LLVector3 land_vel = getVelocity();
+ land_vel.mV[VZ] = 0.f;
+
+ if (!in_air
+ && mUpKey < 0
+ && land_vel.magVecSquared() < MAX_VELOCITY_AUTO_LAND_SQUARED
+ && gSavedSettings.getBOOL("AutomaticFly"))
+ {
+ // land automatically
+ setFlying(FALSE);
+ }
+ }
+
+ // clear keys
+ mAtKey = 0;
+ mWalkKey = 0;
+ mLeftKey = 0;
+ mUpKey = 0;
+ mYawKey = 0.f;
+ mPitchKey = 0;
+}
+
+//-----------------------------------------------------------------------------
+// updateAgentPosition()
+//-----------------------------------------------------------------------------
+void LLAgent::updateAgentPosition(const F32 dt, const F32 yaw_radians, const S32 mouse_x, const S32 mouse_y)
+{
+ propagate(dt);
+
+ // static S32 cameraUpdateCount = 0;
+
+ rotate(yaw_radians, 0, 0, 1);
+
+ //
+ // Check for water and land collision, set underwater flag
+ //
+
+ updateLookAt(mouse_x, mouse_y);
+}
+
+//-----------------------------------------------------------------------------
+// updateLookAt()
+//-----------------------------------------------------------------------------
+void LLAgent::updateLookAt(const S32 mouse_x, const S32 mouse_y)
+{
+ static LLVector3 last_at_axis;
+
+
+ if ( mAvatarObject.isNull() )
+ {
+ return;
+ }
+
+ LLQuaternion av_inv_rot = ~mAvatarObject->mRoot.getWorldRotation();
+ LLVector3 root_at = LLVector3::x_axis * mAvatarObject->mRoot.getWorldRotation();
+
+ if ((gViewerWindow->getMouseVelocityStat()->getCurrent() < 0.01f) &&
+ (root_at * last_at_axis > 0.95f ))
+ {
+ LLVector3 vel = mAvatarObject->getVelocity();
+ if (vel.magVecSquared() > 4.f)
+ {
+ setLookAt(LOOKAT_TARGET_IDLE, mAvatarObject, vel * av_inv_rot);
+ }
+ else
+ {
+ //FIXME: rotate mframeagent by sit object's rotation?
+ LLQuaternion look_rotation = mAvatarObject->mIsSitting ? mAvatarObject->getRenderRotation() : mFrameAgent.getQuaternion(); // use camera's current rotation
+ LLVector3 look_offset = LLVector3(2.f, 0.f, 0.f) * look_rotation * av_inv_rot;
+ setLookAt(LOOKAT_TARGET_IDLE, mAvatarObject, look_offset);
+ }
+ last_at_axis = root_at;
+ return;
+ }
+
+ last_at_axis = root_at;
+
+ if (CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode())
+ {
+ setLookAt(LOOKAT_TARGET_NONE, mAvatarObject, LLVector3(-2.f, 0.f, 0.f));
+ }
+ else
+ {
+ // Move head based on cursor position
+ ELookAtType lookAtType = LOOKAT_TARGET_NONE;
+ LLVector3 headLookAxis;
+ LLCoordFrame frameCamera = *((LLCoordFrame*)gCamera);
+
+ F32 x_from_center = mouse_x_from_center( mouse_x ); // range from -0.5 to 0.5
+ F32 y_from_center = mouse_y_from_center( mouse_y ); // range from -0.5 to 0.5
+
+ if (cameraMouselook())
+ {
+ lookAtType = LOOKAT_TARGET_MOUSELOOK;
+ }
+ else if (cameraThirdPerson())
+ {
+ frameCamera.yaw( - x_from_center * gSavedSettings.getF32("YawFromMousePosition") * DEG_TO_RAD);
+ frameCamera.pitch( - y_from_center * gSavedSettings.getF32("PitchFromMousePosition") * DEG_TO_RAD);
+ lookAtType = LOOKAT_TARGET_FREELOOK;
+ }
+
+ headLookAxis = frameCamera.getAtAxis();
+ // RN: we use world-space offset for mouselook and freelook
+ //headLookAxis = headLookAxis * av_inv_rot;
+ setLookAt(lookAtType, mAvatarObject, headLookAxis);
+ }
+}
+
+// friends and operators
+
+std::ostream& operator<<(std::ostream &s, const LLAgent &agent)
+{
+ // This is unfinished, but might never be used.
+ // We'll just leave it for now; we can always delete it.
+ s << " { "
+ << " Frame = " << agent.mFrameAgent << "\n"
+ << " }";
+ return s;
+}
+
+
+// ------------------- Beginning of legacy LLCamera hack ----------------------
+// This section is included for legacy LLCamera support until
+// it is no longer needed. Some legacy code must exist in
+// non-legacy functions, and is labeled with "// legacy" comments.
+
+//-----------------------------------------------------------------------------
+// setAvatarObject()
+//-----------------------------------------------------------------------------
+void LLAgent::setAvatarObject(LLVOAvatar *avatar)
+{
+ mAvatarObject = avatar;
+
+ if (!avatar)
+ {
+ llinfos << "Setting LLAgent::mAvatarObject to NULL" << llendl;
+ return;
+ }
+
+ if (!mLookAt)
+ {
+ mLookAt = (LLHUDEffectLookAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_LOOKAT);
+ }
+ if (!mPointAt)
+ {
+ mPointAt = (LLHUDEffectPointAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINTAT);
+ }
+
+ if (!mLookAt.isNull())
+ {
+ mLookAt->setSourceObject(avatar);
+ }
+ if (!mPointAt.isNull())
+ {
+ mPointAt->setSourceObject(avatar);
+ }
+
+ sendAgentWearablesRequest();
+}
+
+// TRUE if your own avatar needs to be rendered. Usually only
+// in third person and build.
+//-----------------------------------------------------------------------------
+// needsRenderAvatar()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::needsRenderAvatar()
+{
+ if (cameraMouselook() && !LLVOAvatar::sVisibleInFirstPerson)
+ {
+ return FALSE;
+ }
+
+ return mShowAvatar && mGenderChosen;
+}
+
+// TRUE if we need to render your own avatar's head.
+BOOL LLAgent::needsRenderHead()
+{
+ return mShowAvatar && !cameraMouselook();
+}
+
+//-----------------------------------------------------------------------------
+// startTyping()
+//-----------------------------------------------------------------------------
+void LLAgent::startTyping()
+{
+ mTypingTimer.reset();
+
+ if (getRenderState() & AGENT_STATE_TYPING)
+ {
+ // already typing, don't trigger a different animation
+ return;
+ }
+ setRenderState(AGENT_STATE_TYPING);
+
+ if (mChatTimer.getElapsedTimeF32() < 2.f)
+ {
+ LLViewerObject* chatter = gObjectList.findObject(mLastChatterID);
+ if (chatter && chatter->isAvatar())
+ {
+ gAgent.setLookAt(LOOKAT_TARGET_RESPOND, chatter, LLVector3::zero);
+ }
+ }
+
+ if (gSavedSettings.getBOOL("PlayTypingAnim"))
+ {
+ sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START);
+ }
+ gChatBar->sendChatFromViewer("", CHAT_TYPE_START, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// stopTyping()
+//-----------------------------------------------------------------------------
+void LLAgent::stopTyping()
+{
+ if (mRenderState & AGENT_STATE_TYPING)
+ {
+ clearRenderState(AGENT_STATE_TYPING);
+ sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP);
+ gChatBar->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setRenderState()
+//-----------------------------------------------------------------------------
+void LLAgent::setRenderState(U8 newstate)
+{
+ mRenderState |= newstate;
+}
+
+//-----------------------------------------------------------------------------
+// clearRenderState()
+//-----------------------------------------------------------------------------
+void LLAgent::clearRenderState(U8 clearstate)
+{
+ mRenderState &= ~clearstate;
+}
+
+
+//-----------------------------------------------------------------------------
+// getRenderState()
+//-----------------------------------------------------------------------------
+U8 LLAgent::getRenderState()
+{
+ if (gNoRender || gToolMgr == NULL || gSelectMgr == NULL || gKeyboard == NULL)
+ {
+ return 0;
+ }
+
+ // FIXME: don't do stuff in a getter! This is infinite loop city!
+ if ((mTypingTimer.getElapsedTimeF32() > TYPING_TIMEOUT_SECS)
+ && (mRenderState & AGENT_STATE_TYPING))
+ {
+ stopTyping();
+ }
+
+ if ((!gSelectMgr->isEmpty() && gSelectMgr->shouldShowSelection())
+ || gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) )->isEditing() )
+ {
+ setRenderState(AGENT_STATE_EDITING);
+ }
+ else
+ {
+ clearRenderState(AGENT_STATE_EDITING);
+ }
+
+ return mRenderState;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+static const LLFloaterView::skip_list_t& get_skip_list()
+{
+ static LLFloaterView::skip_list_t skip_list;
+ skip_list.insert(gFloaterMap);
+ return skip_list;
+}
+
+//-----------------------------------------------------------------------------
+// endAnimationUpdateUI()
+//-----------------------------------------------------------------------------
+void LLAgent::endAnimationUpdateUI()
+{
+ if (mCameraMode == mLastCameraMode)
+ {
+ // We're already done endAnimationUpdateUI for this transition.
+ return;
+ }
+
+ // clean up UI from mode we're leaving
+ if ( mLastCameraMode == CAMERA_MODE_MOUSELOOK )
+ {
+ // show mouse cursor
+ gViewerWindow->showCursor();
+ // show menus
+ gMenuBarView->setVisible(TRUE);
+ gStatusBar->setVisibleForMouselook(true);
+
+ gCurrentToolset = gBasicToolset;
+ gToolMgr->useSelectedTool( gCurrentToolset );
+
+ // Only pop if we have pushed...
+ if (TRUE == mViewsPushed)
+ {
+ mViewsPushed = FALSE;
+ gFloaterView->popVisibleAll(get_skip_list());
+ }
+
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+ if( gMorphView )
+ {
+ gMorphView->setVisible( FALSE );
+ }
+
+ // Disable mouselook-specific animations
+ if (mAvatarObject)
+ {
+ if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
+ {
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_RIFLE_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BOW_L) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_START);
+ }
+ }
+ }
+ }
+ else
+ if( mLastCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR )
+ {
+ // make sure we ask to save changes
+
+ gCurrentToolset = gBasicToolset;
+ gToolMgr->useSelectedTool( gCurrentToolset );
+
+ // HACK: If we're quitting, and we were in customize avatar, don't
+ // let the mini-map go visible again. JC
+ if (!gQuitRequested)
+ {
+ gFloaterMap->popVisible();
+ }
+
+ if( gMorphView )
+ {
+ gMorphView->setVisible( FALSE );
+ }
+
+ if (mAvatarObject)
+ {
+ sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_CUSTOMIZE_DONE, ANIM_REQUEST_START);
+ }
+ setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+
+ //---------------------------------------------------------------------
+ // Set up UI for mode we're entering
+ //---------------------------------------------------------------------
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ // hide menus
+ gMenuBarView->setVisible(FALSE);
+ gStatusBar->setVisibleForMouselook(false);
+
+ // clear out camera lag effect
+ mCameraLag.clearVec();
+
+ // JC - Added for always chat in third person option
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+
+ gCurrentToolset = gMouselookToolset;
+ gToolMgr->useSelectedTool( gMouselookToolset );
+
+ mViewsPushed = TRUE;
+
+ gFloaterView->pushVisibleAll(FALSE, get_skip_list());
+
+ if( gMorphView )
+ {
+ gMorphView->setVisible(FALSE);
+ }
+
+ gIMView->setFloaterOpen( FALSE );
+ gConsole->setVisible( TRUE );
+
+ if (mAvatarObject)
+ {
+ // Trigger mouselook-specific animations
+ if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_HOLD_ANIMS, NUM_AGENT_GUN_HOLD_ANIMS) )
+ {
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_RIFLE_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BOW_L) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_START);
+ }
+ }
+ if (mAvatarObject->getParent())
+ {
+ LLVector3 at_axis = gCamera->getAtAxis();
+ LLViewerObject* root_object = (LLViewerObject*)mAvatarObject->getRoot();
+ if (root_object->flagCameraDecoupled())
+ {
+ resetAxes(at_axis);
+ }
+ else
+ {
+ resetAxes(at_axis * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation());
+ }
+ }
+ }
+
+ }
+ else if (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR)
+ {
+ gCurrentToolset = gFaceEditToolset;
+ gToolMgr->useSelectedTool( gFaceEditToolset );
+
+ gFloaterMap->pushVisible(FALSE);
+ /*
+ LLView *view;
+ for (view = gFloaterView->getFirstChild(); view; view = gFloaterView->getNextChild())
+ {
+ view->pushVisible(FALSE);
+ }
+ */
+
+ if( gMorphView )
+ {
+ gMorphView->setVisible( TRUE );
+ }
+
+ // freeze avatar
+ if (mAvatarObject)
+ {
+ mPauseRequest = mAvatarObject->requestPause();
+ }
+ }
+
+ if (getAvatarObject())
+ {
+ getAvatarObject()->updateAttachmentVisibility(mCameraMode);
+ }
+
+ gFloaterTools->dirty();
+
+ // Don't let this be called more than once if the camera
+ // mode hasn't changed. --JC
+ mLastCameraMode = mCameraMode;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateCamera()
+//-----------------------------------------------------------------------------
+void LLAgent::updateCamera()
+{
+ //Ventrella - changed camera_skyward to the new global "mCameraUpVector"
+ mCameraUpVector = LLVector3::z_axis;
+ //LLVector3 camera_skyward(0.f, 0.f, 1.f);
+ //end Ventrella
+
+ U32 camera_mode = mCameraAnimating ? mLastCameraMode : mCameraMode;
+
+ validateFocusObject();
+
+ if (!mAvatarObject.isNull() &&
+ mAvatarObject->mIsSitting &&
+ camera_mode == CAMERA_MODE_MOUSELOOK)
+ {
+ //Ventrella
+ //changed camera_skyward to the new global "mCameraUpVector"
+ mCameraUpVector = mCameraUpVector * mAvatarObject->getRenderRotation();
+ //end Ventrella
+ }
+
+ if (cameraThirdPerson() && mFocusOnAvatar && LLFollowCamMgr::getActiveFollowCamParams())
+ {
+ changeCameraToFollow();
+ }
+
+ //Ventrella
+ //NOTE - this needs to be integrated into a general upVector system here within llAgent.
+ if ( camera_mode == CAMERA_MODE_FOLLOW && mFocusOnAvatar )
+ {
+ mCameraUpVector = mFollowCam.getUpVector();
+ }
+ //end Ventrella
+
+ if (mSitCameraEnabled)
+ {
+ if (mSitCameraReferenceObject->isDead())
+ {
+ setSitCamera(LLUUID::null);
+ }
+ }
+
+ // Update UI with our camera inputs
+ if (gFloaterCamera)
+ {
+ gFloaterCamera->mRotate->setToggleState(
+ mOrbitRightKey > 0.f, // left
+ mOrbitUpKey > 0.f, // top
+ mOrbitLeftKey > 0.f, // right
+ mOrbitDownKey > 0.f); // bottom
+
+ gFloaterCamera->mZoom->setToggleState(
+ mOrbitInKey > 0.f, // top
+ mOrbitOutKey > 0.f); // bottom
+
+ gFloaterCamera->mTrack->setToggleState(
+ mPanLeftKey > 0.f, // left
+ mPanUpKey > 0.f, // top
+ mPanRightKey > 0.f, // right
+ mPanDownKey > 0.f); // bottom
+ }
+
+ // Handle camera movement based on keyboard.
+ const F32 ORBIT_OVER_RATE = 90.f * DEG_TO_RAD; // radians per second
+ const F32 ORBIT_AROUND_RATE = 90.f * DEG_TO_RAD; // radians per second
+ const F32 PAN_RATE = 5.f; // meters per second
+
+ if( mOrbitUpKey || mOrbitDownKey )
+ {
+ F32 input_rate = mOrbitUpKey - mOrbitDownKey;
+ cameraOrbitOver( input_rate * ORBIT_OVER_RATE / gFPSClamped );
+ }
+
+ if( mOrbitLeftKey || mOrbitRightKey)
+ {
+ F32 input_rate = mOrbitLeftKey - mOrbitRightKey;
+ cameraOrbitAround( input_rate * ORBIT_AROUND_RATE / gFPSClamped );
+ }
+
+ if( mOrbitInKey || mOrbitOutKey )
+ {
+ F32 input_rate = mOrbitInKey - mOrbitOutKey;
+
+ LLVector3d to_focus = gAgent.getPosGlobalFromAgent(gCamera->getOrigin()) - calcFocusPositionTargetGlobal();
+ F32 distance_to_focus = (F32)to_focus.magVec();
+ // Move at distance (in meters) meters per second
+ cameraOrbitIn( input_rate * distance_to_focus / gFPSClamped );
+ }
+
+ if( mPanInKey || mPanOutKey )
+ {
+ F32 input_rate = mPanInKey - mPanOutKey;
+ cameraPanIn( input_rate * PAN_RATE / gFPSClamped );
+ }
+
+ if( mPanRightKey || mPanLeftKey )
+ {
+ F32 input_rate = mPanRightKey - mPanLeftKey;
+ cameraPanLeft( input_rate * -PAN_RATE / gFPSClamped );
+ }
+
+ if( mPanUpKey || mPanDownKey )
+ {
+ F32 input_rate = mPanUpKey - mPanDownKey;
+ cameraPanUp( input_rate * PAN_RATE / gFPSClamped );
+ }
+
+ // Clear camera keyboard keys.
+ mOrbitLeftKey = 0.f;
+ mOrbitRightKey = 0.f;
+ mOrbitUpKey = 0.f;
+ mOrbitDownKey = 0.f;
+ mOrbitInKey = 0.f;
+ mOrbitOutKey = 0.f;
+
+ mPanRightKey = 0.f;
+ mPanLeftKey = 0.f;
+ mPanUpKey = 0.f;
+ mPanDownKey = 0.f;
+ mPanInKey = 0.f;
+ mPanOutKey = 0.f;
+
+ // lerp camera focus offset
+ mCameraFocusOffset = lerp(mCameraFocusOffset, mCameraFocusOffsetTarget, LLCriticalDamp::getInterpolant(CAMERA_FOCUS_HALF_LIFE));
+
+ //Ventrella
+ if ( mCameraMode == CAMERA_MODE_FOLLOW )
+ {
+ if ( !mAvatarObject.isNull() )
+ {
+ //--------------------------------------------------------------------------------
+ // this is where the avatar's position and rotation are given to followCam, and
+ // where it is updated. All three of its attributes are updated: (1) position,
+ // (2) focus, and (3) upvector. They can then be queried elsewhere in llAgent.
+ //--------------------------------------------------------------------------------
+ //FIXME: use combined rotation of frameagent and sit object
+ LLQuaternion avatarRotationForFollowCam = mAvatarObject->mIsSitting ? mAvatarObject->getRenderRotation() : mFrameAgent.getQuaternion();
+
+ LLFollowCamParams* current_cam = LLFollowCamMgr::getActiveFollowCamParams();
+ if (current_cam)
+ {
+ mFollowCam.copyParams(*current_cam);
+ mFollowCam.setSubjectPositionAndRotation( mAvatarObject->getRenderPosition(), avatarRotationForFollowCam );
+ mFollowCam.update();
+ }
+ else
+ {
+ changeCameraToThirdPerson(TRUE);
+ }
+ }
+ }
+ // end Ventrella
+
+ BOOL hit_limit;
+ LLVector3d camera_pos_global;
+ LLVector3d camera_target_global = calcCameraPositionTargetGlobal(&hit_limit);
+ mCameraVirtualPositionAgent = getPosAgentFromGlobal(camera_target_global);
+ LLVector3d focus_target_global = calcFocusPositionTargetGlobal();
+
+ // perform field of view correction
+ mCameraFOVZoomFactor = calcCameraFOVZoomFactor();
+ camera_target_global = focus_target_global + (camera_target_global - focus_target_global) * (1.f + mCameraFOVZoomFactor);
+
+ // do alpha fade on focus object
+ F32 fade_increment = mFocusObjectFadeTimer.getElapsedTimeAndResetF32();
+
+ if (mFocusObject.notNull() && !mFocusObject->isAttachment() && mFocusObject->mDrawable.notNull())
+ {
+ F32 increment = fade_increment;
+ if (mFocusObjectDist < -0.2f)
+ {
+ increment *= -1.f;
+ }
+
+ if (mFocusObject->getVObjRadius() > MIN_RADIUS_ALPHA_SIZZLE)
+ {
+ S32 num_faces = mFocusObject->mDrawable->getNumFaces();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ LLFace* facep = mFocusObject->mDrawable->getFace(i);
+ F32 fade = facep->mAlphaFade;
+ fade = llclamp(fade + increment, 0.f, 1.f);
+ facep->mAlphaFade = fade;
+ }
+ }
+ }
+
+ // do alpha fade in on fade objects
+ std::set< LLPointer<LLViewerObject> >::iterator fade_object_it;
+ for (fade_object_it = mFadeObjects.begin(); fade_object_it != mFadeObjects.end(); )
+ {
+ LLViewerObject* fade_object = *fade_object_it;
+ if (fade_object->isDead())
+ {
+ // remove from list
+ mFadeObjects.erase(fade_object_it++);
+ }
+ else
+ {
+ LLDrawable* drawablep = fade_object->mDrawable;
+ if (drawablep && fade_object->getVObjRadius() > MIN_RADIUS_ALPHA_SIZZLE)
+ {
+ S32 num_faces = drawablep->getNumFaces();
+ BOOL fade_done = TRUE;
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ LLFace* facep = drawablep->getFace(i);
+ F32 fade = facep->mAlphaFade;
+ fade = llclamp(fade - fade_increment, 0.f, 1.f);
+ facep->mAlphaFade = fade;
+ if (fade > 0.f)
+ {
+ fade_done = FALSE;
+ }
+ }
+ if (fade_done)
+ {
+ mFadeObjects.erase(fade_object_it++);
+ }
+ else
+ {
+ fade_object_it++;
+ }
+ }
+ else
+ {
+ fade_object_it++;
+ }
+ }
+ }
+
+ mShowAvatar = TRUE; // can see avatar by default
+
+ // Adjust position for animation
+ if (mCameraAnimating)
+ {
+ F32 time = mAnimationTimer.getElapsedTimeF32();
+
+ // yet another instance of critically damped motion, hooray!
+ // F32 fraction_of_animation = 1.f - pow(2.f, -time / CAMERA_ZOOM_HALF_LIFE);
+
+ // linear interpolation
+ F32 fraction_of_animation = time / mAnimationDuration;
+
+ BOOL isfirstPerson = mCameraMode == CAMERA_MODE_MOUSELOOK;
+ BOOL wasfirstPerson = mLastCameraMode == CAMERA_MODE_MOUSELOOK;
+ F32 fraction_animation_to_skip;
+
+ if (mAnimationCameraStartGlobal == camera_target_global)
+ {
+ fraction_animation_to_skip = 0.f;
+ }
+ else
+ {
+ LLVector3d cam_delta = mAnimationCameraStartGlobal - camera_target_global;
+ fraction_animation_to_skip = HEAD_BUFFER_SIZE / (F32)cam_delta.magVec();
+ }
+ F32 animation_start_fraction = (wasfirstPerson) ? fraction_animation_to_skip : 0.f;
+ F32 animation_finish_fraction = (isfirstPerson) ? (1.f - fraction_animation_to_skip) : 1.f;
+
+ if (fraction_of_animation < animation_finish_fraction)
+ {
+ if (fraction_of_animation < animation_start_fraction || fraction_of_animation > animation_finish_fraction )
+ {
+ mShowAvatar = FALSE;
+ }
+
+ // ...adjust position for animation
+ camera_pos_global = lerp(mAnimationCameraStartGlobal, camera_target_global, fraction_of_animation);
+ mFocusGlobal = lerp(mAnimationFocusStartGlobal, focus_target_global, fraction_of_animation);
+ }
+ else
+ {
+ // ...animation complete
+ mCameraAnimating = FALSE;
+
+ camera_pos_global = camera_target_global;
+ mFocusGlobal = focus_target_global;
+
+ endAnimationUpdateUI();
+ mShowAvatar = TRUE;
+ }
+
+ if (getAvatarObject() && mCameraMode != CAMERA_MODE_MOUSELOOK)
+ {
+ getAvatarObject()->updateAttachmentVisibility(mCameraMode);
+ }
+ }
+ else
+ {
+ camera_pos_global = camera_target_global;
+ mFocusGlobal = focus_target_global;
+ mShowAvatar = TRUE;
+ }
+
+ mCameraCurrentFOVZoomFactor = lerp(mCameraCurrentFOVZoomFactor, mCameraFOVZoomFactor, LLCriticalDamp::getInterpolant(FOV_ZOOM_HALF_LIFE));
+
+// llinfos << "Current FOV Zoom: " << mCameraCurrentFOVZoomFactor << " Target FOV Zoom: " << mCameraFOVZoomFactor << " Object penetration: " << mFocusObjectDist << llendl;
+
+ F32 ui_offset = 0.f;
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
+ {
+ ui_offset = calcCustomizeAvatarUIOffset( camera_pos_global );
+ }
+
+
+ LLVector3 focus_agent = getPosAgentFromGlobal(mFocusGlobal);
+
+ mCameraPositionAgent = getPosAgentFromGlobal(camera_pos_global);
+
+ // Move the camera
+
+ //Ventrella
+ gCamera->updateCameraLocation(mCameraPositionAgent, mCameraUpVector, focus_agent);
+ //gCamera->updateCameraLocation(mCameraPositionAgent, camera_skyward, focus_agent);
+ //end Ventrella
+
+ //RN: translate UI offset after camera is oriented properly
+ gCamera->translate(gCamera->getLeftAxis() * ui_offset);
+
+ // Change FOV
+ gCamera->setView(gCamera->getDefaultFOV() / (1.f + mCameraCurrentFOVZoomFactor));
+
+ // follow camera when in customize mode
+ if (cameraCustomizeAvatar())
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, NULL, mCameraPositionAgent);
+ }
+
+ // update the travel distance stat
+ // this isn't directly related to the camera
+ // but this seemed like the best place to do this
+ LLVector3d global_pos = getPositionGlobal();
+ if (! mLastPositionGlobal.isExactlyZero())
+ {
+ LLVector3d delta = global_pos - mLastPositionGlobal;
+ mDistanceTraveled += delta.magVec();
+ }
+ mLastPositionGlobal = global_pos;
+
+ if (LLVOAvatar::sVisibleInFirstPerson && mAvatarObject.notNull() && !mAvatarObject->mIsSitting && cameraMouselook())
+ {
+ LLVector3 head_pos = mAvatarObject->mHeadp->getWorldPosition() +
+ LLVector3(0.08f, 0.f, 0.05f) * mAvatarObject->mHeadp->getWorldRotation() +
+ LLVector3(0.1f, 0.f, 0.f) * mAvatarObject->mPelvisp->getWorldRotation();
+ LLVector3 diff = mCameraPositionAgent - head_pos;
+ diff = diff * ~mAvatarObject->mRoot.getWorldRotation();
+
+ LLJoint* torso_joint = mAvatarObject->mTorsop;
+ LLJoint* chest_joint = mAvatarObject->mChestp;
+ LLVector3 torso_scale = torso_joint->getScale();
+ LLVector3 chest_scale = chest_joint->getScale();
+
+ // shorten avatar skeleton to avoid foot interpenetration
+ if (!mAvatarObject->mInAir)
+ {
+ LLVector3 chest_offset = LLVector3(0.f, 0.f, chest_joint->getPosition().mV[VZ]) * torso_joint->getWorldRotation();
+ F32 z_compensate = llclamp(-diff.mV[VZ], -0.2f, 1.f);
+ F32 scale_factor = llclamp(1.f - ((z_compensate * 0.5f) / chest_offset.mV[VZ]), 0.5f, 1.2f);
+ torso_joint->setScale(LLVector3(1.f, 1.f, scale_factor));
+
+ LLJoint* neck_joint = mAvatarObject->mNeckp;
+ LLVector3 neck_offset = LLVector3(0.f, 0.f, neck_joint->getPosition().mV[VZ]) * chest_joint->getWorldRotation();
+ scale_factor = llclamp(1.f - ((z_compensate * 0.5f) / neck_offset.mV[VZ]), 0.5f, 1.2f);
+ chest_joint->setScale(LLVector3(1.f, 1.f, scale_factor));
+ diff.mV[VZ] = 0.f;
+ }
+
+ mAvatarObject->mPelvisp->setPosition(mAvatarObject->mPelvisp->getPosition() + diff);
+
+ mAvatarObject->mRoot.updateWorldMatrixChildren();
+
+ for(LLViewerJointAttachment *attachment = mAvatarObject->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAvatarObject->mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull())
+ {
+ // clear any existing "early" movements of attachment
+ attached_object->mDrawable->clearState(LLDrawable::EARLY_MOVE);
+ gPipeline.updateMoveNormalAsync(attached_object->mDrawable);
+ attached_object->updateText();
+ }
+ }
+
+ torso_joint->setScale(torso_scale);
+ chest_joint->setScale(chest_scale);
+ }
+}
+
+void LLAgent::updateFocusOffset()
+{
+ validateFocusObject();
+ if (mFocusObject.notNull())
+ {
+ LLVector3d obj_pos = getPosGlobalFromAgent(mFocusObject->getRenderPosition());
+ mFocusObjectOffset.setVec(mFocusTargetGlobal - obj_pos);
+ }
+}
+
+void LLAgent::validateFocusObject()
+{
+ if (mFocusObject.notNull() &&
+ (mFocusObject->isDead()))
+ {
+ mFocusObjectOffset.clearVec();
+ clearFocusObject();
+ mCameraFOVZoomFactor = 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcCustomizeAvatarUIOffset()
+//-----------------------------------------------------------------------------
+F32 LLAgent::calcCustomizeAvatarUIOffset( const LLVector3d& camera_pos_global )
+{
+ F32 ui_offset = 0.f;
+
+ if( gFloaterCustomize )
+ {
+ const LLRect& rect = gFloaterCustomize->getRect();
+
+ // Move the camera so that the avatar isn't covered up by this floater.
+ F32 fraction_of_fov = 0.5f - (0.5f * (1.f - llmin(1.f, ((F32)rect.getWidth() / (F32)gViewerWindow->getWindowWidth()))));
+ F32 apparent_angle = fraction_of_fov * gCamera->getView() * gCamera->getAspect(); // radians
+ F32 offset = tan(apparent_angle);
+
+ if( rect.mLeft < (gViewerWindow->getWindowWidth() - rect.mRight) )
+ {
+ // Move the avatar to the right (camera to the left)
+ ui_offset = offset;
+ }
+ else
+ {
+ // Move the avatar to the left (camera to the right)
+ ui_offset = -offset;
+ }
+ }
+ F32 range = (F32)dist_vec(camera_pos_global, gAgent.getFocusGlobal());
+ mUIOffset = lerp(mUIOffset, ui_offset, LLCriticalDamp::getInterpolant(0.05f));
+ return mUIOffset * range;
+}
+
+//-----------------------------------------------------------------------------
+// calcFocusPositionTargetGlobal()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::calcFocusPositionTargetGlobal()
+{
+ if (mFocusObject.notNull() && mFocusObject->isDead())
+ {
+ clearFocusObject();
+ }
+
+ // Ventrella
+ if ( mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar )
+ {
+ mFocusTargetGlobal = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedFocus());
+ return mFocusTargetGlobal;
+ }// End Ventrella
+ else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ LLVector3d at_axis(1.0, 0.0, 0.0);
+ LLQuaternion agent_rot = mFrameAgent.getQuaternion();
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLViewerObject* root_object = (LLViewerObject*)mAvatarObject->getRoot();
+ if (!root_object->flagCameraDecoupled())
+ {
+ agent_rot *= ((LLViewerObject*)(mAvatarObject->getParent()))->getRenderRotation();
+ }
+ }
+ at_axis = at_axis * agent_rot;
+ mFocusTargetGlobal = calcCameraPositionTargetGlobal() + at_axis;
+ return mFocusTargetGlobal;
+ }
+ else if (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR)
+ {
+ return mFocusTargetGlobal;
+ }
+ else if (!mFocusOnAvatar)
+ {
+ if (mFocusObject.notNull() && !mFocusObject->isDead() && mFocusObject->mDrawable.notNull())
+ {
+ LLDrawable* drawablep = mFocusObject->mDrawable;
+
+ if (mTrackFocusObject && drawablep && drawablep->isActive())
+ {
+ if (mFocusObject->isSelected())
+ {
+ gPipeline.updateMoveNormalAsync(drawablep);
+ }
+ else
+ {
+ if (drawablep->isState(LLDrawable::MOVE_UNDAMPED))
+ {
+ gPipeline.updateMoveNormalAsync(drawablep);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(drawablep);
+ }
+ }
+ }
+ // if not tracking object, update offset based on new object position
+ else
+ {
+ updateFocusOffset();
+ }
+ LLVector3 focus_agent = mFocusObject->getRenderPosition() + mFocusObjectOffset;
+ mFocusTargetGlobal.setVec(getPosGlobalFromAgent(focus_agent));
+ //FIXME: get camera pointat behavior working
+ //if (mTrackFocusObject)
+ //{
+ // mCameraFocusOffset = gAgent.getPosGlobalFromAgent(gCamera->getOrigin()) - mFocusTargetGlobal;
+ //}
+ }
+ return mFocusTargetGlobal;
+ }
+ else if (mSitCameraEnabled && mAvatarObject.notNull() && mAvatarObject->mIsSitting && mSitCameraReferenceObject.notNull())
+ {
+ // sit camera
+ LLVector3 object_pos = mSitCameraReferenceObject->getRenderPosition();
+ LLQuaternion object_rot = mSitCameraReferenceObject->getRenderRotation();
+
+ LLVector3 target_pos = object_pos + (mSitCameraFocus * object_rot);
+ return getPosGlobalFromAgent(target_pos);
+ }
+ else
+ {
+ // ...offset from avatar
+ LLVector3d focus_offset;
+ focus_offset.setVec(gSavedSettings.getVector3("FocusOffsetDefault"));
+
+ LLQuaternion agent_rot = mFrameAgent.getQuaternion();
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ agent_rot *= ((LLViewerObject*)(mAvatarObject->getParent()))->getRenderRotation();
+ }
+
+ focus_offset = focus_offset * agent_rot;
+
+ return getPositionGlobal() + focus_offset;
+ }
+}
+
+void LLAgent::setupSitCamera()
+{
+ // agent frame entering this function is in world coordinates
+ if (mAvatarObject.notNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion parent_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ // slam agent coordinate frame to proper parent local version
+ LLVector3 at_axis = mFrameAgent.getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~parent_rot);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getCameraPositionAgent()
+//-----------------------------------------------------------------------------
+const LLVector3 &LLAgent::getCameraPositionAgent() const
+{
+ return gCamera->getOrigin();
+}
+
+//-----------------------------------------------------------------------------
+// getCameraPositionGlobal()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::getCameraPositionGlobal() const
+{
+ if (gCamera)
+ {
+ return getPosGlobalFromAgent(gCamera->getOrigin());
+ }
+ else
+ {
+ return (LLVector3d::zero);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcCameraFOVZoomFactor()
+//-----------------------------------------------------------------------------
+F32 LLAgent::calcCameraFOVZoomFactor()
+{
+ LLVector3 camera_offset_dir;
+ camera_offset_dir.setVec(mCameraFocusOffset);
+
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ return 0.f;
+ }
+ else if (mFocusObject.notNull() && !mFocusObject->isAvatar())
+ {
+ // don't FOV zoom on mostly transparent objects
+ LLVector3 focus_offset = mFocusObjectOffset;
+ F32 obj_min_dist = 0.f;
+ calcCameraMinDistance(obj_min_dist);
+ F32 current_distance = llmax(0.001f, camera_offset_dir.magVec());
+
+ mFocusObjectDist = obj_min_dist - current_distance;
+
+ F32 new_fov_zoom = llclamp(mFocusObjectDist / current_distance, 0.f, 1000.f);
+ return new_fov_zoom;
+ }
+ else // focusing on land or avatar
+ {
+ // keep old field of view until user changes focus explicitly
+ return mCameraFOVZoomFactor;
+ //return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcCameraPositionTargetGlobal()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::calcCameraPositionTargetGlobal(BOOL *hit_limit)
+{
+ // Compute base camera position and look-at points.
+ F32 camera_land_height;
+ LLVector3d frame_center_global = mAvatarObject.isNull() ? getPositionGlobal()
+ : getPosGlobalFromAgent(mAvatarObject->mRoot.getWorldPosition());
+
+ LLVector3 upAxis = getUpAxis();
+ BOOL isConstrained = FALSE;
+ LLVector3d head_offset;
+ head_offset.setVec(mThirdPersonHeadOffset);
+
+ LLVector3d camera_position_global;
+
+ // Ventrella
+ if ( mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar )
+ {
+ camera_position_global = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedPosition());
+ }// End Ventrella
+ else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ if (mAvatarObject.isNull() || mAvatarObject->mDrawable.isNull())
+ {
+ llwarns << "Null avatar drawable!" << llendl;
+ return LLVector3d::zero;
+ }
+ head_offset.clearVec();
+ if (mAvatarObject->mIsSitting && mAvatarObject->getParent())
+ {
+ mAvatarObject->updateHeadOffset();
+ head_offset.mdV[VX] = mAvatarObject->mHeadOffset.mV[VX];
+ head_offset.mdV[VY] = mAvatarObject->mHeadOffset.mV[VY];
+ head_offset.mdV[VZ] = mAvatarObject->mHeadOffset.mV[VZ] + 0.1f;
+ const LLMatrix4& mat = ((LLViewerObject*) mAvatarObject->getParent())->getRenderMatrix();
+ camera_position_global = getPosGlobalFromAgent
+ ((mAvatarObject->getPosition()+
+ LLVector3(head_offset)*mAvatarObject->getRotation()) * mat);
+ }
+ else
+ {
+ head_offset.mdV[VZ] = mAvatarObject->mHeadOffset.mV[VZ];
+ if (mAvatarObject->mIsSitting)
+ {
+ head_offset.mdV[VZ] += 0.1;
+ }
+ camera_position_global = getPosGlobalFromAgent(mAvatarObject->getRenderPosition());//frame_center_global;
+ head_offset = head_offset * mAvatarObject->getRenderRotation();
+ camera_position_global = camera_position_global + head_offset;
+ }
+ }
+ else if (mCameraMode == CAMERA_MODE_THIRD_PERSON && mFocusOnAvatar)
+ {
+ LLVector3 local_camera_offset;
+ F32 camera_distance = 0.f;
+
+ if (mSitCameraEnabled
+ && mAvatarObject.notNull()
+ && mAvatarObject->mIsSitting
+ && mSitCameraReferenceObject.notNull())
+ {
+ // sit camera
+ LLVector3 object_pos = mSitCameraReferenceObject->getRenderPosition();
+ LLQuaternion object_rot = mSitCameraReferenceObject->getRenderRotation();
+
+ LLVector3 target_pos = object_pos + (mSitCameraPos * object_rot);
+
+ camera_position_global = getPosGlobalFromAgent(target_pos);
+ }
+ else
+ {
+ local_camera_offset = mCameraZoomFraction * mCameraOffsetDefault;
+
+ // are we sitting down?
+ if (mAvatarObject.notNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion parent_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ // slam agent coordinate frame to proper parent local version
+ LLVector3 at_axis = mFrameAgent.getAtAxis() * parent_rot;
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~parent_rot);
+
+ local_camera_offset = local_camera_offset * mFrameAgent.getQuaternion() * parent_rot;
+ }
+ else
+ {
+ local_camera_offset = mFrameAgent.rotateToAbsolute( local_camera_offset );
+ }
+
+ if (!mCameraCollidePlane.isExactlyZero() && (mAvatarObject.isNull() || !mAvatarObject->mIsSitting))
+ {
+ LLVector3 plane_normal;
+ plane_normal.setVec(mCameraCollidePlane.mV);
+
+ F32 offset_dot_norm = local_camera_offset * plane_normal;
+ if (llabs(offset_dot_norm) < 0.001f)
+ {
+ offset_dot_norm = 0.001f;
+ }
+
+ camera_distance = local_camera_offset.normVec();
+
+ F32 pos_dot_norm = getPosAgentFromGlobal(frame_center_global + head_offset) * plane_normal;
+
+ // if agent is outside the colliding half-plane
+ if (pos_dot_norm > mCameraCollidePlane.mV[VW])
+ {
+ // check to see if camera is on the opposite side (inside) the half-plane
+ if (offset_dot_norm + pos_dot_norm < mCameraCollidePlane.mV[VW])
+ {
+ // diminish offset by factor to push it back outside the half-plane
+ camera_distance *= (pos_dot_norm - mCameraCollidePlane.mV[VW] - CAMERA_COLLIDE_EPSILON) / -offset_dot_norm;
+ }
+ }
+ else
+ {
+ if (offset_dot_norm + pos_dot_norm > mCameraCollidePlane.mV[VW])
+ {
+ camera_distance *= (mCameraCollidePlane.mV[VW] - pos_dot_norm - CAMERA_COLLIDE_EPSILON) / offset_dot_norm;
+ }
+ }
+ }
+ else
+ {
+ camera_distance = local_camera_offset.normVec();
+ }
+
+ mTargetCameraDistance = llmax(camera_distance, MIN_CAMERA_DISTANCE);
+
+ if (mTargetCameraDistance != mCurrentCameraDistance)
+ {
+ F32 camera_lerp_amt = LLCriticalDamp::getInterpolant(CAMERA_ZOOM_HALF_LIFE);
+
+ mCurrentCameraDistance = lerp(mCurrentCameraDistance, mTargetCameraDistance, camera_lerp_amt);
+ }
+
+ // Make the camera distance current
+ local_camera_offset *= mCurrentCameraDistance;
+
+ // set the global camera position
+ LLVector3d camera_offset;
+
+ LLVector3 av_pos = mAvatarObject.isNull() ? LLVector3::zero : mAvatarObject->getRenderPosition();
+ camera_offset.setVec( local_camera_offset );
+ camera_position_global = frame_center_global + head_offset + camera_offset;
+
+ if (!mAvatarObject.isNull())
+ {
+ LLVector3d camera_lag_d;
+ F32 lag_interp = LLCriticalDamp::getInterpolant(CAMERA_LAG_HALF_LIFE);
+ LLVector3 target_lag;
+ LLVector3 vel = getVelocity();
+
+ // lag by appropriate amount for flying
+ F32 time_in_air = mAvatarObject->mTimeInAir.getElapsedTimeF32();
+ if(!mCameraAnimating && mAvatarObject->mInAir && time_in_air > GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME)
+ {
+ LLVector3 frame_at_axis = mFrameAgent.getAtAxis();
+ frame_at_axis -= projected_vec(frame_at_axis, getReferenceUpVector());
+ frame_at_axis.normVec();
+
+ //transition smoothly in air mode, to avoid camera pop
+ F32 u = (time_in_air - GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME) / GROUND_TO_AIR_CAMERA_TRANSITION_TIME;
+ u = llclamp(u, 0.f, 1.f);
+
+ lag_interp *= u;
+
+ if (gViewerWindow->getLeftMouseDown() && gLastHitObjectID == mAvatarObject->getID())
+ {
+ // disable camera lag when using mouse-directed steering
+ target_lag.clearVec();
+ }
+ else
+ {
+ target_lag = vel * gSavedSettings.getF32("DynamicCameraStrength") / 30.f;
+ }
+
+ mCameraLag = lerp(mCameraLag, target_lag, lag_interp);
+
+ F32 lag_dist = mCameraLag.magVec();
+ if (lag_dist > MAX_CAMERA_LAG)
+ {
+ mCameraLag = mCameraLag * MAX_CAMERA_LAG / lag_dist;
+ }
+
+ // clamp camera lag so that avatar is always in front
+ F32 dot = (mCameraLag - (frame_at_axis * (MIN_CAMERA_LAG * u))) * frame_at_axis;
+ if (dot < -(MIN_CAMERA_LAG * u))
+ {
+ mCameraLag -= (dot + (MIN_CAMERA_LAG * u)) * frame_at_axis;
+ }
+ }
+ else
+ {
+ mCameraLag = lerp(mCameraLag, LLVector3::zero, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ camera_lag_d.setVec(mCameraLag);
+ camera_position_global = camera_position_global - camera_lag_d;
+ }
+ }
+ }
+ else
+ {
+ LLVector3d focusPosGlobal = calcFocusPositionTargetGlobal();
+ // camera gets pushed out later wrt mCameraFOVZoomFactor...this is "raw" value
+ camera_position_global = focusPosGlobal + mCameraFocusOffset;
+ }
+
+ if (!LLViewerCamera::sDisableCameraConstraints && !gAgent.isGodlike())
+ {
+ LLViewerRegion* regionp = gWorldPointer->getRegionFromPosGlobal(
+ camera_position_global);
+ bool constrain = true;
+ if(regionp && regionp->canManageEstate())
+ {
+ constrain = false;
+ }
+ if(constrain)
+ {
+ F32 max_dist = ( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode ) ?
+ APPEARANCE_MAX_ZOOM : MAX_CAMERA_DISTANCE_FROM_AGENT;
+
+ LLVector3d camera_offset = camera_position_global
+ - gAgent.getPositionGlobal();
+ F32 camera_distance = (F32)camera_offset.magVec();
+
+ if(camera_distance > max_dist)
+ {
+ camera_position_global = gAgent.getPositionGlobal() +
+ (max_dist / camera_distance) * camera_offset;
+ isConstrained = TRUE;
+ }
+ }
+
+// JC - Could constrain camera based on parcel stuff here.
+// LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(camera_position_global);
+//
+// if (regionp && !regionp->mParcelOverlay->isBuildCameraAllowed(regionp->getPosRegionFromGlobal(camera_position_global)))
+// {
+// camera_position_global = last_position_global;
+//
+// isConstrained = TRUE;
+// }
+ }
+
+ // Don't let camera go underground
+ F32 camera_min_off_ground = getCameraMinOffGround();
+
+ if (gWorldPointer)
+ {
+ camera_land_height = gWorldPointer->resolveLandHeightGlobal(camera_position_global);
+ }
+ else
+ {
+ camera_land_height = 0.f;
+ }
+
+ if (camera_position_global.mdV[VZ] < camera_land_height + camera_min_off_ground)
+ {
+ camera_position_global.mdV[VZ] = camera_land_height + camera_min_off_ground;
+
+ gMinObjectDistance = MIN_NEAR_PLANE;
+ isConstrained = TRUE;
+ }
+
+
+ if (hit_limit)
+ {
+ *hit_limit = isConstrained;
+ }
+
+ return camera_position_global;
+}
+
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+void LLAgent::handleScrollWheel(S32 clicks)
+{
+ if ( mCameraMode == CAMERA_MODE_FOLLOW && gAgent.getFocusOnAvatar())
+ {
+ if ( ! mFollowCam.getPositionLocked() ) // not if the followCam position is locked in place
+ {
+ mFollowCam.zoom( clicks );
+ if ( mFollowCam.isZoomedToMinimumDistance() )
+ {
+ changeCameraToMouselook(FALSE);
+ }
+ }
+ }
+ else
+ {
+ const F32 ROOT_ROOT_TWO = sqrt(F_SQRT2);
+
+ // Block if camera is animating
+ if (mCameraAnimating)
+ {
+ return;
+ }
+
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom_factor = (F32)pow(0.8, -clicks);
+ cameraZoomIn(zoom_factor);
+ }
+ else if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ F32 current_zoom_fraction = mTargetCameraDistance / mCameraOffsetDefault.magVec();
+ current_zoom_fraction *= 1.f - pow(ROOT_ROOT_TWO, clicks);
+
+ cameraOrbitIn(current_zoom_fraction * mCameraOffsetDefault.magVec());
+ }
+ else
+ {
+ F32 current_zoom_fraction = (F32)mCameraFocusOffsetTarget.magVec();
+ cameraOrbitIn(current_zoom_fraction * (1.f - pow(ROOT_ROOT_TWO, clicks)));
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getCameraMinOffGround()
+//-----------------------------------------------------------------------------
+F32 LLAgent::getCameraMinOffGround()
+{
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ return 0.f;
+ }
+ else
+ {
+ if (LLViewerCamera::sDisableCameraConstraints)
+ {
+ return -1000.f;
+ }
+ else
+ {
+ return 0.5f;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// resetCamera()
+//-----------------------------------------------------------------------------
+void LLAgent::resetCamera()
+{
+ // Remove any pitch from the avatar
+ LLVector3 at = mFrameAgent.getAtAxis();
+ at.mV[VZ] = 0.f;
+ at.normVec();
+ gAgent.resetAxes(at);
+ // have to explicitly clear field of view zoom now
+ mCameraFOVZoomFactor = 0.f;
+
+ updateCamera();
+}
+
+//-----------------------------------------------------------------------------
+// changeCameraToMouselook()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToMouselook(BOOL animate)
+{
+ // visibility changes at end of animation
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ // unpause avatar animation
+ mPauseRequest = NULL;
+
+ gCurrentToolset = gMouselookToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", TRUE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ if (mAvatarObject)
+ {
+ mAvatarObject->stopMotion( ANIM_AGENT_BODY_NOISE );
+ mAvatarObject->stopMotion( ANIM_AGENT_BREATHE_ROT );
+ }
+
+ //gViewerWindow->stopGrab();
+ gSelectMgr->deselectAll();
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+
+ if( mCameraMode != CAMERA_MODE_MOUSELOOK )
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_MOUSELOOK;
+ U32 old_flags = mControlFlags;
+ setControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// changeCameraToDefault()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToDefault()
+{
+ if (LLFollowCamMgr::getActiveFollowCamParams())
+ {
+ changeCameraToFollow();
+ }
+ else
+ {
+ changeCameraToThirdPerson();
+ }
+}
+
+
+// Ventrella
+//-----------------------------------------------------------------------------
+// changeCameraToFollow()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToFollow(BOOL animate)
+{
+ if( mCameraMode != CAMERA_MODE_FOLLOW )
+ {
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ animate = FALSE;
+ }
+ startCameraAnimation();
+
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_FOLLOW;
+
+ // bang-in the current focus, position, and up vector of the follow cam
+ mFollowCam.reset( mCameraPositionAgent, gCamera->getPointOfInterest(), LLVector3::z_axis );
+
+ if (gBasicToolset)
+ {
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+
+ if (mAvatarObject)
+ {
+ mAvatarObject->mPelvisp->setPosition(LLVector3::zero);
+ mAvatarObject->startMotion( ANIM_AGENT_BODY_NOISE );
+ mAvatarObject->startMotion( ANIM_AGENT_BREATHE_ROT );
+ }
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", FALSE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ // unpause avatar animation
+ mPauseRequest = NULL;
+
+ U32 old_flags = mControlFlags;
+ clearControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ //RN: this doesn't seem to be necessary and destroys the UE for script-driven cameras
+ //gViewerWindow->setKeyboardFocus( NULL, NULL );
+ //gViewerWindow->setMouseCapture( NULL, NULL );
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// changeCameraToThirdPerson()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToThirdPerson(BOOL animate)
+{
+//printf( "changeCameraToThirdPerson\n" );
+
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ mCameraZoomFraction = INITIAL_ZOOM_FRACTION;
+
+ if (mAvatarObject)
+ {
+ mAvatarObject->mPelvisp->setPosition(LLVector3::zero);
+ mAvatarObject->startMotion( ANIM_AGENT_BODY_NOISE );
+ mAvatarObject->startMotion( ANIM_AGENT_BREATHE_ROT );
+ }
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", FALSE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ LLVector3 at_axis;
+
+ // unpause avatar animation
+ mPauseRequest = NULL;
+
+ if( mCameraMode != CAMERA_MODE_THIRD_PERSON )
+ {
+ if (gBasicToolset)
+ {
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+
+ mCameraLag.clearVec();
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ mCurrentCameraDistance = MIN_CAMERA_DISTANCE;
+ mTargetCameraDistance = MIN_CAMERA_DISTANCE;
+ animate = FALSE;
+ }
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_THIRD_PERSON;
+ U32 old_flags = mControlFlags;
+ clearControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ //RN: this doesn't seem to be necessary and destroys the UE for script-driven cameras
+ //gViewerWindow->setKeyboardFocus( NULL, NULL );
+ //gViewerWindow->setMouseCapture( NULL, NULL );
+ }
+
+ // Remove any pitch from the avatar
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion obj_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ at_axis = gCamera->getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~obj_rot);
+ }
+ else
+ {
+ at_axis = mFrameAgent.getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis);
+ }
+
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// changeCameraToCustomizeAvatar()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToCustomizeAvatar(BOOL animate)
+{
+ setControlFlags(AGENT_CONTROL_STAND_UP); // force stand up
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ if (gFaceEditToolset)
+ {
+ gCurrentToolset = gFaceEditToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", FALSE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+
+ // Remove any pitch from the avatar
+ LLVector3 at = mFrameAgent.getAtAxis();
+ at.mV[VZ] = 0.f;
+ at.normVec();
+ gAgent.resetAxes(at);
+
+ if( mCameraMode != CAMERA_MODE_CUSTOMIZE_AVATAR )
+ {
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_CUSTOMIZE_AVATAR;
+ U32 old_flags = mControlFlags;
+ clearControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ gViewerWindow->setMouseCapture( NULL, NULL );
+
+ LLVOAvatar::onCustomizeStart();
+ }
+
+ if (animate && !mAvatarObject.isNull())
+ {
+ sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_START);
+ LLMotion* turn_motion = mAvatarObject->findMotion(ANIM_AGENT_CUSTOMIZE);
+ if (turn_motion)
+ {
+ mAnimationDuration = turn_motion->getDuration() + CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP;
+ }
+ else
+ {
+ mAnimationDuration = gSavedSettings.getF32("ZoomTime");
+ }
+ gAgent.setFocusGlobal(LLVector3d::zero);
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+
+}
+
+
+//
+// Focus point management
+//
+
+//-----------------------------------------------------------------------------
+// startCameraAnimation()
+//-----------------------------------------------------------------------------
+void LLAgent::startCameraAnimation()
+{
+ mAnimationCameraStartGlobal = getCameraPositionGlobal();
+ mAnimationFocusStartGlobal = mFocusGlobal;
+ mAnimationTimer.reset();
+ mCameraAnimating = TRUE;
+ mAnimationDuration = gSavedSettings.getF32("ZoomTime");
+}
+
+//-----------------------------------------------------------------------------
+// stopCameraAnimation()
+//-----------------------------------------------------------------------------
+void LLAgent::stopCameraAnimation()
+{
+ mCameraAnimating = FALSE;
+}
+
+void LLAgent::clearFocusObject()
+{
+ if (mFocusObject.notNull())
+ {
+ startCameraAnimation();
+
+ setFocusObject(NULL);
+ mFocusObjectOffset.clearVec();
+ }
+}
+
+void LLAgent::setFocusObject(LLViewerObject* object)
+{
+ if (mFocusObject.notNull() &&
+ mFocusObject->mDrawable.notNull() &&
+ mFocusObject->getPCode() == LL_PCODE_VOLUME &&
+ mFocusObject != object)
+ {
+ LLPointer<LLViewerObject> fade_object_ptr(mFocusObject);
+
+ if (fade_object_ptr.notNull() && mFadeObjects.find(fade_object_ptr) == mFadeObjects.end())
+ {
+ mFadeObjects.insert(fade_object_ptr);
+ }
+ }
+
+ mFocusObject = object;
+}
+
+// Focus on a point, but try to keep camera position stable.
+//-----------------------------------------------------------------------------
+// setFocusGlobal()
+//-----------------------------------------------------------------------------
+void LLAgent::setFocusGlobal(const LLVector3d& focus, const LLUUID &object_id)
+{
+ setFocusObject(gObjectList.findObject(object_id));
+ LLVector3d old_focus = mFocusTargetGlobal;
+ LLViewerObject *focus_obj = mFocusObject;
+
+ // if focus has changed
+ if (old_focus != focus)
+ {
+ if (focus.isExactlyZero())
+ {
+ if (!mAvatarObject.isNull())
+ {
+ mFocusTargetGlobal = getPosGlobalFromAgent(mAvatarObject->mHeadp->getWorldPosition());
+ }
+ else
+ {
+ mFocusTargetGlobal = getPositionGlobal();
+ }
+ mCameraFocusOffsetTarget = getCameraPositionGlobal() - mFocusTargetGlobal;
+ mCameraFocusOffset = mCameraFocusOffsetTarget;
+ setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+ else
+ {
+ mFocusTargetGlobal = focus;
+ if (!focus_obj)
+ {
+ mCameraFOVZoomFactor = 0.f;
+ }
+
+ mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(mCameraVirtualPositionAgent) - mFocusTargetGlobal;
+
+ startCameraAnimation();
+
+ if (focus_obj)
+ {
+ if (focus_obj->isAvatar())
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, focus_obj);
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, focus_obj, (getPosAgentFromGlobal(focus) - focus_obj->getRenderPosition()) * ~focus_obj->getRenderRotation());
+ }
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, NULL, getPosAgentFromGlobal(mFocusTargetGlobal));
+ }
+ }
+ }
+ else // focus == mFocusTargetGlobal
+ {
+ if (focus.isExactlyZero())
+ {
+ if (!mAvatarObject.isNull())
+ {
+ mFocusTargetGlobal = getPosGlobalFromAgent(mAvatarObject->mHeadp->getWorldPosition());
+ }
+ else
+ {
+ mFocusTargetGlobal = getPositionGlobal();
+ }
+ }
+ mCameraFocusOffsetTarget = (getCameraPositionGlobal() - mFocusTargetGlobal) / (1.f + mCameraFOVZoomFactor);;
+ mCameraFocusOffset = mCameraFocusOffsetTarget;
+ }
+
+ if (mFocusObject.notNull())
+ {
+ // for attachments, make offset relative to avatar, not the attachment
+ if (mFocusObject->isAttachment())
+ {
+ while (!mFocusObject->isAvatar())
+ {
+ mFocusObject = (LLViewerObject*) mFocusObject->getParent();
+ }
+ setFocusObject((LLViewerObject*)mFocusObject);
+ }
+ updateFocusOffset();
+ }
+}
+
+// Used for avatar customization
+//-----------------------------------------------------------------------------
+// setCameraPosAndFocusGlobal()
+//-----------------------------------------------------------------------------
+void LLAgent::setCameraPosAndFocusGlobal(const LLVector3d& camera_pos, const LLVector3d& focus, const LLUUID &object_id)
+{
+ LLVector3d old_focus = mFocusTargetGlobal;
+
+ F64 focus_delta_squared = (old_focus - focus).magVecSquared();
+ const F64 ANIM_EPSILON_SQUARED = 0.0001;
+ if( focus_delta_squared > ANIM_EPSILON_SQUARED )
+ {
+ startCameraAnimation();
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
+ {
+ // Compensate for the fact that the camera has already been offset to make room for LLFloaterCustomize.
+ mAnimationCameraStartGlobal -= LLVector3d(gCamera->getLeftAxis() * calcCustomizeAvatarUIOffset( mAnimationCameraStartGlobal ));
+ }
+ }
+
+ //gCamera->setOrigin( gAgent.getPosAgentFromGlobal( camera_pos ) );
+ setFocusObject(gObjectList.findObject(object_id));
+ mFocusTargetGlobal = focus;
+ mCameraFocusOffsetTarget = camera_pos - focus;
+ mCameraFocusOffset = mCameraFocusOffsetTarget;
+
+ if (mFocusObject)
+ {
+ if (mFocusObject->isAvatar())
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, mFocusObject);
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, mFocusObject, (getPosAgentFromGlobal(focus) - mFocusObject->getRenderPosition()) * ~mFocusObject->getRenderRotation());
+ }
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, NULL, getPosAgentFromGlobal(mFocusTargetGlobal));
+ }
+
+ if( mCameraAnimating )
+ {
+ const F64 ANIM_METERS_PER_SECOND = 10.0;
+ const F64 MIN_ANIM_SECONDS = 0.5;
+ F64 anim_duration = llmax( MIN_ANIM_SECONDS, sqrt(focus_delta_squared) / ANIM_METERS_PER_SECOND );
+ setAnimationDuration( (F32)anim_duration );
+ }
+
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// setSitCamera()
+//-----------------------------------------------------------------------------
+void LLAgent::setSitCamera(const LLUUID &object_id, const LLVector3 &camera_pos, const LLVector3 &camera_focus)
+{
+ BOOL camera_enabled = !object_id.isNull();
+
+ if (camera_enabled)
+ {
+ LLViewerObject *reference_object = gObjectList.findObject(object_id);
+ if (reference_object)
+ {
+ //convert to root object relative?
+ mSitCameraPos = camera_pos;
+ mSitCameraFocus = camera_focus;
+ mSitCameraReferenceObject = reference_object;
+ mSitCameraEnabled = TRUE;
+ }
+ }
+ else
+ {
+ mSitCameraPos.clearVec();
+ mSitCameraFocus.clearVec();
+ mSitCameraReferenceObject = NULL;
+ mSitCameraEnabled = FALSE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setFocusOnAvatar()
+//-----------------------------------------------------------------------------
+void LLAgent::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate)
+{
+ if (focus_on_avatar != mFocusOnAvatar)
+ {
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ stopCameraAnimation();
+ }
+ }
+
+ //RN: when focused on the avatar, we're not "looking" at it
+ // looking implies intent while focusing on avatar means
+ // you're just walking around with a camera on you...eesh.
+ if (focus_on_avatar && !mFocusOnAvatar)
+ {
+ setFocusGlobal(LLVector3d::zero);
+ mCameraFOVZoomFactor = 0.f;
+ if (mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ LLVector3 at_axis;
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion obj_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ at_axis = gCamera->getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~obj_rot);
+ }
+ else
+ {
+ at_axis = gCamera->getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis);
+ }
+ }
+ }
+
+ mFocusOnAvatar = focus_on_avatar;
+}
+
+//-----------------------------------------------------------------------------
+// heardChat()
+//-----------------------------------------------------------------------------
+void LLAgent::heardChat(const LLChat& chat)
+{
+ if (chat.mChatType == CHAT_TYPE_START
+ || chat.mChatType == CHAT_TYPE_STOP)
+ {
+ return;
+ }
+
+ mLastChatterID = chat.mFromID;
+ mChatTimer.reset();
+
+ mNearChatRadius = CHAT_NORMAL_RADIUS / 2.f;
+}
+
+//-----------------------------------------------------------------------------
+// lookAtLastChat()
+//-----------------------------------------------------------------------------
+void LLAgent::lookAtLastChat()
+{
+ // Block if camera is animating or not in normal third person camera mode
+ if (mCameraAnimating || !cameraThirdPerson())
+ {
+ return;
+ }
+
+ LLViewerObject *chatter = gObjectList.findObject(mLastChatterID);
+ if (chatter)
+ {
+ LLVector3 delta_pos;
+ if (chatter->isAvatar())
+ {
+ LLVOAvatar *chatter_av = (LLVOAvatar*)chatter;
+ if (!mAvatarObject.isNull() && chatter_av->mHeadp)
+ {
+ delta_pos = chatter_av->mHeadp->getWorldPosition() - mAvatarObject->mHeadp->getWorldPosition();
+ }
+ else
+ {
+ delta_pos = chatter->getPositionAgent() - getPositionAgent();
+ }
+ delta_pos.normVec();
+
+ setControlFlags(AGENT_CONTROL_STOP);
+
+ changeCameraToThirdPerson();
+
+ LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition();
+ LLVector3 left = delta_pos % LLVector3::z_axis;
+ left.normVec();
+ LLVector3 up = left % delta_pos;
+ up.normVec();
+ new_camera_pos -= delta_pos * 0.4f;
+ new_camera_pos += left * 0.3f;
+ new_camera_pos += up * 0.2f;
+ if (chatter_av->mHeadp)
+ {
+ setFocusGlobal(getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), mLastChatterID);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition());
+ }
+ else
+ {
+ setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
+ }
+ setFocusOnAvatar(FALSE, TRUE);
+ }
+ else
+ {
+ delta_pos = chatter->getRenderPosition() - getPositionAgent();
+ delta_pos.normVec();
+
+ setControlFlags(AGENT_CONTROL_STOP);
+
+ changeCameraToThirdPerson();
+
+ LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition();
+ LLVector3 left = delta_pos % LLVector3::z_axis;
+ left.normVec();
+ LLVector3 up = left % delta_pos;
+ up.normVec();
+ new_camera_pos -= delta_pos * 0.4f;
+ new_camera_pos += left * 0.3f;
+ new_camera_pos += up * 0.2f;
+
+ setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
+ setFocusOnAvatar(FALSE, TRUE);
+ }
+ }
+}
+
+const F32 SIT_POINT_EXTENTS = 0.2f;
+
+// Grabs current position
+void LLAgent::setStartPosition(U32 location_id)
+{
+ LLViewerObject *object;
+
+ if ( !(gAgentID == LLUUID::null) )
+ {
+ // we've got an ID for an agent viewerobject
+ object = gObjectList.findObject(gAgentID);
+ if (object)
+ {
+ // we've got the viewer object
+ // Sometimes the agent can be velocity interpolated off of
+ // this simulator. Clamp it to the region the agent is
+ // in, a little bit in on each side.
+ const F32 INSET = 0.5f; //meters
+ const F32 REGION_WIDTH = gWorldPointer->getRegionWidthInMeters();
+
+ LLVector3 agent_pos = getPositionAgent();
+
+ if (mAvatarObject)
+ {
+ // the z height is at the agent's feet
+ agent_pos.mV[VZ] -= 0.5f * mAvatarObject->mBodySize.mV[VZ];
+ }
+
+ agent_pos.mV[VX] = llclamp( agent_pos.mV[VX], INSET, REGION_WIDTH - INSET );
+ agent_pos.mV[VY] = llclamp( agent_pos.mV[VY], INSET, REGION_WIDTH - INSET );
+
+ // Don't let them go below ground, or too high.
+ agent_pos.mV[VZ] = llclamp( agent_pos.mV[VZ],
+ mRegionp->getLandHeightRegion( agent_pos ),
+ gWorldPointer->getRegionMaxHeight() );
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetStartLocationRequest);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->nextBlockFast( _PREHASH_StartLocationData);
+ // corrected by sim
+ msg->addStringFast(_PREHASH_SimName, "");
+ msg->addU32Fast(_PREHASH_LocationID, location_id);
+ msg->addVector3Fast(_PREHASH_LocationPos, agent_pos);
+ msg->addVector3Fast(_PREHASH_LocationLookAt,mFrameAgent.getAtAxis());
+
+ // Reliable only helps when setting home location. Last
+ // location is sent on quit, and we don't have time to ack
+ // the packets.
+ msg->sendReliable(mRegionp->getHost());
+
+ const U32 HOME_INDEX = 1;
+ if( HOME_INDEX == location_id )
+ {
+ setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
+ }
+ }
+ else
+ {
+ llinfos << "setStartPosition - Can't find agent viewerobject id " << gAgentID << llendl;
+ }
+ }
+}
+
+void LLAgent::requestStopMotion( LLMotion* motion )
+{
+ // Notify all avatars that a motion has stopped.
+ // This is needed to clear the animation state bits
+ LLUUID anim_state = motion->getID();
+
+ // if motion is not looping, it could have stopped by running out of time
+ // so we need to tell the server this
+// llinfos << "Sending stop for motion " << motion->getName() << llendl;
+ sendAnimationRequest( anim_state, ANIM_REQUEST_STOP );
+
+ // handle automatic state transitions (based on completion of animation playback)
+ if (anim_state == ANIM_AGENT_STANDUP)
+ {
+ // send stand up command
+ setControlFlags(AGENT_CONTROL_FINISH_ANIM);
+
+ // now trigger dusting self off animation
+ if (mAvatarObject.notNull() && !mAvatarObject->mBelowWater && rand() % 3 == 0)
+ sendAnimationRequest( ANIM_AGENT_BRUSH, ANIM_REQUEST_START );
+ }
+ else if (anim_state == ANIM_AGENT_PRE_JUMP || anim_state == ANIM_AGENT_LAND || anim_state == ANIM_AGENT_MEDIUM_LAND)
+ {
+ setControlFlags(AGENT_CONTROL_FINISH_ANIM);
+ }
+}
+
+BOOL LLAgent::isGodlike() const
+{
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+ if(mAdminOverride) return TRUE;
+ return mGodLevel > GOD_NOT;
+#endif
+}
+
+U8 LLAgent::getGodLevel() const
+{
+#ifdef HACKED_GODLIKE_VIEWER
+ return GOD_MAINTENANCE;
+#else
+ if(mAdminOverride) return GOD_FULL;
+ return mGodLevel;
+#endif
+}
+
+
+void LLAgent::buildFullname(std::string& name) const
+{
+ if (mAvatarObject)
+ {
+ name = mAvatarObject->getFullname();
+ }
+}
+
+void LLAgent::buildFullnameAndTitle(std::string& name) const
+{
+ if (isGroupMember())
+ {
+ name = mGroupTitle;
+ name += ' ';
+ }
+ else
+ {
+ name.erase(0, name.length());
+ }
+
+ if (mAvatarObject)
+ {
+ name += mAvatarObject->getFullname();
+ }
+}
+
+BOOL LLAgent::isInGroup(const LLUUID& group_id) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// This implementation should mirror LLAgentInfo::hasPowerInGroup
+BOOL LLAgent::hasPowerInGroup(const LLUUID& group_id, U64 power) const
+{
+ // GP_NO_POWERS can also mean no power is enough to grant an ability.
+ if (GP_NO_POWERS == power) return FALSE;
+
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ return (BOOL)((mGroups.get(i).mPowers & power) > 0);
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::hasPowerInActiveGroup(U64 power) const
+{
+ return (mGroupID.notNull() && (hasPowerInGroup(mGroupID, power)));
+}
+
+U64 LLAgent::getPowerInGroup(const LLUUID& group_id) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ return (mGroups.get(i).mPowers);
+ }
+ }
+
+ return GP_NO_POWERS;
+}
+
+BOOL LLAgent::getGroupData(const LLUUID& group_id, LLGroupData& data) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ data = mGroups.get(i);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+S32 LLAgent::getGroupContribution(const LLUUID& group_id) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ S32 contribution = mGroups.get(i).mContribution;
+ return contribution;
+ }
+ }
+ return 0;
+}
+
+BOOL LLAgent::setGroupContribution(const LLUUID& group_id, S32 contribution)
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ mGroups.get(i).mContribution = contribution;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("SetGroupContribution");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgentID);
+ msg->addUUID("SessionID", gAgentSessionID);
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", group_id);
+ msg->addS32("Contribution", contribution);
+ sendReliableMessage();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::setGroupAcceptNotices(const LLUUID& group_id, BOOL accept_notices)
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ mGroups.get(i).mAcceptNotices = accept_notices;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("SetGroupAcceptNotices");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgentID);
+ msg->addUUID("SessionID", gAgentSessionID);
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", group_id);
+ msg->addBOOL("AcceptNotices", accept_notices);
+ sendReliableMessage();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// utility to build a location string
+void LLAgent::buildLocationString(std::string& str)
+{
+ const LLVector3& agent_pos_region = getPositionAgent();
+ S32 pos_x = S32(agent_pos_region.mV[VX]);
+ S32 pos_y = S32(agent_pos_region.mV[VY]);
+ S32 pos_z = S32(agent_pos_region.mV[VZ]);
+
+ // Round the numbers based on the velocity
+ LLVector3 agent_velocity = getVelocity();
+ F32 velocity_mag_sq = agent_velocity.magVecSquared();
+
+ const F32 FLY_CUTOFF = 6.f; // meters/sec
+ const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF;
+ const F32 WALK_CUTOFF = 1.5f; // meters/sec
+ const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF;
+
+ if (velocity_mag_sq > FLY_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 4;
+ pos_y -= pos_y % 4;
+ }
+ else if (velocity_mag_sq > WALK_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 2;
+ pos_y -= pos_y % 2;
+ }
+
+ // create a defult name and description for the landmark
+ std::string buffer;
+ if( !strcmp("", gParcelMgr->getAgentParcelName()) )
+ {
+ // the parcel doesn't have a name
+ buffer = llformat("%.32s (%d, %d, %d)",
+ getRegion()->getName().c_str(),
+ pos_x, pos_y, pos_z);
+ }
+ else
+ {
+ // the parcel has a name, so include it in the landmark name
+ buffer = llformat("%.32s, %.32s (%d, %d, %d)",
+ gParcelMgr->getAgentParcelName(),
+ getRegion()->getName().c_str(),
+ pos_x, pos_y, pos_z);
+ }
+ str = buffer;
+}
+
+LLQuaternion LLAgent::getHeadRotation()
+{
+ if (mAvatarObject.isNull() || !mAvatarObject->mPelvisp || !mAvatarObject->mHeadp)
+ {
+ return LLQuaternion::DEFAULT;
+ }
+
+ if (!gAgent.cameraMouselook())
+ {
+ return mAvatarObject->getRotation();
+ }
+
+ // We must be in mouselook
+ LLVector3 look_dir( gCamera->getAtAxis() );
+ LLVector3 up = look_dir % mFrameAgent.getLeftAxis();
+ LLVector3 left = up % look_dir;
+
+ LLQuaternion rot(look_dir, left, up);
+ if (mAvatarObject->getParent())
+ {
+ rot = rot * ~mAvatarObject->getParent()->getRotation();
+ }
+
+ return rot;
+}
+
+void LLAgent::sendAnimationRequests(LLDynamicArray<LLUUID> &anim_ids, EAnimRequest request)
+{
+ if (gAgentID.isNull())
+ {
+ return;
+ }
+
+ S32 num_valid_anims = 0;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentAnimation);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ for (S32 i = 0; i < anim_ids.count(); i++)
+ {
+ if (anim_ids[i].isNull())
+ {
+ continue;
+ }
+ msg->nextBlockFast(_PREHASH_AnimationList);
+ msg->addUUIDFast(_PREHASH_AnimID, (anim_ids[i]) );
+ msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
+ num_valid_anims++;
+ }
+
+ if (num_valid_anims)
+ {
+ sendReliableMessage();
+ }
+}
+
+void LLAgent::sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request)
+{
+ if (gAgentID.isNull() || anim_id.isNull() || !mRegionp)
+ {
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentAnimation);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ msg->nextBlockFast(_PREHASH_AnimationList);
+ msg->addUUIDFast(_PREHASH_AnimID, (anim_id) );
+ msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
+
+ sendReliableMessage();
+}
+
+void LLAgent::friendsChanged()
+{
+ LLCollectProxyBuddies collector;
+ LLAvatarTracker::instance().applyFunctor(collector);
+ mProxyForAgents = collector.mProxy;
+}
+
+BOOL LLAgent::isGrantedProxy(const LLPermissions& perm)
+{
+ return (mProxyForAgents.count(perm.getOwner()) > 0);
+}
+
+BOOL LLAgent::allowOperation(PermissionBit op,
+ const LLPermissions& perm,
+ U64 group_proxy_power,
+ U8 god_minimum)
+{
+ // Check god level.
+ if (getGodLevel() >= god_minimum) return TRUE;
+
+ if (!perm.isOwned()) return FALSE;
+
+ // A group member with group_proxy_power can act as owner.
+ BOOL is_group_owned;
+ LLUUID owner_id;
+ perm.getOwnership(owner_id, is_group_owned);
+ LLUUID group_id(perm.getGroup());
+ LLUUID agent_proxy(getID());
+
+ if (is_group_owned)
+ {
+ if (hasPowerInGroup(group_id, group_proxy_power))
+ {
+ // Let the member assume the group's id for permission requests.
+ agent_proxy = owner_id;
+ }
+ }
+ else
+ {
+ // Check for granted mod permissions.
+ if ((PERM_OWNER != op) && isGrantedProxy(perm))
+ {
+ agent_proxy = owner_id;
+ }
+ }
+
+ // This is the group id to use for permission requests.
+ // Only group members may use this field.
+ LLUUID group_proxy = LLUUID::null;
+ if (group_id.notNull() && isInGroup(group_id))
+ {
+ group_proxy = group_id;
+ }
+
+ // We now have max ownership information.
+ if (PERM_OWNER == op)
+ {
+ // This this was just a check for ownership, we can now return the answer.
+ return (agent_proxy == owner_id);
+ }
+
+ return perm.allowOperationBy(op, agent_proxy, group_proxy);
+}
+
+
+void LLAgent::getName(LLString& name)
+{
+ // Note: assumes that name points to a buffer of at least DB_FULL_NAME_BUF_SIZE bytes.
+ name.clear();
+
+ if (mAvatarObject)
+ {
+ LLNameValue *first_nv = mAvatarObject->getNVPair("FirstName");
+ LLNameValue *last_nv = mAvatarObject->getNVPair("LastName");
+ if (first_nv && last_nv)
+ {
+ name = first_nv->printData() + " " + last_nv->printData();
+ }
+ else
+ {
+ llwarns << "Agent is missing FirstName and/or LastName nv pair." << llendl;
+ }
+ }
+ else
+ {
+ name = gSavedSettings.getString("FirstName") + " " + gSavedSettings.getString("LastName");
+ }
+}
+
+const LLColor4 &LLAgent::getEffectColor()
+{
+ return mEffectColor;
+}
+
+void LLAgent::setEffectColor(const LLColor4 &color)
+{
+ mEffectColor = color;
+}
+
+void LLAgent::initOriginGlobal(const LLVector3d &origin_global)
+{
+ mAgentOriginGlobal = origin_global;
+}
+
+void update_group_floaters(const LLUUID& group_id)
+{
+ // *HACK: added to do a live update of the groups floater if it is
+ // open.
+ LLFloaterGroups* fg = LLFloaterGroups::getInstance(gAgent.getID());
+ if(fg)
+ {
+ fg->reset();
+ }
+
+ LLFloaterGroupInfo::refreshGroup(group_id);
+
+ // update avatar info
+ LLFloaterAvatarInfo* fa = LLFloaterAvatarInfo::getInstance(gAgent.getID());
+ if(fa)
+ {
+ fa->resetGroupList();
+ }
+
+ if (gIMView)
+ {
+ // update the talk view
+ gIMView->refresh();
+ }
+}
+
+// static
+void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ if (agent_id != gAgentID)
+ {
+ llwarns << "processAgentDropGroup for agent other than me" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+
+ // Remove the group if it already exists remove it and add the new data to pick up changes.
+ LLGroupData gd;
+ gd.mID = group_id;
+ S32 index = gAgent.mGroups.find(gd);
+ if (index != -1)
+ {
+ gAgent.mGroups.remove(index);
+ if (gAgent.getGroupID() == group_id)
+ {
+ gAgent.mGroupID.setNull();
+ gAgent.mGroupPowers = 0;
+ gAgent.mGroupName[0] = '\0';
+ gAgent.mGroupTitle[0] = '\0';
+ }
+
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ gGroupMgr->clearGroupData(group_id);
+ // close the floater for this group, if any.
+ LLFloaterGroupInfo::closeGroup(group_id);
+ // refresh the group panel of the search window, if necessary.
+ LLFloaterDirectory::refreshGroup(group_id);
+ }
+ else
+ {
+ llwarns << "processAgentDropGroup, agent is not part of group " << group_id << llendl;
+ }
+}
+
+// static
+void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ if (agent_id != gAgentID)
+ {
+ llwarns << "processAgentGroupDataUpdate for agent other than me" << llendl;
+ return;
+ }
+
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
+ LLGroupData group;
+ S32 index = -1;
+ bool need_floater_update = false;
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group.mID, i);
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupInsigniaID, group.mInsigniaID, i);
+ msg->getU64(_PREHASH_GroupData, "GroupPowers", group.mPowers, i);
+ msg->getBOOL(_PREHASH_GroupData, "AcceptNotices", group.mAcceptNotices, i);
+ msg->getS32(_PREHASH_GroupData, "Contribution", group.mContribution, i);
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, group_name, i);
+ group.mName.assign(group_name);
+
+ if(group.mID.notNull())
+ {
+ need_floater_update = true;
+ // Remove the group if it already exists remove it and add the new data to pick up changes.
+ index = gAgent.mGroups.find(group);
+ if (index != -1)
+ {
+ gAgent.mGroups.remove(index);
+ }
+ gAgent.mGroups.put(group);
+ }
+ if (need_floater_update)
+ {
+ update_group_floaters(group.mID);
+ }
+ }
+
+}
+
+// static
+void LLAgent::processAgentDataUpdate(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ if (agent_id != gAgentID)
+ {
+ llwarns << "processAgentDataUpdate for agent other than me" << llendl;
+ return;
+ }
+
+ msg->getStringFast(_PREHASH_AgentData, _PREHASH_GroupTitle, DB_GROUP_TITLE_BUF_SIZE, gAgent.mGroupTitle);
+ LLUUID active_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_ActiveGroupID, active_id);
+
+
+ if(active_id.notNull())
+ {
+ gAgent.mGroupID = active_id;
+ msg->getU64(_PREHASH_AgentData, "GroupPowers", gAgent.mGroupPowers);
+ msg->getString(_PREHASH_AgentData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, gAgent.mGroupName);
+ }
+ else
+ {
+ gAgent.mGroupID.setNull();
+ gAgent.mGroupPowers = 0;
+ gAgent.mGroupName[0] = '\0';
+ }
+
+ update_group_floaters(active_id);
+
+ gAgent.fireEvent(new LLEvent(&gAgent, "new group"), "");
+}
+
+// static
+void LLAgent::processScriptControlChange(LLMessageSystem *msg, void **)
+{
+ S32 block_count = msg->getNumberOfBlocks("Data");
+ for (S32 block_index = 0; block_index < block_count; block_index++)
+ {
+ BOOL take_controls;
+ U32 controls;
+ BOOL passon;
+ U32 i;
+ msg->getBOOL("Data", "TakeControls", take_controls, block_index);
+ if (take_controls)
+ {
+ // take controls
+ msg->getU32("Data", "Controls", controls, block_index );
+ msg->getBOOL("Data", "PassToAgent", passon, block_index );
+ U32 total_count = 0;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]++;
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]++;
+ }
+ total_count++;
+ }
+ }
+
+ // Any control taken? If so, might be first time.
+ if (total_count > 0)
+ {
+ LLFirstUse::useOverrideKeys();
+ }
+ }
+ else
+ {
+ // release controls
+ msg->getU32("Data", "Controls", controls, block_index );
+ msg->getBOOL("Data", "PassToAgent", passon, block_index );
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]--;
+ if (gAgent.mControlsTakenPassedOnCount[i] < 0)
+ {
+ gAgent.mControlsTakenPassedOnCount[i] = 0;
+ }
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]--;
+ if (gAgent.mControlsTakenCount[i] < 0)
+ {
+ gAgent.mControlsTakenCount[i] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+// static
+void LLAgent::processControlTake(LLMessageSystem *msg, void **)
+{
+ U32 controls;
+ msg->getU32("Data", "Controls", controls );
+ U32 passon;
+ msg->getBOOL("Data", "PassToAgent", passon );
+
+ S32 i;
+ S32 total_count = 0;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]++;
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]++;
+ }
+ total_count++;
+ }
+ }
+
+ // Any control taken? If so, might be first time.
+ if (total_count > 0)
+ {
+ LLFirstUse::useOverrideKeys();
+ }
+}
+
+// static
+void LLAgent::processControlRelease(LLMessageSystem *msg, void **)
+{
+ U32 controls;
+ msg->getU32("Data", "Controls", controls );
+ U32 passon;
+ msg->getBOOL("Data", "PassToAgent", passon );
+
+ S32 i;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]--;
+ if (gAgent.mControlsTakenPassedOnCount[i] < 0)
+ {
+ gAgent.mControlsTakenPassedOnCount[i] = 0;
+ }
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]--;
+ if (gAgent.mControlsTakenCount[i] < 0)
+ {
+ gAgent.mControlsTakenCount[i] = 0;
+ }
+ }
+ }
+ }
+}
+*/
+
+//static
+void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
+{
+ gAgent.mNumPendingQueries--;
+
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (!avatarp || avatarp->isDead())
+ {
+ llwarns << "No avatar for user in cached texture update!" << llendl;
+ return;
+ }
+
+ if (gAgent.cameraCustomizeAvatar())
+ {
+ // ignore baked textures when in customize mode
+ return;
+ }
+
+ S32 query_id;
+ mesgsys->getS32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, query_id);
+
+ S32 num_texture_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_WearableData);
+
+
+ S32 num_results = 0;
+ for (S32 texture_block = 0; texture_block < num_texture_blocks; texture_block++)
+ {
+ LLUUID texture_id;
+ U8 texture_index;
+
+ mesgsys->getUUIDFast(_PREHASH_WearableData, _PREHASH_TextureID, texture_id, texture_block);
+ mesgsys->getU8Fast(_PREHASH_WearableData, _PREHASH_TextureIndex, texture_index, texture_block);
+
+ if (texture_id.notNull()
+ && (S32)texture_index < BAKED_TEXTURE_COUNT
+ && gAgent.mActiveCacheQueries[ texture_index ] == query_id)
+ {
+ //llinfos << "Received cached texture " << (U32)texture_index << ": " << texture_id << llendl;
+ avatarp->setCachedBakedTexture((LLVOAvatar::ETextureIndex)LLVOAvatar::sBakedTextureIndices[texture_index], texture_id);
+ //avatarp->setTETexture( LLVOAvatar::sBakedTextureIndices[texture_index], texture_id );
+ gAgent.mActiveCacheQueries[ texture_index ] = 0;
+ num_results++;
+ }
+ }
+
+ llinfos << "Received cached texture response for " << num_results << " textures." << llendl;
+
+ avatarp->updateMeshTextures();
+
+ if (gAgent.mNumPendingQueries == 0)
+ {
+ // RN: not sure why composites are disabled at this point
+ avatarp->setCompositeUpdatesEnabled(TRUE);
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+BOOL LLAgent::anyControlGrabbed() const
+{
+ U32 i;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (gAgent.mControlsTakenCount[i] > 0)
+ return TRUE;
+ if (gAgent.mControlsTakenPassedOnCount[i] > 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::isControlGrabbed(S32 control_index) const
+{
+ return mControlsTakenCount[control_index] > 0;
+}
+
+void LLAgent::forceReleaseControls()
+{
+ gMessageSystem->newMessage("ForceScriptControlRelease");
+ gMessageSystem->nextBlock("AgentData");
+ gMessageSystem->addUUID("AgentID", getID());
+ gMessageSystem->addUUID("SessionID", getSessionID());
+ sendReliableMessage();
+}
+
+void LLAgent::setHomePosRegion( const U64& region_handle, const LLVector3& pos_region)
+{
+ mHaveHomePosition = TRUE;
+ mHomeRegionHandle = region_handle;
+ mHomePosRegion = pos_region;
+}
+
+BOOL LLAgent::getHomePosGlobal( LLVector3d* pos_global )
+{
+ if(!mHaveHomePosition)
+ {
+ return FALSE;
+ }
+ F32 x = 0;
+ F32 y = 0;
+ from_region_handle( mHomeRegionHandle, &x, &y);
+ pos_global->setVec( x + mHomePosRegion.mV[VX], y + mHomePosRegion.mV[VY], mHomePosRegion.mV[VZ] );
+ return TRUE;
+}
+
+void LLAgent::clearVisualParams(void *data)
+{
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp)
+ {
+ avatarp->clearVisualParamWeights();
+ avatarp->updateVisualParams();
+ }
+}
+
+//---------------------------------------------------------------------------
+// Teleport
+//---------------------------------------------------------------------------
+
+// teleportCore() - stuff to do on any teleport
+// protected
+bool LLAgent::teleportCore(bool is_local)
+{
+ if(TELEPORT_NONE != mTeleportState)
+ {
+ llwarns << "Attempt to teleport when already teleporting." << llendl;
+ return false;
+ }
+
+ // Don't call LLFirstUse::useTeleport because we don't know
+ // yet if the teleport will succeed. Look in
+ // process_teleport_location_reply
+
+ // close the map and find panels so we can see our destination
+ LLFloaterWorldMap::hide(NULL);
+ LLFloaterDirectory::hide(NULL);
+
+ // Close all pie menus, deselect land, etc.
+ // Don't change the camera until we know teleport succeeded. JC
+ resetView(FALSE);
+
+ // local logic
+ gViewerStats->incStat(LLViewerStats::ST_TELEPORT_COUNT);
+ if (!is_local)
+ {
+ gTeleportDisplay = TRUE;
+ gAgent.setTeleportState( LLAgent::TELEPORT_START );
+ }
+ make_ui_sound("UISndTeleportOut");
+ return true;
+}
+
+void LLAgent::teleportRequest(
+ const U64& region_handle,
+ const LLVector3& pos_local)
+{
+ LLViewerRegion* regionp = getRegion();
+ if(regionp && teleportCore())
+ {
+ llinfos << "TeleportRequest: '" << region_handle << "':" << pos_local
+ << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("TeleportLocationRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU64("RegionHandle", region_handle);
+ msg->addVector3("Position", pos_local);
+ LLVector3 look_at(0,1,0);
+ msg->addVector3("LookAt", look_at);
+ sendReliableMessage();
+ }
+}
+
+// Landmark ID = LLUUID::null means teleport home
+void LLAgent::teleportViaLandmark(const LLUUID& landmark_id)
+{
+ LLViewerRegion *regionp = getRegion();
+ if(regionp && teleportCore())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TeleportLandmarkRequest);
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->addUUIDFast(_PREHASH_LandmarkID, landmark_id);
+ sendReliableMessage();
+ }
+}
+
+void LLAgent::teleportViaLure(const LLUUID& lure_id, BOOL godlike)
+{
+ LLViewerRegion* regionp = getRegion();
+ if(regionp && teleportCore())
+ {
+ U32 teleport_flags = 0x0;
+ if (godlike)
+ {
+ teleport_flags |= TELEPORT_FLAGS_VIA_GODLIKE_LURE;
+ teleport_flags |= TELEPORT_FLAGS_DISABLE_CANCEL;
+ }
+ else
+ {
+ teleport_flags |= TELEPORT_FLAGS_VIA_LURE;
+ }
+
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TeleportLureRequest);
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->addUUIDFast(_PREHASH_LureID, lure_id);
+ msg->addU32("TeleportFlags", teleport_flags);
+ sendReliableMessage();
+ }
+}
+
+
+// James Cook, July 28, 2005
+void LLAgent::teleportCancel()
+{
+ LLViewerRegion* regionp = getRegion();
+ if(regionp)
+ {
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("TeleportCancel");
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ sendReliableMessage();
+ }
+ gTeleportDisplay = FALSE;
+ gAgent.setTeleportState( LLAgent::TELEPORT_CANCELLING );
+}
+
+
+void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
+{
+ LLViewerRegion* regionp = getRegion();
+ LLSimInfo* info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if(regionp && info)
+ {
+ U32 x_pos;
+ U32 y_pos;
+ from_region_handle(info->mHandle, &x_pos, &y_pos);
+ LLVector3 pos_local(
+ (F32)(pos_global.mdV[VX] - x_pos),
+ (F32)(pos_global.mdV[VY] - y_pos),
+ (F32)(pos_global.mdV[VZ]));
+ teleportRequest(info->mHandle, pos_local);
+ }
+ else if(regionp &&
+ teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY])))
+ {
+ llwarns << "Using deprecated teleportlocationrequest." << llendl;
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TeleportLocationRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ msg->nextBlockFast(_PREHASH_Info);
+ F32 width = regionp->getWidth();
+ LLVector3 pos(fmod((F32)pos_global.mdV[VX], width),
+ fmod((F32)pos_global.mdV[VY], width),
+ (F32)pos_global.mdV[VZ]);
+ F32 region_x = (F32)(pos_global.mdV[VX]);
+ F32 region_y = (F32)(pos_global.mdV[VY]);
+ U64 region_handle = to_region_handle_global(region_x, region_y);
+ msg->addU64Fast(_PREHASH_RegionHandle, region_handle);
+ msg->addVector3Fast(_PREHASH_Position, pos);
+ pos.mV[VX] += 1;
+ msg->addVector3Fast(_PREHASH_LookAt, pos);
+ sendReliableMessage();
+ }
+}
+
+void LLAgent::setTeleportState(ETeleportState state)
+{
+ mTeleportState = state;
+ if (mTeleportState > TELEPORT_NONE && gSavedSettings.getBOOL("FreezeTime"))
+ {
+ LLFloaterSnapshot::hide(0);
+ }
+}
+
+void LLAgent::fidget()
+{
+ if (!getAFK())
+ {
+ F32 curTime = mFidgetTimer.getElapsedTimeF32();
+ if (curTime > mNextFidgetTime)
+ {
+ // pick a random fidget anim here
+ S32 oldFidget = mCurrentFidget;
+
+ mCurrentFidget = gLindenLabRandomNumber.llrand(NUM_AGENT_STAND_ANIMS);
+
+ if (mCurrentFidget != oldFidget)
+ {
+ LLAgent::stopFidget();
+
+
+ switch(mCurrentFidget)
+ {
+ case 0:
+ mCurrentFidget = 0;
+ break;
+ case 1:
+ sendAnimationRequest(ANIM_AGENT_STAND_1, ANIM_REQUEST_START);
+ mCurrentFidget = 1;
+ break;
+ case 2:
+ sendAnimationRequest(ANIM_AGENT_STAND_2, ANIM_REQUEST_START);
+ mCurrentFidget = 2;
+ break;
+ case 3:
+ sendAnimationRequest(ANIM_AGENT_STAND_3, ANIM_REQUEST_START);
+ mCurrentFidget = 3;
+ break;
+ case 4:
+ sendAnimationRequest(ANIM_AGENT_STAND_4, ANIM_REQUEST_START);
+ mCurrentFidget = 4;
+ break;
+ }
+ }
+
+ // calculate next fidget time
+ mNextFidgetTime = curTime + gLindenLabRandomNumber.llfrand(MAX_FIDGET_TIME - MIN_FIDGET_TIME) + MIN_FIDGET_TIME;
+ }
+ }
+}
+
+void LLAgent::stopFidget()
+{
+ LLDynamicArray<LLUUID> anims;
+ anims.put(ANIM_AGENT_STAND_1);
+ anims.put(ANIM_AGENT_STAND_2);
+ anims.put(ANIM_AGENT_STAND_3);
+ anims.put(ANIM_AGENT_STAND_4);
+
+ gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP);
+}
+
+
+void LLAgent::requestEnterGodMode()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestGodlikePowers);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_RequestBlock);
+ msg->addBOOLFast(_PREHASH_Godlike, TRUE);
+ msg->addUUIDFast(_PREHASH_Token, LLUUID::null);
+
+ // simulator and userserver need to know about your request
+ sendReliableMessage();
+ msg->sendReliable(gUserServer);
+}
+
+void LLAgent::requestLeaveGodMode()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestGodlikePowers);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_RequestBlock);
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE);
+ msg->addUUIDFast(_PREHASH_Token, LLUUID::null);
+
+ // simulator and userserver need to know about your request
+ sendReliableMessage();
+ msg->sendReliable(gUserServer);
+}
+
+// wearables
+LLAgent::createStandardWearablesAllDoneCallback::~createStandardWearablesAllDoneCallback()
+{
+ gAgent.createStandardWearablesAllDone();
+}
+
+LLAgent::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCallback()
+{
+ gAgent.sendAgentWearablesUpdate();
+}
+
+LLAgent::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount> cb, S32 index, LLWearable* wearable, U32 todo) :
+ mIndex(index),
+ mWearable(wearable),
+ mTodo(todo),
+ mCB(cb)
+{
+}
+
+void LLAgent::addWearableToAgentInventoryCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ gAgent.addWearabletoAgentInventoryDone(mIndex, inv_item, mWearable);
+
+ if (mTodo & CALL_UPDATE)
+ {
+ gAgent.sendAgentWearablesUpdate();
+ }
+ if (mTodo & CALL_RECOVERDONE)
+ {
+ gAgent.recoverMissingWearableDone();
+ }
+ /*
+ * Do this for every one in the loop
+ */
+ if (mTodo & CALL_CREATESTANDARDDONE)
+ {
+ gAgent.createStandardWearablesDone(mIndex);
+ }
+ if (mTodo & CALL_MAKENEWOUTFITDONE)
+ {
+ gAgent.makeNewOutfitDone(mIndex);
+ }
+}
+
+void LLAgent::addWearabletoAgentInventoryDone(
+ S32 index,
+ const LLUUID& item_id,
+ LLWearable* wearable)
+{
+ if (item_id.isNull())
+ return;
+
+ LLUUID old_item_id = mWearableEntry[index].mItemID;
+ mWearableEntry[index].mItemID = item_id;
+ mWearableEntry[index].mWearable = wearable;
+ if (old_item_id.notNull())
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id);
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if(item && wearable)
+ {
+ // We're changing the asset id, so we both need to set it
+ // locally via setAssetUUID() and via setTransactionID() which
+ // will be decoded on the server. JC
+ item->setAssetUUID(wearable->getID());
+ item->setTransactionID(wearable->getTransactionID());
+ gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id);
+ item->updateServer(FALSE);
+ }
+ gInventory.notifyObservers();
+}
+
+void LLAgent::sendAgentWearablesUpdate()
+{
+ // First make sure that we have inventory items for each wearable
+ S32 i;
+ for(i=0; i < WT_COUNT; ++i)
+ {
+ LLWearable* wearable = mWearableEntry[ i ].mWearable;
+ if (wearable)
+ {
+ if( mWearableEntry[ i ].mItemID.isNull() )
+ {
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ i,
+ wearable,
+ addWearableToAgentInventoryCallback::CALL_NONE);
+ addWearableToAgentInventory(cb, wearable);
+ }
+ else
+ {
+ gInventory.addChangedMask( LLInventoryObserver::LABEL,
+ mWearableEntry[i].mItemID );
+ }
+ }
+ }
+
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.notifyObservers();
+
+ // Send the AgentIsNowWearing
+ gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing);
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ lldebugs << "sendAgentWearablesUpdate()" << llendl;
+ for(i=0; i < WT_COUNT; ++i)
+ {
+ gMessageSystem->nextBlockFast(_PREHASH_WearableData);
+
+ U8 type_u8 = (U8)i;
+ gMessageSystem->addU8Fast(_PREHASH_WearableType, type_u8 );
+
+ LLWearable* wearable = mWearableEntry[ i ].mWearable;
+ if( wearable )
+ {
+ //llinfos << "Sending wearable " << wearable->getName() << llendl;
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, mWearableEntry[ i ].mItemID );
+ }
+ else
+ {
+ //llinfos << "Not wearing wearable type " << LLWearable::typeToTypeName((EWearableType)i) << llendl;
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID::null );
+ }
+
+ lldebugs << " " << LLWearable::typeToTypeLabel((EWearableType)i) << ": " << (wearable ? wearable->getID() : LLUUID::null) << llendl;
+ }
+ gAgent.sendReliableMessage();
+}
+
+void LLAgent::saveWearable( EWearableType type, BOOL send_update )
+{
+ LLWearable* old_wearable = mWearableEntry[(S32)type].mWearable;
+ if( old_wearable && (old_wearable->isDirty() || old_wearable->isOldVersion()) )
+ {
+ LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable );
+ mWearableEntry[(S32)type].mWearable = new_wearable;
+
+ LLInventoryItem* item = gInventory.getItem(mWearableEntry[(S32)type].mItemID);
+ if( item )
+ {
+ // Update existing inventory item
+ LLPointer<LLViewerInventoryItem> template_item =
+ new LLViewerInventoryItem(item->getUUID(),
+ item->getParentUUID(),
+ item->getPermissions(),
+ new_wearable->getID(),
+ new_wearable->getAssetType(),
+ item->getInventoryType(),
+ item->getName(),
+ item->getDescription(),
+ item->getSaleInfo(),
+ item->getFlags(),
+ item->getCreationDate());
+ template_item->setTransactionID(new_wearable->getTransactionID());
+ template_item->updateServer(FALSE);
+ gInventory.updateItem(template_item);
+ }
+ else
+ {
+ // Add a new inventory item (shouldn't ever happen here)
+ U32 todo = addWearableToAgentInventoryCallback::CALL_NONE;
+ if (send_update)
+ {
+ todo |= addWearableToAgentInventoryCallback::CALL_UPDATE;
+ }
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ (S32)type,
+ new_wearable,
+ todo);
+ addWearableToAgentInventory(cb, new_wearable);
+ return;
+ }
+
+ if( send_update )
+ {
+ sendAgentWearablesUpdate();
+ }
+ }
+}
+
+void LLAgent::saveWearableAs(
+ EWearableType type,
+ const std::string& new_name,
+ BOOL save_in_lost_and_found)
+{
+ if(!isWearableCopyable(type))
+ {
+ llwarns << "LLAgent::saveWearableAs() not copyable." << llendl;
+ return;
+ }
+ LLWearable* old_wearable = getWearable(type);
+ if(!old_wearable)
+ {
+ llwarns << "LLAgent::saveWearableAs() no old wearable." << llendl;
+ return;
+ }
+ LLInventoryItem* item = gInventory.getItem(mWearableEntry[type].mItemID);
+ if(!item)
+ {
+ llwarns << "LLAgent::saveWearableAs() no inventory item." << llendl;
+ return;
+ }
+ std::string trunc_name(new_name);
+ LLString::truncate(trunc_name, DB_INV_ITEM_NAME_STR_LEN);
+ LLWearable* new_wearable = gWearableList.createCopyFromAvatar(
+ old_wearable,
+ trunc_name);
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type,
+ new_wearable,
+ addWearableToAgentInventoryCallback::CALL_UPDATE);
+ LLUUID category_id;
+ if (save_in_lost_and_found)
+ {
+ category_id = gInventory.findCategoryUUIDForType(
+ LLAssetType::AT_LOST_AND_FOUND);
+ }
+ else
+ {
+ // put in same folder as original
+ category_id = item->getParentUUID();
+ }
+
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ category_id,
+ new_name,
+ cb);
+
+/*
+ LLWearable* old_wearable = getWearable( type );
+ if( old_wearable )
+ {
+ LLString old_name = old_wearable->getName();
+ old_wearable->setName( new_name );
+ LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable );
+ old_wearable->setName( old_name );
+
+ LLUUID category_id;
+ LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID );
+ if( item )
+ {
+ new_wearable->setPermissions(item->getPermissions());
+ if (save_in_lost_and_found)
+ {
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ }
+ else
+ {
+ // put in same folder as original
+ category_id = item->getParentUUID();
+ }
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO);
+ }
+ }
+
+ mWearableEntry[ type ].mWearable = new_wearable;
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type,
+ addWearableToAgentInventoryCallback::CALL_UPDATE);
+ addWearableToAgentInventory(cb, new_wearable, category_id);
+ }
+*/
+}
+
+void LLAgent::revertWearable( EWearableType type )
+{
+ LLWearable* wearable = mWearableEntry[(S32)type].mWearable;
+ if( wearable )
+ {
+ wearable->writeToAvatar( TRUE );
+ }
+ sendAgentSetAppearance();
+}
+
+void LLAgent::revertAllWearables()
+{
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ revertWearable( (EWearableType)i );
+ }
+}
+
+void LLAgent::saveAllWearables()
+{
+ //if(!gInventory.isLoaded())
+ //{
+ // return;
+ //}
+
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ saveWearable( (EWearableType)i, FALSE );
+ }
+ sendAgentWearablesUpdate();
+}
+
+// Called when the user changes the name of a wearable inventory item that is currenlty being worn.
+void LLAgent::setWearableName( const LLUUID& item_id, const std::string& new_name )
+{
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ if( mWearableEntry[i].mItemID == item_id )
+ {
+ LLWearable* old_wearable = mWearableEntry[i].mWearable;
+ llassert( old_wearable );
+
+ LLString old_name = old_wearable->getName();
+ old_wearable->setName( new_name );
+ LLWearable* new_wearable = gWearableList.createCopy( old_wearable );
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item)
+ {
+ new_wearable->setPermissions(item->getPermissions());
+ }
+ old_wearable->setName( old_name );
+
+ mWearableEntry[i].mWearable = new_wearable;
+ sendAgentWearablesUpdate();
+ break;
+ }
+ }
+}
+
+
+BOOL LLAgent::isWearableModifiable(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ if(!item_id.isNull())
+ {
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item && item->getPermissions().allowModifyBy(gAgent.getID(),
+ gAgent.getGroupID()))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::isWearableCopyable(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ if(!item_id.isNull())
+ {
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item && item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID()))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+U32 LLAgent::getWearablePermMask(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ if(!item_id.isNull())
+ {
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item)
+ {
+ return item->getPermissions().getMaskOwner();
+ }
+ }
+ return PERM_NONE;
+}
+
+LLInventoryItem* LLAgent::getWearableInventoryItem(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ LLInventoryItem* item = NULL;
+ if(item_id.notNull())
+ {
+ item = gInventory.getItem(item_id);
+ }
+ return item;
+}
+
+LLWearable* LLAgent::getWearableFromWearableItem( const LLUUID& item_id )
+{
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ if( mWearableEntry[i].mItemID == item_id )
+ {
+ return mWearableEntry[i].mWearable;
+ }
+ }
+ return NULL;
+}
+
+
+void LLAgent::sendAgentWearablesRequest()
+{
+ gMessageSystem->newMessageFast(_PREHASH_AgentWearablesRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ sendReliableMessage();
+}
+
+// Used to enable/disable menu items.
+// static
+BOOL LLAgent::selfHasWearable( void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+ return gAgent.getWearable( type ) != NULL;
+}
+
+BOOL LLAgent::isWearingItem( const LLUUID& item_id )
+{
+ return (getWearableFromWearableItem( item_id ) != NULL);
+}
+
+
+// static
+void LLAgent::processAgentInitialWearablesUpdate( LLMessageSystem* mesgsys, void** user_data )
+{
+ // We should only receive this message a single time. Ignore subsequent AgentWearablesUpdates
+ // that may result from AgentWearablesRequest having been sent more than once.
+ static BOOL first = TRUE;
+ if( first )
+ {
+ first = FALSE;
+ }
+ else
+ {
+ return;
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ LLUUID agent_id;
+ gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar && (agent_id == avatar->getID()) )
+ {
+ gMessageSystem->getU32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, gAgent.mAgentWearablesUpdateSerialNum );
+
+ S32 num_wearables = gMessageSystem->getNumberOfBlocksFast(_PREHASH_WearableData);
+ if( num_wearables < 4 )
+ {
+ // Transitional state. Avatars should always have at least their body parts (hair, eyes, shape and skin).
+ // The fact that they don't have any here (only a dummy is sent) implies that this account existed
+ // before we had wearables, or that the database has gotten messed up.
+ // Deal with this by creating new body parts.
+ //avatar->createStandardWearables();
+
+ // no, deal with it by noting that we need to choose a
+ // gender.
+ gAgent.setGenderChosen(FALSE);
+ return;
+ }
+
+ //lldebugs << "processAgentInitialWearablesUpdate()" << llendl;
+ // Add wearables
+ LLUUID asset_id_array[ WT_COUNT ];
+ S32 i;
+ for( i=0; i < num_wearables; i++ )
+ {
+ U8 type_u8 = 0;
+ gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i );
+ if( type_u8 >= WT_COUNT )
+ {
+ continue;
+ }
+ EWearableType type = (EWearableType) type_u8;
+
+ LLUUID item_id;
+ gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_ItemID, item_id, i );
+
+ LLUUID asset_id;
+ gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_AssetID, asset_id, i );
+ if( asset_id.isNull() )
+ {
+ LLWearable::removeFromAvatar( type, FALSE );
+ }
+ else
+ {
+ LLAssetType::EType asset_type = LLWearable::typeToAssetType( type );
+ if( asset_type == LLAssetType::AT_NONE )
+ {
+ continue;
+ }
+
+ gAgent.mWearableEntry[type].mItemID = item_id;
+ asset_id_array[type] = asset_id;
+ }
+
+ lldebugs << " " << LLWearable::typeToTypeLabel(type) << llendl;
+ }
+
+ // now that we have the asset ids...request the wearable assets
+ for( i = 0; i < WT_COUNT; i++ )
+ {
+ if( !gAgent.mWearableEntry[i].mItemID.isNull() )
+ {
+ gWearableList.getAsset(
+ asset_id_array[i],
+ LLString::null,
+ LLWearable::typeToAssetType( (EWearableType) i ),
+ LLAgent::onInitialWearableAssetArrived, (void*)(intptr_t)i );
+ }
+ }
+ }
+}
+
+// A single wearable that the avatar was wearing on start-up has arrived from the database.
+// static
+void LLAgent::onInitialWearableAssetArrived( LLWearable* wearable, void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return;
+ }
+
+ if( wearable )
+ {
+ llassert( type == wearable->getType() );
+ gAgent.mWearableEntry[ type ].mWearable = wearable;
+
+ // disable composites if initial textures are baked
+ avatar->setupComposites();
+ gAgent.queryWearableCache();
+
+ wearable->writeToAvatar( FALSE );
+ avatar->setCompositeUpdatesEnabled(TRUE);
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, gAgent.mWearableEntry[type].mItemID );
+ }
+ else
+ {
+ // Somehow the asset doesn't exist in the database.
+ gAgent.recoverMissingWearable( type );
+ }
+
+ gInventory.notifyObservers();
+
+ // Have all the wearables that the avatar was wearing at log-in arrived?
+ if( !gAgent.mWearablesLoaded )
+ {
+ gAgent.mWearablesLoaded = TRUE;
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ if( !gAgent.mWearableEntry[i].mItemID.isNull() && !gAgent.mWearableEntry[i].mWearable )
+ {
+ gAgent.mWearablesLoaded = FALSE;
+ break;
+ }
+ }
+ }
+
+ if( gAgent.mWearablesLoaded )
+ {
+ // Make sure that the server's idea of the avatar's wearables actually match the wearables.
+ gAgent.sendAgentSetAppearance();
+
+ // Check to see if there are any baked textures that we hadn't uploaded before we logged off last time.
+ // If there are any, schedule them to be uploaded as soon as the layer textures they depend on arrive.
+ if( !gAgent.cameraCustomizeAvatar() )
+ {
+ avatar->requestLayerSetUploads();
+ }
+ }
+}
+
+// Normally, all wearables referred to "AgentWearablesUpdate" will correspond to actual assets in the
+// database. If for some reason, we can't load one of those assets, we can try to reconstruct it so that
+// the user isn't left without a shape, for example. (We can do that only after the inventory has loaded.)
+void LLAgent::recoverMissingWearable( EWearableType type )
+{
+ // Try to recover by replacing missing wearable with a new one.
+ LLNotifyBox::showXml("ReplacedMissingWearable");
+ lldebugs << "Wearable " << LLWearable::typeToTypeLabel( type ) << " could not be downloaded. Replaced inventory item with default wearable." << llendl;
+ LLWearable* new_wearable = gWearableList.createNewWearable(type);
+
+ S32 type_s32 = (S32) type;
+ mWearableEntry[type_s32].mWearable = new_wearable;
+ new_wearable->writeToAvatar( TRUE );
+
+ // Add a new one in the lost and found folder.
+ // (We used to overwrite the "not found" one, but that could potentially
+ // destory content.) JC
+ LLUUID lost_and_found_id =
+ gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type_s32,
+ new_wearable,
+ addWearableToAgentInventoryCallback::CALL_RECOVERDONE);
+ addWearableToAgentInventory( cb, new_wearable, lost_and_found_id, TRUE);
+}
+
+void LLAgent::recoverMissingWearableDone()
+{
+ // Have all the wearables that the avatar was wearing at log-in arrived or been fabricated?
+ mWearablesLoaded = TRUE;
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ if( !mWearableEntry[i].mItemID.isNull() && !mWearableEntry[i].mWearable )
+ {
+ mWearablesLoaded = FALSE;
+ break;
+ }
+ }
+
+ if( mWearablesLoaded )
+ {
+ // Make sure that the server's idea of the avatar's wearables actually match the wearables.
+ sendAgentSetAppearance();
+ }
+ else
+ {
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, LLUUID::null );
+ gInventory.notifyObservers();
+ }
+}
+
+void LLAgent::createStandardWearables(BOOL female)
+{
+ llwarns << "Creating Standard " << (female ? "female" : "male" )
+ << " Wearables" << llendl;
+
+ if (mAvatarObject.isNull())
+ {
+ return;
+ }
+
+ if(female) mAvatarObject->setSex(SEX_FEMALE);
+ else mAvatarObject->setSex(SEX_MALE);
+
+ BOOL create[WT_COUNT] =
+ {
+ TRUE, //WT_SHAPE
+ TRUE, //WT_SKIN
+ TRUE, //WT_HAIR
+ TRUE, //WT_EYES
+ TRUE, //WT_SHIRT
+ TRUE, //WT_PANTS
+ TRUE, //WT_SHOES
+ TRUE, //WT_SOCKS
+ FALSE, //WT_JACKET
+ FALSE, //WT_GLOVES
+ TRUE, //WT_UNDERSHIRT
+ TRUE, //WT_UNDERPANTS
+ FALSE //WT_SKIRT
+ };
+
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ bool once = false;
+ LLPointer<LLRefCount> donecb = NULL;
+ if( create[i] )
+ {
+ if (!once)
+ {
+ once = true;
+ donecb = new createStandardWearablesAllDoneCallback;
+ }
+ llassert( mWearableEntry[i].mWearable == NULL );
+ LLWearable* wearable = gWearableList.createNewWearable((EWearableType)i);
+ mWearableEntry[i].mWearable = wearable;
+ // no need to update here...
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ donecb,
+ i,
+ wearable,
+ addWearableToAgentInventoryCallback::CALL_CREATESTANDARDDONE);
+ addWearableToAgentInventory(cb, wearable, LLUUID::null, FALSE);
+ }
+ }
+}
+void LLAgent::createStandardWearablesDone(S32 index)
+{
+ LLWearable* wearable = mWearableEntry[index].mWearable;
+
+ if (wearable)
+ {
+ wearable->writeToAvatar(TRUE);
+ }
+}
+
+void LLAgent::createStandardWearablesAllDone()
+{
+ // ... because sendAgentWearablesUpdate will notify inventory
+ // observers.
+ mWearablesLoaded = TRUE;
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+
+ // Treat this as the first texture entry message, if none received yet
+ mAvatarObject->onFirstTEMessageReceived();
+}
+
+void LLAgent::makeNewOutfit(
+ const std::string& new_folder_name,
+ const LLDynamicArray<S32>& wearables_to_include,
+ const LLDynamicArray<S32>& attachments_to_include,
+ BOOL rename_clothing)
+{
+ if (mAvatarObject.isNull())
+ {
+ return;
+ }
+
+ // First, make a folder in the Clothes directory.
+ LLUUID folder_id = gInventory.createNewCategory(
+ gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING),
+ LLAssetType::AT_NONE,
+ new_folder_name);
+
+ bool found_first_item = false;
+
+ ///////////////////
+ // Wearables
+
+ if( wearables_to_include.count() )
+ {
+ // Then, iterate though each of the wearables and save copies of them in the folder.
+ S32 i;
+ S32 count = wearables_to_include.count();
+ LLDynamicArray<LLUUID> delete_items;
+ LLPointer<LLRefCount> cbdone = NULL;
+ for( i = 0; i < count; ++i )
+ {
+ S32 index = wearables_to_include[i];
+ LLWearable* old_wearable = mWearableEntry[ index ].mWearable;
+ if( old_wearable )
+ {
+ std::string new_name;
+ LLWearable* new_wearable;
+ new_wearable = gWearableList.createCopy(old_wearable);
+ if (rename_clothing)
+ {
+ new_name = new_folder_name;
+ new_name.append(" ");
+ new_name.append(old_wearable->getTypeLabel());
+ LLString::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN);
+ new_wearable->setName(new_name);
+ }
+
+ LLViewerInventoryItem* item = gInventory.getItem(mWearableEntry[index].mItemID);
+ S32 todo = addWearableToAgentInventoryCallback::CALL_NONE;
+ if (!found_first_item)
+ {
+ found_first_item = true;
+ /* set the focus to the first item */
+ todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE;
+ /* send the agent wearables update when done */
+ cbdone = new sendAgentWearablesUpdateCallback;
+ }
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ cbdone,
+ index,
+ new_wearable,
+ todo);
+ if (isWearableCopyable((EWearableType)index))
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ folder_id,
+ new_name,
+ cb);
+ }
+ else
+ {
+ move_inventory_item(
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getUUID(),
+ folder_id,
+ new_name,
+ cb);
+ }
+ }
+ }
+ gInventory.notifyObservers();
+ }
+
+
+ ///////////////////
+ // Attachments
+
+ if( attachments_to_include.count() )
+ {
+ BOOL msg_started = FALSE;
+ LLMessageSystem* msg = gMessageSystem;
+ S32 i;
+ for( i = 0; i < attachments_to_include.count(); i++ )
+ {
+ S32 attachment_pt = attachments_to_include[i];
+ LLViewerJointAttachment* attachment = mAvatarObject->mAttachmentPoints.getIfThere( attachment_pt );
+ if(!attachment) continue;
+ LLViewerObject* attached_object = attachment->getObject(0);
+ if(!attached_object) continue;
+ const LLUUID& item_id = attachment->getItemID();
+ if(item_id.isNull()) continue;
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(!item) continue;
+ if(!msg_started)
+ {
+ msg_started = TRUE;
+ msg->newMessage("CreateNewOutfitAttachments");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", getID());
+ msg->addUUID("SessionID", getSessionID());
+ msg->nextBlock("HeaderData");
+ msg->addUUID("NewFolderID", folder_id);
+ }
+ msg->nextBlock("ObjectData");
+ msg->addUUID("OldItemID", item_id);
+ msg->addUUID("OldFolderID", item->getParentUUID());
+ }
+
+ if( msg_started )
+ {
+ sendReliableMessage();
+ }
+
+ }
+}
+
+void LLAgent::makeNewOutfitDone(S32 index)
+{
+ LLUUID first_item_id = mWearableEntry[index].mItemID;
+ // Open the inventory and select the first item we added.
+ if( first_item_id.notNull() )
+ {
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(first_item_id, TAKE_FOCUS_NO);
+ }
+ }
+}
+
+
+void LLAgent::addWearableToAgentInventory(
+ LLPointer<LLInventoryCallback> cb,
+ LLWearable* wearable,
+ const LLUUID& category_id,
+ BOOL notify)
+{
+ create_inventory_item(
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ category_id,
+ wearable->getTransactionID(),
+ wearable->getName(),
+ wearable->getDescription(),
+ wearable->getAssetType(),
+ LLInventoryType::IT_WEARABLE,
+ wearable->getType(),
+ wearable->getPermissions().getMaskNextOwner(),
+ cb);
+}
+
+//-----------------------------------------------------------------------------
+// sendAgentSetAppearance()
+//-----------------------------------------------------------------------------
+void LLAgent::sendAgentSetAppearance()
+{
+ if (mAvatarObject.isNull()) return;
+
+ if (mNumPendingQueries > 0 && !gAgent.cameraCustomizeAvatar())
+ {
+ return;
+ }
+
+ llinfos << "TAT: Sent AgentSetAppearance: " <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "HEAD " : "head " ) <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "UPPER " : "upper " ) <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "LOWER " : "lower " ) <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "EYES" : "eyes" ) << llendl;
+ //dumpAvatarTEs( "sendAgentSetAppearance()" );
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentSetAppearance);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ // correct for the collisiton tolerance (to make it look like the
+ // agent is actually walking on the ground/object)
+ // NOTE -- when we start correcting all of the other Havok geometry
+ // to compensate for the COLLISION_TOLERANCE ugliness we will have
+ // to tweak this number again
+ LLVector3 body_size = mAvatarObject->mBodySize;
+ msg->addVector3Fast(_PREHASH_Size, body_size);
+
+ // To guard against out of order packets
+ // Note: always start by sending 1. This resets the server's count. 0 on the server means "uninitialized"
+ mAppearanceSerialNum++;
+ msg->addU32Fast(_PREHASH_SerialNum, mAppearanceSerialNum );
+
+ // is texture data current relative to wearables?
+ // KLW - TAT this will probably need to check the local queue.
+ BOOL textures_current = !mAvatarObject->hasPendingBakedUploads() && mWearablesLoaded;
+
+ S32 baked_texture_index;
+ for( baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++ )
+ {
+ S32 tex_index = LLVOAvatar::sBakedTextureIndices[baked_texture_index];
+
+ // if we're not wearing a skirt, we don't need the texture to be baked
+ if (tex_index == LLVOAvatar::TEX_SKIRT_BAKED && !mAvatarObject->isWearingWearableType(WT_SKIRT))
+ {
+ continue;
+ }
+
+ // IMG_DEFAULT_AVATAR means not baked
+ if (mAvatarObject->getTEImage( tex_index)->getID() == IMG_DEFAULT_AVATAR)
+ {
+ textures_current = FALSE;
+ break;
+ }
+ }
+
+ // only update cache entries if we have all our baked textures
+ if (textures_current)
+ {
+ llinfos << "TAT: Sending cached texture data" << llendl;
+ for (baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++)
+ {
+ LLUUID hash;
+
+ for( S32 wearable_num = 0; wearable_num < MAX_WEARABLES_PER_LAYERSET; wearable_num++ )
+ {
+ EWearableType wearable_type = WEARABLE_BAKE_TEXTURE_MAP[baked_texture_index][wearable_num];
+
+ LLWearable* wearable = getWearable( wearable_type );
+ if (wearable)
+ {
+ hash ^= wearable->getID();
+ }
+ }
+
+ if (hash.notNull())
+ {
+ hash ^= BAKED_TEXTURE_HASH[baked_texture_index];
+ }
+
+ S32 tex_index = LLVOAvatar::sBakedTextureIndices[baked_texture_index];
+
+ msg->nextBlockFast(_PREHASH_WearableData);
+ msg->addUUIDFast(_PREHASH_CacheID, hash);
+ msg->addU8Fast(_PREHASH_TextureIndex, (U8)tex_index);
+ }
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ mAvatarObject->packTEMessage( gMessageSystem );
+
+ S32 transmitted_params = 0;
+ for (LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarObject->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*)mAvatarObject->getNextVisualParam())
+ {
+ F32 param_value = param->getWeight();
+
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ msg->nextBlockFast(_PREHASH_VisualParam );
+
+ // We don't send the param ids. Instead, we assume that the receiver has the same params in the same sequence.
+ U8 new_weight = F32_to_U8(param_value, param->getMinWeight(), param->getMaxWeight());
+ msg->addU8Fast(_PREHASH_ParamValue, new_weight );
+ transmitted_params++;
+ }
+ }
+
+// llinfos << "Avatar XML num VisualParams transmitted = " << transmitted_params << llendl;
+ sendReliableMessage();
+}
+
+void LLAgent::sendAgentDataUpdateRequest()
+{
+ gMessageSystem->newMessageFast(_PREHASH_AgentDataUpdateRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ sendReliableMessage();
+}
+
+void LLAgent::removeWearable( EWearableType type )
+{
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+
+ if ( (gAgent.mAccess < SIM_ACCESS_MATURE)
+ && (type == WT_UNDERSHIRT || type == WT_UNDERPANTS))
+ {
+ // Can't take off underclothing in simple UI mode or on PG accounts
+ return;
+ }
+
+ if( old_wearable )
+ {
+ if( old_wearable->isDirty() )
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("RemoveWearableSave", LLAgent::onRemoveWearableDialog, (void*)(S32)type );
+ return;
+ }
+ else
+ {
+ removeWearableFinal( type );
+ }
+ }
+}
+
+// static
+void LLAgent::onRemoveWearableDialog( S32 option, void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+ switch( option )
+ {
+ case 0: // "Save"
+ gAgent.saveWearable( type );
+ gAgent.removeWearableFinal( type );
+ break;
+
+ case 1: // "Don't Save"
+ gAgent.removeWearableFinal( type );
+ break;
+
+ case 2: // "Cancel"
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+// Called by removeWearable() and onRemoveWearableDialog() to actually do the removal.
+void LLAgent::removeWearableFinal( EWearableType type )
+{
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, mWearableEntry[type].mItemID );
+
+ mWearableEntry[ type ].mWearable = NULL;
+ mWearableEntry[ type ].mItemID.setNull();
+
+ queryWearableCache();
+
+ if( old_wearable )
+ {
+ old_wearable->removeFromAvatar( TRUE );
+ }
+
+ // Update the server
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+ gInventory.notifyObservers();
+}
+
+void LLAgent::copyWearableToInventory( EWearableType type )
+{
+ LLWearable* wearable = mWearableEntry[ type ].mWearable;
+ if( wearable )
+ {
+ // Save the old wearable if it has changed.
+ if( wearable->isDirty() )
+ {
+ wearable = gWearableList.createCopyFromAvatar( wearable );
+ mWearableEntry[ type ].mWearable = wearable;
+ }
+
+ // Make a new entry in the inventory. (Put it in the same folder as the original item if possible.)
+ LLUUID category_id;
+ LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID );
+ if( item )
+ {
+ category_id = item->getParentUUID();
+ wearable->setPermissions(item->getPermissions());
+ }
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type,
+ wearable);
+ addWearableToAgentInventory(cb, wearable, category_id);
+ }
+}
+
+
+// A little struct to let setWearable() communicate more than one value with onSetWearableDialog().
+struct LLSetWearableData
+{
+ LLSetWearableData( const LLUUID& new_item_id, LLWearable* new_wearable ) :
+ mNewItemID( new_item_id ), mNewWearable( new_wearable ) {}
+ LLUUID mNewItemID;
+ LLWearable* mNewWearable;
+};
+
+BOOL LLAgent::needsReplacement(EWearableType wearableType, S32 remove)
+{
+ return TRUE;
+ /*if (remove) return TRUE;
+
+ return getWearable(wearableType) ? TRUE : FALSE;*/
+}
+
+// Assumes existing wearables are not dirty.
+void LLAgent::setWearableOutfit(
+ const LLInventoryItem::item_array_t& items,
+ const LLDynamicArray< LLWearable* >& wearables,
+ BOOL remove )
+{
+ lldebugs << "setWearableOutfit() start" << llendl;
+
+ BOOL wearables_to_remove[WT_COUNT];
+ wearables_to_remove[WT_SHAPE] = FALSE;
+ wearables_to_remove[WT_SKIN] = FALSE;
+ wearables_to_remove[WT_HAIR] = FALSE;
+ wearables_to_remove[WT_EYES] = FALSE;
+ wearables_to_remove[WT_SHIRT] = remove;
+ wearables_to_remove[WT_PANTS] = remove;
+ wearables_to_remove[WT_SHOES] = remove;
+ wearables_to_remove[WT_SOCKS] = remove;
+ wearables_to_remove[WT_JACKET] = remove;
+ wearables_to_remove[WT_GLOVES] = remove;
+ wearables_to_remove[WT_UNDERSHIRT] = (gAgent.mAccess >= SIM_ACCESS_MATURE) & remove;
+ wearables_to_remove[WT_UNDERPANTS] = (gAgent.mAccess >= SIM_ACCESS_MATURE) & remove;
+ wearables_to_remove[WT_SKIRT] = remove;
+
+ S32 count = wearables.count();
+ llassert( items.count() == count );
+
+ S32 i;
+ for( i = 0; i < count; i++ )
+ {
+ LLWearable* new_wearable = wearables[i];
+ LLPointer<LLInventoryItem> new_item = items[i];
+
+ EWearableType type = new_wearable->getType();
+ wearables_to_remove[type] = FALSE;
+
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+ if( old_wearable )
+ {
+ const LLUUID& old_item_id = mWearableEntry[ type ].mItemID;
+ if( (old_wearable->getID() == new_wearable->getID()) &&
+ (old_item_id == new_item->getUUID()) )
+ {
+ lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl;
+ continue;
+ }
+
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id);
+
+ // Assumes existing wearables are not dirty.
+ if( old_wearable->isDirty() )
+ {
+ llassert(0);
+ continue;
+ }
+ }
+
+ mWearableEntry[ type ].mItemID = new_item->getUUID();
+ mWearableEntry[ type ].mWearable = new_wearable;
+ }
+
+ std::vector<LLWearable*> wearables_being_removed;
+
+ for( i = 0; i < WT_COUNT; i++ )
+ {
+ if( wearables_to_remove[i] )
+ {
+ wearables_being_removed.push_back(mWearableEntry[ i ].mWearable);
+ mWearableEntry[ i ].mWearable = NULL;
+
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, mWearableEntry[ i ].mItemID);
+ mWearableEntry[ i ].mItemID.setNull();
+ }
+ }
+
+ gInventory.notifyObservers();
+
+ queryWearableCache();
+
+ std::vector<LLWearable*>::iterator wearable_iter;
+
+ for( wearable_iter = wearables_being_removed.begin();
+ wearable_iter != wearables_being_removed.end();
+ ++wearable_iter)
+ {
+ LLWearable* wearablep = *wearable_iter;
+ if (wearablep)
+ {
+ wearablep->removeFromAvatar( TRUE );
+ }
+ }
+
+ for( i = 0; i < count; i++ )
+ {
+ wearables[i]->writeToAvatar( TRUE );
+ }
+
+ LLFloaterCustomize::setCurrentWearableType( WT_SHAPE );
+
+ // Start rendering & update the server
+ mWearablesLoaded = TRUE;
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+
+ lldebugs << "setWearableOutfit() end" << llendl;
+}
+
+
+// User has picked "wear on avatar" from a menu.
+void LLAgent::setWearable( LLInventoryItem* new_item, LLWearable* new_wearable )
+{
+ EWearableType type = new_wearable->getType();
+
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+ if( old_wearable )
+ {
+ const LLUUID& old_item_id = mWearableEntry[ type ].mItemID;
+ if( (old_wearable->getID() == new_wearable->getID()) &&
+ (old_item_id == new_item->getUUID()) )
+ {
+ lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl;
+ return;
+ }
+
+ if( old_wearable->isDirty() )
+ {
+ // Bring up modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml( "SetWearableSave", LLAgent::onSetWearableDialog,
+ new LLSetWearableData( new_item->getUUID(), new_wearable ));
+ return;
+ }
+ }
+
+ setWearableFinal( new_item, new_wearable );
+}
+
+// static
+void LLAgent::onSetWearableDialog( S32 option, void* userdata )
+{
+ LLSetWearableData* data = (LLSetWearableData*)userdata;
+ LLInventoryItem* new_item = gInventory.getItem( data->mNewItemID );
+ if( !new_item )
+ {
+ delete data;
+ return;
+ }
+
+ switch( option )
+ {
+ case 0: // "Save"
+ gAgent.saveWearable( data->mNewWearable->getType() );
+ gAgent.setWearableFinal( new_item, data->mNewWearable );
+ break;
+
+ case 1: // "Don't Save"
+ gAgent.setWearableFinal( new_item, data->mNewWearable );
+ break;
+
+ case 2: // "Cancel"
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+
+ delete data;
+}
+
+// Called from setWearable() and onSetWearableDialog() to actually set the wearable.
+void LLAgent::setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable )
+{
+ EWearableType type = new_wearable->getType();
+
+ // Replace the old wearable with a new one.
+ llassert( new_item->getAssetUUID() == new_wearable->getID() );
+ LLUUID old_item_id = mWearableEntry[ type ].mItemID;
+ mWearableEntry[ type ].mItemID = new_item->getUUID();
+ mWearableEntry[ type ].mWearable = new_wearable;
+
+ if (old_item_id.notNull())
+ {
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, old_item_id );
+ gInventory.notifyObservers();
+ }
+
+ //llinfos << "LLVOAvatar::setWearable()" << llendl;
+ queryWearableCache();
+ new_wearable->writeToAvatar( TRUE );
+
+ // Update the server
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+}
+
+void LLAgent::queryWearableCache()
+{
+ if (!mWearablesLoaded)
+ {
+ return;
+ }
+
+ // Look up affected baked textures.
+ // If they exist:
+ // disallow updates for affected layersets (until dataserver responds with cache request.)
+ // If cache miss…turn updates back on and invalidate composite.
+ // If cache hit, modify baked texture entries.
+ //
+ // Cache requests contain list of hashes for each baked texture entry.
+ // Response is list of valid baked texture assets. (same message)
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentCachedTexture);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ gMessageSystem->addS32Fast(_PREHASH_SerialNum, mTextureCacheQueryID);
+
+ S32 num_queries = 0;
+ for (S32 baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++)
+ {
+ LLUUID hash;
+ for (S32 wearable_num = 0; wearable_num < MAX_WEARABLES_PER_LAYERSET; wearable_num++)
+ {
+ EWearableType wearable_type = WEARABLE_BAKE_TEXTURE_MAP[baked_texture_index][wearable_num];
+
+ LLWearable* wearable = getWearable( wearable_type );
+ if (wearable)
+ {
+ hash ^= wearable->getID();
+ }
+ }
+ if (hash.notNull())
+ {
+ hash ^= BAKED_TEXTURE_HASH[baked_texture_index];
+ num_queries++;
+ //FIXME: make sure at least one request gets packed
+
+ //llinfos << "Requesting texture for hash " << hash << " in baked texture slot " << baked_texture_index << llendl;
+ gMessageSystem->nextBlockFast(_PREHASH_WearableData);
+ gMessageSystem->addUUIDFast(_PREHASH_ID, hash);
+ gMessageSystem->addU8Fast(_PREHASH_TextureIndex, (U8)baked_texture_index);
+ }
+
+ mActiveCacheQueries[ baked_texture_index ] = mTextureCacheQueryID;
+ }
+
+ llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl;
+ gMessageSystem->sendReliable(getRegion()->getHost());
+ mNumPendingQueries++;
+ mTextureCacheQueryID++;
+}
+
+// User has picked "remove from avatar" from a menu.
+// static
+void LLAgent::userRemoveWearable( void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+
+ if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR ) ) //&&
+ //!((gAgent.mAccess >= SIM_ACCESS_MATURE) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
+ {
+ gAgent.removeWearable( type );
+ }
+}
+
+void LLAgent::userRemoveAllClothes( void* userdata )
+{
+ // We have to do this up front to avoid having to deal with the case of multiple wearables being dirty.
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveAllIfDirty( LLAgent::userRemoveAllClothesStep2, NULL );
+ }
+ else
+ {
+ LLAgent::userRemoveAllClothesStep2( TRUE, NULL );
+ }
+}
+
+void LLAgent::userRemoveAllClothesStep2( BOOL proceed, void* userdata )
+{
+ if( proceed )
+ {
+ gAgent.removeWearable( WT_SHIRT );
+ gAgent.removeWearable( WT_PANTS );
+ gAgent.removeWearable( WT_SHOES );
+ gAgent.removeWearable( WT_SOCKS );
+ gAgent.removeWearable( WT_JACKET );
+ gAgent.removeWearable( WT_GLOVES );
+ gAgent.removeWearable( WT_UNDERSHIRT );
+ gAgent.removeWearable( WT_UNDERPANTS );
+ gAgent.removeWearable( WT_SKIRT );
+ }
+}
+
+void LLAgent::userRemoveAllAttachments( void* userdata )
+{
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if(!avatarp)
+ {
+ llwarns << "No avatar found." << llendl;
+ return;
+ }
+
+ gMessageSystem->newMessage("ObjectDetach");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ LLViewerJointAttachment* attachment;
+ for (attachment = avatarp->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = avatarp->mAttachmentPoints.getNextData())
+ {
+ LLViewerObject* objectp = attachment->getObject(0);
+ if (objectp)
+ {
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID());
+ }
+ }
+ gMessageSystem->sendReliable( gAgent.getRegionHost() );
+}
+
+bool LLAgent::LLHideGroupTitleListener::handleEvent(LLPointer<LLEvent> event, const LLSD &userdata)
+{
+ gAgent.setHideGroupTitle(event->getValue());
+ return true;
+
+}
+
+bool LLAgent::LLEffectColorListener::handleEvent(LLPointer<LLEvent> event, const LLSD &userdata)
+{
+ gAgent.setEffectColor(LLColor4(event->getValue()));
+ return true;
+}
+
+void LLAgent::observeFriends()
+{
+ if(!mFriendObserver)
+ {
+ mFriendObserver = new LLAgentFriendObserver;
+ LLAvatarTracker::instance().addObserver(mFriendObserver);
+ friendsChanged();
+ }
+}
+
+// EOF
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
new file mode 100644
index 0000000000..5fe6cd3ab4
--- /dev/null
+++ b/indra/newview/llagent.h
@@ -0,0 +1,896 @@
+/**
+ * @file llagent.h
+ * @brief LLAgent class header file
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENT_H
+#define LL_LLAGENT_H
+
+#include <set>
+
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llcoordframe.h"
+#include "llevent.h"
+#include "llagentconstants.h"
+#include "llanimationstates.h"
+#include "lldbstrings.h"
+#include "llhudeffectlookat.h"
+#include "llhudeffectpointat.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "lltimer.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "v4color.h"
+#include "v4math.h"
+//#include "vmath.h"
+#include "stdenums.h"
+#include "llwearable.h"
+#include "llcharacter.h"
+#include "llinventory.h"
+#include "llviewerinventory.h"
+#include "llagentdata.h"
+
+// Ventrella
+#include "llfollowcam.h"
+// end Ventrella
+
+const U8 AGENT_STATE_TYPING = 0x04; // Typing indication
+const U8 AGENT_STATE_EDITING = 0x10; // Set when agent has objects selected
+
+const BOOL ANIMATE = TRUE;
+
+typedef enum e_camera_modes
+{
+ CAMERA_MODE_THIRD_PERSON,
+ CAMERA_MODE_MOUSELOOK,
+ CAMERA_MODE_CUSTOMIZE_AVATAR,
+ CAMERA_MODE_FOLLOW
+} ECameraMode;
+
+typedef enum e_anim_request
+{
+ ANIM_REQUEST_START,
+ ANIM_REQUEST_STOP
+} EAnimRequest;
+
+class LLChat;
+class LLVOAvatar;
+class LLViewerRegion;
+class LLMotion;
+class LLToolset;
+class LLMessageSystem;
+class LLPermissions;
+class LLHost;
+class LLFriendObserver;
+
+struct LLGroupData
+{
+ LLUUID mID;
+ LLUUID mInsigniaID;
+ U64 mPowers;
+ BOOL mAcceptNotices;
+ S32 mContribution;
+ std::string mName;
+};
+
+inline bool operator==(const LLGroupData &a, const LLGroupData &b)
+{
+ return (a.mID == b.mID);
+}
+
+// forward declarations
+
+//
+
+class LLAgent : public LLObservable
+{
+public:
+ // When the agent hasn't typed anything for this duration, it leaves the
+ // typing state (for both chat and IM).
+ static const F32 TYPING_TIMEOUT_SECS;
+
+ LLAgent();
+ ~LLAgent();
+
+ void init();
+ void cleanup();
+
+ //
+ // MANIPULATORS
+ //
+ // TODO: Put all non-const functions here.
+
+ // Called whenever the agent moves. Puts camera back in default position,
+ // deselects items, etc.
+ void resetView(BOOL reset_camera = TRUE);
+
+ // Called on camera movement, to allow the camera to be unlocked from the
+ // default position behind the avatar.
+ void unlockView();
+
+ void sendMessage(); // Send message to this agent's region.
+ void sendReliableMessage();
+
+ LLVector3d calcCameraPositionTargetGlobal(BOOL *hit_limit = NULL); // Calculate the camera position target
+ LLVector3d calcFocusPositionTargetGlobal(); // target for this mode
+ LLVector3d getCameraPositionGlobal() const;
+ const LLVector3 &getCameraPositionAgent() const;
+ F32 calcCameraFOVZoomFactor();
+ F32 getCameraMinOffGround(); // minimum height off ground for this mode, meters
+ void endAnimationUpdateUI();
+ void setKey(const S32 direction, S32 &key); // sets key to +1 for +direction, -1 for -direction
+ void handleScrollWheel(S32 clicks); // mousewheel driven zoom
+
+ void setAvatarObject(LLVOAvatar *avatar);
+
+ // rendering state bitmask helpers
+ void startTyping();
+ void stopTyping();
+ void setRenderState(U8 newstate);
+ void clearRenderState(U8 clearstate);
+ U8 getRenderState();
+
+ // Set the home data
+ void setRegion(LLViewerRegion *regionp);
+ LLViewerRegion *getRegion() const;
+ const LLHost& getRegionHost() const;
+
+ void updateAgentPosition(const F32 dt, const F32 yaw, const S32 mouse_x, const S32 mouse_y); // call once per frame to update position, angles radians
+ void updateLookAt(const S32 mouse_x, const S32 mouse_y);
+
+
+ void updateCamera(); // call once per frame to update camera location/orientation
+ void resetCamera(); // slam camera into its default position
+ void setupSitCamera();
+ void setCameraCollidePlane(LLVector4 &plane) { mCameraCollidePlane = plane; }
+
+ void changeCameraToDefault();
+ void changeCameraToMouselook(BOOL animate = TRUE);
+ void changeCameraToThirdPerson(BOOL animate = TRUE);
+ void changeCameraToCustomizeAvatar(BOOL animate = TRUE); // trigger transition animation
+ // Ventrella
+ void changeCameraToFollow(BOOL animate = TRUE);
+ //end Ventrella
+
+ void setFocusGlobal(const LLVector3d &focus, const LLUUID &object_id = LLUUID::null);
+ void setFocusOnAvatar(BOOL focus, BOOL animate);
+ void setCameraPosAndFocusGlobal(const LLVector3d& pos, const LLVector3d& focus, const LLUUID &object_id);
+ void setSitCamera(const LLUUID &object_id, const LLVector3 &camera_pos = LLVector3::zero, const LLVector3 &camera_focus = LLVector3::zero);
+ void clearFocusObject();
+ void setFocusObject(LLViewerObject* object);
+ void setObjectTracking(BOOL track) { mTrackFocusObject = track; }
+// void setLookingAtAvatar(BOOL looking);
+
+ void heardChat(const LLChat& chat);
+ void lookAtLastChat();
+ LLUUID getLastChatter() { return mLastChatterID; }
+ F32 getTypingTime() { return mTypingTimer.getElapsedTimeF32(); }
+
+ void setAFK();
+ void clearAFK();
+ BOOL getAFK() const;
+
+ void setAlwaysRun() { mbAlwaysRun = TRUE; }
+ void clearAlwaysRun() { mbAlwaysRun = FALSE; }
+ BOOL getAlwaysRun() const { return mbAlwaysRun; }
+
+ void setBusy();
+ void clearBusy();
+ BOOL getBusy() const;
+
+ void setAdminOverride(BOOL b) { mAdminOverride = b; }
+ void setGodLevel(U8 god_level) { mGodLevel = god_level; }
+ void setFirstLogin(BOOL b) { mFirstLogin = b; }
+ void setGenderChosen(BOOL b) { mGenderChosen = b; }
+
+ BOOL getAdminOverride() const { return mAdminOverride; }
+ // update internal datastructures and update the server with the
+ // new contribution level. Returns true if the group id was found
+ // and contribution could be set.
+ BOOL setGroupContribution(const LLUUID& group_id, S32 contribution);
+ BOOL setGroupAcceptNotices(const LLUUID& group_id, BOOL accept_notices);
+ void setHideGroupTitle(BOOL hide) { mHideGroupTitle = hide; }
+
+ //
+ // ACCESSORS
+ //
+ // TODO: Put all read functions here, make them const
+
+ const LLUUID& getID() const { return gAgentID; }
+ const LLUUID& getSessionID() const { return gAgentSessionID; }
+
+ const LLUUID& getSecureSessionID() const { return mSecureSessionID; }
+ // Note: NEVER send this value in the clear or over any weakly
+ // encrypted channel (such as simple XOR masking). If you are unsure
+ // ask Aaron or MarkL.
+
+ BOOL isGodlike() const;
+ U8 getGodLevel() const;
+ BOOL isGroupTitleHidden() const { return mHideGroupTitle; }
+ BOOL isGroupMember() const { return !mGroupID.isNull(); } // This is only used for building titles!
+ const LLUUID &getGroupID() const { return mGroupID; }
+ ECameraMode getCameraMode() const { return mCameraMode; }
+ BOOL getFocusOnAvatar() const { return mFocusOnAvatar; }
+ LLViewerObject* getFocusObject() const { return mFocusObject; }
+ F32 getFocusObjectDist() const { return mFocusObjectDist; }
+ BOOL inPrelude();
+ BOOL canManageEstate() const;
+
+ const LLUUID& getInventoryRootID() const { return mInventoryRootID; }
+
+ void buildFullname(std::string &name) const;
+ void buildFullnameAndTitle(std::string &name) const;
+
+ // Check against all groups in the entire agent group list.
+ BOOL isInGroup(const LLUUID& group_id) const;
+ BOOL hasPowerInGroup(const LLUUID& group_id, U64 power) const;
+ // Check for power in just the active group.
+ BOOL hasPowerInActiveGroup(const U64 power) const;
+ U64 getPowerInGroup(const LLUUID& group_id) const;
+
+ // Get group information by group_id. if not in group, data is
+ // left unchanged and method returns FALSE. otherwise, values are
+ // copied and returns TRUE.
+ BOOL getGroupData(const LLUUID& group_id, LLGroupData& data) const;
+ // Get just the agent's contribution to the given group.
+ S32 getGroupContribution(const LLUUID& group_id) const;
+
+ // return TRUE if the database reported this login as the first
+ // for this particular user.
+ BOOL isFirstLogin() const { return mFirstLogin; }
+
+ // On the very first login, gender isn't chosen until the user clicks
+ // in a dialog. We don't render the avatar until they choose.
+ BOOL isGenderChosen() const { return mGenderChosen; }
+
+ // utility to build a location string
+ void buildLocationString(std::string& str);
+
+ LLQuaternion getHeadRotation();
+ LLVOAvatar *getAvatarObject() const { return mAvatarObject; }
+
+ BOOL needsRenderAvatar(); // TRUE when camera mode is such that your own avatar should draw
+ // Not const because timers can't be accessed in const-fashion.
+ BOOL needsRenderHead();
+ BOOL cameraThirdPerson() const { return (mCameraMode == CAMERA_MODE_THIRD_PERSON && mLastCameraMode == CAMERA_MODE_THIRD_PERSON); }
+ BOOL cameraMouselook() const { return (mCameraMode == CAMERA_MODE_MOUSELOOK && mLastCameraMode == CAMERA_MODE_MOUSELOOK); }
+ BOOL cameraCustomizeAvatar() const { return (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR /*&& !mCameraAnimating*/); }
+ BOOL cameraFollow() const { return (mCameraMode == CAMERA_MODE_FOLLOW && mLastCameraMode == CAMERA_MODE_FOLLOW); }
+
+ LLVector3 getPosAgentFromGlobal(const LLVector3d &pos_global) const;
+ LLVector3d getPosGlobalFromAgent(const LLVector3 &pos_agent) const;
+
+ // Get the data members
+ const LLVector3& getAtAxis() const { return mFrameAgent.getAtAxis(); } // direction avatar is looking, not camera
+ const LLVector3& getUpAxis() const { return mFrameAgent.getUpAxis(); } // direction avatar is looking, not camera
+ const LLVector3& getLeftAxis() const { return mFrameAgent.getLeftAxis(); } // direction avatar is looking, not camera
+
+ LLCoordFrame getFrameAgent() const { return mFrameAgent; }
+ LLVector3 getVelocity() const;
+ F32 getVelocityZ() const { return getVelocity().mV[VZ]; } // a hack
+
+ const LLVector3d &getPositionGlobal();
+ const LLVector3 &getPositionAgent();
+ const S32 getRegionsVisited() const;
+ const F64 getDistanceTraveled() const;
+
+ const LLVector3d &getFocusGlobal() const { return mFocusGlobal; }
+ const LLVector3d &getFocusTargetGlobal() const { return mFocusTargetGlobal; }
+
+ BOOL getJump() const { return mbJump; }
+ BOOL getAutoPilot() const { return mAutoPilot; }
+ LLVector3d getAutoPilotTargetGlobal() const { return mAutoPilotTargetGlobal; }
+
+ LLQuaternion getQuat() const; // returns the quat that represents the rotation
+ // of the agent in the absolute frame
+// BOOL getLookingAtAvatar() const;
+
+ void getName(LLString& name);
+
+ const LLColor4 &getEffectColor();
+ void setEffectColor(const LLColor4 &color);
+ //
+ // UTILITIES
+ //
+
+ // Set the physics data
+ void slamLookAt(const LLVector3 &look_at);
+
+ void setPositionAgent(const LLVector3 &center);
+
+ void resetAxes();
+ void resetAxes(const LLVector3 &look_at); // makes reasonable left and up
+
+ // Move the avatar's frame
+ void rotate(F32 angle, const LLVector3 &axis);
+ void rotate(F32 angle, F32 x, F32 y, F32 z);
+ void rotate(const LLMatrix3 &matrix);
+ void rotate(const LLQuaternion &quaternion);
+ void pitch(F32 angle);
+ void roll(F32 angle);
+ void yaw(F32 angle);
+ LLVector3 getReferenceUpVector();
+
+ void setThirdPersonHeadOffset(LLVector3 offset) { mThirdPersonHeadOffset = offset; }
+ // Flight management
+ BOOL getFlying() const { return mControlFlags & AGENT_CONTROL_FLY; }
+ void setFlying(BOOL fly);
+ void toggleFlying();
+
+ // Does this parcel allow you to fly?
+ BOOL canFly();
+
+ // Animation functions
+ void requestStopMotion( LLMotion* motion );
+
+ void sendAnimationRequests(LLDynamicArray<LLUUID> &anim_ids, EAnimRequest request);
+ void sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request);
+
+ LLVector3d calcFocusOffset(LLViewerObject *object, S32 x, S32 y);
+ BOOL calcCameraMinDistance(F32 &obj_min_distance);
+
+ void startCameraAnimation();
+ void stopCameraAnimation();
+
+ void cameraZoomIn(const F32 factor); // zoom in by fraction of current distance
+ void cameraOrbitAround(const F32 radians); // rotate camera CCW radians about build focus point
+ void cameraOrbitOver(const F32 radians); // rotate camera forward radians over build focus point
+ void cameraOrbitIn(const F32 meters); // move camera in toward build focus point
+
+ F32 getCameraZoomFraction(); // get camera zoom as fraction of minimum and maximum zoom
+ void setCameraZoomFraction(F32 fraction); // set camera zoom as fraction of minimum and maximum zoom
+
+ void cameraPanIn(const F32 meters);
+ void cameraPanLeft(const F32 meters);
+ void cameraPanUp(const F32 meters);
+
+ void updateFocusOffset();
+ void validateFocusObject();
+
+ void setUsingFollowCam( bool using_follow_cam);
+
+ F32 calcCustomizeAvatarUIOffset( const LLVector3d& camera_pos_global );
+
+ // marks current location as start, sends information to servers
+ void setStartPosition(U32 location_id);
+
+ // Movement from user input. All set the appropriate animation flags.
+ // All turn off autopilot and make sure the camera is behind the avatar.
+ // direction is either positive, zero, or negative
+ void moveAt(S32 direction);
+ void moveAtNudge(S32 direction);
+ void moveLeft(S32 direction);
+ void moveLeftNudge(S32 direction);
+ void moveUp(S32 direction);
+ void moveYaw(F32 mag);
+ void movePitch(S32 direction);
+
+ void setOrbitLeftKey(F32 mag) { mOrbitLeftKey = mag; }
+ void setOrbitRightKey(F32 mag) { mOrbitRightKey = mag; }
+ void setOrbitUpKey(F32 mag) { mOrbitUpKey = mag; }
+ void setOrbitDownKey(F32 mag) { mOrbitDownKey = mag; }
+ void setOrbitInKey(F32 mag) { mOrbitInKey = mag; }
+ void setOrbitOutKey(F32 mag) { mOrbitOutKey = mag; }
+
+ void setPanLeftKey(F32 mag) { mPanLeftKey = mag; }
+ void setPanRightKey(F32 mag) { mPanRightKey = mag; }
+ void setPanUpKey(F32 mag) { mPanUpKey = mag; }
+ void setPanDownKey(F32 mag) { mPanDownKey = mag; }
+ void setPanInKey(F32 mag) { mPanInKey = mag; }
+ void setPanOutKey(F32 mag) { mPanOutKey = mag; }
+
+ U32 getControlFlags();
+ void setControlFlags(U32 mask); // performs bitwise mControlFlags |= mask
+ void clearControlFlags(U32 mask); // performs bitwise mControlFlags &= mask
+ BOOL controlFlagsDirty() const;
+ void enableControlFlagReset();
+ void resetControlFlags();
+
+ void propagate(const F32 dt); // BUG: should roll into updateAgentPosition
+
+ void updateWanderTarget(); // drones will pick points in the world to autopilot towards
+
+ void startAutoPilotGlobal(const LLVector3d &pos_global, const std::string& behavior_name = "", const LLQuaternion *target_rotation = NULL,
+ void (*finish_callback)(BOOL, void *) = NULL, void *callback_data = NULL, F32 stop_distance = 0.f, F32 rotation_threshold = 0.03f);
+
+ void startFollowPilot(const LLUUID &leader_id);
+ void stopAutoPilot(BOOL user_cancel = FALSE);
+ void setAutoPilotGlobal(const LLVector3d &pos_global);
+ void autoPilot(F32 *delta_yaw); // autopilot walking action, angles in radians
+ void renderAutoPilotTarget();
+
+ //
+ // teportation methods
+ //
+
+ // go to a named location home
+ void teleportRequest(
+ const U64& region_handle,
+ const LLVector3& pos_local);
+
+ // teleport to a landmark
+ void teleportViaLandmark(const LLUUID& landmark_id);
+
+ // go home
+ void teleportHome() { teleportViaLandmark(LLUUID::null); }
+
+ // to an invited location
+ void teleportViaLure(const LLUUID& lure_id, BOOL godlike);
+
+ // to a global location - this will probably need to be
+ // deprecated.
+ void teleportViaLocation(const LLVector3d& pos_global);
+
+ // cancel the teleport, may or may not be allowed by server
+ void teleportCancel();
+
+ void setTargetVelocity(const LLVector3 &vel);
+ const LLVector3 &getTargetVelocity() const;
+
+
+ // Setting the ability for this avatar to proxy for another avatar.
+ //static void processAddModifyAbility(LLMessageSystem* msg, void**);
+ //static void processGrantedProxies(LLMessageSystem* msg, void**);
+ //static void processRemoveModifyAbility(LLMessageSystem* msg, void**);
+ //BOOL isProxyFor(const LLUUID& agent_id);// *FIX should be const
+
+ static void processAgentDataUpdate(LLMessageSystem *msg, void **);
+ static void processAgentGroupDataUpdate(LLMessageSystem *msg, void **);
+ static void processAgentDropGroup(LLMessageSystem *msg, void **);
+ static void processScriptControlChange(LLMessageSystem *msg, void **);
+ static void processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data);
+ //static void processControlTake(LLMessageSystem *msg, void **);
+ //static void processControlRelease(LLMessageSystem *msg, void **);
+
+ // This method checks to see if this agent can modify an object
+ // based on the permissions and the agent's proxy status.
+ BOOL isGrantedProxy(const LLPermissions& perm);
+
+ BOOL allowOperation(PermissionBit op,
+ const LLPermissions& perm,
+ U64 group_proxy_power = 0,
+ U8 god_minimum = GOD_MAINTENANCE);
+
+ friend std::ostream& operator<<(std::ostream &s, const LLAgent &sphere);
+
+ void initOriginGlobal(const LLVector3d &origin_global); // Only to be used in ONE place! - djs 08/07/02
+
+ BOOL leftButtonGrabbed() const { return ( (!cameraMouselook() && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
+ ||(cameraMouselook() && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0)
+ ||(!cameraMouselook() && mControlsTakenPassedOnCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
+ ||(cameraMouselook() && mControlsTakenPassedOnCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0)); }
+ BOOL rotateGrabbed() const { return ( (mControlsTakenCount[CONTROL_YAW_POS_INDEX] > 0)
+ ||(mControlsTakenCount[CONTROL_YAW_NEG_INDEX] > 0)); }
+ BOOL forwardGrabbed() const { return ( (mControlsTakenCount[CONTROL_AT_POS_INDEX] > 0)); }
+ BOOL backwardGrabbed() const { return ( (mControlsTakenCount[CONTROL_AT_NEG_INDEX] > 0)); }
+ BOOL upGrabbed() const { return ( (mControlsTakenCount[CONTROL_UP_POS_INDEX] > 0)); }
+ BOOL downGrabbed() const { return ( (mControlsTakenCount[CONTROL_UP_NEG_INDEX] > 0)); }
+
+ // True iff a script has taken over a control.
+ BOOL anyControlGrabbed() const;
+
+ BOOL isControlGrabbed(S32 control_index) const;
+
+ // Send message to simulator to force grabbed controls to be
+ // released, in case of a poorly written script.
+ void forceReleaseControls();
+
+ BOOL sitCameraEnabled() { return mSitCameraEnabled; }
+
+ F32 getCurrentCameraBuildOffset() { return (F32)mCameraFocusOffset.magVec(); }
+
+ // look at behavior
+ BOOL setLookAt(ELookAtType target_type, LLViewerObject *object = NULL, LLVector3 position = LLVector3::zero);
+ ELookAtType getLookAtType();
+
+ // point at behavior
+ BOOL setPointAt(EPointAtType target_type, LLViewerObject *object = NULL, LLVector3 position = LLVector3::zero);
+ EPointAtType getPointAtType();
+
+ void setHomePosRegion( const U64& region_handle, const LLVector3& pos_region );
+ BOOL getHomePosGlobal( LLVector3d* pos_global );
+ void setCameraAnimating( BOOL b ) { mCameraAnimating = b; }
+ void setAnimationDuration( F32 seconds ) { mAnimationDuration = seconds; }
+
+ F32 getNearChatRadius() { return mNearChatRadius; }
+
+ enum ETeleportState
+ {
+ TELEPORT_NONE = 0, // No teleport in progress
+ TELEPORT_START = 1, // Transition to REQUESTED. Viewer has sent a TeleportRequest to the source simulator
+ TELEPORT_REQUESTED = 2, // Waiting for source simulator to respond
+ TELEPORT_MOVING = 3, // Viewer has received destination location from source simulator
+ TELEPORT_START_ARRIVAL = 4, // Transition to ARRIVING. Viewer has received avatar update, etc., from destination simulator
+ TELEPORT_ARRIVING = 5, // Make the user wait while content "pre-caches"
+ TELEPORT_CANCELLING = 6 // used only if user clicks "cancel" button
+ };
+
+ ETeleportState getTeleportState() const { return mTeleportState; }
+ void setTeleportState( ETeleportState state );
+ const LLString& getTeleportMessage() const { return mTeleportMessage; }
+ void setTeleportMessage(const LLString& message)
+ {
+ mTeleportMessage = message;
+ }
+
+ // trigger random fidget animations
+ void fidget();
+
+ void requestEnterGodMode();
+ void requestLeaveGodMode();
+
+ void sendAgentSetAppearance();
+
+ void sendAgentDataUpdateRequest();
+
+ // Ventrella
+ LLFollowCam mFollowCam;
+ // end Ventrella
+
+ //--------------------------------------------------------------------
+ // Wearables
+ //--------------------------------------------------------------------
+ BOOL getWearablesLoaded() const { return mWearablesLoaded; }
+
+ void setWearable( LLInventoryItem* new_item, LLWearable* wearable );
+ static void onSetWearableDialog( S32 option, void* userdata );
+ void setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable );
+ void setWearableOutfit( const LLInventoryItem::item_array_t& items, const LLDynamicArray< LLWearable* >& wearables, BOOL remove );
+ void queryWearableCache();
+
+ BOOL isWearableModifiable(EWearableType type);
+ BOOL isWearableCopyable(EWearableType type);
+ BOOL needsReplacement(EWearableType wearableType, S32 remove);
+ U32 getWearablePermMask(EWearableType type);
+
+ LLInventoryItem* getWearableInventoryItem(EWearableType type);
+
+ LLWearable* getWearable( EWearableType type ) { return (type < WT_COUNT) ? mWearableEntry[ type ].mWearable : NULL; }
+ BOOL isWearingItem( const LLUUID& item_id );
+ LLWearable* getWearableFromWearableItem( const LLUUID& item_id );
+ const LLUUID& getWearableItem( EWearableType type ) { return (type < WT_COUNT) ? mWearableEntry[ type ].mItemID : LLUUID::null; }
+
+ static EWearableType getTEWearableType( S32 te );
+ static LLUUID getDefaultTEImageID( S32 te );
+
+ void copyWearableToInventory( EWearableType type );
+
+ void makeNewOutfit(
+ const std::string& new_folder_name,
+ const LLDynamicArray<S32>& wearables_to_include,
+ const LLDynamicArray<S32>& attachments_to_include,
+ BOOL rename_clothing);
+ void makeNewOutfitDone(S32 index);
+
+ void removeWearable( EWearableType type );
+ static void onRemoveWearableDialog( S32 option, void* userdata );
+ void removeWearableFinal( EWearableType type );
+
+ void sendAgentWearablesUpdate();
+
+ /**
+ * @brief Only public because of addWearableToAgentInventoryCallback.
+ *
+ * NOTE: Do not call this method unless you are the inventory callback.
+ * NOTE: This can suffer from race conditions when working on the
+ * same values for index.
+ * @param index The index in mWearableEntry.
+ * @param item_id The inventory item id of the new wearable to wear.
+ * @param wearable The actual wearable data.
+ */
+ void addWearabletoAgentInventoryDone(
+ S32 index,
+ const LLUUID& item_id,
+ LLWearable* wearable);
+
+ void saveWearableAs( EWearableType type, const std::string& new_name, BOOL save_in_lost_and_found );
+ void saveWearable( EWearableType type, BOOL send_update = TRUE );
+ void saveAllWearables();
+
+ void revertWearable( EWearableType type );
+ void revertAllWearables();
+
+ void setWearableName( const LLUUID& item_id, const std::string& new_name );
+ void createStandardWearables(BOOL female);
+ void createStandardWearablesDone(S32 index);
+ void createStandardWearablesAllDone();
+
+ BOOL areWearablesLoaded() { return mWearablesLoaded; }
+
+ void observeFriends();
+ void friendsChanged();
+
+ // statics
+ static void stopFidget();
+ static void processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data);
+ static void userRemoveWearable( void* userdata ); // userdata is EWearableType
+ static void userRemoveAllClothes( void* userdata ); // userdata is NULL
+ static void userRemoveAllClothesStep2(BOOL proceed, void* userdata ); // userdata is NULL
+ static void userRemoveAllAttachments( void* userdata); // userdata is NULL
+ static BOOL selfHasWearable( void* userdata ); // userdata is EWearableType
+
+ //debug methods
+ static void clearVisualParams(void *);
+
+protected:
+ // stuff to do for any sort of teleport. Returns true if the
+ // teleport can proceed.
+ bool teleportCore(bool is_local = false);
+
+ // helper function to prematurely age chat when agent is moving
+ void ageChat();
+
+ // internal wearable functions
+ void sendAgentWearablesRequest();
+ static void onInitialWearableAssetArrived(LLWearable* wearable, void* userdata);
+ void recoverMissingWearable(EWearableType type);
+ void recoverMissingWearableDone();
+ void addWearableToAgentInventory(LLPointer<LLInventoryCallback> cb,
+ LLWearable* wearable, const LLUUID& category_id = LLUUID::null,
+ BOOL notify = TRUE);
+public:
+ // TODO: Make these private!
+ U32 mViewerPort; // Port this agent transmits on.
+ LLUUID mSecureSessionID; // secure token for this login session
+
+ F32 mDrawDistance;
+
+ // Access or "maturity" level
+ U8 mAccess; // SIM_ACCESS_MATURE or SIM_ACCESS_PG
+ U64 mGroupPowers;
+ BOOL mHideGroupTitle;
+ char mGroupTitle[DB_GROUP_TITLE_BUF_SIZE]; // honorific, like "Sir"
+ char mGroupName[DB_GROUP_NAME_BUF_SIZE];
+ LLUUID mGroupID;
+ //LLUUID mGroupInsigniaID;
+ LLUUID mInventoryRootID;
+ LLUUID mMapID;
+ F64 mMapOriginX; // Global x coord of mMapID's bottom left corner.
+ F64 mMapOriginY; // Global y coord of mMapID's bottom left corner.
+ S32 mMapWidth; // Width of map in meters
+ S32 mMapHeight; // Height of map in meters
+ std::string mMOTD; // message of the day
+
+ LLPointer<LLHUDEffectLookAt> mLookAt;
+ LLPointer<LLHUDEffectPointAt> mPointAt;
+
+ LLDynamicArray<LLGroupData> mGroups;
+
+ BOOL mInitialized;
+
+ static BOOL sDebugDisplayTarget;
+ S32 mNumPendingQueries;
+ S32* mActiveCacheQueries;
+
+ BOOL mForceMouselook;
+
+private:
+ ETeleportState mTeleportState;
+ LLString mTeleportMessage;
+
+ S32 mControlsTakenCount[TOTAL_CONTROLS];
+ S32 mControlsTakenPassedOnCount[TOTAL_CONTROLS];
+
+ LLViewerRegion *mRegionp;
+ LLVector3d mAgentOriginGlobal; // Origin of agent coords from global coords
+ mutable LLVector3d mPositionGlobal;
+
+ std::set<U64> mRegionsVisited; // stat - what distinct regions has the avatar been to?
+ F64 mDistanceTraveled; // stat - how far has the avatar moved?
+ LLVector3d mLastPositionGlobal; // Used to calculate travel distance
+
+ LLPointer<LLVOAvatar> mAvatarObject; // NULL until avatar object sent down from simulator
+
+ U8 mRenderState; // Current behavior state of agent
+ LLFrameTimer mTypingTimer;
+
+ ECameraMode mCameraMode; // target mode after transition animation is done
+ ECameraMode mLastCameraMode;
+ BOOL mViewsPushed; // keep track of whether or not we have pushed views.
+
+ BOOL mbAlwaysRun; // should the avatar run rather than walk
+ BOOL mShowAvatar; // should we render the avatar?
+ BOOL mCameraAnimating; // camera is transitioning from one mode to another
+ LLVector3d mAnimationCameraStartGlobal; // camera start position, global coords
+ LLVector3d mAnimationFocusStartGlobal; // camera focus point, global coords
+ LLFrameTimer mAnimationTimer; // seconds that transition animation has been active
+ F32 mAnimationDuration; // seconds
+ F32 mCameraFOVZoomFactor; // amount of fov zoom applied to camera when zeroing in on an object
+ F32 mCameraCurrentFOVZoomFactor; // interpolated fov zoom
+ F32 mCameraFOVDefault; // default field of view that is basis for FOV zoom effect
+ LLVector3d mCameraFocusOffset; // offset from focus point in build mode
+ LLVector3d mCameraFocusOffsetTarget; // target towards which we are lerping the camera's focus offset
+ LLVector3 mCameraOffsetDefault; // default third-person camera offset
+ LLVector4 mCameraCollidePlane; // colliding plane for camera
+ F32 mCurrentCameraDistance; // current camera offset from avatar
+ F32 mTargetCameraDistance; // target camera offset from avatar
+ F32 mCameraZoomFraction; // mousewheel driven fraction of zoom
+ LLVector3 mCameraLag; // third person camera lag
+ LLVector3 mThirdPersonHeadOffset; // head offset for third person camera position
+ LLVector3 mCameraPositionAgent; // camera position in agent coordinates
+ LLVector3 mCameraVirtualPositionAgent; // camera virtual position (target) before performing FOV zoom
+ BOOL mSitCameraEnabled; // use provided camera information when sitting?
+ LLVector3 mSitCameraPos; // root relative camera pos when sitting
+ LLVector3 mSitCameraFocus; // root relative camera target when sitting
+
+ //Ventrella
+ LLVector3 mCameraUpVector; // camera's up direction in world coordinates (determines the 'roll' of the view)
+ //End Ventrella
+
+ LLPointer<LLViewerObject> mSitCameraReferenceObject; // object to which camera is related when sitting
+
+ BOOL mFocusOnAvatar;
+ LLVector3d mFocusGlobal;
+ LLVector3d mFocusTargetGlobal;
+ LLPointer<LLViewerObject> mFocusObject;
+ F32 mFocusObjectDist;
+ LLVector3 mFocusObjectOffset;
+ F32 mFocusDotRadius; // meters
+ BOOL mTrackFocusObject;
+ F32 mUIOffset;
+
+ LLCoordFrame mFrameAgent; // Agent position and view, agent-region coordinates
+
+ BOOL mCrouching;
+ BOOL mIsBusy;
+
+ S32 mAtKey; // Either 1, 0, or -1... indicates that movement-key is pressed
+ S32 mWalkKey; // like AtKey, but causes less forward thrust
+ S32 mLeftKey;
+ S32 mUpKey;
+ F32 mYawKey;
+ S32 mPitchKey;
+
+ F32 mOrbitLeftKey;
+ F32 mOrbitRightKey;
+ F32 mOrbitUpKey;
+ F32 mOrbitDownKey;
+ F32 mOrbitInKey;
+ F32 mOrbitOutKey;
+
+ F32 mPanUpKey;
+ F32 mPanDownKey;
+ F32 mPanLeftKey;
+ F32 mPanRightKey;
+ F32 mPanInKey;
+ F32 mPanOutKey;
+
+ U32 mControlFlags; // replacement for the mFooKey's
+ BOOL mbFlagsDirty;
+ BOOL mbFlagsNeedReset; // HACK for preventing incorrect flags sent when crossing region boundaries
+
+ BOOL mbJump;
+
+ LLFrameTimer mWanderTimer;
+ LLVector3d mWanderTargetGlobal;
+
+ BOOL mAutoPilot;
+ BOOL mAutoPilotFlyOnStop;
+ LLVector3d mAutoPilotTargetGlobal;
+ F32 mAutoPilotStopDistance;
+ BOOL mAutoPilotUseRotation;
+ LLVector3 mAutoPilotTargetFacing;
+ F32 mAutoPilotTargetDist;
+ S32 mAutoPilotNoProgressFrameCount;
+ F32 mAutoPilotRotationThreshold;
+ std::string mAutoPilotBehaviorName;
+ void (*mAutoPilotFinishedCallback)(BOOL, void *);
+ void* mAutoPilotCallbackData;
+ LLUUID mLeaderID;
+
+ std::set<LLUUID> mProxyForAgents;
+
+ LLColor4 mEffectColor;
+
+ BOOL mHaveHomePosition;
+ U64 mHomeRegionHandle;
+ LLVector3 mHomePosRegion;
+ LLFrameTimer mChatTimer;
+ LLUUID mLastChatterID;
+ F32 mNearChatRadius;
+ BOOL mAdminOverride;
+
+ // See indra_constants.h for values.
+ U8 mGodLevel;
+ LLFrameTimer mFidgetTimer;
+ LLFrameTimer mFocusObjectFadeTimer;
+ std::set<LLPointer <LLViewerObject> > mFadeObjects;
+ F32 mNextFidgetTime;
+ S32 mCurrentFidget;
+ BOOL mFirstLogin;
+ BOOL mGenderChosen;
+
+ //--------------------------------------------------------------------
+ // Wearables
+ //--------------------------------------------------------------------
+ struct LLWearableEntry
+ {
+ LLWearableEntry() : mItemID( LLUUID::null ), mWearable( NULL ) {}
+
+ LLUUID mItemID; // ID of the inventory item in the agent's inventory.
+ LLWearable* mWearable;
+ };
+ LLWearableEntry mWearableEntry[ WT_COUNT ];
+ U32 mAgentWearablesUpdateSerialNum;
+ BOOL mWearablesLoaded;
+ S32 mTextureCacheQueryID;
+ U32 mAppearanceSerialNum;
+ LLAnimPauseRequest mPauseRequest;
+
+ class createStandardWearablesAllDoneCallback : public LLRefCount
+ {
+ public:
+ ~createStandardWearablesAllDoneCallback();
+ };
+ class sendAgentWearablesUpdateCallback : public LLRefCount
+ {
+ public:
+ ~sendAgentWearablesUpdateCallback();
+ };
+
+ class addWearableToAgentInventoryCallback : public LLInventoryCallback
+ {
+ public:
+ enum {
+ CALL_NONE = 0,
+ CALL_UPDATE = 1,
+ CALL_RECOVERDONE = 2,
+ CALL_CREATESTANDARDDONE = 4,
+ CALL_MAKENEWOUTFITDONE = 8
+ } EType;
+
+ /**
+ * @brief Construct a callback for dealing with the wearables.
+ *
+ * Would like to pass the agent in here, but we can't safely
+ * count on it being around later. Just use gAgent directly.
+ * @param cb callback to execute on completion (??? unused ???)
+ * @param index Index for the wearable in the agent
+ * @param wearable The wearable data.
+ * @param todo Bitmask of actions to take on completion.
+ */
+ addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount> cb,
+ S32 index,
+ LLWearable* wearable,
+ U32 todo = CALL_NONE);
+ ~addWearableToAgentInventoryCallback() {};
+ virtual void fire(const LLUUID& inv_item);
+
+ private:
+ S32 mIndex;
+ LLWearable* mWearable;
+ U32 mTodo;
+ LLPointer<LLRefCount> mCB;
+ };
+
+ //control listeners
+ class LLHideGroupTitleListener: public LLSimpleListener
+ {
+ public:
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD &userdata);
+ };
+
+ class LLEffectColorListener: public LLSimpleListener
+ {
+ public:
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD &userdata);
+ };
+
+ LLHideGroupTitleListener mHideGroupTitleListener;
+ LLEffectColorListener mEffectColorListener;
+ LLFriendObserver* mFriendObserver;
+};
+
+extern LLAgent gAgent;
+
+#endif
diff --git a/indra/newview/llagentdata.cpp b/indra/newview/llagentdata.cpp
new file mode 100644
index 0000000000..39ed421628
--- /dev/null
+++ b/indra/newview/llagentdata.cpp
@@ -0,0 +1,15 @@
+/**
+ * @file llagentdata.cpp
+ * @brief Contains commonly used agent data.
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llagentdata.h"
+
+LLUUID gAgentID;
+LLUUID gAgentSessionID;
diff --git a/indra/newview/llagentdata.h b/indra/newview/llagentdata.h
new file mode 100644
index 0000000000..eb120c7572
--- /dev/null
+++ b/indra/newview/llagentdata.h
@@ -0,0 +1,16 @@
+/**
+ * @file llagentdata.h
+ * @brief Contains commonly used agent data
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENTDATA_H
+#define LL_LLAGENTDATA_H
+
+extern LLUUID gAgentID;
+extern LLUUID gAgentSessionID;
+
+#endif
diff --git a/indra/newview/llagentpilot.cpp b/indra/newview/llagentpilot.cpp
new file mode 100644
index 0000000000..2a6353fe2e
--- /dev/null
+++ b/indra/newview/llagentpilot.cpp
@@ -0,0 +1,250 @@
+/**
+ * @file llagentpilot.cpp
+ * @brief LLAgentPilot class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+
+#include "llagentpilot.h"
+#include "llagent.h"
+#include "llframestats.h"
+#include "viewer.h"
+#include "llviewercontrol.h"
+
+LLAgentPilot gAgentPilot;
+
+BOOL LLAgentPilot::sLoop = TRUE;
+
+LLAgentPilot::LLAgentPilot()
+{
+ mRecording = FALSE;
+ mPlaying = FALSE;
+ mStarted = FALSE;
+ mNumRuns = -1;
+}
+
+LLAgentPilot::~LLAgentPilot()
+{
+}
+
+void LLAgentPilot::load(const char *filename)
+{
+ llifstream file;
+
+ file.open(filename);
+
+ if (!file)
+ {
+ llinfos << "Couldn't open " << filename << ", aborting agentpilot load!" << llendl;
+ return;
+ }
+ else
+ {
+ llinfos << "Opening pilot file " << filename << llendl;
+ }
+
+ S32 num_actions;
+
+ file >> num_actions;
+
+ S32 i;
+ for (i = 0; i < num_actions; i++)
+ {
+ S32 action_type;
+ Action new_action;
+ file >> new_action.mTime >> action_type;
+ file >> new_action.mTarget.mdV[VX] >> new_action.mTarget.mdV[VY] >> new_action.mTarget.mdV[VZ];
+ new_action.mType = (EActionType)action_type;
+ mActions.put(new_action);
+ }
+
+ file.close();
+}
+
+void LLAgentPilot::save(const char *filename)
+{
+ llofstream file;
+ file.open(filename);
+
+ if (!file)
+ {
+ llinfos << "Couldn't open " << filename << ", aborting agentpilot save!" << llendl;
+ }
+
+ file << mActions.count() << '\n';
+
+ S32 i;
+ for (i = 0; i < mActions.count(); i++)
+ {
+ file << mActions[i].mTime << "\t" << mActions[i].mType << "\t";
+ file << std::setprecision(32) << mActions[i].mTarget.mdV[VX] << "\t" << mActions[i].mTarget.mdV[VY] << "\t" << mActions[i].mTarget.mdV[VZ] << '\n';
+ }
+
+ file.close();
+}
+
+void LLAgentPilot::startRecord()
+{
+ mActions.reset();
+ mTimer.reset();
+ addAction(STRAIGHT);
+ mRecording = TRUE;
+}
+
+void LLAgentPilot::stopRecord()
+{
+ gAgentPilot.addAction(STRAIGHT);
+ gAgentPilot.save(gSavedSettings.getString("StatsPilotFile").c_str());
+ mRecording = FALSE;
+}
+
+void LLAgentPilot::addAction(enum EActionType action_type)
+{
+ llinfos << "Adding waypoint: " << gAgent.getPositionGlobal() << llendl;
+ Action action;
+ action.mType = action_type;
+ action.mTarget = gAgent.getPositionGlobal();
+ action.mTime = mTimer.getElapsedTimeF32();
+ mLastRecordTime = (F32)action.mTime;
+ mActions.put(action);
+}
+
+void LLAgentPilot::startPlayback()
+{
+ if (!mPlaying)
+ {
+ mPlaying = TRUE;
+ mCurrentAction = 0;
+ mTimer.reset();
+
+ if (mActions.count())
+ {
+ llinfos << "Starting playback, moving to waypoint 0" << llendl;
+ gAgent.startAutoPilotGlobal(mActions[0].mTarget);
+ mStarted = FALSE;
+ }
+ else
+ {
+ llinfos << "No autopilot data, cancelling!" << llendl;
+ mPlaying = FALSE;
+ }
+ }
+}
+
+void LLAgentPilot::stopPlayback()
+{
+ if (mPlaying)
+ {
+ mPlaying = FALSE;
+ mCurrentAction = 0;
+ mTimer.reset();
+ gAgent.stopAutoPilot();
+ }
+}
+
+void LLAgentPilot::updateTarget()
+{
+ if (mPlaying)
+ {
+ if (mCurrentAction < mActions.count())
+ {
+ if (0 == mCurrentAction)
+ {
+ if (gAgent.getAutoPilot())
+ {
+ // Wait until we get to the first location before starting.
+ return;
+ }
+ else
+ {
+ if (!mStarted)
+ {
+ llinfos << "At start, beginning playback" << llendl;
+ mTimer.reset();
+ LLFrameStats::startLogging(NULL);
+ mStarted = TRUE;
+ }
+ }
+ }
+ if (mTimer.getElapsedTimeF32() > mActions[mCurrentAction].mTime)
+ {
+ //gAgent.stopAutoPilot();
+ mCurrentAction++;
+
+ if (mCurrentAction < mActions.count())
+ {
+ gAgent.startAutoPilotGlobal(mActions[mCurrentAction].mTarget);
+ }
+ else
+ {
+ stopPlayback();
+ LLFrameStats::stopLogging(NULL);
+ mNumRuns--;
+ if (sLoop)
+ {
+ if ((mNumRuns < 0) || (mNumRuns > 0))
+ {
+ llinfos << "Looping, restarting playback" << llendl;
+ startPlayback();
+ }
+ else if (mQuitAfterRuns)
+ {
+ llinfos << "Done with all runs, quitting viewer!" << llendl;
+ app_force_quit(NULL);
+ }
+ else
+ {
+ llinfos << "Done with all runs, disabling pilot" << llendl;
+ stopPlayback();
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ stopPlayback();
+ }
+ }
+ else if (mRecording)
+ {
+ if (mTimer.getElapsedTimeF32() - mLastRecordTime > 1.f)
+ {
+ addAction(STRAIGHT);
+ }
+ }
+}
+
+// static
+void LLAgentPilot::startRecord(void *)
+{
+ gAgentPilot.startRecord();
+}
+
+void LLAgentPilot::saveRecord(void *)
+{
+ gAgentPilot.stopRecord();
+}
+
+void LLAgentPilot::addWaypoint(void *)
+{
+ gAgentPilot.addAction(STRAIGHT);
+}
+
+void LLAgentPilot::startPlayback(void *)
+{
+ gAgentPilot.mNumRuns = -1;
+ gAgentPilot.startPlayback();
+}
+
+void LLAgentPilot::stopPlayback(void *)
+{
+ gAgentPilot.stopPlayback();
+}
diff --git a/indra/newview/llagentpilot.h b/indra/newview/llagentpilot.h
new file mode 100644
index 0000000000..c4223ba490
--- /dev/null
+++ b/indra/newview/llagentpilot.h
@@ -0,0 +1,77 @@
+/**
+ * @file llagentpilot.h
+ * @brief LLAgentPilot class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENTPILOT_H
+#define LL_LLAGENTPILOT_H
+
+#include "stdtypes.h"
+#include "lltimer.h"
+#include "v3dmath.h"
+#include "lldarray.h"
+
+// Class that drives the agent around according to a "script".
+
+class LLAgentPilot
+{
+public:
+ enum EActionType
+ {
+ STRAIGHT,
+ TURN
+ };
+
+ LLAgentPilot();
+ virtual ~LLAgentPilot();
+
+ void load(const char *filename);
+ void save(const char *filename);
+
+ void startRecord();
+ void stopRecord();
+ void addAction(enum EActionType action);
+
+ void startPlayback();
+ void stopPlayback();
+
+ void updateTarget();
+
+ static void startRecord(void *);
+ static void addWaypoint(void *);
+ static void saveRecord(void *);
+ static void startPlayback(void *);
+ static void stopPlayback(void *);
+ static BOOL sLoop;
+
+ S32 mNumRuns;
+ BOOL mQuitAfterRuns;
+private:
+ void setAutopilotTarget(const S32 id);
+
+ BOOL mRecording;
+ F32 mLastRecordTime;
+
+ BOOL mStarted;
+ BOOL mPlaying;
+ S32 mCurrentAction;
+
+ class Action
+ {
+ public:
+
+ EActionType mType;
+ LLVector3d mTarget;
+ F64 mTime;
+ };
+
+ LLDynamicArray<Action> mActions;
+ LLTimer mTimer;
+};
+
+extern LLAgentPilot gAgentPilot;
+
+#endif // LL_LLAGENTPILOT_H
diff --git a/indra/newview/llappearance.h b/indra/newview/llappearance.h
new file mode 100644
index 0000000000..b198429865
--- /dev/null
+++ b/indra/newview/llappearance.h
@@ -0,0 +1,33 @@
+/**
+ * @file llappearance.h
+ * @brief LLAppearance class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAPPEARANCE_H
+#define LL_LLAPPEARANCE_H
+
+#include "llskiplist.h"
+#include "lluuid.h"
+
+class LLAppearance
+{
+public:
+ LLAppearance() {}
+ ~LLAppearance() { mParamMap.deleteAllData(); }
+
+ void addParam( S32 id, F32 value ) { mParamMap.addData( id, new F32(value) ); }
+ F32* getParam( S32 id ) { F32* temp = mParamMap.getIfThere( id ); return temp; } // temp works around an invalid warning.
+
+ void addTexture( S32 te, const LLUUID& uuid ) { if( te < LLVOAvatar::TEX_NUM_ENTRIES ) mTextures[te] = uuid; }
+ const LLUUID& getTexture( S32 te ) { return ( te < LLVOAvatar::TEX_NUM_ENTRIES ) ? mTextures[te] : LLUUID::null; }
+
+ void clear() { mParamMap.deleteAllData(); for( S32 i=0; i<LLVOAvatar::TEX_NUM_ENTRIES; i++ ) mTextures[i].setNull(); }
+
+ LLPtrSkipMap<S32, F32*> mParamMap;
+ LLUUID mTextures[LLVOAvatar::TEX_NUM_ENTRIES];
+};
+
+#endif // LL_LLAPPEARANCE_H
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
new file mode 100644
index 0000000000..cb96be2401
--- /dev/null
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -0,0 +1,197 @@
+/**
+ * @file llmapresponders.h
+ * @brief Processes responses received for asset upload requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llassetuploadresponders.h"
+
+#include "llagent.h"
+#include "llfloaterbuycurrency.h"
+#include "lleconomy.h"
+#include "llfilepicker.h"
+#include "llfocusmgr.h"
+#include "llnotify.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llpermissionsflags.h"
+#include "lluploaddialog.h"
+#include "llviewermenu.h" // FIXME -- for upload_new_resource()
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(const LLUUID& uuid,
+ const LLSD &post_data)
+ : LLHTTPClient::Responder()
+{
+ mUUID = uuid;
+ mPostData = post_data;
+}
+
+// virtual
+void LLNewAgentInventoryResponder::error(U32 statusNum, const std::string& reason)
+{
+ llinfos << "LLNewAgentInventoryResponder::error " << statusNum << llendl;
+ LLStringBase<char>::format_map_t args;
+ switch(statusNum)
+ {
+ case 400:
+ args["[FILE]"] = mPostData["inventory_type"].asString();
+ args["[REASON]"] = "invalid parameters in upload request";
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ break;
+ case 402:
+ //(result["message"].asString() == "insufficient funds")
+ LLFloaterBuyCurrency::buyCurrency("Uploading costs", gGlobalEconomy->getPriceUpload());
+ break;
+ case 500:
+ default:
+ args["[FILE]"] = mPostData["inventory_type"].asString();
+ args["[REASON]"] = "the server is experiencing unexpected difficulties";
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ break;
+ }
+ LLUploadDialog::modalUploadFinished();
+}
+
+//virtual
+void LLNewAgentInventoryResponder::result(const LLSD& result)
+{
+ lldebugs << "LLNewAgentInventoryResponder::result from capabilities" << llendl;
+
+ if (!result["success"])
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = mPostData["inventory_type"].asString();
+ args["[REASON]"] = "the server is experiencing unexpected difficulties";
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ return;
+ }
+
+ std::string uploader = result["uploader"];
+ LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString().c_str());
+ LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString().c_str());
+ // request succeeded
+ if (!uploader.empty())
+ {
+ LLHTTPClient::postFile(uploader, mUUID, asset_type, this);
+ }
+ // upload succeeded
+ else
+ {
+ // rename the file in the VFS to the actual asset id
+ gVFS->renameFile(mUUID, asset_type, result["new_asset"].asUUID(), asset_type);
+
+ // TODO: only request for textures, sound, and animation uploads
+ // Update money and ownership credit information
+ // since it probably changed on the server
+ if (mPostData["asset_type"].asString() == "texture" ||
+ mPostData["asset_type"].asString() == "sound" ||
+ mPostData["asset_type"].asString() == "animatn")
+ {
+ gMessageSystem->newMessageFast(_PREHASH_MoneyBalanceRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_MoneyData);
+ gMessageSystem->addUUIDFast(_PREHASH_TransactionID, LLUUID::null );
+ gAgent.sendReliableMessage();
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d",gGlobalEconomy->getPriceUpload());
+ LLNotifyBox::showXml("UploadPayment", args);
+ }
+ // Actually add the upload to viewer inventory
+ llinfos << "Adding " << result["new_inventory_item"].asUUID() << " "
+ << result["new_asset"].asUUID() << " to inventory." << llendl;
+ if(mPostData["folder_id"].asUUID().notNull())
+ {
+ LLPermissions perm;
+ U32 next_owner_perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
+ if (mPostData["inventory_type"].asString() == "snapshot")
+ {
+ next_owner_perm = PERM_ALL;
+ }
+ else
+ {
+ next_owner_perm = PERM_MOVE | PERM_TRANSFER;
+ }
+ perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, next_owner_perm);
+ S32 creation_date_now = time_corrected();
+ LLPointer<LLViewerInventoryItem> item
+ = new LLViewerInventoryItem(result["new_inventory_item"].asUUID(),
+ mPostData["folder_id"].asUUID(),
+ perm,
+ result["new_asset"].asUUID(),
+ asset_type,
+ inventory_type,
+ mPostData["name"].asString(),
+ mPostData["description"].asString(),
+ LLSaleInfo::DEFAULT,
+ LLInventoryItem::II_FLAGS_NONE,
+ creation_date_now);
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+
+ // Show the preview panel for textures and sounds to let
+ // user know that the image (or snapshot) arrived intact.
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+
+ view->getPanel()->setSelection(result["new_inventory_item"].asUUID(), TAKE_FOCUS_NO);
+ if((LLAssetType::AT_TEXTURE == asset_type)
+ || (LLAssetType::AT_SOUND == asset_type))
+ {
+ view->getPanel()->openSelected();
+ }
+ //LLInventoryView::dumpSelectionInformation((void*)view);
+ // restore keyboard focus
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ }
+ }
+ else
+ {
+ llwarns << "Can't find a folder to put it in" << llendl;
+ }
+
+ // remove the "Uploading..." message
+ LLUploadDialog::modalUploadFinished();
+
+ // *FIX: This is a pretty big hack. What this does is check the
+ // file picker if there are any more pending uploads. If so,
+ // upload that file.
+ const char* next_file = LLFilePicker::instance().getNextFile();
+ if(next_file)
+ {
+ const char* name = LLFilePicker::instance().getDirname();
+
+ LLString asset_name = name;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ upload_new_resource(next_file, asset_name, asset_name,
+ 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
+ }
+ }
+}
diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h
new file mode 100644
index 0000000000..b4d5377601
--- /dev/null
+++ b/indra/newview/llassetuploadresponders.h
@@ -0,0 +1,26 @@
+/**
+ * @file llmapresponders.h
+ * @brief Processes responses received for asset upload requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNEWAGENTINVENTORYRESPONDER_H
+#define LL_LLNEWAGENTINVENTORYRESPONDER_H
+
+#include "llhttpclient.h"
+
+class LLNewAgentInventoryResponder : public LLHTTPClient::Responder
+{
+public:
+ LLNewAgentInventoryResponder(const LLUUID& uuid, const LLSD& post_data);
+ void error(U32 statusNum, const std::string& reason);
+ virtual void result(const LLSD& content);
+
+private:
+ LLUUID mUUID;
+ LLSD mPostData;
+};
+
+#endif // LL_LLNEWAGENTINVENTORYRESPONDER_H
diff --git a/indra/newview/llaudiosourcevo.cpp b/indra/newview/llaudiosourcevo.cpp
new file mode 100644
index 0000000000..86cc1e206e
--- /dev/null
+++ b/indra/newview/llaudiosourcevo.cpp
@@ -0,0 +1,129 @@
+/**
+ * @file llaudiosourcevo.cpp
+ * @author Douglas Soo, James Cook
+ * @brief Audio sources attached to viewer objects
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llaudiosourcevo.h"
+
+#include "llagent.h"
+#include "llmutelist.h"
+#include "llviewerparcelmgr.h"
+
+LLAudioSourceVO::LLAudioSourceVO(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, LLViewerObject *objectp)
+: LLAudioSource(sound_id, owner_id, gain),
+ mObjectp(objectp),
+ mActualGain(gain)
+{
+ setAmbient(FALSE);
+ updateGain();
+ update();
+}
+
+LLAudioSourceVO::~LLAudioSourceVO()
+{
+ if (mObjectp)
+ {
+ mObjectp->clearAttachedSound();
+ }
+ mObjectp = NULL;
+}
+
+void LLAudioSourceVO::setGain(const F32 gain)
+{
+ mActualGain = llclamp(gain, 0.f, 1.f);
+ updateGain();
+}
+
+void LLAudioSourceVO::updateGain()
+{
+ if (!mObjectp)
+ {
+ return;
+ }
+
+ BOOL mute = FALSE;
+ if (gParcelMgr)
+ {
+ LLVector3d pos_global = mObjectp->getPositionGlobal();
+ if (!gParcelMgr->canHearSound(pos_global))
+ {
+ mute = TRUE;
+ }
+ }
+
+ if (!mute && gMuteListp)
+ {
+ if (gMuteListp->isMuted(mObjectp->getID()))
+ {
+ mute = TRUE;
+ }
+ else if (gMuteListp->isMuted(mOwnerID))
+ {
+ mute = TRUE;
+ }
+ else if (mObjectp->isAttachment())
+ {
+ LLViewerObject* parent = mObjectp;
+ while (parent
+ && !parent->isAvatar())
+ {
+ parent = (LLViewerObject*)parent->getParent();
+ }
+ if (parent
+ && gMuteListp->isMuted(parent->getID()))
+ {
+ mute = TRUE;
+ }
+ }
+ }
+
+ if (!mute)
+ {
+ mGain = mActualGain;
+ }
+ else
+ {
+ mGain = 0.f;
+ }
+}
+
+
+void LLAudioSourceVO::update()
+{
+ if (!mObjectp)
+ {
+ return;
+ }
+
+ if (mObjectp->isDead())
+ {
+ mObjectp = NULL;
+ return;
+ }
+
+ updateGain();
+ if (mObjectp->isHUDAttachment())
+ {
+ mPositionGlobal = gAgent.getCameraPositionGlobal();
+ }
+ else
+ {
+ mPositionGlobal = mObjectp->getPositionGlobal();
+ }
+ if (mObjectp->getSubParent())
+ {
+ mVelocity = mObjectp->getSubParent()->getVelocity();
+ }
+ else
+ {
+ mVelocity = mObjectp->getVelocity();
+ }
+
+ LLAudioSource::update();
+}
diff --git a/indra/newview/llaudiosourcevo.h b/indra/newview/llaudiosourcevo.h
new file mode 100644
index 0000000000..b16a70ec98
--- /dev/null
+++ b/indra/newview/llaudiosourcevo.h
@@ -0,0 +1,34 @@
+/**
+ * @file llaudiosourcevo.h
+ * @author Douglas Soo, James Cook
+ * @brief Audio sources attached to viewer objects
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAUDIOSOURCEVO_H
+#define LL_LLAUDIOSOURCEVO_H
+
+#include "audioengine.h"
+#include "llviewerobject.h"
+
+class LLViewerObject;
+
+class LLAudioSourceVO : public LLAudioSource
+{
+public:
+ LLAudioSourceVO(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, LLViewerObject *objectp);
+ virtual ~LLAudioSourceVO();
+ /*virtual*/ void update();
+ /*virtual*/ void setGain(const F32 gain);
+
+private:
+ void updateGain();
+
+private:
+ LLPointer<LLViewerObject> mObjectp;
+ F32 mActualGain; // The "real" gain, when not off due to parcel effects
+};
+
+#endif // LL_LLAUDIOSOURCEVO_H
diff --git a/indra/newview/llbox.cpp b/indra/newview/llbox.cpp
new file mode 100644
index 0000000000..3d5a6b6fea
--- /dev/null
+++ b/indra/newview/llbox.cpp
@@ -0,0 +1,105 @@
+/**
+ * @file llbox.cpp
+ * @brief Draws a box using display lists for speed.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llbox.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+
+LLBox gBox;
+
+// These routines support multiple textures on a box
+void LLBox::prerender()
+{
+ F32 size = 1.f;
+
+ mTriangleCount = 6 * 2;
+
+ mVertex[0][0] = mVertex[1][0] = mVertex[2][0] = mVertex[3][0] = -size / 2;
+ mVertex[4][0] = mVertex[5][0] = mVertex[6][0] = mVertex[7][0] = size / 2;
+ mVertex[0][1] = mVertex[1][1] = mVertex[4][1] = mVertex[5][1] = -size / 2;
+ mVertex[2][1] = mVertex[3][1] = mVertex[6][1] = mVertex[7][1] = size / 2;
+ mVertex[0][2] = mVertex[3][2] = mVertex[4][2] = mVertex[7][2] = -size / 2;
+ mVertex[1][2] = mVertex[2][2] = mVertex[5][2] = mVertex[6][2] = size / 2;
+}
+
+// These routines support multiple textures on a box
+void LLBox::cleanupGL()
+{
+ // No GL state, a noop.
+}
+
+void LLBox::renderface(S32 which_face)
+{
+ static F32 normals[6][3] =
+ {
+ {-1.0f, 0.0f, 0.0f},
+ { 0.0f, 1.0f, 0.0f},
+ { 1.0f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, 1.0f},
+ { 0.0f, 0.0f, -1.0f}
+ };
+ static S32 faces[6][4] =
+ {
+ {0, 1, 2, 3},
+ {3, 2, 6, 7},
+ {7, 6, 5, 4},
+ {4, 5, 1, 0},
+ {5, 6, 2, 1},
+ {7, 4, 0, 3}
+ };
+
+ glBegin(GL_QUADS);
+ glNormal3fv(&normals[which_face][0]);
+ glTexCoord2f(1,0);
+ glVertex3fv(&mVertex[ faces[which_face][0] ][0]);
+ glTexCoord2f(1,1);
+ glVertex3fv(&mVertex[ faces[which_face][1] ][0]);
+ glTexCoord2f(0,1);
+ glVertex3fv(&mVertex[ faces[which_face][2] ][0]);
+ glTexCoord2f(0,0);
+ glVertex3fv(&mVertex[ faces[which_face][3] ][0]);
+ glEnd();
+}
+
+void LLBox::render()
+{
+ // This is a flattend representation of the box as render here
+ // .
+ // (-++) (+++) /|\t
+ // +------------+ | (texture coordinates)
+ // |2 1| |
+ // | 4 | (*) --->s
+ // | TOP |
+ // | |
+ // (-++) (--+)|3 0|(+-+) (+++) (-++)
+ // +------------+------------+------------+------------+
+ // |2 1|2 1|2 1|2 1|
+ // | 0 | 1 | 2 | 3 |
+ // | BACK | RIGHT | FRONT | LEFT |
+ // | | | | |
+ // |3 0|3 0|3 0|3 0|
+ // +------------+------------+------------+------------+
+ // (-+-) (---)|2 1|(+--) (++-) (-+-)
+ // | 5 |
+ // | BOTTOM |
+ // | |
+ // |3 0|
+ // +------------+
+ // (-+-) (++-)
+
+ renderface(5);
+ renderface(4);
+ renderface(3);
+ renderface(2);
+ renderface(1);
+ renderface(0);
+}
diff --git a/indra/newview/llbox.h b/indra/newview/llbox.h
new file mode 100644
index 0000000000..038d82a4b6
--- /dev/null
+++ b/indra/newview/llbox.h
@@ -0,0 +1,32 @@
+/**
+ * @file llbox.h
+ * @brief Draws a box using display lists for speed.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBOX_H
+#define LL_LLBOX_H
+
+#include "stdtypes.h"
+
+class LLBox
+{
+protected:
+// GLuint mDisplayList;
+ F32 mVertex[8][3];
+ U32 mTriangleCount;
+public:
+ void prerender();
+ void cleanupGL();
+
+ void renderface(S32 which_face);
+ void render();
+
+ U32 getTriangleCount() { return mTriangleCount; }
+};
+
+extern LLBox gBox;
+
+#endif
diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp
new file mode 100644
index 0000000000..156a064a41
--- /dev/null
+++ b/indra/newview/llcallbacklist.cpp
@@ -0,0 +1,173 @@
+/**
+ * @file llcallbacklist.cpp
+ * @brief A simple list of callback functions to call.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcallbacklist.h"
+
+// Library includes
+#include "llerror.h"
+
+
+//
+// Globals
+//
+LLCallbackList gIdleCallbacks;
+
+//
+// Member functions
+//
+
+LLCallbackList::LLCallbackList()
+{
+ // nothing
+}
+
+LLCallbackList::~LLCallbackList()
+{
+}
+
+
+void LLCallbackList::addFunction( callback_t func, void *data)
+{
+ if (!func)
+ {
+ llerrs << "LLCallbackList::addFunction - function is NULL" << llendl;
+ return;
+ }
+
+ // only add one callback per func/data pair
+ callback_pair_t t(func, data);
+ callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
+ if (iter == mCallbackList.end())
+ {
+ mCallbackList.push_back(t);
+ }
+}
+
+
+BOOL LLCallbackList::containsFunction( callback_t func, void *data)
+{
+ callback_pair_t t(func, data);
+ callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
+ if (iter != mCallbackList.end())
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+BOOL LLCallbackList::deleteFunction( callback_t func, void *data)
+{
+ callback_pair_t t(func, data);
+ callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
+ if (iter != mCallbackList.end())
+ {
+ mCallbackList.erase(iter);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+void LLCallbackList::deleteAllFunctions()
+{
+ mCallbackList.clear();
+}
+
+
+void LLCallbackList::callFunctions()
+{
+ for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); )
+ {
+ callback_list_t::iterator curiter = iter++;
+ curiter->first(curiter->second);
+ }
+}
+
+#ifdef _DEBUG
+
+void test1(void *data)
+{
+ S32 *s32_data = (S32 *)data;
+ llinfos << "testfunc1 " << *s32_data << llendl;
+}
+
+
+void test2(void *data)
+{
+ S32 *s32_data = (S32 *)data;
+ llinfos << "testfunc2 " << *s32_data << llendl;
+}
+
+
+void
+LLCallbackList::test()
+{
+ S32 a = 1;
+ S32 b = 2;
+ LLCallbackList *list = new LLCallbackList;
+
+ llinfos << "Testing LLCallbackList" << llendl;
+
+ if (!list->deleteFunction(NULL))
+ {
+ llinfos << "passed 1" << llendl;
+ }
+ else
+ {
+ llinfos << "error, removed function from empty list" << llendl;
+ }
+
+ // llinfos << "This should crash" << llendl;
+ // list->addFunction(NULL);
+
+ list->addFunction(&test1, &a);
+ list->addFunction(&test1, &a);
+
+ llinfos << "Expect: test1 1, test1 1" << llendl;
+ list->callFunctions();
+
+ list->addFunction(&test1, &b);
+ list->addFunction(&test2, &b);
+
+ llinfos << "Expect: test1 1, test1 1, test1 2, test2 2" << llendl;
+ list->callFunctions();
+
+ if (list->deleteFunction(&test1, &b))
+ {
+ llinfos << "passed 3" << llendl;
+ }
+ else
+ {
+ llinfos << "error removing function" << llendl;
+ }
+
+ llinfos << "Expect: test1 1, test1 1, test2 2" << llendl;
+ list->callFunctions();
+
+ list->deleteAllFunctions();
+
+ llinfos << "Expect nothing" << llendl;
+ list->callFunctions();
+
+ llinfos << "nothing :-)" << llendl;
+
+ delete list;
+
+ llinfos << "test complete" << llendl;
+}
+
+#endif // _DEBUG
diff --git a/indra/newview/llcallbacklist.h b/indra/newview/llcallbacklist.h
new file mode 100644
index 0000000000..e647f50644
--- /dev/null
+++ b/indra/newview/llcallbacklist.h
@@ -0,0 +1,39 @@
+/**
+ * @file llcallbacklist.h
+ * @brief A simple list of callback functions to call.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCALLBACKLIST_H
+#define LL_LLCALLBACKLIST_H
+
+#include "llstl.h"
+
+class LLCallbackList
+{
+public:
+ typedef void (*callback_t)(void*);
+
+ LLCallbackList();
+ ~LLCallbackList();
+
+ void addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data)
+ BOOL containsFunction( callback_t func, void *data = NULL ); // true if list already contains the function/data pair
+ BOOL deleteFunction( callback_t func, void *data = NULL ); // removes the first instance of this function/data pair from the list, false if not found
+ void callFunctions(); // calls all functions
+ void deleteAllFunctions();
+
+ static void test();
+
+protected:
+ // Use a list so that the callbacks are ordered in case that matters
+ typedef std::pair<callback_t,void*> callback_pair_t;
+ typedef std::list<callback_pair_t > callback_list_t;
+ callback_list_t mCallbackList;
+};
+
+extern LLCallbackList gIdleCallbacks;
+
+#endif
diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
new file mode 100644
index 0000000000..c53d1d9ebc
--- /dev/null
+++ b/indra/newview/llcallingcard.cpp
@@ -0,0 +1,797 @@
+/**
+ * @file llcallingcard.cpp
+ * @brief Implementation of the LLPreviewCallingCard class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#if LL_WINDOWS
+#pragma warning( disable : 4800 ) // performance warning in <functional>
+#endif
+
+#include "llcallingcard.h"
+
+#include <vector>
+#include <algorithm>
+//#include <iterator>
+
+#include "indra_constants.h"
+#include "llcachename.h"
+#include "llstl.h"
+#include "lltimer.h"
+#include "lluuid.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+//#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "llimview.h"
+#include "llviewercontrol.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+class LLTrackingData
+{
+public:
+ LLTrackingData(const LLUUID& avatar_id, const std::string& name);
+ bool haveTrackingInfo();
+ void setTrackedCoarseLocation(const LLVector3d& global_pos);
+ void agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos);
+
+public:
+ LLUUID mAvatarID;
+ LLString mName;
+ LLVector3d mGlobalPositionEstimate;
+ bool mHaveInfo;
+ bool mHaveCoarseInfo;
+ LLTimer mCoarseLocationTimer;
+ LLTimer mUpdateTimer;
+ LLTimer mAgentGone;
+};
+
+const F32 COARSE_FREQUENCY = 2.2f;
+const F32 FIND_FREQUENCY = 29.7f; // This results in a database query, so cut these back
+const F32 OFFLINE_SECONDS = FIND_FREQUENCY + 8.0f;
+
+// static
+LLAvatarTracker LLAvatarTracker::sInstance;
+
+/*
+class LLAvatarTrackerInventoryObserver : public LLInventoryObserver
+{
+public:
+ LLAvatarTrackerInventoryObserver(LLAvatarTracker* at) :
+ mAT(at) {}
+ virtual ~LLAvatarTrackerInventoryObserver() {}
+ virtual void changed(U32 mask);
+protected:
+ LLAvatarTracker* mAT;
+};
+*/
+
+/*
+void LLAvatarTrackerInventoryObserver::changed(U32 mask)
+{
+ // if there's a calling card change, just do it.
+ if((mask & LLInventoryObserver::CALLING_CARD) != 0)
+ {
+ mAT->inventoryChanged();
+ }
+}
+*/
+
+///----------------------------------------------------------------------------
+/// Class LLAvatarTracker
+///----------------------------------------------------------------------------
+
+LLAvatarTracker::LLAvatarTracker() :
+ mTrackingData(NULL),
+ mTrackedAgentValid(false),
+ //mInventory(NULL),
+ //mInventoryObserver(NULL),
+ mModifyMask(0x0)
+{
+}
+
+LLAvatarTracker::~LLAvatarTracker()
+{
+ deleteTrackingData();
+ std::for_each(mObservers.begin(), mObservers.end(), DeletePointer());
+}
+
+void LLAvatarTracker::track(const LLUUID& avatar_id, const std::string& name)
+{
+ deleteTrackingData();
+ mTrackedAgentValid = false;
+ mTrackingData = new LLTrackingData(avatar_id, name);
+ findAgent();
+
+ // We track here because findAgent() is called on a timer (for now).
+ if(avatar_id.notNull())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TrackAgent);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_PreyID, avatar_id);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLAvatarTracker::untrack(const LLUUID& avatar_id)
+{
+ if (mTrackingData && mTrackingData->mAvatarID == avatar_id)
+ {
+ deleteTrackingData();
+ mTrackedAgentValid = false;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TrackAgent);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_PreyID, LLUUID::null);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLAvatarTracker::setTrackedCoarseLocation(const LLVector3d& global_pos)
+{
+ if(mTrackingData)
+ {
+ mTrackingData->setTrackedCoarseLocation(global_pos);
+ }
+}
+
+bool LLAvatarTracker::haveTrackingInfo()
+{
+ if(mTrackingData)
+ {
+ return mTrackingData->haveTrackingInfo();
+ }
+ return false;
+}
+
+LLVector3d LLAvatarTracker::getGlobalPos()
+{
+ if(!mTrackedAgentValid) return LLVector3d();
+ LLVector3d global_pos;
+
+ LLViewerObject* object = gObjectList.findObject(mTrackingData->mAvatarID);
+ if(object && !object->isDead())
+ {
+ global_pos = object->getPositionGlobal();
+ // HACK - for making the tracker point above the avatar's head
+ // rather than its groin
+ global_pos.mdV[VZ] += 0.7f * ((LLVOAvatar *)object)->mBodySize.mV[VZ];
+
+ mTrackingData->mGlobalPositionEstimate = global_pos;
+ }
+ else
+ {
+ global_pos = mTrackingData->mGlobalPositionEstimate;
+ }
+
+ return global_pos;
+}
+
+void LLAvatarTracker::getDegreesAndDist(F32& rot,
+ F64& horiz_dist,
+ F64& vert_dist)
+{
+ if(!mTrackingData) return;
+
+ LLVector3d global_pos;
+
+ LLViewerObject* object = gObjectList.findObject(mTrackingData->mAvatarID);
+ if(object && !object->isDead())
+ {
+ global_pos = object->getPositionGlobal();
+ mTrackingData->mGlobalPositionEstimate = global_pos;
+ }
+ else
+ {
+ global_pos = mTrackingData->mGlobalPositionEstimate;
+ }
+ LLVector3d to_vec = global_pos - gAgent.getPositionGlobal();
+ horiz_dist = sqrt(to_vec.mdV[VX] * to_vec.mdV[VX] + to_vec.mdV[VY] * to_vec.mdV[VY]);
+ vert_dist = to_vec.mdV[VZ];
+ rot = F32(RAD_TO_DEG * atan2(to_vec.mdV[VY], to_vec.mdV[VX]));
+}
+
+const LLString& LLAvatarTracker::getName()
+{
+ if(mTrackingData)
+ {
+ return mTrackingData->mName;
+ }
+ else
+ {
+ return LLString::null;
+ }
+}
+
+const LLUUID& LLAvatarTracker::getAvatarID()
+{
+ if(mTrackingData)
+ {
+ return mTrackingData->mAvatarID;
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds)
+{
+ using namespace std;
+
+ U32 new_buddy_count = 0;
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ LLUUID agent_id;
+ for(buddy_map_t::const_iterator itr = buds.begin(); itr != buds.end(); ++itr)
+ {
+ agent_id = (*itr).first;
+ if(mBuddyInfo.find(agent_id) == mBuddyInfo.end())
+ {
+ ++new_buddy_count;
+ mBuddyInfo[agent_id] = (*itr).second;
+ gCacheName->getName(agent_id, first, last);
+ mModifyMask |= LLFriendObserver::ADD;
+ }
+ }
+
+ return new_buddy_count;
+}
+
+
+void LLAvatarTracker::copyBuddyList(buddy_map_t& buddies) const
+{
+ buddy_map_t::const_iterator it = mBuddyInfo.begin();
+ buddy_map_t::const_iterator end = mBuddyInfo.end();
+ for(; it != end; ++it)
+ {
+ buddies[(*it).first] = (*it).second;
+ }
+}
+
+void LLAvatarTracker::terminateBuddy(const LLUUID& id)
+{
+ lldebugs << "LLAvatarTracker::terminateBuddy()" << llendl;
+ LLRelationship* buddy = get_ptr_in_map(mBuddyInfo, id);
+ if(!buddy) return;
+ mBuddyInfo.erase(id);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("TerminateFriendship");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ExBlock");
+ msg->addUUID("OtherID", id);
+ gAgent.sendReliableMessage();
+ mModifyMask |= LLFriendObserver::REMOVE;
+ delete buddy;
+}
+
+// get all buddy info
+const LLRelationship* LLAvatarTracker::getBuddyInfo(const LLUUID& id) const
+{
+ if(id.isNull()) return NULL;
+ return get_ptr_in_map(mBuddyInfo, id);
+}
+
+// online status
+void LLAvatarTracker::setBuddyOnline(const LLUUID& id, bool is_online)
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ info->online(is_online);
+ mModifyMask |= LLFriendObserver::ONLINE;
+ }
+}
+
+bool LLAvatarTracker::isBuddyOnline(const LLUUID& id) const
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ return info->isOnline();
+ }
+ return false;
+}
+
+// empowered status
+void LLAvatarTracker::setBuddyEmpowered(const LLUUID& id, bool is_empowered)
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ info->grantRights(LLRelationship::GRANT_MODIFY_OBJECTS, 0);
+ mModifyMask |= LLFriendObserver::POWERS;
+ }
+}
+
+bool LLAvatarTracker::isBuddyEmpowered(const LLUUID& id) const
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ return info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);
+ }
+ return false;
+}
+
+void LLAvatarTracker::empower(const LLUUID& id, bool grant)
+{
+ // wrapper for ease of use in some situations.
+ buddy_map_t list;
+ /*
+ list.insert(id);
+ empowerList(list, grant);
+ */
+}
+
+void LLAvatarTracker::empowerList(const buddy_map_t& list, bool grant)
+{
+ llwarns << "LLAvatarTracker::empowerList() not implemented." << llendl;
+/*
+ LLMessageSystem* msg = gMessageSystem;
+ const char* message_name;
+ const char* block_name;
+ const char* field_name;
+ if(grant)
+ {
+ message_name = _PREHASH_GrantModification;
+ block_name = _PREHASH_EmpoweredBlock;
+ field_name = _PREHASH_EmpoweredID;
+ }
+ else
+ {
+ message_name = _PREHASH_RevokeModification;
+ block_name = _PREHASH_RevokedBlock;
+ field_name = _PREHASH_RevokedID;
+ }
+
+ std::string name;
+ gAgent.buildFullnameAndTitle(name);
+
+ bool start_new_message = true;
+ buddy_list_t::const_iterator it = list.begin();
+ buddy_list_t::const_iterator end = list.end();
+ for(; it != end; ++it)
+ {
+ if(NULL == get_ptr_in_map(mBuddyInfo, (*it))) continue;
+ setBuddyEmpowered((*it), grant);
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(message_name);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addStringFast(_PREHASH_GranterName, name);
+ }
+ msg->nextBlockFast(block_name);
+ msg->addUUIDFast(field_name, (*it));
+ if(msg->isSendFullFast(block_name))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+*/
+}
+
+void LLAvatarTracker::deleteTrackingData()
+{
+ delete mTrackingData;
+ mTrackingData = NULL;
+}
+
+void LLAvatarTracker::findAgent()
+{
+ if (!mTrackingData) return;
+ if (mTrackingData->mAvatarID.isNull()) return;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_FindAgent); // Request
+ msg->nextBlockFast(_PREHASH_AgentBlock);
+ msg->addUUIDFast(_PREHASH_Hunter, gAgentID);
+ msg->addUUIDFast(_PREHASH_Prey, mTrackingData->mAvatarID);
+ msg->addU32Fast(_PREHASH_SpaceIP, 0); // will get filled in by userserver
+ msg->nextBlockFast(_PREHASH_LocationBlock);
+ const F64 NO_LOCATION = 0.0;
+ msg->addF64Fast(_PREHASH_GlobalX, NO_LOCATION);
+ msg->addF64Fast(_PREHASH_GlobalY, NO_LOCATION);
+ gAgent.sendReliableMessage();
+}
+
+void LLAvatarTracker::addObserver(LLFriendObserver* observer)
+{
+ if(observer)
+ {
+ mObservers.push_back(observer);
+ }
+}
+
+void LLAvatarTracker::removeObserver(LLFriendObserver* observer)
+{
+ mObservers.erase(
+ std::remove(mObservers.begin(), mObservers.end(), observer),
+ mObservers.end());
+}
+
+void LLAvatarTracker::notifyObservers()
+{
+ observer_list_t observers(mObservers);
+ observer_list_t::iterator it = observers.begin();
+ observer_list_t::iterator end = observers.end();
+ for(; it != end; ++it)
+ {
+ (*it)->changed(mModifyMask);
+ }
+ mModifyMask = LLFriendObserver::NONE;
+}
+
+void LLAvatarTracker::applyFunctor(LLRelationshipFunctor& f)
+{
+ buddy_map_t::iterator it = mBuddyInfo.begin();
+ buddy_map_t::iterator end = mBuddyInfo.end();
+ for(; it != end; ++it)
+ {
+ f((*it).first, (*it).second);
+ }
+}
+
+void LLAvatarTracker::registerCallbacks(LLMessageSystem* msg)
+{
+ msg->setHandlerFuncFast(_PREHASH_FindAgent, processAgentFound);
+ msg->setHandlerFuncFast(_PREHASH_OnlineNotification,
+ processOnlineNotification);
+ msg->setHandlerFuncFast(_PREHASH_OfflineNotification,
+ processOfflineNotification);
+ //msg->setHandlerFuncFast(_PREHASH_GrantedProxies,
+ // processGrantedProxies);
+ msg->setHandlerFunc("TerminateFriendship", processTerminateFriendship);
+ msg->setHandlerFunc(_PREHASH_ChangeUserRights, processChangeUserRights);
+}
+
+// static
+void LLAvatarTracker::processAgentFound(LLMessageSystem* msg, void**)
+{
+ LLUUID id;
+
+
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Hunter, id);
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Prey, id);
+ // *FIX: should make sure prey id matches.
+ LLVector3d estimated_global_pos;
+ msg->getF64Fast(_PREHASH_LocationBlock, _PREHASH_GlobalX,
+ estimated_global_pos.mdV[VX]);
+ msg->getF64Fast(_PREHASH_LocationBlock, _PREHASH_GlobalY,
+ estimated_global_pos.mdV[VY]);
+ LLAvatarTracker::instance().agentFound(id, estimated_global_pos);
+}
+
+void LLAvatarTracker::agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos)
+{
+ if(!mTrackingData) return;
+ //if we get a valid reply from the server, that means the agent
+ //is our friend and mappable, so enable interest list based updates
+ LLAvatarTracker::instance().setTrackedAgentValid(true);
+ mTrackingData->agentFound(prey, estimated_global_pos);
+}
+
+// static
+void LLAvatarTracker::processOnlineNotification(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLAvatarTracker::processOnlineNotification()" << llendl;
+ instance().processNotify(msg, true);
+}
+
+// static
+void LLAvatarTracker::processOfflineNotification(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLAvatarTracker::processOfflineNotification()" << llendl;
+ instance().processNotify(msg, false);
+}
+
+void LLAvatarTracker::processChange(LLMessageSystem* msg)
+{
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_Rights);
+ LLUUID agent_id, agent_related;
+ S32 new_rights;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ for(int i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_Rights, _PREHASH_AgentRelated, agent_related, i);
+ msg->getS32Fast(_PREHASH_Rights,_PREHASH_RelatedRights, new_rights, i);
+ if(agent_id == gAgent.getID())
+ {
+ if(mBuddyInfo.find(agent_related) != mBuddyInfo.end())
+ {
+ (mBuddyInfo[agent_related])->setRightsTo(new_rights);
+ }
+ }
+ else
+ {
+ if(mBuddyInfo.find(agent_id) != mBuddyInfo.end())
+ {
+ if((mBuddyInfo[agent_id]->getRightsGrantedFrom() ^ new_rights) & LLRelationship::GRANT_MODIFY_OBJECTS)
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ LLStringBase<char>::format_map_t args;
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ args["[FIRST_NAME]"] = first;
+ args["[LAST_NAME]"] = last;
+ }
+ if(LLRelationship::GRANT_MODIFY_OBJECTS & new_rights)
+ {
+ gViewerWindow->alertXml("GrantedModifyRights",args);
+ }
+ else
+ {
+ gViewerWindow->alertXml("RevokedModifyRights",args);
+ }
+ }
+ (mBuddyInfo[agent_id])->setRightsFrom(new_rights);
+ }
+ }
+ }
+ mModifyMask |= LLFriendObserver::POWERS;
+
+ notifyObservers();
+}
+
+void LLAvatarTracker::processChangeUserRights(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLAvatarTracker::processChangeUserRights()" << llendl;
+ instance().processChange(msg);
+}
+
+void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
+{
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_AgentBlock);
+ BOOL chat_notify = gSavedSettings.getBOOL("ChatOnlineNotification");
+
+ if(count > 0)
+ {
+ LLUUID agent_id;
+ const LLRelationship* info = NULL;
+ LLUUID tracking_id;
+ if(mTrackingData)
+ {
+ tracking_id = mTrackingData->mAvatarID;
+ }
+ BOOL notify = FALSE;
+ LLString::format_map_t args;
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_AgentID, agent_id, i);
+ info = getBuddyInfo(agent_id);
+ if(info)
+ {
+ setBuddyOnline(agent_id,online);
+ if(chat_notify)
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ notify = TRUE;
+ args["[FIRST]"] = first;
+ args["[LAST]"] = last;
+ }
+ }
+ }
+ if(tracking_id == agent_id)
+ {
+ // we were tracking someone who went offline
+ deleteTrackingData();
+ }
+ //FIXME get actual inventory id
+ gInventory.addChangedMask(LLInventoryObserver::CALLING_CARD, LLUUID::null);
+ }
+ if(notify)
+ {
+ LLNotifyBox::showXml(online ? "FriendOnline" : "FriendOffline", args);
+ }
+
+ mModifyMask |= LLFriendObserver::ONLINE;
+ instance().notifyObservers();
+ gInventory.notifyObservers();
+ }
+}
+
+// XUI:translate
+void LLAvatarTracker::formFriendship(const LLUUID& id)
+{
+ if(id.notNull())
+ {
+ LLRelationship* buddy_info = get_ptr_in_map(instance().mBuddyInfo, id);
+ if(!buddy_info)
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ //The default for relationship establishment is to have both parties
+ //visible online to each other.
+ buddy_info = new LLRelationship(LLRelationship::GRANT_ONLINE_STATUS,LLRelationship::GRANT_ONLINE_STATUS, false);
+ at.mBuddyInfo[id] = buddy_info;
+ at.mModifyMask |= LLFriendObserver::ADD;
+ at.notifyObservers();
+ }
+ }
+}
+
+void LLAvatarTracker::processTerminateFriendship(LLMessageSystem* msg, void**)
+{
+ LLUUID id;
+ msg->getUUID("ExBlock", "OtherID", id);
+ if(id.notNull())
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLRelationship* buddy = get_ptr_in_map(at.mBuddyInfo, id);
+ if(!buddy) return;
+ at.mBuddyInfo.erase(id);
+ at.mModifyMask |= LLFriendObserver::REMOVE;
+ delete buddy;
+ at.notifyObservers();
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Tracking Data
+///----------------------------------------------------------------------------
+
+LLTrackingData::LLTrackingData(const LLUUID& avatar_id, const std::string& name)
+: mAvatarID(avatar_id),
+ mHaveInfo(false),
+ mHaveCoarseInfo(false)
+{
+ mCoarseLocationTimer.setTimerExpirySec(COARSE_FREQUENCY);
+ mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ if(!name.empty())
+ {
+ mName = name;
+ }
+}
+
+void LLTrackingData::agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos)
+{
+ if(prey != mAvatarID)
+ {
+ llwarns << "LLTrackingData::agentFound() - found " << prey
+ << " but looking for " << mAvatarID << llendl;
+ }
+ mHaveInfo = true;
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ mGlobalPositionEstimate = estimated_global_pos;
+}
+
+bool LLTrackingData::haveTrackingInfo()
+{
+ LLViewerObject* object = gObjectList.findObject(mAvatarID);
+ if(object && !object->isDead())
+ {
+ mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY);
+ mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ mHaveInfo = true;
+ return true;
+ }
+ if(mHaveCoarseInfo &&
+ !mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY))
+ {
+ // if we reach here, then we have a 'recent' coarse update
+ mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ return true;
+ }
+ if(mUpdateTimer.checkExpirationAndReset(FIND_FREQUENCY))
+ {
+ LLAvatarTracker::instance().findAgent();
+ mHaveCoarseInfo = false;
+ }
+ if(mAgentGone.checkExpirationAndReset(OFFLINE_SECONDS))
+ {
+ mHaveInfo = false;
+ mHaveCoarseInfo = false;
+ }
+ return mHaveInfo;
+}
+
+void LLTrackingData::setTrackedCoarseLocation(const LLVector3d& global_pos)
+{
+ mCoarseLocationTimer.setTimerExpirySec(COARSE_FREQUENCY);
+ mGlobalPositionEstimate = global_pos;
+ mHaveInfo = true;
+ mHaveCoarseInfo = true;
+}
+
+///----------------------------------------------------------------------------
+// various buddy functors
+///----------------------------------------------------------------------------
+
+bool LLCollectProxyBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ if(buddy->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ mProxy.insert(buddy_id);
+ }
+ return true;
+}
+
+bool LLCollectMappableBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ mFirst[0] = '\0';
+ mLast[0] = '\0';
+ gCacheName->getName(buddy_id, mFirst, mLast);
+ std::ostringstream fullname;
+ fullname << mFirst << " " << mLast;
+ buddy_map_t::value_type value(fullname.str(), buddy_id);
+ if(buddy->isOnline() && buddy->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION))
+ {
+ mMappable.insert(value);
+ }
+ return true;
+}
+
+bool LLCollectOnlineBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ mFirst[0] = '\0';
+ mLast[0] = '\0';
+ gCacheName->getName(buddy_id, mFirst, mLast);
+ std::ostringstream fullname;
+ fullname << mFirst << " " << mLast;
+ buddy_map_t::value_type value(fullname.str(), buddy_id);
+ if(buddy->isOnline())
+ {
+ mOnline.insert(value);
+ }
+ return true;
+}
+
+bool LLCollectAllBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ mFirst[0] = '\0';
+ mLast[0] = '\0';
+ gCacheName->getName(buddy_id, mFirst, mLast);
+ std::ostringstream fullname;
+ fullname << mFirst << " " << mLast;
+ buddy_map_t::value_type value(fullname.str(), buddy_id);
+ if(buddy->isOnline())
+ {
+ mOnline.insert(value);
+ }
+ else
+ {
+ mOffline.insert(value);
+ }
+ return true;
+}
+
diff --git a/indra/newview/llcallingcard.h b/indra/newview/llcallingcard.h
new file mode 100644
index 0000000000..b557de1c77
--- /dev/null
+++ b/indra/newview/llcallingcard.h
@@ -0,0 +1,224 @@
+/**
+ * @file llcallingcard.h
+ * @brief Definition of the LLPreviewCallingCard class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCALLINGCARD_H
+#define LL_LLCALLINGCARD_H
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include "lluserrelations.h"
+#include "lluuid.h"
+#include "v3dmath.h"
+
+//class LLInventoryModel;
+//class LLInventoryObserver;
+class LLMessageSystem;
+class LLTrackingData;
+class LLFriendObserver
+{
+public:
+ // This enumeration is a way to refer to what changed in a more
+ // human readable format. You can mask the value provided by
+ // chaged() to see if the observer is interested in the change.
+ enum
+ {
+ NONE = 0,
+ ADD = 1,
+ REMOVE = 2,
+ ONLINE = 4,
+ POWERS = 8,
+ ALL = 0xffffffff
+ };
+ virtual ~LLFriendObserver() {}
+ virtual void changed(U32 mask) = 0;
+};
+
+/*
+struct LLBuddyInfo
+{
+ bool mIsOnline;
+ bool mIsEmpowered;
+ LLBuddyInfo() : mIsOnline(false), mIsEmpowered(false) {}
+};
+*/
+
+// This is used as a base class for doing operations on all buddies.
+class LLRelationshipFunctor
+{
+public:
+ virtual ~LLRelationshipFunctor() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy) = 0;
+};
+
+
+class LLAvatarTracker
+{
+public:
+ static LLAvatarTracker& instance() { return sInstance; }
+
+ void track(const LLUUID& avatar_id, const std::string& name);
+ void untrack(const LLUUID& avatar_id);
+ bool isTrackedAgentValid() { return mTrackedAgentValid; }
+ void setTrackedAgentValid(bool valid) { mTrackedAgentValid = valid; }
+ void findAgent();
+
+ // coarse update information
+ void setTrackedCoarseLocation(const LLVector3d& global_pos);
+
+ // dealing with the tracked agent location
+ bool haveTrackingInfo();
+ void getDegreesAndDist(F32& rot, F64& horiz_dist, F64& vert_dist);
+ LLVector3d getGlobalPos();
+
+ // Get the name passed in, returns null string if uninitialized.
+ const LLString& getName();
+
+ // Get the avatar being tracked, returns LLUUID::null if uninitialized
+ const LLUUID& getAvatarID();
+
+ // Deal with inventory
+ //void observe(LLInventoryModel* model);
+ //void inventoryChanged();
+
+ // add or remove agents from buddy list. Each method takes a set
+ // of buddies and returns how many were actually added or removed.
+ typedef std::map<LLUUID, LLRelationship*> buddy_map_t;
+
+ S32 addBuddyList(const buddy_map_t& buddies);
+ //S32 removeBuddyList(const buddy_list_t& exes);
+ void copyBuddyList(buddy_map_t& buddies) const;
+
+ // deal with termination of friendhsip
+ void terminateBuddy(const LLUUID& id);
+
+ // get full info
+ const LLRelationship* getBuddyInfo(const LLUUID& id) const;
+
+ // online status
+ void setBuddyOnline(const LLUUID& id, bool is_online);
+ bool isBuddyOnline(const LLUUID& id) const;
+
+ // simple empowered status
+ void setBuddyEmpowered(const LLUUID& id, bool is_empowered);
+ bool isBuddyEmpowered(const LLUUID& id) const;
+
+ // set the empower bit & message the server.
+ void empowerList(const buddy_map_t& list, bool grant);
+ void empower(const LLUUID& id, bool grant); // wrapper for above
+
+ // register callbacks
+ void registerCallbacks(LLMessageSystem* msg);
+
+ // Add/remove an observer. If the observer is destroyed, be sure
+ // to remove it. On destruction of the tracker, it will delete any
+ // observers left behind.
+ void addObserver(LLFriendObserver* observer);
+ void removeObserver(LLFriendObserver* observer);
+ void notifyObservers();
+
+ // Apply the functor to every buddy. Do not actually modify the
+ // buddy list in the functor or bad things will happen.
+ void applyFunctor(LLRelationshipFunctor& f);
+
+ static void formFriendship(const LLUUID& friend_id);
+
+protected:
+ void deleteTrackingData();
+ void agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos);
+
+ // Message system functionality
+ static void processAgentFound(LLMessageSystem* msg, void**);
+ static void processOnlineNotification(LLMessageSystem* msg, void**);
+ static void processOfflineNotification(LLMessageSystem* msg, void**);
+ //static void processGrantedProxies(LLMessageSystem* msg, void**);
+ static void processTerminateFriendship(LLMessageSystem* msg, void**);
+ static void processChangeUserRights(LLMessageSystem* msg, void**);
+
+ void processNotify(LLMessageSystem* msg, bool online);
+ void processChange(LLMessageSystem* msg);
+
+protected:
+ static LLAvatarTracker sInstance;
+ LLTrackingData* mTrackingData;
+ bool mTrackedAgentValid;
+ U32 mModifyMask;
+ //LLInventoryModel* mInventory;
+ //LLInventoryObserver* mInventoryObserver;
+
+ buddy_map_t mBuddyInfo;
+
+ typedef std::vector<LLFriendObserver*> observer_list_t;
+ observer_list_t mObservers;
+
+private:
+ // do not implement
+ LLAvatarTracker(const LLAvatarTracker&);
+ bool operator==(const LLAvatarTracker&);
+
+public:
+ // don't you dare create or delete this object
+ LLAvatarTracker();
+ ~LLAvatarTracker();
+};
+
+// collect set of LLUUIDs we're a proxy for
+class LLCollectProxyBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectProxyBuddies() {}
+ virtual ~LLCollectProxyBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::set<LLUUID> buddy_list_t;
+ buddy_list_t mProxy;
+};
+
+// collect dictionary sorted map of name -> agent_id for every online buddy
+class LLCollectMappableBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectMappableBuddies() {}
+ virtual ~LLCollectMappableBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t;
+ buddy_map_t mMappable;
+ char mFirst[DB_FIRST_NAME_BUF_SIZE];
+ char mLast[DB_LAST_NAME_BUF_SIZE];
+};
+
+// collect dictionary sorted map of name -> agent_id for every online buddy
+class LLCollectOnlineBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectOnlineBuddies() {}
+ virtual ~LLCollectOnlineBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t;
+ buddy_map_t mOnline;
+ char mFirst[DB_FIRST_NAME_BUF_SIZE];
+ char mLast[DB_LAST_NAME_BUF_SIZE];
+};
+
+// collect dictionary sorted map of name -> agent_id for every buddy,
+// one map is offline and the other map is online.
+class LLCollectAllBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectAllBuddies() {}
+ virtual ~LLCollectAllBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t;
+ buddy_map_t mOnline;
+ buddy_map_t mOffline;
+ char mFirst[DB_FIRST_NAME_BUF_SIZE];
+ char mLast[DB_LAST_NAME_BUF_SIZE];
+};
+
+#endif // LL_LLCALLINGCARD_H
diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
new file mode 100644
index 0000000000..208a14a6c6
--- /dev/null
+++ b/indra/newview/llchatbar.cpp
@@ -0,0 +1,706 @@
+/**
+ * @file llchatbar.cpp
+ * @brief LLChatBar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llchatbar.h"
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llparcel.h"
+#include "llstring.h"
+#include "message.h"
+#include "llfocusmgr.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "llfloaterchat.h"
+#include "llgesturemgr.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "lluiconstants.h"
+#include "llviewergesture.h" // for triggering gestures
+#include "llviewermenu.h" // for deleting object with DEL key
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llframetimer.h"
+#include "llresmgr.h"
+#include "llworld.h"
+#include "llinventorymodel.h"
+#include "llmultigesture.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llvieweruictrlfactory.h"
+
+
+//
+// Globals
+//
+const F32 AGENT_TYPING_TIMEOUT = 5.f; // seconds
+
+LLChatBar *gChatBar = NULL;
+
+LLChatBarGestureObserver* LLChatBar::sObserver = NULL;
+
+
+class LLChatBarGestureObserver : public LLGestureManagerObserver
+{
+public:
+ LLChatBarGestureObserver() {}
+ virtual ~LLChatBarGestureObserver() {}
+ virtual void changed() { gChatBar->refreshGestures(); }
+};
+
+
+//
+// Functions
+//
+
+LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
+: LLPanel(name, rect, BORDER_NO),
+ mInputEditor(NULL),
+ mGestureLabelTimer(),
+ mLastSpecialChatChannel(0),
+ mIsBuilt(FALSE)
+{
+ setIsChrome(TRUE);
+
+ gUICtrlFactory->buildPanel(this,"panel_chat_bar.xml");
+
+ mIsFocusRoot = TRUE;
+
+ setRect(rect); // override xml rect
+
+ setBackgroundOpaque(TRUE);
+ setBackgroundVisible(TRUE);
+
+ // Start visible if we left the app while chatting.
+ setVisible( gSavedSettings.getBOOL("ChatVisible") );
+
+ mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor");
+ if (mInputEditor)
+ {
+ mInputEditor->setCallbackUserData(this);
+ mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
+ mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);
+ mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );
+ mInputEditor->setCommitOnFocusLost( FALSE );
+ mInputEditor->setIgnoreTab(TRUE);
+ mInputEditor->setPassDelete(TRUE);
+ }
+
+ mInputEditor->setMaxTextLength(1023);
+ // Build the list of gestures
+ refreshGestures();
+
+ sObserver = new LLChatBarGestureObserver;
+ gGestureManager.addObserver(sObserver);
+
+ mIsBuilt = TRUE;
+
+ // Apply custom layout.
+ layout();
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+ childDisplayNotFound();
+#endif
+
+}
+
+
+LLChatBar::~LLChatBar()
+{
+ delete sObserver;
+ sObserver = NULL;
+ // LLView destructor cleans up children
+}
+
+BOOL LLChatBar::postBuild()
+{
+ childSetAction("History", LLFloaterChat::toggle, this);
+ childSetAction("Say", onClickSay, this);
+ childSetAction("Shout", onClickShout, this);
+ childSetCommitCallback("Gesture", onCommitGesture, this);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------
+// Overrides
+//-----------------------------------------------------------------------
+
+// virtual
+void LLChatBar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPanel::reshape(width, height, called_from_parent);
+ if (mIsBuilt)
+ {
+ layout();
+ }
+}
+
+// virtual
+BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ if( getVisible() && getEnabled() && !called_from_parent)
+ {
+ // ALT-RETURN is reserved for windowed/fullscreen toggle
+ if( KEY_RETURN == key )
+ {
+ //if (childGetValue("Chat Editor").asString().empty())
+ //{
+ // // no text, just close chat bar
+ // stopChat();
+ // return TRUE;
+ //}
+
+ if (mask == MASK_CONTROL)
+ {
+ // shout
+ sendChat(CHAT_TYPE_SHOUT);
+ handled = TRUE;
+ }
+ else if (mask == MASK_NONE)
+ {
+ // say
+ sendChat( CHAT_TYPE_NORMAL );
+ handled = TRUE;
+ }
+ }
+ else if ( KEY_ESCAPE == key )
+ {
+ stopChat();
+
+ handled = TRUE;
+ }
+ }
+ return handled;
+}
+
+
+void LLChatBar::layout()
+{
+ S32 rect_width = mRect.getWidth();
+ S32 count = 9; // number of elements in LLToolBar
+ S32 pad = 4;
+
+ LLRect gesture_rect;
+ S32 gesture_width = 0;
+ if (childGetRect("Gesture", gesture_rect))
+ {
+ gesture_width = gesture_rect.getWidth();
+ }
+ F32 segment_width = (F32)(rect_width - (pad + gesture_width)) / (F32)count;
+
+ S32 btn_width = lltrunc(segment_width-pad);
+
+ S32 x = 0;
+ S32 y = 1;
+ LLRect r;
+
+ x = llround(0 * segment_width);
+ r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
+ childSetRect("History", r);
+
+ x = llround(1 * segment_width);
+ // Hack this one up so it looks nice.
+ if (mInputEditor)
+ {
+ r.setOriginAndSize(x, y+2, llfloor(6*segment_width-pad), 18);
+ mInputEditor->reshape(r.getWidth(), r.getHeight(), TRUE);
+ mInputEditor->setRect(r);
+ }
+
+ x = llround(7 * segment_width);
+ r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
+ childSetRect("Say", r);
+
+ x = llround(8 * segment_width);
+ r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
+ childSetRect("Shout", r);
+
+ x = rect_width - (pad + gesture_width);
+ r.setOriginAndSize(x, y, gesture_width, BTN_HEIGHT);
+ childSetRect("Gesture", r);
+}
+
+
+void LLChatBar::refresh()
+{
+ //BOOL chat_mode = gSavedSettings.getBOOL("ChatVisible");
+
+ //// Grab focus when no one else has it, and we're in chat mode.
+ //if (!gFocusMgr.getKeyboardFocus()
+ // && chat_mode)
+ //{
+ // childSetFocus("Chat Editor", TRUE);
+ //}
+
+ // Only show this view when user wants to be chatting
+ //setVisible(chat_mode);
+
+ // hide in mouselook, but keep previous visibility state
+ //BOOL mouselook = gAgent.cameraMouselook();
+ // call superclass setVisible so that we don't overwrite the saved setting
+ LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible"));
+
+ // HACK: Leave the name of the gesture in place for a few seconds.
+ const F32 SHOW_GESTURE_NAME_TIME = 2.f;
+ if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
+ {
+ LLCtrlListInterface* gestures = childGetListInterface("Gesture");
+ if (gestures) gestures->selectFirstItem();
+ mGestureLabelTimer.stop();
+ }
+
+ if ((gAgent.getTypingTime() > AGENT_TYPING_TIMEOUT) && (gAgent.getRenderState() & AGENT_STATE_TYPING))
+ {
+ gAgent.stopTyping();
+ }
+
+ childSetEnabled("Say", mInputEditor->getText().size());
+ childSetEnabled("Shout", mInputEditor->getText().size());
+
+}
+
+void LLChatBar::refreshGestures()
+{
+ LLCtrlListInterface* gestures = childGetListInterface("Gesture");
+ if (gestures)
+ {
+ //store current selection so we can maintain it
+ LLString cur_gesture = childGetValue("Gesture").asString();
+ gestures->selectFirstItem();
+ LLString label = childGetValue("Gesture").asString();
+ // clear
+ gestures->clearRows();
+ // add gestures
+ LLGestureManager::item_map_t::iterator it;
+ for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+ if (gesture)
+ {
+ if (!gesture->mTrigger.empty())
+ {
+ gestures->addSimpleElement(gesture->mTrigger);
+ }
+ }
+ }
+ gestures->sortByColumn(0, TRUE);
+ // Insert label after sorting
+ gestures->addSimpleElement(label, ADD_TOP);
+
+ if (!cur_gesture.empty())
+ {
+ gestures->selectByValue(LLSD(cur_gesture));
+ }
+ else
+ {
+ gestures->selectFirstItem();
+ }
+ }
+}
+
+// Move the cursor to the correct input field.
+void LLChatBar::setKeyboardFocus(BOOL focus)
+{
+ if (focus)
+ {
+ if (mInputEditor)
+ {
+ mInputEditor->setFocus(TRUE);
+ mInputEditor->selectAll();
+ }
+ }
+ else if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ if (mInputEditor)
+ {
+ mInputEditor->deselect();
+ }
+ setFocus(FALSE);
+ }
+}
+
+
+// Ignore arrow keys in chat bar
+void LLChatBar::setIgnoreArrowKeys(BOOL b)
+{
+ if (mInputEditor)
+ {
+ mInputEditor->setIgnoreArrowKeys(b);
+ }
+}
+
+BOOL LLChatBar::inputEditorHasFocus()
+{
+ return mInputEditor && mInputEditor->hasFocus();
+}
+
+LLString LLChatBar::getCurrentChat()
+{
+ return mInputEditor ? mInputEditor->getText() : LLString::null;
+}
+
+//-----------------------------------------------------------------------
+// Internal functions
+//-----------------------------------------------------------------------
+
+// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20.
+// Otherwise returns input and channel 0.
+LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel)
+{
+ if (mesg[0] == '/'
+ && mesg[1] == '/')
+ {
+ // This is a "repeat channel send"
+ *channel = mLastSpecialChatChannel;
+ return mesg.substr(2, mesg.length() - 2);
+ }
+ else if (mesg[0] == '/'
+ && mesg[1]
+ && isdigit(mesg[1]))
+ {
+ // This a special "/20" speak on a channel
+ S32 pos = 0;
+
+ // Copy the channel number into a string
+ llwchar channel_string[64];
+ llwchar c;
+ do
+ {
+ c = mesg[pos+1];
+ channel_string[pos] = c;
+ pos++;
+ }
+ while(c && pos < 64 && isdigit(c));
+
+ // Move the pointer forward to the first non-whitespace char
+ // Check isspace before looping, so we can handle "/33foo"
+ // as well as "/33 foo"
+ while(c && iswspace(c))
+ {
+ c = mesg[pos+1];
+ pos++;
+ }
+
+
+ mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10);
+ *channel = mLastSpecialChatChannel;
+ return mesg.substr(pos, mesg.length() - pos);
+ }
+ else
+ {
+ // This is normal chat.
+ *channel = 0;
+ return mesg;
+ }
+}
+
+
+void LLChatBar::sendChat( EChatType type )
+{
+ LLWString text;
+ if (mInputEditor) text = mInputEditor->getWText();
+ LLWString::trim(text);
+
+ if (!text.empty())
+ {
+ // Check if this is destined for another channel
+ S32 channel = 0;
+ stripChannelNumber(text, &channel);
+
+ std::string utf8text = wstring_to_utf8str(text);
+ // Try to trigger a gesture, if not chat to a script.
+ std::string utf8_revised_text;
+ if (0 == channel)
+ {
+ // discard returned "found" boolean
+ gGestureManager.triggerAndReviseString(utf8text, &utf8_revised_text);
+ }
+ else
+ {
+ utf8_revised_text = utf8text;
+ }
+
+ utf8_revised_text = utf8str_trim(utf8_revised_text);
+
+ if (!utf8_revised_text.empty())
+ {
+ // Chat with animation
+ sendChatFromViewer(utf8_revised_text, type, TRUE);
+ }
+ }
+ childSetValue("Chat Editor", LLSD(LLString::null));
+
+ gAgent.stopTyping();
+
+ // If the user wants to stop chatting on hitting return, lose focus
+ // and go out of chat mode.
+ if (gSavedSettings.getBOOL("CloseChatOnReturn"))
+ {
+ stopChat();
+ }
+}
+
+
+//-----------------------------------------------------------------------
+// Static functions
+//-----------------------------------------------------------------------
+
+// static
+void LLChatBar::startChat(void* userdata)
+{
+ const char* line = (const char*)userdata;
+
+ gChatBar->setVisible(TRUE);
+ gChatBar->setKeyboardFocus(TRUE);
+ gSavedSettings.setBOOL("ChatVisible", TRUE);
+
+ if (line && gChatBar->mInputEditor)
+ {
+ std::string line_string(line);
+ gChatBar->mInputEditor->setText(line_string);
+ }
+ // always move cursor to end so users don't obliterate chat when accidentally hitting WASD
+ gChatBar->mInputEditor->setCursorToEnd();
+}
+
+
+// Exit "chat mode" and do the appropriate focus changes
+// static
+void LLChatBar::stopChat()
+{
+ // In simple UI mode, we never release focus from the chat bar
+ gChatBar->setKeyboardFocus(FALSE);
+
+ // If we typed a movement key and pressed return during the
+ // same frame, the keyboard handlers will see the key as having
+ // gone down this frame and try to move the avatar.
+ gKeyboard->resetKeys();
+ gKeyboard->resetMaskKeys();
+
+ // stop typing animation
+ gAgent.stopTyping();
+
+ // hide chat bar so it doesn't grab focus back
+ gChatBar->setVisible(FALSE);
+}
+
+void LLChatBar::setVisible(BOOL visible)
+{
+ gSavedSettings.setBOOL("ChatVisible", visible);
+ LLPanel::setVisible(visible);
+}
+
+// static
+void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata )
+{
+ LLChatBar* self = (LLChatBar *)userdata;
+
+ LLWString raw_text;
+ if (self->mInputEditor) raw_text = self->mInputEditor->getWText();
+
+ // Can't trim the end, because that will cause autocompletion
+ // to eat trailing spaces that might be part of a gesture.
+ LLWString::trimHead(raw_text);
+
+ S32 length = raw_text.length();
+
+ if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences
+ {
+ gAgent.startTyping();
+ }
+ else
+ {
+ gAgent.stopTyping();
+ }
+
+ /* Doesn't work -- can't tell the difference between a backspace
+ that killed the selection vs. backspace at the end of line.
+ if (length > 1
+ && text[0] == '/'
+ && key == KEY_BACKSPACE)
+ {
+ // the selection will already be deleted, but we need to trim
+ // off the character before
+ LLString new_text = raw_text.substr(0, length-1);
+ self->mInputEditor->setText( new_text );
+ self->mInputEditor->setCursorToEnd();
+ length = length - 1;
+ }
+ */
+
+ KEY key = gKeyboard->currentKey();
+
+ // Ignore "special" keys, like backspace, arrows, etc.
+ if (length > 1
+ && raw_text[0] == '/'
+ && key < KEY_SPECIAL)
+ {
+ // we're starting a gesture, attempt to autocomplete
+
+ std::string utf8_trigger = wstring_to_utf8str(raw_text);
+ std::string utf8_out_str(utf8_trigger);
+
+ if (gGestureManager.matchPrefix(utf8_trigger, &utf8_out_str))
+ {
+ if (self->mInputEditor)
+ {
+ self->mInputEditor->setText(utf8_out_str);
+ S32 outlength = self->mInputEditor->getLength(); // in characters
+
+ // Select to end of line, starting from the character
+ // after the last one the user typed.
+ self->mInputEditor->setSelection(length, outlength);
+ }
+ }
+
+ //llinfos << "GESTUREDEBUG " << trigger
+ // << " len " << length
+ // << " outlen " << out_str.getLength()
+ // << llendl;
+ }
+ // make sure we don't do UI-only render as it is apparent avatar isn't animating
+ gViewerWindow->finishFastFrame();
+}
+
+// static
+void LLChatBar::onInputEditorFocusLost( LLLineEditor* caller, void* userdata)
+{
+ // stop typing animation
+ gAgent.stopTyping();
+}
+
+// static
+void LLChatBar::onInputEditorGainFocus( LLUICtrl* caller, void* userdata )
+{
+ LLFloaterChat::setHistoryCursorAndScrollToEnd();
+}
+
+// static
+void LLChatBar::onClickSay( void* userdata )
+{
+ LLChatBar* self = (LLChatBar*) userdata;
+ self->sendChat( CHAT_TYPE_NORMAL );
+}
+
+// static
+void LLChatBar::onClickShout( void* userdata )
+{
+ LLChatBar *self = (LLChatBar *)userdata;
+ self->sendChat( CHAT_TYPE_SHOUT );
+}
+
+void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate)
+{
+ sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate);
+}
+
+void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ // Look for "/20 foo" channel chats.
+ S32 channel = 0;
+ LLWString out_text = stripChannelNumber(wtext, &channel);
+ std::string utf8_out_text = wstring_to_utf8str(out_text);
+ std::string utf8_text = wstring_to_utf8str(wtext);
+
+ utf8_text = utf8str_trim(utf8_text);
+ if (!utf8_text.empty())
+ {
+ utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
+ }
+
+ // Don't animate for chats people can't hear (chat to scripts)
+ if (animate && (channel == 0))
+ {
+ if (type == CHAT_TYPE_WHISPER)
+ {
+ lldebugs << "You whisper " << utf8_text << llendl;
+ gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START);
+ }
+ else if (type == CHAT_TYPE_NORMAL)
+ {
+ lldebugs << "You say " << utf8_text << llendl;
+ gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START);
+ }
+ else if (type == CHAT_TYPE_SHOUT)
+ {
+ lldebugs << "You shout " << utf8_text << llendl;
+ gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START);
+ }
+ else
+ {
+ llinfos << "send_chat_from_viewer() - invalid volume" << llendl;
+ return;
+ }
+ }
+ else
+ {
+ if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP)
+ {
+ lldebugs << "Channel chat: " << utf8_text << llendl;
+ }
+ }
+
+ msg->newMessageFast(_PREHASH_ChatFromViewer);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ChatData);
+ msg->addStringFast(_PREHASH_Message, utf8_out_text);
+ msg->addU8Fast(_PREHASH_Type, type);
+ msg->addS32("Channel", channel);
+
+ gAgent.sendReliableMessage();
+
+ gViewerStats->incStat(LLViewerStats::ST_CHAT_COUNT);
+}
+
+
+// static
+void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
+{
+ LLChatBar* self = (LLChatBar*)data;
+ LLCtrlListInterface* gestures = self->childGetListInterface("Gesture");
+ if (gestures)
+ {
+ S32 index = gestures->getFirstSelectedIndex();
+ if (index == 0)
+ {
+ return;
+ }
+ const std::string& trigger = gestures->getSimpleSelectedValue().asString();
+
+ // pretend the user chatted the trigger string, to invoke
+ // substitution and logging.
+ std::string text(trigger);
+ std::string revised_text;
+ gGestureManager.triggerAndReviseString(text, &revised_text);
+
+ revised_text = utf8str_trim(revised_text);
+ if (!revised_text.empty())
+ {
+ // Don't play nodding animation
+ self->sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE);
+ }
+ }
+ self->mGestureLabelTimer.start();
+ // free focus back to chat bar
+ self->childSetFocus("Gesture", FALSE);
+}
diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h
new file mode 100644
index 0000000000..e0f31ae124
--- /dev/null
+++ b/indra/newview/llchatbar.h
@@ -0,0 +1,98 @@
+/**
+ * @file llchatbar.h
+ * @brief LLChatBar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHATBAR_H
+#define LL_LLCHATBAR_H
+
+#include "llpanel.h"
+#include "llframetimer.h"
+#include "llchat.h"
+
+class LLButton;
+class LLComboBox;
+class LLLineEditor;
+class LLMessageSystem;
+class LLTextBox;
+class LLTextEditor;
+class LLUICtrl;
+class LLUUID;
+class LLFrameTimer;
+class LLStatGraph;
+class LLChatBarGestureObserver;
+
+class LLChatBar
+: public LLPanel
+{
+public:
+ LLChatBar(const std::string& name, const LLRect& rect );
+ ~LLChatBar();
+ virtual BOOL postBuild();
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+ // Adjust buttons and input field for width
+ void layout();
+
+ void refresh();
+ void refreshGestures();
+
+ // Move cursor into chat input field.
+ void setKeyboardFocus(BOOL b);
+
+ // Ignore arrow keys for chat bar
+ void setIgnoreArrowKeys(BOOL b);
+
+ BOOL inputEditorHasFocus();
+ LLString getCurrentChat();
+
+ // Send a chat (after stripping /20foo channel chats).
+ // "Animate" means the nodding animation for regular text.
+ void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate);
+ void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate);
+
+ // If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20.
+ // Otherwise returns input and channel 0.
+ LLWString stripChannelNumber(const LLWString &mesg, S32* channel);
+
+ // callbacks
+ static void onClickHistory( void* userdata );
+ static void onClickSay( void* userdata );
+ static void onClickShout( void* userdata );
+
+ static void onTabClick( void* userdata );
+ static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata);
+ static void onInputEditorFocusLost(LLLineEditor* caller,void* userdata);
+ static void onInputEditorGainFocus(LLUICtrl* caller,void* userdata);
+
+ static void onCommitGesture(LLUICtrl* ctrl, void* data);
+
+ static void startChat(void*);
+ static void stopChat();
+
+ /*virtual*/ void setVisible(BOOL visible);
+protected:
+ void sendChat(EChatType type);
+ void updateChat();
+
+protected:
+ LLLineEditor* mInputEditor;
+
+ LLFrameTimer mGestureLabelTimer;
+
+ // Which non-zero channel did we last chat on?
+ S32 mLastSpecialChatChannel;
+
+ BOOL mIsBuilt;
+
+ static LLChatBarGestureObserver* sObserver;
+};
+
+extern LLChatBar *gChatBar;
+
+#endif
diff --git a/indra/newview/llclassifiedinfo.cpp b/indra/newview/llclassifiedinfo.cpp
new file mode 100644
index 0000000000..768145b63c
--- /dev/null
+++ b/indra/newview/llclassifiedinfo.cpp
@@ -0,0 +1,49 @@
+/**
+ * @file llclassifiedinfo.cpp
+ * @brief LLClassifiedInfo class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llclassifiedinfo.h"
+
+#include "viewer.h" // for gPacificDaylightTime
+#include "lluuid.h"
+
+LLClassifiedInfo::cat_map LLClassifiedInfo::sCategories;
+
+// static
+void LLClassifiedInfo::loadCategories(LLUserAuth::options_t classified_options)
+{
+ LLUserAuth::options_t::iterator resp_it;
+ for (resp_it = classified_options.begin();
+ resp_it != classified_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+
+ LLUserAuth::response_t::const_iterator option_it;
+
+ S32 cat_id = 0;
+ option_it = response.find("category_id");
+ if (option_it != response.end())
+ {
+ cat_id = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ continue;
+ }
+
+ // Add the category id/name pair
+ option_it = response.find("category_name");
+ if (option_it != response.end())
+ {
+ LLClassifiedInfo::sCategories[cat_id] = option_it->second;
+ }
+
+ }
+
+}
diff --git a/indra/newview/llclassifiedinfo.h b/indra/newview/llclassifiedinfo.h
new file mode 100644
index 0000000000..5c58048db0
--- /dev/null
+++ b/indra/newview/llclassifiedinfo.h
@@ -0,0 +1,31 @@
+/**
+ * @file llclassifiedinfo.h
+ * @brief LLClassifiedInfo class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLASSIFIEDINFO_H
+#define LL_LLCLASSIFIEDINFO_H
+
+#include <map>
+
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "lluserauth.h"
+
+class LLMessageSystem;
+
+class LLClassifiedInfo
+{
+public:
+ LLClassifiedInfo() {}
+
+ static void loadCategories(LLUserAuth::options_t event_options);
+
+ typedef std::map<U32, std::string> cat_map;
+ static cat_map sCategories;
+};
+
+#endif // LL_LLCLASSIFIEDINFO_H
diff --git a/indra/newview/llcloud.cpp b/indra/newview/llcloud.cpp
new file mode 100644
index 0000000000..c7e69a043f
--- /dev/null
+++ b/indra/newview/llcloud.cpp
@@ -0,0 +1,555 @@
+/**
+ * @file llcloud.cpp
+ * @brief Implementation of viewer LLCloudLayer class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "v4color.h"
+
+#include "llwind.h"
+#include "llcloud.h"
+#include "llgl.h"
+#include "llviewerobjectlist.h"
+#include "llvoclouds.h"
+#include "llvosky.h"
+#include "llsky.h"
+#include "llviewerregion.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+#include "llglheaders.h"
+#include "pipeline.h"
+#include "lldrawpool.h"
+#include "llworld.h"
+
+extern LLPipeline gPipeline;
+
+const F32 CLOUD_UPDATE_RATE = 1.0f; // Global time dilation for clouds
+const F32 CLOUD_GROW_RATE = 0.05f;
+const F32 CLOUD_DECAY_RATE = -0.05f;
+const F32 CLOUD_VELOCITY_SCALE = 0.01f;
+const F32 CLOUD_DENSITY = 25.f;
+const S32 CLOUD_COUNT_MAX = 20;
+const F32 CLOUD_HEIGHT_RANGE = 48.f;
+const F32 CLOUD_HEIGHT_MEAN = 192.f;
+
+enum
+{
+ LL_PUFF_GROWING = 0,
+ LL_PUFF_DYING = 1
+};
+
+// Used for patch decoder
+S32 gBuffer[16*16];
+
+
+//static
+S32 LLCloudPuff::sPuffCount = 0;
+
+LLCloudPuff::LLCloudPuff() :
+ mAlpha(0.01f),
+ mRate(CLOUD_GROW_RATE*CLOUD_UPDATE_RATE),
+ mLifeState(LL_PUFF_GROWING)
+{
+}
+
+LLCloudGroup::LLCloudGroup() :
+ mCloudLayerp(NULL),
+ mDensity(0.f),
+ mTargetPuffCount(0),
+ mVOCloudsp(NULL)
+{
+}
+
+void LLCloudGroup::cleanup()
+{
+ if (mVOCloudsp)
+ {
+ if (!mVOCloudsp->isDead())
+ {
+ gObjectList.killObject(mVOCloudsp);
+ }
+ mVOCloudsp = NULL;
+ }
+}
+
+void LLCloudGroup::setCenterRegion(const LLVector3 &center)
+{
+ mCenterRegion = center;
+}
+
+void LLCloudGroup::updatePuffs(const F32 dt)
+{
+ mDensity = mCloudLayerp->getDensityRegion(mCenterRegion);
+
+ if (!mVOCloudsp)
+ {
+ mVOCloudsp = (LLVOClouds *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_CLOUDS, mCloudLayerp->getRegion());
+ mVOCloudsp->setCloudGroup(this);
+ mVOCloudsp->setPositionRegion(mCenterRegion);
+ mVOCloudsp->setScale(LLVector3(256.f/CLOUD_GROUPS_PER_EDGE + CLOUD_PUFF_WIDTH,
+ 256.f/CLOUD_GROUPS_PER_EDGE + CLOUD_PUFF_WIDTH,
+ CLOUD_HEIGHT_RANGE + CLOUD_PUFF_HEIGHT));
+ gPipeline.addObject(mVOCloudsp);
+ }
+
+ S32 i;
+
+ LLVector3 velocity;
+ LLVector3d vel_d;
+ // Update the positions of all of the clouds
+ for (i = 0; i < mCloudPuffs.count(); i++)
+ {
+ LLCloudPuff &puff = mCloudPuffs[i];
+ velocity = mCloudLayerp->getRegion()->mWind.getCloudVelocity(mCloudLayerp->getRegion()->getPosRegionFromGlobal(puff.mPositionGlobal));
+ velocity *= CLOUD_VELOCITY_SCALE*CLOUD_UPDATE_RATE;
+ vel_d.setVec(velocity);
+ mCloudPuffs[i].mPositionGlobal += vel_d;
+ mCloudPuffs[i].mAlpha += mCloudPuffs[i].mRate * dt;
+ mCloudPuffs[i].mAlpha = llmin(1.f, mCloudPuffs[i].mAlpha);
+ mCloudPuffs[i].mAlpha = llmax(0.f, mCloudPuffs[i].mAlpha);
+ }
+}
+
+void LLCloudGroup::updatePuffOwnership()
+{
+ S32 i = 0;
+ while (i < mCloudPuffs.count())
+ {
+ if (mCloudPuffs[i].getLifeState() == LL_PUFF_DYING)
+ {
+ i++;
+ continue;
+ }
+ if (inGroup(mCloudPuffs[i]))
+ {
+ i++;
+ continue;
+ }
+
+ //llinfos << "Cloud moving to new group" << llendl;
+ LLCloudGroup *new_cgp = gWorldPointer->findCloudGroup(mCloudPuffs[i]);
+ if (!new_cgp)
+ {
+ //llinfos << "Killing puff not in group" << llendl;
+ mCloudPuffs[i].setLifeState(LL_PUFF_DYING);
+ mCloudPuffs[i].mRate = CLOUD_DECAY_RATE*CLOUD_UPDATE_RATE;
+ i++;
+ continue;
+ }
+ //llinfos << "Puff handed off!" << llendl;
+ LLCloudPuff *puffp = new_cgp->mCloudPuffs.reserve_block(1);
+ puffp->mPositionGlobal = mCloudPuffs[i].mPositionGlobal;
+ puffp->mAlpha = mCloudPuffs[i].mAlpha;
+ mCloudPuffs.remove(i);
+ }
+
+ //llinfos << "Puff count: " << LLCloudPuff::sPuffCount << llendl;
+}
+
+void LLCloudGroup::updatePuffCount()
+{
+ if (!mVOCloudsp)
+ {
+ return;
+ }
+ S32 i;
+ S32 target_puff_count = llround(CLOUD_DENSITY * mDensity);
+ target_puff_count = llmax(0, target_puff_count);
+ target_puff_count = llmin(CLOUD_COUNT_MAX, target_puff_count);
+ S32 current_puff_count = mCloudPuffs.count();
+ // Create a new cloud if we need one
+ if (current_puff_count < target_puff_count)
+ {
+ LLVector3d puff_pos_global;
+ mCloudPuffs.resize(target_puff_count);
+ for (i = current_puff_count; i < target_puff_count; i++)
+ {
+ puff_pos_global = mVOCloudsp->getPositionGlobal();
+ F32 x = frand(256.f/CLOUD_GROUPS_PER_EDGE) - 128.f/CLOUD_GROUPS_PER_EDGE;
+ F32 y = frand(256.f/CLOUD_GROUPS_PER_EDGE) - 128.f/CLOUD_GROUPS_PER_EDGE;
+ F32 z = frand(CLOUD_HEIGHT_RANGE) - 0.5f*CLOUD_HEIGHT_RANGE;
+ puff_pos_global += LLVector3d(x, y, z);
+ mCloudPuffs[i].mPositionGlobal = puff_pos_global;
+ mCloudPuffs[i].mAlpha = 0.01f;
+ LLCloudPuff::sPuffCount++;
+ }
+ }
+
+ // Count the number of live puffs
+ S32 live_puff_count = 0;
+ for (i = 0; i < mCloudPuffs.count(); i++)
+ {
+ if (mCloudPuffs[i].getLifeState() != LL_PUFF_DYING)
+ {
+ live_puff_count++;
+ }
+ }
+
+
+ // Start killing enough puffs so the live puff count == target puff count
+ S32 new_dying_count = llmax(0, live_puff_count - target_puff_count);
+ i = 0;
+ while (new_dying_count > 0)
+ {
+ if (mCloudPuffs[i].getLifeState() != LL_PUFF_DYING)
+ {
+ //llinfos << "Killing extra live cloud" << llendl;
+ mCloudPuffs[i].setLifeState(LL_PUFF_DYING);
+ mCloudPuffs[i].mRate = CLOUD_DECAY_RATE*CLOUD_UPDATE_RATE;
+ new_dying_count--;
+ }
+ i++;
+ }
+
+ // Remove fully dead puffs
+ i = 0;
+ while (i < mCloudPuffs.count())
+ {
+ if (mCloudPuffs[i].isDead())
+ {
+ //llinfos << "Removing dead puff!" << llendl;
+ mCloudPuffs.remove(i);
+ LLCloudPuff::sPuffCount--;
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+BOOL LLCloudGroup::inGroup(const LLCloudPuff &puff) const
+{
+ // Do min/max check on center of the cloud puff
+ F32 min_x, min_y, max_x, max_y;
+ F32 delta = 128.f/CLOUD_GROUPS_PER_EDGE;
+ min_x = mCenterRegion.mV[VX] - delta;
+ min_y = mCenterRegion.mV[VY] - delta;
+ max_x = mCenterRegion.mV[VX] + delta;
+ max_y = mCenterRegion.mV[VY] + delta;
+
+ LLVector3 pos_region = mCloudLayerp->getRegion()->getPosRegionFromGlobal(puff.getPositionGlobal());
+
+ if ((pos_region.mV[VX] < min_x)
+ || (pos_region.mV[VY] < min_y)
+ || (pos_region.mV[VX] > max_x)
+ || (pos_region.mV[VY] > max_y))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+LLCloudLayer::LLCloudLayer()
+: mOriginGlobal(0.0f, 0.0f, 0.0f),
+ mMetersPerEdge(1.0f),
+ mMetersPerGrid(1.0f),
+ mWindp(NULL),
+ mDensityp(NULL)
+{
+ S32 i, j;
+ for (i = 0; i < 4; i++)
+ {
+ mNeighbors[i] = NULL;
+ }
+
+ F32 x, y;
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ y = (0.5f + i)*(256.f/CLOUD_GROUPS_PER_EDGE);
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ x = (0.5f + j)*(256.f/CLOUD_GROUPS_PER_EDGE);
+
+ mCloudGroups[i][j].setCloudLayerp(this);
+ mCloudGroups[i][j].setCenterRegion(LLVector3(x, y, CLOUD_HEIGHT_MEAN));
+ }
+ }
+}
+
+
+
+LLCloudLayer::~LLCloudLayer()
+{
+ destroy();
+}
+
+
+void LLCloudLayer::create(LLViewerRegion *regionp)
+{
+ llassert(regionp);
+
+ mRegionp = regionp;
+ mDensityp = new F32 [CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE];
+
+ U32 i;
+ for (i = 0; i < CLOUD_GRIDS_PER_EDGE*CLOUD_GRIDS_PER_EDGE; i++)
+ {
+ mDensityp[i] = 0.f;
+ }
+}
+
+void LLCloudLayer::setRegion(LLViewerRegion *regionp)
+{
+ mRegionp = regionp;
+}
+
+void LLCloudLayer::destroy()
+{
+ // Kill all of the existing puffs
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].cleanup();
+ }
+ }
+
+ delete [] mDensityp;
+ mDensityp = NULL;
+ mWindp = NULL;
+}
+
+
+void LLCloudLayer::reset()
+{
+}
+
+
+void LLCloudLayer::setWindPointer(LLWind *windp)
+{
+ if (mWindp)
+ {
+ mWindp->setCloudDensityPointer(NULL);
+ }
+ mWindp = windp;
+ if (mWindp)
+ {
+ mWindp->setCloudDensityPointer(mDensityp);
+ }
+}
+
+
+void LLCloudLayer::setWidth(F32 width)
+{
+ mMetersPerEdge = width;
+ mMetersPerGrid = width / CLOUD_GRIDS_PER_EDGE;
+}
+
+
+F32 LLCloudLayer::getDensityRegion(const LLVector3 &pos_region)
+{
+ // "position" is region-local
+ S32 i, j, ii, jj;
+
+ i = lltrunc(pos_region.mV[VX] / mMetersPerGrid);
+ j = lltrunc(pos_region.mV[VY] / mMetersPerGrid);
+ ii = i + 1;
+ jj = j + 1;
+
+
+ // clamp
+ if (i >= (S32)CLOUD_GRIDS_PER_EDGE)
+ {
+ i = CLOUD_GRIDS_PER_EDGE - 1;
+ ii = i;
+ }
+ else if (i < 0)
+ {
+ i = 0;
+ ii = i;
+ }
+ else if (ii >= (S32)CLOUD_GRIDS_PER_EDGE || ii < 0)
+ {
+ ii = i;
+ }
+
+ if (j >= (S32)CLOUD_GRIDS_PER_EDGE)
+ {
+ j = CLOUD_GRIDS_PER_EDGE - 1;
+ jj = j;
+ }
+ else if (j < 0)
+ {
+ j = 0;
+ jj = j;
+ }
+ else if (jj >= (S32)CLOUD_GRIDS_PER_EDGE || jj < 0)
+ {
+ jj = j;
+ }
+
+ F32 dx = (pos_region.mV[VX] - (F32) i * mMetersPerGrid) / mMetersPerGrid;
+ F32 dy = (pos_region.mV[VY] - (F32) j * mMetersPerGrid) / mMetersPerGrid;
+ F32 omdx = 1.0f - dx;
+ F32 omdy = 1.0f - dy;
+
+ F32 density = dx * dy * *(mDensityp + ii + jj * CLOUD_GRIDS_PER_EDGE) +
+ dx * omdy * *(mDensityp + i + jj * CLOUD_GRIDS_PER_EDGE) +
+ omdx * dy * *(mDensityp + ii + j * CLOUD_GRIDS_PER_EDGE) +
+ omdx * omdy * *(mDensityp + i + j * CLOUD_GRIDS_PER_EDGE);
+
+ return density;
+}
+
+// a debug method that may yet be useful
+void LLCloudLayer::renderDensityField()
+{
+// F32 x, y, z;
+// U32 i, j, k;
+// LLGLSNoTexture gls_ui_no_texture;
+// // Render a bunch of triangles to represent the cloud density field
+// glBegin(GL_TRIANGLES);
+// for(j=0; j<CLOUD_GRIDS_PER_EDGE-1; j++)
+// {
+// y = j * mMetersPerGrid;
+
+// for(i=0; i<CLOUD_GRIDS_PER_EDGE; i++)
+// {
+// k = i + j*CLOUD_GRIDS_PER_EDGE;
+// x = i * mMetersPerGrid;
+
+// z = 0.5f * CLOUD_MAX_HEIGHT + 40.0f * *(mDensityp + k + CLOUD_GRIDS_PER_EDGE);
+// glColor3f(1.0f - *(mDensityp + k + CLOUD_GRIDS_PER_EDGE), *(mDensityp + k + CLOUD_GRIDS_PER_EDGE), 0.0f);
+// glVertex3f(x, y+mMetersPerGrid, z);
+
+// z = 0.5f * CLOUD_MAX_HEIGHT + 40.0f * *(mDensityp + k);
+// glColor3f(1.0f - *(mDensityp + k), *(mDensityp + k), 0.0f);
+// glVertex3f(x, y, z);
+
+// z = 0.5f * CLOUD_MAX_HEIGHT + 40.0f * *(mDensityp + k + 1);
+// glColor3f(1.0f - *(mDensityp + k + 1), *(mDensityp + k + 1), 0.0f);
+// glVertex3f(x+mMetersPerGrid, y, z);
+
+// }
+// }
+// glEnd();
+}
+
+
+void LLCloudLayer::decompress(LLBitPack &bitpack, LLGroupHeader *group_headerp)
+{
+ LLPatchHeader patch_header;
+
+ init_patch_decompressor(group_headerp->patch_size);
+
+ // Don't use the packed group_header stride because the strides used on
+ // simulator and viewer are not equal.
+ group_headerp->stride = group_headerp->patch_size; // offset required to step up one row
+ set_group_of_patch_header(group_headerp);
+
+ decode_patch_header(bitpack, &patch_header);
+ decode_patch(bitpack, gBuffer);
+ decompress_patch(mDensityp, gBuffer, &patch_header);
+}
+
+void LLCloudLayer::updatePuffs(const F32 dt)
+{
+ // We want to iterate through all of the cloud groups
+ // and update their density targets
+
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].updatePuffs(dt);
+ }
+ }
+}
+
+void LLCloudLayer::updatePuffOwnership()
+{
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].updatePuffOwnership();
+ }
+ }
+}
+
+void LLCloudLayer::updatePuffCount()
+{
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].updatePuffCount();
+ }
+ }
+}
+
+LLCloudGroup *LLCloudLayer::findCloudGroup(const LLCloudPuff &puff)
+{
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ if (mCloudGroups[i][j].inGroup(puff))
+ {
+ return &(mCloudGroups[i][j]);
+ }
+ }
+ }
+ return NULL;
+}
+
+
+
+void LLCloudLayer::connectNeighbor(LLCloudLayer *cloudp, U32 direction)
+{
+ if (direction >= 4)
+ {
+ // Only care about cardinal 4 directions.
+ return;
+ }
+
+ mNeighbors[direction] = cloudp;
+ if (cloudp)
+ mNeighbors[direction]->mNeighbors[gDirOpposite[direction]] = this;
+}
+
+
+void LLCloudLayer::disconnectNeighbor(U32 direction)
+{
+ if (direction >= 4)
+ {
+ // Only care about cardinal 4 directions.
+ return;
+ }
+
+ if (mNeighbors[direction])
+ {
+ mNeighbors[direction]->mNeighbors[gDirOpposite[direction]] = NULL;
+ mNeighbors[direction] = NULL;
+ }
+}
+
+
+void LLCloudLayer::disconnectAllNeighbors()
+{
+ S32 i;
+ for (i = 0; i < 4; i++)
+ {
+ disconnectNeighbor(i);
+ }
+}
diff --git a/indra/newview/llcloud.h b/indra/newview/llcloud.h
new file mode 100644
index 0000000000..06c2b5c9ff
--- /dev/null
+++ b/indra/newview/llcloud.h
@@ -0,0 +1,182 @@
+/**
+ * @file llcloud.h
+ * @brief Description of viewer LLCloudLayer class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLOUD_H
+#define LL_LLCLOUD_H
+
+// Some ideas on how clouds should work
+//
+// Each region has a cloud layer
+// Each cloud layer has pre-allocated space for N clouds
+// The LLSky class knows the max number of clouds to render M.
+// All clouds use the same texture, but the tex-coords can take on 8 configurations
+// (four rotations, front and back)
+//
+// The sky's part
+// --------------
+// The sky knows that A clouds have been assigned to regions and there are B left over.
+// Divide B by number of active regions to get C.
+// Ask each region to add C more clouds and return total number D.
+// Add up all the D's to get a new A.
+//
+// The cloud layer's part
+// ----------------------
+// The cloud layer is a grid of possibility. Each grid's value represents the probablility
+// (0.0 to 1.0) that a cloud placement query will succeed.
+//
+// The sky asks the region to add C more clouds.
+// The cloud layer tries a total of E times to place clouds and returns total cloud count.
+//
+// Clouds move according to local wind velocity.
+// If a cloud moves out of region then it's location is sent to neighbor region
+// or it is allowed to drift and decay.
+//
+// The clouds in non-visible regions do not propagate every frame.
+// Each frame one non-visible region is allowed to propagate it's clouds
+// (might have to check to see if incoming cloud was already visible or not).
+//
+//
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "v4color.h"
+#include "llmemory.h"
+#include "lldarray.h"
+
+#include "llframetimer.h"
+
+const U32 CLOUD_GRIDS_PER_EDGE = 16;
+
+const F32 CLOUD_PUFF_WIDTH = 64.f;
+const F32 CLOUD_PUFF_HEIGHT = 48.f;
+
+class LLWind;
+class LLVOClouds;
+class LLViewerRegion;
+class LLCloudLayer;
+class LLBitPack;
+class LLGroupHeader;
+
+const S32 CLOUD_GROUPS_PER_EDGE = 4;
+
+class LLCloudPuff
+{
+public:
+ LLCloudPuff();
+
+ const LLVector3d &getPositionGlobal() const { return mPositionGlobal; }
+ friend class LLCloudGroup;
+
+ void updatePuffs(const F32 dt);
+ void updatePuffOwnership();
+
+ F32 getAlpha() const { return mAlpha; }
+ U32 getLifeState() const { return mLifeState; }
+ void setLifeState(const U32 state) { mLifeState = state; }
+ BOOL isDead() const { return mAlpha <= 0.f; }
+
+
+ static S32 sPuffCount;
+protected:
+ F32 mAlpha;
+ F32 mRate;
+ LLVector3d mPositionGlobal;
+
+ BOOL mLifeState;
+};
+
+class LLCloudGroup
+{
+public:
+ LLCloudGroup();
+
+ void cleanup();
+
+ void setCloudLayerp(LLCloudLayer *clp) { mCloudLayerp = clp; }
+ void setCenterRegion(const LLVector3 &center);
+
+ void updatePuffs(const F32 dt);
+ void updatePuffOwnership();
+ void updatePuffCount();
+
+ BOOL inGroup(const LLCloudPuff &puff) const;
+
+ F32 getDensity() const { return mDensity; }
+ S32 getNumPuffs() const { return mCloudPuffs.count(); }
+ const LLCloudPuff &getPuff(const S32 i) { return mCloudPuffs[i]; }
+protected:
+ LLCloudLayer *mCloudLayerp;
+ LLVector3 mCenterRegion;
+ F32 mDensity;
+ S32 mTargetPuffCount;
+
+ LLDynamicArray<LLCloudPuff> mCloudPuffs;
+ LLPointer<LLVOClouds> mVOCloudsp;
+};
+
+
+class LLCloudLayer
+{
+public:
+ LLCloudLayer();
+ ~LLCloudLayer();
+
+ void create(LLViewerRegion *regionp);
+ void destroy();
+
+ void reset(); // Clears all active cloud puffs
+
+
+ void updatePuffs(const F32 dt);
+ void updatePuffOwnership();
+ void updatePuffCount();
+
+ LLCloudGroup *findCloudGroup(const LLCloudPuff &puff);
+
+ void setRegion(LLViewerRegion *regionp);
+ LLViewerRegion* getRegion() const { return mRegionp; }
+ void setWindPointer(LLWind *windp);
+ void setOriginGlobal(const LLVector3d &origin_global) { mOriginGlobal = origin_global; }
+ void setWidth(F32 width);
+
+ void setBrightness(F32 brightness);
+ void setSunColor(const LLColor4 &color);
+
+ F32 getDensityRegion(const LLVector3 &pos_region); // "position" is in local coordinates
+
+ void renderDensityField();
+ void decompress(LLBitPack &bitpack, LLGroupHeader *group_header);
+
+ LLCloudLayer* getNeighbor(const S32 n) const { return mNeighbors[n]; }
+
+ void connectNeighbor(LLCloudLayer *cloudp, U32 direction);
+ void disconnectNeighbor(U32 direction);
+ void disconnectAllNeighbors();
+
+public:
+ LLVector3d mOriginGlobal;
+ F32 mMetersPerEdge;
+ F32 mMetersPerGrid;
+
+
+ F32 mMaxAlpha; // The max cloud puff _render_ alpha
+
+protected:
+ LLCloudLayer *mNeighbors[4];
+ LLWind *mWindp;
+ LLViewerRegion *mRegionp;
+ F32 *mDensityp; // the probability density grid
+
+ LLCloudGroup mCloudGroups[CLOUD_GROUPS_PER_EDGE][CLOUD_GROUPS_PER_EDGE];
+};
+
+
+#endif
diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp
new file mode 100644
index 0000000000..4dade7f683
--- /dev/null
+++ b/indra/newview/llcolorswatch.cpp
@@ -0,0 +1,386 @@
+/**
+ * @file llcolorswatch.cpp
+ * @brief LLColorSwatch class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File include
+#include "llcolorswatch.h"
+
+// Linden library includes
+#include "v4color.h"
+
+// Project includes
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llbutton.h"
+#include "lltextbox.h"
+#include "llfloatercolorpicker.h"
+#include "llviewborder.h"
+#include "llviewerimagelist.h"
+#include "llfocusmgr.h"
+
+LLColorSwatchCtrl::LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const LLColor4& color,
+ void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* userdata )
+: LLUICtrl(name, rect, TRUE, commit_callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mValid( TRUE ),
+ mColor( color ),
+ mBorderColor( gColors.getColor("DefaultHighlightLight") ),
+ mCanApplyImmediately(FALSE),
+ mOnCancelCallback(NULL),
+ mOnSelectCallback(NULL)
+{
+ mCaption = new LLTextBox( name,
+ LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
+ LLString::null,
+ LLFontGL::sSansSerifSmall );
+ mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCaption );
+
+ // Scalable UI made this off-by-one, I don't know why. JC
+ LLRect border_rect(0, mRect.getHeight()-1, mRect.getWidth()-1, 0);
+ border_rect.mBottom += BTN_HEIGHT_SMALL;
+ mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
+ addChild(mBorder);
+
+ mAlphaGradientImage = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("color_swatch_alpha.tga")),
+ MIPMAP_FALSE, TRUE, GL_ALPHA8, GL_ALPHA);
+}
+
+LLColorSwatchCtrl::LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const std::string& label, const LLColor4& color,
+ void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* userdata )
+: LLUICtrl(name, rect, TRUE, commit_callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mValid( TRUE ),
+ mColor( color ),
+ mBorderColor( gColors.getColor("DefaultHighlightLight") ),
+ mCanApplyImmediately(FALSE),
+ mOnCancelCallback(NULL),
+ mOnSelectCallback(NULL)
+{
+ mCaption = new LLTextBox( label,
+ LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
+ LLString::null,
+ LLFontGL::sSansSerifSmall );
+ mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCaption );
+
+ // Scalable UI made this off-by-one, I don't know why. JC
+ LLRect border_rect(0, mRect.getHeight()-1, mRect.getWidth()-1, 0);
+ border_rect.mBottom += BTN_HEIGHT_SMALL;
+ mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
+ addChild(mBorder);
+
+ mAlphaGradientImage = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("color_swatch_alpha.tga")),
+ MIPMAP_FALSE, TRUE, GL_ALPHA8, GL_ALPHA);
+}
+
+LLColorSwatchCtrl::~LLColorSwatchCtrl ()
+{
+ // parent dialog is destroyed so we are too and we need to cancel selection
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->cancelSelection();
+ pickerp->close();
+ }
+ mAlphaGradientImage = NULL;
+}
+
+BOOL LLColorSwatchCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return handleMouseDown(x, y, mask);
+}
+
+BOOL LLColorSwatchCtrl::handleHover(S32 x, S32 y, MASK mask)
+{
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+}
+
+BOOL LLColorSwatchCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char )
+ {
+ showPicker(TRUE);
+ }
+ return LLUICtrl::handleUnicodeCharHere(uni_char, called_from_parent);
+}
+
+// forces color of this swatch and any associated floater to the input value, if currently invalid
+void LLColorSwatchCtrl::setOriginal(const LLColor4& color)
+{
+ mColor = color;
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->setOrigRgb(mColor.mV[VRED], mColor.mV[VGREEN], mColor.mV[VBLUE]);
+ }
+}
+
+void LLColorSwatchCtrl::set(const LLColor4& color, BOOL update_picker, BOOL from_event)
+{
+ mColor = color;
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp && update_picker)
+ {
+ pickerp->setCurRgb(mColor.mV[VRED], mColor.mV[VGREEN], mColor.mV[VBLUE]);
+ }
+ if (!from_event)
+ {
+ setControlValue(mColor.getValue());
+ }
+}
+
+void LLColorSwatchCtrl::setLabel(const std::string& label)
+{
+ mCaption->setText(label);
+}
+
+BOOL LLColorSwatchCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ // No handler is needed for capture lost since this object has no state that depends on it.
+ gViewerWindow->setMouseCapture( this, NULL );
+
+ return TRUE;
+}
+
+
+BOOL LLColorSwatchCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // We only handle the click if the click both started and ended within us
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ // Release the mouse
+ gViewerWindow->setMouseCapture( NULL, NULL );
+
+ // If mouseup in the widget, it's been clicked
+ if ( pointInView(x, y) )
+ {
+ llassert(mEnabled);
+ llassert(getVisible());
+
+ showPicker(FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+
+// assumes GL state is set for 2D
+void LLColorSwatchCtrl::draw()
+{
+ if( getVisible() )
+ {
+ mBorder->setKeyboardFocusHighlight(hasFocus());
+ // Draw border
+ LLRect border( 0, mRect.getHeight(), mRect.getWidth(), BTN_HEIGHT_SMALL );
+ gl_rect_2d( border, mBorderColor, FALSE );
+
+ LLRect interior = border;
+ interior.stretch( -1 );
+
+ // Check state
+ if ( mValid )
+ {
+ LLGLSTexture gls_texture;
+
+ // Draw the color swatch
+ gl_rect_2d_checkerboard( interior );
+ gl_rect_2d(interior, mColor, TRUE);
+ LLColor4 opaque_color = mColor;
+ opaque_color.mV[VALPHA] = 1.f;
+ glColor4fv(opaque_color.mV);
+ if (mAlphaGradientImage.notNull())
+ {
+ glPushMatrix();
+ {
+ glTranslatef((F32)interior.mLeft, (F32)interior.mBottom, 0.f);
+ LLViewerImage::bindTexture(mAlphaGradientImage);
+ gl_rect_2d_simple_tex(interior.getWidth(), interior.getHeight());
+ }
+ glPopMatrix();
+ }
+ }
+ else
+ {
+ // Draw grey and an X
+ gl_rect_2d(interior, LLColor4::grey, TRUE);
+
+ gl_draw_x(interior, LLColor4::black);
+ }
+
+ LLUICtrl::draw();
+ }
+}
+
+void LLColorSwatchCtrl::setEnabled( BOOL enabled )
+{
+ mCaption->setEnabled( enabled );
+ LLUICtrl::setEnabled( enabled );
+
+ if (!enabled)
+ {
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->cancelSelection();
+ pickerp->close();
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// called when parent filters down a visibility changed message
+void LLColorSwatchCtrl::onVisibilityChange ( BOOL curVisibilityIn )
+{
+ // visibility changed - moved away to different tab for instance - cancel selection
+ //if ( ! curVisibilityIn)
+ //{
+ // LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ // if (pickerp)
+ // {
+ // pickerp->cancelSelection();
+ // }
+ //}
+}
+
+void LLColorSwatchCtrl::setValue(const LLSD& value)
+{
+ set(LLColor4(value), TRUE, TRUE);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called (infrequently) when the color changes so the subject of the swatch can be updated.
+void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op )
+{
+ LLColorSwatchCtrl* subject = ( LLColorSwatchCtrl* )data;
+ if ( subject )
+ {
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(subject->mPickerHandle);
+ if (pickerp)
+ {
+ // move color across from selector to internal widget storage
+ LLColor4 updatedColor ( pickerp->getCurR (),
+ pickerp->getCurG (),
+ pickerp->getCurB (),
+ subject->mColor.mV[VALPHA] ); // keep current alpha
+ subject->mColor = updatedColor;
+ subject->setControlValue(updatedColor.getValue());
+
+ if (pick_op == COLOR_CANCEL && subject->mOnCancelCallback)
+ {
+ subject->mOnCancelCallback(subject, subject->mCallbackUserData);
+ }
+ else if (pick_op == COLOR_SELECT && subject->mOnSelectCallback)
+ {
+ subject->mOnSelectCallback(subject, subject->mCallbackUserData);
+ }
+ else
+ {
+ // just commit change
+ subject->onCommit ();
+ }
+ }
+ };
+}
+
+void LLColorSwatchCtrl::setValid(BOOL valid )
+{
+ mValid = valid;
+
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->setActive(valid);
+ }
+}
+
+void LLColorSwatchCtrl::showPicker(BOOL take_focus)
+{
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (!pickerp)
+ {
+ pickerp = new LLFloaterColorPicker(this, mCanApplyImmediately);
+ gFloaterView->getParentFloater(this)->addDependentFloater(pickerp);
+ mPickerHandle = pickerp->getHandle();
+ }
+
+ // initialize picker singleton with current color
+ pickerp->initUI ( mColor.mV [ VRED ], mColor.mV [ VGREEN ], mColor.mV [ VBLUE ] );
+
+ // display it
+ pickerp->showUI ();
+
+ if (take_focus)
+ {
+ pickerp->setFocus(TRUE);
+ }
+}
+
+// virtual
+LLXMLNodePtr LLColorSwatchCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("color", TRUE)->setFloatValue(4, mColor.mV);
+
+ node->createChild("border_color", TRUE)->setFloatValue(4, mBorderColor.mV);
+
+ if (mCaption)
+ {
+ node->createChild("label", TRUE)->setStringValue(mCaption->getText());
+ }
+
+ node->createChild("can_apply_immediately", TRUE)->setBoolValue(mCanApplyImmediately);
+
+ return node;
+}
+
+LLView* LLColorSwatchCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("colorswatch");
+ node->getAttributeString("name", name);
+
+ LLString label;
+ node->getAttributeString("label", label);
+
+ LLColor4 color(1.f, 1.f, 1.f, 1.f);
+ node->getAttributeColor("initial_color", color);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL can_apply_immediately = FALSE;
+ node->getAttributeBOOL("can_apply_immediately", can_apply_immediately);
+
+ LLUICtrlCallback callback = NULL;
+
+ if (label.empty())
+ {
+ label.assign(node->getValue());
+ }
+
+ LLColorSwatchCtrl* color_swatch = new LLColorSwatchCtrl(
+ name,
+ rect,
+ label,
+ color,
+ callback,
+ NULL );
+
+ color_swatch->setCanApplyImmediately(can_apply_immediately);
+ color_swatch->initFromXML(node, parent);
+
+ return color_swatch;
+}
diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h
new file mode 100644
index 0000000000..020f700dfb
--- /dev/null
+++ b/indra/newview/llcolorswatch.h
@@ -0,0 +1,88 @@
+/**
+ * @file llcolorswatch.h
+ * @brief LLColorSwatch class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCOLORSWATCH_H
+#define LL_LLCOLORSWATCH_H
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llfloater.h"
+
+//
+// Classes
+//
+class LLColor4;
+class LLTextBox;
+class LLFloaterColorPicker;
+class LLViewerImage;
+
+class LLColorSwatchCtrl
+: public LLUICtrl
+{
+public:
+ typedef enum e_color_pick_op
+ {
+ COLOR_CHANGE,
+ COLOR_SELECT,
+ COLOR_CANCEL
+ } EColorPickOp;
+
+ LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const LLColor4& color,
+ void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_userdata);
+ LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const std::string& label, const LLColor4& color,
+ void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_userdata);
+
+ ~LLColorSwatchCtrl ();
+
+ virtual void setValue(const LLSD& value);
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_COLOR_SWATCH; }
+ virtual LLString getWidgetTag() const { return LL_COLOR_SWATCH_CTRL_TAG; }
+ virtual LLSD getValue() const { return mColor.getValue(); }
+ const LLColor4& get() { return mColor; }
+
+ void set(const LLColor4& color, BOOL update_picker = FALSE, BOOL from_event = FALSE);
+ void setOriginal(const LLColor4& color);
+ void setValid(BOOL valid);
+ void setLabel(const std::string& label);
+ void setCanApplyImmediately(BOOL apply) { mCanApplyImmediately = apply; }
+ void setOnCancelCallback(LLUICtrlCallback cb) { mOnCancelCallback = cb; }
+ void setOnSelectCallback(LLUICtrlCallback cb) { mOnSelectCallback = cb; }
+
+ void showPicker(BOOL take_focus);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x,S32 y,MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+ virtual void draw();
+ virtual void setEnabled( BOOL enabled );
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual void onVisibilityChange ( BOOL curVisibilityIn );
+
+ static void onColorChanged ( void* data, EColorPickOp pick_op = COLOR_CHANGE );
+
+protected:
+ BOOL mValid;
+ LLColor4 mColor;
+ LLColor4 mBorderColor;
+ LLTextBox* mCaption;
+ LLViewHandle mPickerHandle;
+ LLViewBorder* mBorder;
+ BOOL mCanApplyImmediately;
+ LLUICtrlCallback mOnCancelCallback;
+ LLUICtrlCallback mOnSelectCallback;
+
+ LLPointer<LLViewerImage> mAlphaGradientImage;
+};
+
+#endif // LL_LLBUTTON_H
diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp
new file mode 100644
index 0000000000..3d9ecd7c51
--- /dev/null
+++ b/indra/newview/llcompilequeue.cpp
@@ -0,0 +1,756 @@
+/**
+ * @file llcompilequeue.cpp
+ * @brief LLCompileQueueData class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * Implementation of the script queue which keeps an array of object
+ * UUIDs and manipulates all of the scripts on each of them.
+ *
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcompilequeue.h"
+
+#include "llagent.h"
+#include "llchat.h"
+#include "llviewerwindow.h"
+#include "llcallbacklist.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "lscript_rt_interface.h"
+#include "llviewercontrol.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llresmgr.h"
+#include "llbutton.h"
+#include "lldir.h"
+#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "llvieweruictrlfactory.h"
+
+// XUI:translate
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+// XUI:translate
+const char* COMPILE_QUEUE_TITLE = "Recompilation Progress";
+const char* COMPILE_START_STRING = "recompile";
+const char* RESET_QUEUE_TITLE = "Reset Progress";
+const char* RESET_START_STRING = "reset";
+const char* RUN_QUEUE_TITLE = "Set Running Progress";
+const char* RUN_START_STRING = "set running";
+const char* NOT_RUN_QUEUE_TITLE = "Set Not Running Progress";
+const char* NOT_RUN_START_STRING = "set not running";
+
+struct LLCompileQueueData
+{
+ LLUUID mQueueID;
+ LLUUID mOldAssetID;
+ LLCompileQueueData(const LLUUID& q_id, const LLUUID& old_asset_id) :
+ mQueueID(q_id), mOldAssetID(old_asset_id) {}
+};
+
+struct LLScriptQueueData
+{
+ LLUUID mQueueID;
+ LLString mScriptName;
+ LLScriptQueueData(const LLUUID& q_id, const char* name) :
+ mQueueID(q_id), mScriptName(name) {}
+};
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterScriptQueue
+///----------------------------------------------------------------------------
+
+// static
+LLMap<LLUUID, LLFloaterScriptQueue*> LLFloaterScriptQueue::sInstances;
+
+
+// Default constructor
+LLFloaterScriptQueue::LLFloaterScriptQueue(const std::string& name,
+ const LLRect& rect,
+ const char* title,
+ const char* start_string) :
+ LLFloater(name, rect, title,
+ RESIZE_YES, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT,
+ DRAG_ON_TOP, MINIMIZE_YES, CLOSE_YES)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_script_queue.xml");
+
+ childSetAction("close",onCloseBtn,this);
+ childSetEnabled("close",FALSE);
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+ mStartString = start_string;
+ mDone = FALSE;
+ sInstances.addData(mID, this);
+}
+
+// Destroys the object
+LLFloaterScriptQueue::~LLFloaterScriptQueue()
+{
+ sInstances.removeData(mID);
+}
+
+// find an instance by ID. Return NULL if it does not exist.
+// static
+LLFloaterScriptQueue* LLFloaterScriptQueue::findInstance(const LLUUID& id)
+{
+ if(sInstances.checkData(id))
+ {
+ return sInstances.getData(id);
+ }
+ return NULL;
+}
+
+
+// This is the callback method for the viewer object currently being
+// worked on.
+// NOT static, virtual!
+void LLFloaterScriptQueue::inventoryChanged(LLViewerObject* viewer_object,
+ InventoryObjectList* inv,
+ S32,
+ void* q_id)
+{
+ llinfos << "LLFloaterScriptQueue::inventoryChanged() for object "
+ << viewer_object->getID() << llendl;
+
+ //Remove this listener from the object since its
+ //listener callback is now being executed.
+
+ //We remove the listener here because the function
+ //removeVOInventoryListener removes the listener from a ViewerObject
+ //which it internally stores.
+
+ //If we call this further down in the function, calls to handleInventory
+ //and nextObject may update the interally stored viewer object causing
+ //the removal of the incorrect listener from an incorrect object.
+
+ //Fixes SL-6119:Recompile scripts fails to complete
+ removeVOInventoryListener();
+
+ if (viewer_object && inv && (viewer_object->getID() == mCurrentObjectID) )
+ {
+ handleInventory(viewer_object, inv);
+ }
+ else
+ {
+ // something went wrong...
+ // note that we're not working on this one, and move onto the
+ // next object in the list.
+ llwarns << "No inventory for " << mCurrentObjectID
+ << llendl;
+ nextObject();
+ }
+}
+
+
+// static
+void LLFloaterScriptQueue::onCloseBtn(void* user_data)
+{
+ LLFloaterScriptQueue* self = (LLFloaterScriptQueue*)user_data;
+ self->close();
+}
+
+void LLFloaterScriptQueue::addObject(const LLUUID& id)
+{
+ mObjectIDs.put(id);
+}
+
+BOOL LLFloaterScriptQueue::start()
+{
+ //llinfos << "LLFloaterCompileQueue::start()" << llendl;
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Starting %s of %d items.", mStartString, mObjectIDs.count());
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ list->addSimpleItem(buffer);
+
+ return nextObject();
+}
+
+BOOL LLFloaterScriptQueue::isDone() const
+{
+ return (mCurrentObjectID.isNull() && (mObjectIDs.count() == 0));
+}
+
+// go to the next object. If no objects left, it falls out silently
+// and waits to be killed by the window being closed.
+BOOL LLFloaterScriptQueue::nextObject()
+{
+ S32 count;
+ BOOL successful_start = FALSE;
+ do
+ {
+ count = mObjectIDs.count();
+ llinfos << "LLFloaterScriptQueue::nextObject() - " << count
+ << " objects left to process." << llendl;
+ mCurrentObjectID.setNull();
+ if(count > 0)
+ {
+ successful_start = popNext();
+ }
+ llinfos << "LLFloaterScriptQueue::nextObject() "
+ << (successful_start ? "successful" : "unsuccessful")
+ << llendl;
+ } while((mObjectIDs.count() > 0) && !successful_start);
+ if(isDone() && !mDone)
+ {
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+
+ mDone = TRUE;
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Done.");
+ list->addSimpleItem(buffer);
+ childSetEnabled("close",TRUE);
+ }
+ return successful_start;
+}
+
+// returns true if the queue has started, otherwise false. This
+// method pops the top object off of the queue.
+BOOL LLFloaterScriptQueue::popNext()
+{
+ // get the first element off of the container, and attempt to get
+ // the inventory.
+ BOOL rv = FALSE;
+ S32 count = mObjectIDs.count();
+ if(mCurrentObjectID.isNull() && (count > 0))
+ {
+ mCurrentObjectID = mObjectIDs.get(0);
+ llinfos << "LLFloaterScriptQueue::popNext() - mCurrentID: "
+ << mCurrentObjectID << llendl;
+ mObjectIDs.remove(0);
+ LLViewerObject* obj = gObjectList.findObject(mCurrentObjectID);
+ if(obj)
+ {
+ llinfos << "LLFloaterScriptQueue::popNext() requesting inv for "
+ << mCurrentObjectID << llendl;
+ LLUUID* id = new LLUUID(mID);
+ registerVOInventoryListener(obj,id);
+ requestVOInventory();
+ rv = TRUE;
+ }
+ }
+ return rv;
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterCompileQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterCompileQueue* LLFloaterCompileQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterCompileQueue* new_queue = new LLFloaterCompileQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterCompileQueue::LLFloaterCompileQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, COMPILE_QUEUE_TITLE, COMPILE_START_STRING)
+{ }
+
+LLFloaterCompileQueue::~LLFloaterCompileQueue()
+{
+}
+
+void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+
+ typedef std::map<LLUUID, LLPointer<LLInventoryItem> > uuid_item_map;
+ uuid_item_map asset_item_map;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ // Check permissions before allowing the user to retrieve data.
+ if (item->getPermissions().allowModifyBy(gAgent.getID()) &&
+ item->getPermissions().allowCopyBy(gAgent.getID()) )
+ {
+ LLPointer<LLViewerInventoryItem> script = new LLViewerInventoryItem(item);
+ mCurrentScripts.put(script);
+
+ if (!asset_item_map.count(item->getAssetUUID()))
+ {
+ // No entry, put in an entry for this supposedly permissive script
+ asset_item_map[item->getAssetUUID()] = item;
+ }
+ }
+ }
+ }
+
+ if (asset_item_map.empty())
+ {
+ // There are no scripts in this object. move on.
+ nextObject();
+ }
+ else
+ {
+ // request all of the assets.
+ uuid_item_map::iterator iter;
+ for(iter = asset_item_map.begin(); iter != asset_item_map.end(); iter++)
+ {
+ LLInventoryItem *itemp = iter->second;
+ LLScriptQueueData* datap = new LLScriptQueueData(getID(), itemp->getName().c_str());
+
+ //llinfos << "ITEM NAME 2: " << names.get(i) << llendl;
+ gAssetStorage->getInvItemAsset(viewer_object->getRegion()->getHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ itemp->getPermissions().getOwner(),
+ viewer_object->getID(),
+ itemp->getUUID(),
+ itemp->getAssetUUID(),
+ itemp->getType(),
+ LLFloaterCompileQueue::scriptArrived,
+ (void*)datap);
+ }
+ }
+}
+
+
+// This is the callback for when each script arrives
+// static
+void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "LLFloaterCompileQueue::scriptArrived()" << llendl;
+ LLScriptQueueData* data = (LLScriptQueueData*)user_data;
+ if(!data) return;
+ LLFloaterCompileQueue* queue = static_cast<LLFloaterCompileQueue*>
+ (LLFloaterScriptQueue::findInstance(data->mQueueID));
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+ if(queue && (0 == status))
+ {
+ //llinfos << "ITEM NAME 3: " << data->mScriptName << llendl;
+
+ // Dump this into a file on the local disk so we can compile it.
+ char filename[LL_MAX_PATH] = "";
+ LLVFile file(vfs, asset_id, type);
+ char uuid_str[UUID_STR_LENGTH];
+ asset_id.toString(uuid_str);
+ sprintf(filename,"%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type));
+
+ FILE *fp = LLFile::fopen(filename, "wb");
+ if (fp)
+ {
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while (file.read(copy_buf, buf_size))
+ {
+ if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1)
+ {
+ // return a bad file error if we can't write the whole thing
+ status = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ // It's now in the file, now compile it.
+ sprintf(buffer, "Downloaded, now compiling '%s'.", data->mScriptName.c_str());
+ queue->compile(filename, asset_id);
+
+ // Delete it after we're done compiling?
+ LLFile::remove(filename);
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status )
+ {
+ LLChat chat("Script not found on server.");
+ LLFloaterChat::addChat(chat);
+ sprintf(buffer, "Problem downloading %s.",
+ data->mScriptName.c_str());
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLChat chat("Insufficient permissions to download a script.");
+ LLFloaterChat::addChat(chat);
+ sprintf(buffer, "Insufficient permissions for '%s'.",
+ data->mScriptName.c_str());
+ }
+ else
+ {
+ sprintf(buffer, "Unknown failure to download %s.",
+ data->mScriptName.c_str());
+ }
+
+ llwarns << "Problem downloading script asset." << llendl;
+ if(queue) queue->removeItemByAssetID(asset_id);
+ }
+ if(queue)
+ {
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(queue, "queue output");
+ list->addSimpleItem(buffer);
+ }
+ delete data;
+}
+
+// static
+void LLFloaterCompileQueue::onSaveTextComplete(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ llinfos << "LLFloaterCompileQueue::onSaveTextComplete()" << llendl;
+ if (status)
+ {
+ llwarns << "Unable to save text for script." << llendl;
+ LLString::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveText", args);
+ }
+}
+
+// static
+void LLFloaterCompileQueue::onSaveBytecodeComplete(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ llinfos << "LLFloaterCompileQueue::onSaveBytecodeComplete()" << llendl;
+ LLCompileQueueData* data = (LLCompileQueueData*)user_data;
+ LLFloaterCompileQueue* queue = static_cast<LLFloaterCompileQueue*>
+ (LLFloaterScriptQueue::findInstance(data->mQueueID));
+ if(queue && (0 == status) && data)
+ {
+ queue->updateAssetID(data->mOldAssetID, asset_id);
+ queue->saveItemByAssetID(asset_id);
+ queue->removeItemByAssetID(asset_id);
+ }
+ else
+ {
+ llwarns << "Unable to save bytecode for script." << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveBytecode", args);
+ }
+ delete data;
+ data = NULL;
+}
+
+// compile the file given and save it out.
+void LLFloaterCompileQueue::compile(const char* filename,
+ const LLUUID& asset_id)
+{
+ LLUUID new_asset_id;
+ LLTransactionID tid;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ char uuid_string[UUID_STR_LENGTH];
+ new_asset_id.toString(uuid_string);
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ char err_filename[LL_MAX_PATH];
+ sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+
+ gAssetStorage->storeAssetData(filename, tid,
+ LLAssetType::AT_LSL_TEXT,
+ &onSaveTextComplete, NULL, FALSE);
+ if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike()))
+ {
+ llwarns << "compile failed" << llendl;
+ removeItemByAssetID(asset_id);
+ }
+ else
+ {
+ llinfos << "compile successful." << llendl;
+ // Save the bytecode
+ LLCompileQueueData* data = new LLCompileQueueData(mID, asset_id);
+ gAssetStorage->storeAssetData(dst_filename, tid,
+ LLAssetType::AT_LSL_BYTECODE,
+ &onSaveBytecodeComplete,
+ (void*)data, FALSE);
+ }
+}
+
+void LLFloaterCompileQueue::removeItemByAssetID(const LLUUID& asset_id)
+{
+ llinfos << "LLFloaterCompileQueue::removeItemByAssetID()" << llendl;
+ for(S32 i = 0; i < mCurrentScripts.count(); )
+ {
+ if(asset_id == mCurrentScripts.get(i)->getAssetUUID())
+ {
+ mCurrentScripts.remove(i);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ if(mCurrentScripts.count() == 0)
+ {
+ nextObject();
+ }
+}
+
+void LLFloaterCompileQueue::saveItemByAssetID(const LLUUID& asset_id)
+{
+ llinfos << "LLFloaterCompileQueue::saveItemByAssetID()" << llendl;
+ LLViewerObject* viewer_object = gObjectList.findObject(mCurrentObjectID);
+ if(viewer_object)
+ {
+ S32 count = mCurrentScripts.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(asset_id == mCurrentScripts.get(i)->getAssetUUID())
+ {
+ // *FIX: this auto-resets active to TRUE. That might
+ // be a bad idea.
+ viewer_object->saveScript(mCurrentScripts.get(i), TRUE, false);
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Unable to finish save!" << llendl;
+ }
+}
+
+// find old_asst_id, and set the asset id to new_asset_id
+void LLFloaterCompileQueue::updateAssetID(const LLUUID& old_asset_id,
+ const LLUUID& new_asset_id)
+{
+ S32 count = mCurrentScripts.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(old_asset_id == mCurrentScripts.get(i)->getAssetUUID())
+ {
+ mCurrentScripts.get(i)->setAssetUUID(new_asset_id);
+ }
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterResetQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterResetQueue* LLFloaterResetQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterResetQueue* new_queue = new LLFloaterResetQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterResetQueue::LLFloaterResetQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, RESET_QUEUE_TITLE, RESET_START_STRING)
+{ }
+
+LLFloaterResetQueue::~LLFloaterResetQueue()
+{
+}
+
+void LLFloaterResetQueue::handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+ LLDynamicArray<const char*> names;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
+
+ if (object)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Resetting '%s'.", item->getName().c_str());
+ list->addSimpleItem(buffer);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ScriptReset);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, viewer_obj->getID());
+ msg->addUUIDFast(_PREHASH_ItemID, (*it)->getUUID());
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ }
+ }
+
+ nextObject();
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterRunQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterRunQueue* LLFloaterRunQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterRunQueue* new_queue = new LLFloaterRunQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterRunQueue::LLFloaterRunQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, RUN_QUEUE_TITLE, RUN_START_STRING)
+{ }
+
+LLFloaterRunQueue::~LLFloaterRunQueue()
+{
+}
+
+void LLFloaterRunQueue::handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+ LLDynamicArray<const char*> names;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
+
+ if (object)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Running '%s'.", item->getName().c_str());
+ list->addSimpleItem(buffer);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetScriptRunning);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, viewer_obj->getID());
+ msg->addUUIDFast(_PREHASH_ItemID, (*it)->getUUID());
+ msg->addBOOLFast(_PREHASH_Running, TRUE);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ }
+ }
+
+ nextObject();
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterNotRunQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterNotRunQueue* LLFloaterNotRunQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterNotRunQueue* new_queue = new LLFloaterNotRunQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterNotRunQueue::LLFloaterNotRunQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, NOT_RUN_QUEUE_TITLE, NOT_RUN_START_STRING)
+{ }
+
+LLFloaterNotRunQueue::~LLFloaterNotRunQueue()
+{
+}
+
+void LLFloaterNotRunQueue::handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+ LLDynamicArray<const char*> names;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
+
+ if (object)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Not running '%s'.", item->getName().c_str());
+ list->addSimpleItem(buffer);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetScriptRunning);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, viewer_obj->getID());
+ msg->addUUIDFast(_PREHASH_ItemID, (*it)->getUUID());
+ msg->addBOOLFast(_PREHASH_Running, FALSE);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ }
+ }
+
+ nextObject();
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h
new file mode 100644
index 0000000000..fc0a872907
--- /dev/null
+++ b/indra/newview/llcompilequeue.h
@@ -0,0 +1,211 @@
+/**
+ * @file llcompilequeue.h
+ * @brief LLCompileQueue class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCOMPILEQUEUE_H
+#define LL_LLCOMPILEQUEUE_H
+
+#include "lldarray.h"
+#include "llinventory.h"
+#include "llviewerobject.h"
+#include "llvoinventorylistener.h"
+#include "llmap.h"
+#include "lluuid.h"
+
+#include "llfloater.h"
+#include "llscrolllistctrl.h"
+
+#include "llviewerinventory.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterScriptQueue
+//
+// This class provides a mechanism of adding objects to a list that
+// will go through and execute action for the scripts on each object. The
+// objects will be accessed serially and the scripts may be
+// manipulated in parallel. For example, selecting two objects each
+// with three scripts will result in the first object having all three
+// scripts manipulated.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterScriptQueue : public LLFloater, public LLVOInventoryListener
+{
+public:
+ // addObject() accepts an object id.
+ void addObject(const LLUUID& id);
+
+ // start() returns TRUE if the queue has started, otherwise FALSE.
+ BOOL start();
+
+protected:
+ LLFloaterScriptQueue(const std::string& name, const LLRect& rect,
+ const char* title, const char* start_string);
+ virtual ~LLFloaterScriptQueue();
+
+ // This is the callback method for the viewer object currently
+ // being worked on.
+ /*virtual*/ void inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* queue);
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv) = 0;
+
+ static void onCloseBtn(void* user_data);
+
+ // returns true if this is done
+ BOOL isDone() const;
+
+ // go to the next object. If no objects left, it falls out
+ // silently and waits to be killed by the deleteIfDone() callback.
+ BOOL nextObject();
+ BOOL popNext();
+
+ // Get this instances ID.
+ const LLUUID& getID() const { return mID; }
+
+ // find an instance by ID. Return NULL if it does not exist.
+ static LLFloaterScriptQueue* findInstance(const LLUUID& id);
+
+protected:
+ // UI
+ LLScrollListCtrl* mMessages;
+ LLButton* mCloseBtn;
+
+ // Object Queue
+ LLDynamicArray<LLUUID> mObjectIDs;
+ LLUUID mCurrentObjectID;
+ BOOL mDone;
+
+ LLUUID mID;
+ static LLMap<LLUUID, LLFloaterScriptQueue*> sInstances;
+
+ const char* mStartString;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterCompileQueue
+//
+// This script queue will recompile each script.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterCompileQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a compile queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterCompileQueue* create();
+
+protected:
+ LLFloaterCompileQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterCompileQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+ // This is the callback for when each script arrives
+ static void scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ static void onSaveTextComplete(const LLUUID& asset_id, void* user_data, S32 status);
+ static void onSaveBytecodeComplete(const LLUUID& asset_id,
+ void* user_data,
+ S32 status);
+
+ // compile the file given and save it out.
+ void compile(const char* filename, const LLUUID& asset_id);
+
+ // remove any object in mScriptScripts with the matching uuid.
+ void removeItemByAssetID(const LLUUID& asset_id);
+
+ // save the items indicatd by the asset id.
+ void saveItemByAssetID(const LLUUID& asset_id);
+
+ // find old_asst_id, and set the asset id to new_asset_id
+ void updateAssetID(const LLUUID& old_asset_id, const LLUUID& new_asset_id);
+
+protected:
+ LLViewerInventoryItem::item_array_t mCurrentScripts;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterResetQueue
+//
+// This script queue will reset each script.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterResetQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a reset queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterResetQueue* create();
+
+protected:
+ LLFloaterResetQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterResetQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+protected:
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterRunQueue
+//
+// This script queue will set each script as running.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterRunQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a run queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterRunQueue* create();
+
+protected:
+ LLFloaterRunQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterRunQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+protected:
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterNotRunQueue
+//
+// This script queue will set each script as not running.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterNotRunQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a not run queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterNotRunQueue* create();
+
+protected:
+ LLFloaterNotRunQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterNotRunQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+protected:
+};
+
+#endif // LL_LLCOMPILEQUEUE_H
diff --git a/indra/newview/llconfirmationmanager.cpp b/indra/newview/llconfirmationmanager.cpp
new file mode 100644
index 0000000000..e47fcde225
--- /dev/null
+++ b/indra/newview/llconfirmationmanager.cpp
@@ -0,0 +1,100 @@
+/**
+ * @file llconfirmationmanager.cpp
+ * @brief LLConfirmationManager class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llconfirmationmanager.h"
+
+#include "lluictrlfactory.h"
+
+// viewer includes
+#include "llviewerwindow.h"
+#include "lllineeditor.h"
+#include "llstring.h"
+
+LLConfirmationManager::ListenerBase::~ListenerBase()
+{
+}
+
+
+static void onConfirmAlert(S32 option, void* data)
+{
+ LLConfirmationManager::ListenerBase* listener
+ = (LLConfirmationManager::ListenerBase*)data;
+
+ if (option == 0)
+ {
+ listener->confirmed("");
+ }
+
+ delete listener;
+}
+
+static void onConfirmAlertPassword(
+ S32 option, const LLString& text, void* data)
+{
+ LLConfirmationManager::ListenerBase* listener
+ = (LLConfirmationManager::ListenerBase*)data;
+
+ if (option == 0)
+ {
+ listener->confirmed(text);
+ }
+
+ delete listener;
+}
+
+
+void LLConfirmationManager::confirm(Type type,
+ const std::string& action,
+ ListenerBase* listener)
+{
+ LLString::format_map_t args;
+ args["[ACTION]"] = action;
+
+ switch (type)
+ {
+ case TYPE_CLICK:
+ gViewerWindow->alertXml("ConfirmPurchase", args,
+ onConfirmAlert, listener);
+ break;
+
+ case TYPE_PASSWORD:
+ gViewerWindow->alertXmlEditText("ConfirmPurchasePassword", args,
+ NULL, NULL,
+ onConfirmAlertPassword, listener,
+ LLString::format_map_t(),
+ TRUE);
+ break;
+ case TYPE_NONE:
+ default:
+ listener->confirmed("");
+ break;
+ }
+}
+
+
+void LLConfirmationManager::confirm(
+ const std::string& type,
+ const std::string& action,
+ ListenerBase* listener)
+{
+ Type decodedType = TYPE_NONE;
+
+ if (type == "click")
+ {
+ decodedType = TYPE_CLICK;
+ }
+ else if (type == "password")
+ {
+ decodedType = TYPE_PASSWORD;
+ }
+
+ confirm(decodedType, action, listener);
+}
+
diff --git a/indra/newview/llconfirmationmanager.h b/indra/newview/llconfirmationmanager.h
new file mode 100644
index 0000000000..3aa33774e5
--- /dev/null
+++ b/indra/newview/llconfirmationmanager.h
@@ -0,0 +1,67 @@
+/**
+ * @file llconfirmationmanager.h
+ * @brief LLConfirmationManager class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCONFIRMATIONMANAGER_H
+#define LL_LLCONFIRMATIONMANAGER_H
+
+class LLConfirmationManager
+{
+public:
+ class ListenerBase
+ {
+ public:
+ virtual ~ListenerBase();
+ virtual void confirmed(const std::string& password) = 0;
+ };
+
+ enum Type { TYPE_NONE, TYPE_CLICK, TYPE_PASSWORD };
+
+ static void confirm(Type type,
+ const std::string& purchase, ListenerBase* listener);
+ static void confirm(const std::string& type,
+ const std::string& purchase, ListenerBase* listener);
+ // note: these take control of, and delete the listener when done
+
+ template <class T>
+ class Listener : public ListenerBase
+ {
+ public:
+ typedef void (T::*ConfirmationMemberFunction)(const std::string&);
+
+ Listener(T& object, ConfirmationMemberFunction function)
+ : mObject(object), mFunction(function)
+ { }
+
+ void confirmed(const std::string& password)
+ {
+ (mObject.*mFunction)(password);
+ }
+
+ private:
+ T& mObject;
+ ConfirmationMemberFunction mFunction;
+ };
+
+ template <class T>
+ static void confirm(Type type,
+ const std::string& action,
+ T& obj, void(T::*func)(const std::string&))
+ {
+ confirm(type, action, new Listener<T>(obj, func));
+ }
+
+ template <class T>
+ static void confirm(const std::string& type,
+ const std::string& action,
+ T& obj, void(T::*func)(const std::string&))
+ {
+ confirm(type, action, new Listener<T>(obj, func));
+ }
+};
+
+#endif
diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp
new file mode 100644
index 0000000000..ffea2928c2
--- /dev/null
+++ b/indra/newview/llcurrencyuimanager.cpp
@@ -0,0 +1,497 @@
+/**
+ * @file llcurrencyuimanager.cpp
+ * @brief LLCurrencyUIManager class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lluictrlfactory.h"
+#include "lltextbox.h"
+#include "lllineeditor.h"
+
+#include "llcurrencyuimanager.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llconfirmationmanager.h"
+#include "llframetimer.h"
+#include "lllineeditor.h"
+#include "llviewchildren.h"
+#include "llxmlrpctransaction.h"
+#include "viewer.h"
+
+
+
+const F64 CURRENCY_ESTIMATE_FREQUENCY = 2.0;
+ // how long of a pause in typing a currency buy amount before an
+ // esimate is fetched from the server
+
+class LLCurrencyUIManager::Impl
+{
+public:
+ Impl(LLPanel& dialog);
+ virtual ~Impl();
+
+ LLPanel& mPanel;
+
+ bool mHidden;
+
+ bool mError;
+ std::string mErrorMessage;
+ std::string mErrorURI;
+
+ std::string mZeroMessage;
+
+ // user's choices
+ S32 mUserCurrencyBuy;
+ bool mUserEnteredCurrencyBuy;
+
+ // from website
+ bool mSiteCurrencyEstimated;
+ S32 mSiteCurrencyEstimatedCost;
+ std::string mSiteConfirm;
+
+ bool mBought;
+
+ enum TransactionType
+ {
+ TransactionNone,
+ TransactionCurrency,
+ TransactionBuy
+ };
+
+ TransactionType mTransactionType;
+ LLXMLRPCTransaction* mTransaction;
+
+ bool mCurrencyChanged;
+ LLFrameTimer mCurrencyKeyTimer;
+
+
+ void updateCurrencyInfo();
+ void finishCurrencyInfo();
+
+ void startCurrencyBuy(const std::string& password);
+ void finishCurrencyBuy();
+
+ void startTransaction(TransactionType type,
+ const char* method, LLXMLRPCValue params);
+ bool checkTransaction();
+ // return true if update needed
+
+ void setError(const std::string& message, const std::string& uri);
+ void clearError();
+
+ bool considerUpdateCurrency();
+ // return true if update needed
+ void currencyKey(S32);
+ static void onCurrencyKey(LLLineEditor* caller, void* data);
+
+ void prepare();
+ void updateUI();
+};
+
+// is potentially not fully constructed.
+LLCurrencyUIManager::Impl::Impl(LLPanel& dialog)
+ : mPanel(dialog),
+ mHidden(false),
+ mError(false),
+ mUserCurrencyBuy(1000), mUserEnteredCurrencyBuy(false),
+ mSiteCurrencyEstimated(false),
+ mBought(false),
+ mTransactionType(TransactionNone), mTransaction(0),
+ mCurrencyChanged(false)
+{
+}
+
+LLCurrencyUIManager::Impl::~Impl()
+{
+ delete mTransaction;
+}
+
+void LLCurrencyUIManager::Impl::updateCurrencyInfo()
+{
+ mSiteCurrencyEstimated = false;
+ mSiteCurrencyEstimatedCost = 0;
+ mBought = false;
+ mCurrencyChanged = false;
+
+ if (mUserCurrencyBuy == 0)
+ {
+ mSiteCurrencyEstimated = true;
+ return;
+ }
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId",
+ gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionCurrency, "getCurrencyQuote", params);
+}
+
+void LLCurrencyUIManager::Impl::finishCurrencyInfo()
+{
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ bool success = result["success"].asBool();
+ if (!success)
+ {
+ setError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ return;
+ }
+
+ LLXMLRPCValue currency = result["currency"];
+ mSiteCurrencyEstimated = true;
+ mSiteCurrencyEstimatedCost = currency["estimatedCost"].asInt();
+
+ S32 newCurrencyBuy = currency["currencyBuy"].asInt();
+ if (newCurrencyBuy != mUserCurrencyBuy)
+ {
+ mUserCurrencyBuy = newCurrencyBuy;
+ mUserEnteredCurrencyBuy = false;
+ }
+
+ mSiteConfirm = result["confirm"].asString();
+}
+
+void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
+{
+ mSiteCurrencyEstimated = false;
+ mSiteCurrencyEstimatedCost = 0;
+ mCurrencyChanged = false;
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId",
+ gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
+ keywordArgs.appendInt("estimatedCost", mSiteCurrencyEstimatedCost);
+ keywordArgs.appendString("confirm", mSiteConfirm);
+ if (!password.empty())
+ {
+ keywordArgs.appendString("password", password);
+ }
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionBuy, "buyCurrency", params);
+}
+
+void LLCurrencyUIManager::Impl::finishCurrencyBuy()
+{
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ bool success = result["success"].asBool();
+ if (!success)
+ {
+ setError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ }
+ else
+ {
+ mUserCurrencyBuy = 0;
+ mUserEnteredCurrencyBuy = false;
+ mBought = true;
+ }
+}
+
+void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
+ const char* method, LLXMLRPCValue params)
+{
+ static std::string transactionURI;
+ if (transactionURI.empty())
+ {
+ transactionURI = getHelperURI() + "currency.php";
+ }
+
+ delete mTransaction;
+
+ mTransactionType = type;
+ mTransaction = new LLXMLRPCTransaction(
+ transactionURI,
+ method,
+ params,
+ false /* don't use gzip */
+ );
+
+ clearError();
+}
+
+bool LLCurrencyUIManager::Impl::checkTransaction()
+{
+ if (!mTransaction)
+ {
+ return false;
+ }
+
+ if (!mTransaction->process())
+ {
+ return false;
+ }
+
+ if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete)
+ {
+ setError(mTransaction->statusMessage(), mTransaction->statusURI());
+ }
+ else {
+ switch (mTransactionType)
+ {
+ case TransactionCurrency: finishCurrencyInfo(); break;
+ case TransactionBuy: finishCurrencyBuy(); break;
+ default: ;
+ }
+ }
+
+ delete mTransaction;
+ mTransaction = NULL;
+ mTransactionType = TransactionNone;
+
+ return true;
+}
+
+void LLCurrencyUIManager::Impl::setError(
+ const std::string& message, const std::string& uri)
+{
+ mError = true;
+ mErrorMessage = message;
+ mErrorURI = uri;
+}
+
+void LLCurrencyUIManager::Impl::clearError()
+{
+ mError = false;
+ mErrorMessage.clear();
+ mErrorURI.clear();
+}
+
+bool LLCurrencyUIManager::Impl::considerUpdateCurrency()
+{
+ if (mCurrencyChanged
+ && !mTransaction
+ && mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
+ {
+ updateCurrencyInfo();
+ return true;
+ }
+
+ return false;
+}
+
+void LLCurrencyUIManager::Impl::currencyKey(S32 value)
+{
+ mUserEnteredCurrencyBuy = true;
+ mCurrencyKeyTimer.reset();
+
+ if (mUserCurrencyBuy == value)
+ {
+ return;
+ }
+
+ mUserCurrencyBuy = value;
+
+ if (mSiteCurrencyEstimated) {
+ mSiteCurrencyEstimated = false;
+ //cannot just simply refresh the whole UI, as the edit field will
+ // get reset and the cursor will change...
+
+ mPanel.childHide("currency_est");
+ }
+
+ mCurrencyChanged = true;
+}
+
+// static
+void LLCurrencyUIManager::Impl::onCurrencyKey(
+ LLLineEditor* caller, void* data)
+{
+ S32 value = atoi(caller->getText().c_str());
+ LLCurrencyUIManager::Impl* self = (LLCurrencyUIManager::Impl*)data;
+ self->currencyKey(value);
+}
+
+void LLCurrencyUIManager::Impl::prepare()
+{
+ LLLineEditor* lindenAmount = LLUICtrlFactory::getLineEditorByName(&mPanel,"currency_amt");
+ if (lindenAmount)
+ {
+ lindenAmount->setPrevalidate(LLLineEditor::prevalidateNonNegativeS32);
+ lindenAmount->setKeystrokeCallback(onCurrencyKey);
+ lindenAmount->setCallbackUserData(this);
+ }
+}
+
+void LLCurrencyUIManager::Impl::updateUI()
+{
+ if (mHidden)
+ {
+ mPanel.childHide("currency_action");
+ mPanel.childHide("currency_amt");
+ mPanel.childHide("currency_est");
+ return;
+ }
+
+ mPanel.childShow("currency_action");
+
+ LLLineEditor* lindenAmount = LLUICtrlFactory::getLineEditorByName(&mPanel,"currency_amt");
+ if (lindenAmount)
+ {
+ lindenAmount->setVisible(true);
+ lindenAmount->setLabel(mZeroMessage);
+
+ if (!mUserEnteredCurrencyBuy)
+ {
+ if (!mZeroMessage.empty() && mUserCurrencyBuy == 0)
+ {
+ lindenAmount->setText("");
+ }
+ else
+ {
+ lindenAmount->setText(llformat("%d", mUserCurrencyBuy));
+ }
+
+ lindenAmount->selectAll();
+ }
+ }
+
+ mPanel.childSetTextArg("currency_est", "[USD]", llformat("%#.2f", mSiteCurrencyEstimatedCost / 100.0));
+ mPanel.childSetVisible("currency_est", mSiteCurrencyEstimated && mUserCurrencyBuy > 0);
+}
+
+
+
+LLCurrencyUIManager::LLCurrencyUIManager(LLPanel& dialog)
+ : impl(* new Impl(dialog))
+{
+}
+
+LLCurrencyUIManager::~LLCurrencyUIManager()
+{
+ delete &impl;
+}
+
+void LLCurrencyUIManager::setAmount(int amount, bool noEstimate)
+{
+ impl.mUserCurrencyBuy = amount;
+ impl.mUserEnteredCurrencyBuy = false;
+ impl.updateUI();
+
+ impl.mCurrencyChanged = !noEstimate;
+}
+
+int LLCurrencyUIManager::getAmount()
+{
+ return impl.mUserCurrencyBuy;
+}
+
+void LLCurrencyUIManager::setZeroMessage(const std::string& message)
+{
+ impl.mZeroMessage = message;
+}
+
+void LLCurrencyUIManager::setEstimate(int amount)
+{
+ impl.mSiteCurrencyEstimatedCost = amount;
+ impl.mSiteCurrencyEstimated = true;
+ impl.updateUI();
+
+ impl.mCurrencyChanged = false;
+}
+
+int LLCurrencyUIManager::getEstimate()
+{
+ return impl.mSiteCurrencyEstimated ? impl.mSiteCurrencyEstimatedCost : 0;
+}
+
+void LLCurrencyUIManager::prepare()
+{
+ impl.prepare();
+}
+
+void LLCurrencyUIManager::updateUI(bool show)
+{
+ impl.mHidden = !show;
+ impl.updateUI();
+}
+
+bool LLCurrencyUIManager::process()
+{
+ bool changed = false;
+ changed |= impl.checkTransaction();
+ changed |= impl.considerUpdateCurrency();
+ return changed;
+}
+
+void LLCurrencyUIManager::buy()
+{
+ if (!canBuy())
+ {
+ return;
+ }
+
+ // XUI:translate
+ LLConfirmationManager::confirm(impl.mSiteConfirm,
+ llformat("Buy L$ %d for approx. US$ %#.2f\n",
+ impl.mUserCurrencyBuy,
+ impl.mSiteCurrencyEstimatedCost / 100.0),
+ impl,
+ &LLCurrencyUIManager::Impl::startCurrencyBuy);
+}
+
+
+bool LLCurrencyUIManager::inProcess()
+{
+ return impl.mTransactionType != Impl::TransactionNone;
+}
+
+bool LLCurrencyUIManager::canCancel()
+{
+ return impl.mTransactionType != Impl::TransactionBuy;
+}
+
+bool LLCurrencyUIManager::canBuy()
+{
+ return impl.mTransactionType == Impl::TransactionNone
+ && impl.mSiteCurrencyEstimated
+ && impl.mUserCurrencyBuy > 0;
+}
+
+bool LLCurrencyUIManager::buying()
+{
+ return impl.mTransactionType == Impl::TransactionBuy;
+}
+
+bool LLCurrencyUIManager::bought()
+{
+ return impl.mBought;
+}
+
+bool LLCurrencyUIManager::hasError()
+{
+ return impl.mError;
+}
+
+std::string LLCurrencyUIManager::errorMessage()
+{
+ return impl.mErrorMessage;
+}
+
+std::string LLCurrencyUIManager::errorURI()
+{
+ return impl.mErrorURI;
+}
+
diff --git a/indra/newview/llcurrencyuimanager.h b/indra/newview/llcurrencyuimanager.h
new file mode 100644
index 0000000000..3c5ad22ee5
--- /dev/null
+++ b/indra/newview/llcurrencyuimanager.h
@@ -0,0 +1,72 @@
+/**
+ * @file llcurrencyuimanager.h
+ * @brief LLCurrencyUIManager class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCURRENCYUIMANAGER_H
+#define LL_LLCURRENCYUIMANAGER_H
+
+class LLPanel;
+
+
+class LLCurrencyUIManager
+ // manages the currency purchase portion of any dialog
+ // takes control of, and assumes repsonsibility for several
+ // fields:
+ // 'currency_action' - the text "Buy L$" before the entry field
+ // 'currency_amt' - the line editor for the entry amount
+ // 'currency_est' - the estimated cost from the web site
+{
+public:
+ LLCurrencyUIManager(LLPanel& parent);
+ virtual ~LLCurrencyUIManager();
+
+ void setAmount(int, bool noEstimate = false);
+ int getAmount();
+ // the amount in L$ to purchase
+ // setting it overwrites the user's entry
+ // if noEstimate is true, than no web request is made
+
+ void setZeroMessage(const std::string& message);
+ // sets the gray message to show when zero
+
+ void setEstimate(int);
+ int getEstimate();
+ // the amount in US$ * 100 (in otherwords, in cents)
+ // use set when you get this information from elsewhere
+
+ void prepare();
+ // call once after dialog is built, from postBuild()
+ void updateUI(bool show = true);
+ // update all UI elements, if show is false, they are all set not visible
+ // normally, this is done automatically, but you can force it
+ // the show/hidden state is remembered
+ bool process();
+ // call periodically, for example, from draw()
+ // returns true if the UI needs to be updated
+
+ void buy();
+ // call to initiate the purchase
+
+ bool inProcess(); // is a transaction in process
+ bool canCancel(); // can we cancel it (by destructing this object)
+ bool canBuy(); // can the user choose to buy now?
+ bool buying(); // are we in the process of buying?
+ bool bought(); // did the buy() transaction complete successfully
+
+ bool hasError();
+ std::string errorMessage();
+ std::string errorURI();
+ // error information for the user, the URI may be blank
+ // the technical error details will have already been logged
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+#endif
+
diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp
new file mode 100644
index 0000000000..1774ff4ccf
--- /dev/null
+++ b/indra/newview/llcylinder.cpp
@@ -0,0 +1,308 @@
+/**
+ * @file llcylinder.cpp
+ * @brief Draws a cylinder using display lists for speed.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcylinder.h"
+
+#include "llerror.h"
+#include "math.h"
+#include "llmath.h"
+#include "noise.h"
+#include "v3math.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+
+LLCylinder gCylinder;
+LLCone gCone;
+
+GLUquadricObj* gQuadObj = NULL;
+
+// draws a cylinder or cone
+// returns approximate number of triangles required
+U32 draw_cylinder_side(GLint slices, GLint stacks, GLfloat base_radius, GLfloat top_radius)
+{
+ U32 triangles = 0;
+ GLfloat height = 1.0f;
+
+ if (!gQuadObj)
+ {
+ gQuadObj = gluNewQuadric();
+ if (!gQuadObj) llerror("draw_cylindrical_body couldn't allocated quadric", 0);
+ }
+
+ gluQuadricDrawStyle(gQuadObj, GLU_FILL);
+ gluQuadricNormals(gQuadObj, GLU_SMOOTH);
+ gluQuadricOrientation(gQuadObj, GLU_OUTSIDE);
+ gluQuadricTexture(gQuadObj, GL_TRUE);
+ gluCylinder(gQuadObj, base_radius, top_radius, height, slices, stacks);
+ triangles += stacks * (slices * 2);
+
+
+ return triangles;
+}
+
+
+// Returns number of triangles required to draw
+// Need to know if top or not to set lighting normals
+const BOOL TOP = TRUE;
+const BOOL BOTTOM = FALSE;
+U32 draw_cylinder_cap(GLint slices, GLfloat base_radius, BOOL is_top)
+{
+ U32 triangles = 0;
+
+ if (!gQuadObj)
+ {
+ gQuadObj = gluNewQuadric();
+ if (!gQuadObj) llerror("draw_cylinder_base couldn't allocated quadric", 0);
+ }
+
+ gluQuadricDrawStyle(gQuadObj, GLU_FILL);
+ gluQuadricNormals(gQuadObj, GLU_SMOOTH);
+ gluQuadricOrientation(gQuadObj, GLU_OUTSIDE);
+ gluQuadricTexture(gQuadObj, GL_TRUE);
+
+ // no hole in the middle of the disk, and just one ring
+ GLdouble inner_radius = 0.0;
+ GLint rings = 1;
+
+ // normals point in +z for top, -z for base
+ if (is_top)
+ {
+ gluQuadricOrientation(gQuadObj, GLU_OUTSIDE);
+ }
+ else
+ {
+ gluQuadricOrientation(gQuadObj, GLU_INSIDE);
+ }
+ gluDisk(gQuadObj, inner_radius, base_radius, slices, rings);
+ triangles += slices;
+
+ return triangles;
+}
+
+
+void LLCylinder::prerender()
+{
+ GLint stacks = 2;
+ GLfloat radius = 0.5f;
+ GLint slices[CYLINDER_LEVELS_OF_DETAIL] = { 30, 20, 12, 6 }; // same as sphere slices
+
+ for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
+ {
+ mTriangleCount[detail] = 0;
+
+ mDisplayListSide[detail] = glGenLists(1);
+ glNewList(mDisplayListSide[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_side( slices[detail], stacks, radius, radius );
+ glEndList();
+
+ mDisplayListTop[detail] = glGenLists(1);
+ glNewList( mDisplayListTop[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, TOP );
+ glEndList();
+
+ mDisplayListBottom[detail] = glGenLists(1);
+ glNewList( mDisplayListBottom[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, BOTTOM );
+ glEndList();
+ }
+}
+
+void LLCylinder::cleanupGL()
+{
+ for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
+ {
+ glDeleteLists(mDisplayListSide[detail], 1);
+ mDisplayListSide[detail] = 0;
+ glDeleteLists(mDisplayListTop[detail], 1);
+ mDisplayListTop[detail] = 0;
+ glDeleteLists(mDisplayListBottom[detail], 1);
+ mDisplayListBottom[detail] = 0;
+ }
+
+ if (gQuadObj)
+ {
+ gluDeleteQuadric(gQuadObj);
+ gQuadObj = NULL;
+ }
+}
+
+void LLCylinder::render(F32 pixel_area)
+{
+ renderface(pixel_area, 0);
+ renderface(pixel_area, 1);
+ renderface(pixel_area, 2);
+}
+
+
+void LLCylinder::renderface(F32 pixel_area, S32 face)
+{
+ if (face < 0 || face > 2)
+ {
+ llerror("LLCylinder::renderface() invalid face number", face);
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ S32 level_of_detail;
+
+ if (pixel_area > 20000.f)
+ {
+ level_of_detail = 0;
+ }
+ else if (pixel_area > 1600.f)
+ {
+ level_of_detail = 1;
+ }
+ else if (pixel_area > 200.f)
+ {
+ level_of_detail = 2;
+ }
+ else
+ {
+ level_of_detail = 3;
+ }
+
+ if (level_of_detail < 0 || CYLINDER_LEVELS_OF_DETAIL <= level_of_detail)
+ {
+ llerror("LLCylinder::renderface() invalid level of detail", level_of_detail);
+ return;
+ }
+
+ switch(face)
+ {
+ case 0:
+ glTranslatef(0.f, 0.f, -0.5f);
+ glCallList(mDisplayListSide[level_of_detail]);
+ break;
+ case 1:
+ glTranslatef(0.0f, 0.f, 0.5f);
+ glCallList(mDisplayListTop[level_of_detail]);
+ break;
+ case 2:
+ glTranslatef(0.0f, 0.f, -0.5f);
+ glCallList(mDisplayListBottom[level_of_detail]);
+ break;
+ default:
+ llerror("LLCylinder::renderface() fell out of switch", 0);
+ break;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+
+//
+// Cones
+//
+
+void LLCone::prerender()
+{
+ GLint stacks = 2;
+ GLfloat radius = 0.5f;
+ GLint slices[CONE_LEVELS_OF_DETAIL] = { 32, 18, 12, 6 };
+
+ for (S32 detail = 0; detail < CONE_LEVELS_OF_DETAIL; detail++)
+ {
+ mTriangleCount[detail] = 0;
+
+ mDisplayListSide[detail] = glGenLists(1);
+ glNewList(mDisplayListSide[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_side( slices[detail], stacks, radius, 0.f );
+ glEndList();
+
+ mDisplayListBottom[detail] = glGenLists(1);
+ glNewList( mDisplayListBottom[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, BOTTOM );
+ glEndList();
+ }
+}
+
+void LLCone::cleanupGL()
+{
+ for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
+ {
+ glDeleteLists(mDisplayListSide[detail], 1);
+ mDisplayListSide[detail] = 0;
+
+ glDeleteLists(mDisplayListBottom[detail], 1);
+ mDisplayListBottom[detail] = 0;
+ }
+
+ if (gQuadObj)
+ {
+ gluDeleteQuadric(gQuadObj);
+ gQuadObj = NULL;
+ }
+}
+
+
+void LLCone::render(S32 level_of_detail)
+{
+ GLfloat height = 1.0f;
+
+ if (level_of_detail < 0 || CONE_LEVELS_OF_DETAIL <= level_of_detail)
+ {
+ llerror("LLCone::render() invalid level of detail", level_of_detail);
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ // center object at 0
+ glTranslatef(0.f, 0.f, - height / 2.0f);
+
+ glCallList(mDisplayListSide[level_of_detail]);
+ glCallList(mDisplayListBottom[level_of_detail]);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+
+void LLCone::renderface(S32 level_of_detail, S32 face)
+{
+ if (face < 0 || face > 1)
+ {
+ llerror("LLCone::renderface() invalid face number", face);
+ return;
+ }
+
+ if (level_of_detail < 0 || CONE_LEVELS_OF_DETAIL <= level_of_detail)
+ {
+ llerror("LLCone::renderface() invalid level of detail", level_of_detail);
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ switch(face)
+ {
+ case 0:
+ glTranslatef(0.f, 0.f, -0.5f);
+ glCallList(mDisplayListSide[level_of_detail]);
+ break;
+ case 1:
+ glTranslatef(0.f, 0.f, -0.5f);
+ glCallList(mDisplayListBottom[level_of_detail]);
+ break;
+ default:
+ llerror("LLCylinder::renderface() fell out of switch", 0);
+ break;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
diff --git a/indra/newview/llcylinder.h b/indra/newview/llcylinder.h
new file mode 100644
index 0000000000..9150db4fb1
--- /dev/null
+++ b/indra/newview/llcylinder.h
@@ -0,0 +1,67 @@
+/**
+ * @file llcylinder.h
+ * @brief Draws a cylinder, and a cone, which is a special case cylinder
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCYLINDER_H
+#define LL_LLCYLINDER_H
+
+//#include "stdtypes.h"
+//#include "llgl.h"
+
+//
+// Cylinders
+//
+const S32 CYLINDER_LEVELS_OF_DETAIL = 4;
+const S32 CYLINDER_FACES = 3;
+
+class LLCylinder
+{
+protected:
+ U32 mDisplayListSide[CYLINDER_LEVELS_OF_DETAIL];
+ U32 mDisplayListTop[CYLINDER_LEVELS_OF_DETAIL];
+ U32 mDisplayListBottom[CYLINDER_LEVELS_OF_DETAIL];
+ U32 mTriangleCount[CYLINDER_LEVELS_OF_DETAIL];
+
+public:
+ void prerender();
+ void cleanupGL();
+
+ void render(F32 pixel_area);
+ void renderface(F32 pixel_area, S32 face);
+
+ U32 getTriangleCount(S32 level_of_detail) { return mTriangleCount[level_of_detail]; }
+};
+
+
+//
+// Cones
+//
+
+const S32 CONE_LOD_HIGHEST = 0;
+const S32 CONE_LEVELS_OF_DETAIL = 4;
+const S32 CONE_FACES = 2;
+
+class LLCone
+{
+protected:
+ U32 mDisplayListSide[CONE_LEVELS_OF_DETAIL];
+ U32 mDisplayListBottom[CONE_LEVELS_OF_DETAIL];
+ U32 mTriangleCount[CONE_LEVELS_OF_DETAIL];
+
+public:
+ void prerender();
+ void cleanupGL();
+
+ void render(S32 level_of_detail);
+ void renderface(S32 level_of_detail, S32 face);
+
+ U32 getTriangleCount(S32 level_of_detail) { return mTriangleCount[level_of_detail]; }
+};
+
+extern LLCylinder gCylinder;
+extern LLCone gCone;
+#endif
diff --git a/indra/newview/lldebugmessagebox.cpp b/indra/newview/lldebugmessagebox.cpp
new file mode 100644
index 0000000000..fbfe9e3864
--- /dev/null
+++ b/indra/newview/lldebugmessagebox.cpp
@@ -0,0 +1,226 @@
+/**
+ * @file lldebugmessagebox.cpp
+ * @brief Implementation of a simple, non-modal message box.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldebugmessagebox.h"
+
+#include "llresmgr.h"
+#include "llfontgl.h"
+#include "llbutton.h"
+#include "llsliderctrl.h"
+#include "llcheckboxctrl.h"
+#include "lltextbox.h"
+#include "llcallbacklist.h"
+#include "lllineeditor.h"
+#include "llfocusmgr.h"
+
+///----------------------------------------------------------------------------
+/// Class LLDebugVarMessageBox
+///----------------------------------------------------------------------------
+
+std::map<LLString, LLDebugVarMessageBox*> LLDebugVarMessageBox::sInstances;
+
+LLDebugVarMessageBox::LLDebugVarMessageBox(const std::string& title, EDebugVarType var_type, void *var) :
+ LLFloater("msg box", LLRect(10,160,400,10), title),
+ mVarType(var_type), mVarData(var), mAnimate(FALSE)
+{
+ switch(var_type)
+ {
+ case VAR_TYPE_F32:
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, *((F32*)var), -100.f, 100.f, 0.1f, NULL);
+ mSlider1->setPrecision(3);
+ addChild(mSlider1);
+ mSlider2 = NULL;
+ mSlider3 = NULL;
+ break;
+ case VAR_TYPE_S32:
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, NULL);
+ mSlider1->setPrecision(0);
+ addChild(mSlider1);
+ mSlider2 = NULL;
+ mSlider3 = NULL;
+ break;
+ case VAR_TYPE_VEC3:
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), "x: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, NULL);
+ mSlider1->setPrecision(3);
+ mSlider2 = new LLSliderCtrl("slider 2", LLRect(20,100,190,80), "y: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, NULL);
+ mSlider2->setPrecision(3);
+ mSlider3 = new LLSliderCtrl("slider 3", LLRect(20,70,190,50), "z: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, NULL);
+ mSlider3->setPrecision(3);
+ addChild(mSlider1);
+ addChild(mSlider2);
+ addChild(mSlider3);
+ break;
+ }
+
+ mAnimateButton = new LLButton("Animate", LLRect(20, 45, 180, 25), "", onAnimateClicked, this);
+ addChild(mAnimateButton);
+
+ mText = new LLTextBox("value", LLRect(20,20,190,0));
+ addChild(mText);
+
+ //disable hitting enter closes dialog
+ setDefaultBtn();
+}
+
+LLDebugVarMessageBox::~LLDebugVarMessageBox()
+{
+ sInstances.erase(mTitle);
+}
+
+void LLDebugVarMessageBox::show(const std::string& title, F32 *var, F32 max_value, F32 increment)
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLDebugVarMessageBox* box = show(title, VAR_TYPE_F32, (void*)var);
+ max_value = llabs(max_value);
+ box->mSlider1->setMaxValue(max_value);
+ box->mSlider1->setMinValue(-max_value);
+ box->mSlider1->setIncrement(increment);
+ if (!gFocusMgr.childHasKeyboardFocus(box))
+ {
+ box->mSlider1->setValue(*var);
+ }
+ box->mSlider1->setCommitCallback(slider_changed);
+ box->mSlider1->setCallbackUserData(box);
+#endif
+}
+
+void LLDebugVarMessageBox::show(const std::string& title, S32 *var, S32 max_value, S32 increment)
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLDebugVarMessageBox* box = show(title, VAR_TYPE_S32, (void*)var);
+ F32 max_val = llabs((F32)max_value);
+ box->mSlider1->setMaxValue(max_val);
+ box->mSlider1->setMinValue(-max_val);
+ box->mSlider1->setIncrement((F32)increment);
+ if (!gFocusMgr.childHasKeyboardFocus(box))
+ {
+ box->mSlider1->setValue((F32)*var);
+ }
+ box->mSlider1->setCommitCallback(slider_changed);
+ box->mSlider1->setCallbackUserData(box);
+#endif
+}
+
+void LLDebugVarMessageBox::show(const std::string& title, LLVector3 *var, LLVector3 max_value, LLVector3 increment)
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLDebugVarMessageBox* box = show(title, VAR_TYPE_VEC3, (LLVector3*)var);
+ max_value.abs();
+ box->mSlider1->setMaxValue(max_value.mV[VX]);
+ box->mSlider1->setMinValue(-max_value.mV[VX]);
+ box->mSlider1->setIncrement(increment.mV[VX]);
+ box->mSlider1->setCommitCallback(slider_changed);
+ box->mSlider1->setCallbackUserData(box);
+
+ box->mSlider2->setMaxValue(max_value.mV[VX]);
+ box->mSlider2->setMinValue(-max_value.mV[VX]);
+ box->mSlider2->setIncrement(increment.mV[VX]);
+ box->mSlider2->setCommitCallback(slider_changed);
+ box->mSlider2->setCallbackUserData(box);
+
+ box->mSlider3->setMaxValue(max_value.mV[VX]);
+ box->mSlider3->setMinValue(-max_value.mV[VX]);
+ box->mSlider3->setIncrement(increment.mV[VX]);
+ box->mSlider3->setCommitCallback(slider_changed);
+ box->mSlider3->setCallbackUserData(box);
+#endif
+}
+
+LLDebugVarMessageBox* LLDebugVarMessageBox::show(const std::string& title, EDebugVarType var_type, void *var)
+{
+ LLString title_string(title);
+
+ LLDebugVarMessageBox *box = sInstances[title_string];
+ if (!box)
+ {
+ box = new LLDebugVarMessageBox(title, var_type, var);
+ sInstances[title_string] = box;
+ gFloaterView->addChild(box);
+ box->reshape(200,150);
+ box->open();
+ box->mTitle = title_string;
+ }
+
+ return box;
+}
+
+void LLDebugVarMessageBox::slider_changed(LLUICtrl* ctrl, void* user_data)
+{
+ LLDebugVarMessageBox *msg_box = (LLDebugVarMessageBox*)user_data;
+ if (!msg_box || !msg_box->mVarData) return;
+
+ switch(msg_box->mVarType)
+ {
+ case VAR_TYPE_F32:
+ *((F32*)msg_box->mVarData) = (F32)msg_box->mSlider1->getValue().asReal();
+ break;
+ case VAR_TYPE_S32:
+ *((S32*)msg_box->mVarData) = (S32)msg_box->mSlider1->getValue().asInteger();
+ break;
+ case VAR_TYPE_VEC3:
+ LLVector3* vec_p = (LLVector3*)msg_box->mVarData;
+ vec_p->setVec((F32)msg_box->mSlider1->getValue().asReal(),
+ (F32)msg_box->mSlider2->getValue().asReal(),
+ (F32)msg_box->mSlider3->getValue().asReal());
+ break;
+ }
+}
+
+void LLDebugVarMessageBox::onAnimateClicked(void* user_data)
+{
+ LLDebugVarMessageBox* msg_boxp = (LLDebugVarMessageBox*)user_data;
+ msg_boxp->mAnimate = !msg_boxp->mAnimate;
+ msg_boxp->mAnimateButton->setToggleState(msg_boxp->mAnimate);
+}
+
+void LLDebugVarMessageBox::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+}
+
+void LLDebugVarMessageBox::draw()
+{
+ char text[128];
+ switch(mVarType)
+ {
+ case VAR_TYPE_F32:
+ sprintf(text, "%.3f", *((F32*)mVarData));
+ break;
+ case VAR_TYPE_S32:
+ sprintf(text, "%d", *((S32*)mVarData));
+ break;
+ case VAR_TYPE_VEC3:
+ LLVector3* vec_p = (LLVector3*)mVarData;
+ sprintf(text, "%.3f %.3f %.3f", vec_p->mV[VX], vec_p->mV[VY], vec_p->mV[VZ]);
+ break;
+ }
+ mText->setText(text);
+
+ if(mAnimate)
+ {
+ F32 animated_val = clamp_rescale(fmodf((F32)LLFrameTimer::getElapsedSeconds() / 5.f, 1.f), 0.f, 1.f, 0.f, mSlider1->getMaxValue());
+ if (mSlider1)
+ {
+ mSlider1->setValue(animated_val);
+ slider_changed(mSlider1, this);
+ }
+ if (mSlider2)
+ {
+ mSlider2->setValue(animated_val);
+ slider_changed(mSlider2, this);
+ }
+ if (mSlider3)
+ {
+ mSlider3->setValue(animated_val);
+ slider_changed(mSlider3, this);
+ }
+ }
+ LLFloater::draw();
+}
diff --git a/indra/newview/lldebugmessagebox.h b/indra/newview/lldebugmessagebox.h
new file mode 100644
index 0000000000..fa34c7cc41
--- /dev/null
+++ b/indra/newview/lldebugmessagebox.h
@@ -0,0 +1,72 @@
+/**
+ * @file lldebugmessagebox.h
+ * @brief Debug message box.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLDEBUGMESSAGEBOX_H
+#define LL_LLDEBUGMESSAGEBOX_H
+
+#include "lldarray.h"
+#include "llfloater.h"
+#include "v3math.h"
+#include "lltextbox.h"
+#include "llstring.h"
+#include "llframetimer.h"
+#include <vector>
+#include <map>
+
+class LLSliderCtrl;
+
+//----------------------------------------------------------------------------
+// LLDebugVarMessageBox
+//----------------------------------------------------------------------------
+
+typedef enum e_debug_var_type
+{
+ VAR_TYPE_F32,
+ VAR_TYPE_S32,
+ VAR_TYPE_VEC2,
+ VAR_TYPE_VEC3,
+ VAR_TYPE_VEC4,
+ VAR_TYPE_COUNT
+} EDebugVarType;
+
+class LLDebugVarMessageBox : public LLFloater
+{
+protected:
+ LLDebugVarMessageBox(const std::string& title, EDebugVarType var_type, void *var);
+ ~LLDebugVarMessageBox();
+
+ static LLDebugVarMessageBox* show(const std::string& title, EDebugVarType var_type, void *var);
+ static void slider_changed(LLUICtrl* ctrl, void* user_data);
+ static void onAnimateClicked(void* user_data);
+
+public:
+ static void show(const std::string& title, F32 *var, F32 max_value = 100.f, F32 increment = 0.1f);
+ static void show(const std::string& title, S32 *var, S32 max_value = 255, S32 increment = 1);
+ static void show(const std::string& title, LLVector2 *var, LLVector2 max_value = LLVector2(100.f, 100.f), LLVector2 increment = LLVector2(0.1f, 0.1f));
+ static void show(const std::string& title, LLVector3 *var, LLVector3 max_value = LLVector3(100.f, 100.f, 100.f), LLVector3 increment = LLVector3(0.1f, 0.1f, 0.1f));
+ //static void show(const std::string& title, LLVector4 *var, LLVector4 max_value = LLVector4(100.f, 100.f, 100.f, 100.f), LLVector4 increment = LLVector4(0.1f, 0.1f, 0.1f, 0.1f));
+
+ virtual void onClose(bool app_quitting);
+ virtual void draw();
+
+protected:
+ EDebugVarType mVarType;
+ void* mVarData;
+ LLSliderCtrl* mSlider1;
+ LLSliderCtrl* mSlider2;
+ LLSliderCtrl* mSlider3;
+ LLButton* mAnimateButton;
+ LLTextBox* mText;
+ LLString mTitle;
+ BOOL mAnimate;
+
+ static std::map<LLString, LLDebugVarMessageBox*> sInstances;
+};
+
+#endif // LL_LLMESSAGEBOX_H
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
new file mode 100644
index 0000000000..d20c431987
--- /dev/null
+++ b/indra/newview/lldebugview.cpp
@@ -0,0 +1,133 @@
+/**
+ * @file lldebugview.cpp
+ * @brief A view containing UI elements only visible in build mode.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldebugview.h"
+
+// library includes
+#include "llframestatview.h"
+#include "llfasttimerview.h"
+#include "llmemoryview.h"
+#include "llconsole.h"
+#include "lltextureview.h"
+#include "llresmgr.h"
+#include "llaudiostatus.h"
+#include "imageids.h"
+#include "llvelocitybar.h"
+#include "llviewerwindow.h"
+
+//
+// Globals
+//
+
+LLDebugView* gDebugView = NULL;
+
+//
+// Methods
+//
+
+LLDebugView::LLDebugView(const std::string& name, const LLRect &rect)
+: LLView(name, rect, FALSE)
+{
+ LLRect r;
+
+ r.set(0, rect.getHeight() - 100, rect.getWidth()/2, 100);
+ mDebugConsolep = new LLConsole("debug console", 20, r, -1, 0.f );
+ mDebugConsolep->setFollowsBottom();
+ mDebugConsolep->setFollowsLeft();
+ mDebugConsolep->setVisible( FALSE );
+ addChild(mDebugConsolep);
+
+ r.set(150 - 25, rect.getHeight() - 50, rect.getWidth()/2 - 25, rect.getHeight() - 450);
+ mFrameStatView = new LLFrameStatView("frame stat", r);
+ mFrameStatView->setFollowsTop();
+ mFrameStatView->setFollowsLeft();
+ mFrameStatView->setVisible(FALSE); // start invisible
+ addChild(mFrameStatView);
+
+ r.set(25, rect.getHeight() - 50, (S32) (gViewerWindow->getVirtualWindowRect().getWidth() * 0.75f),
+ (S32) (gViewerWindow->getVirtualWindowRect().getHeight() * 0.75f));
+ mFastTimerView = new LLFastTimerView("fast timers", r);
+ mFastTimerView->setFollowsTop();
+ mFastTimerView->setFollowsLeft();
+ mFastTimerView->setVisible(FALSE); // start invisible
+ addChild(mFastTimerView);
+
+ r.set(25, rect.getHeight() - 50, rect.getWidth()/2, rect.getHeight() - 450);
+ mMemoryView = new LLMemoryView("memory", r);
+ mMemoryView->setFollowsTop();
+ mMemoryView->setFollowsLeft();
+ mMemoryView->setVisible(FALSE); // start invisible
+ addChild(mMemoryView);
+
+ r.set(150, rect.getHeight() - 50, 820, 100);
+ gTextureView = new LLTextureView("gTextureView", r);
+ gTextureView->setRect(r);
+ gTextureView->setFollowsBottom();
+ gTextureView->setFollowsLeft();
+ addChild(gTextureView);
+ //gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE);
+
+ //
+ // Debug statistics
+ //
+ r.set(rect.getWidth() - 250,
+ rect.getHeight(),
+ rect.getWidth(),
+ rect.getHeight() - 400);
+ mStatViewp = new LLContainerView("statistics", r);
+ mStatViewp->setLabel("Statistics");
+ mStatViewp->setFollowsTop();
+ mStatViewp->setFollowsRight();
+ // Default to off
+ mStatViewp->setVisible(FALSE);
+ addChild(mStatViewp);
+
+ //
+ // Audio debugging stuff
+ //
+ const S32 AUDIO_STATUS_LEFT = rect.getWidth()/2-100;
+ const S32 AUDIO_STATUS_WIDTH = 320;
+ const S32 AUDIO_STATUS_TOP = (rect.getHeight()/2)+400;
+ const S32 AUDIO_STATUS_HEIGHT = 320;
+ r.setLeftTopAndSize( AUDIO_STATUS_LEFT, AUDIO_STATUS_TOP, AUDIO_STATUS_WIDTH, AUDIO_STATUS_HEIGHT );
+ LLAudiostatus* gAudioStatus = new LLAudiostatus("AudioStatus", r);
+ gAudioStatus->setFollowsTop();
+ gAudioStatus->setFollowsRight();
+ addChild(gAudioStatus);
+
+ const S32 VELOCITY_LEFT = 10; // 370;
+ const S32 VELOCITY_WIDTH = 500;
+ const S32 VELOCITY_TOP = 140;
+ const S32 VELOCITY_HEIGHT = 45;
+ r.setLeftTopAndSize( VELOCITY_LEFT, VELOCITY_TOP, VELOCITY_WIDTH, VELOCITY_HEIGHT );
+ gVelocityBar = new LLVelocityBar("Velocity Bar", r);
+ gVelocityBar->setFollowsBottom();
+ gVelocityBar->setFollowsLeft();
+ addChild(gVelocityBar);
+}
+
+
+LLDebugView::~LLDebugView()
+{
+ // These have already been deleted. Fix the globals appropriately.
+ gDebugView = NULL;
+ gTextureView = NULL;
+}
+
+EWidgetType LLDebugView::getWidgetType() const
+{
+ return WIDGET_TYPE_DEBUG_VIEW;
+}
+
+LLString LLDebugView::getWidgetTag() const
+{
+ return LL_DEBUG_VIEW_TAG;
+}
+
diff --git a/indra/newview/lldebugview.h b/indra/newview/lldebugview.h
new file mode 100644
index 0000000000..5406099189
--- /dev/null
+++ b/indra/newview/lldebugview.h
@@ -0,0 +1,46 @@
+/**
+ * @file lldebugview.h
+ * @brief A view containing debug UI elements
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDEBUGVIEW_H
+#define LL_LLDEBUGVIEW_H
+
+// requires:
+// stdtypes.h
+
+#include "llview.h"
+
+// declarations
+class LLButton;
+class LLToolView;
+class LLStatusPanel;
+class LLFrameStatView;
+class LLFastTimerView;
+class LLMemoryView;
+class LLConsole;
+class LLTextureView;
+class LLContainerView;
+
+class LLDebugView : public LLView
+{
+public:
+ LLDebugView(const std::string& name, const LLRect &rect);
+ ~LLDebugView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ LLFrameStatView* mFrameStatView;
+ LLFastTimerView* mFastTimerView;
+ LLMemoryView* mMemoryView;
+ LLConsole* mDebugConsolep;
+ LLContainerView* mStatViewp;
+};
+
+extern LLDebugView* gDebugView;
+
+#endif
diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp
new file mode 100644
index 0000000000..5a01bfdc84
--- /dev/null
+++ b/indra/newview/lldirpicker.cpp
@@ -0,0 +1,266 @@
+/**
+ * @file lldirpicker.cpp
+ * @brief OS-specific file picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldirpicker.h"
+//#include "viewer.h"
+//#include "llviewermessage.h"
+#include "llworld.h"
+#include "llviewerwindow.h"
+#include "llkeyboard.h"
+#include "lldir.h"
+#include "llframetimer.h"
+
+//
+// Globals
+//
+
+LLDirPicker LLDirPicker::sInstance;
+
+#if LL_WINDOWS
+#include <shlobj.h>
+#endif
+
+//
+// Implementation
+//
+#if LL_WINDOWS
+
+LLDirPicker::LLDirPicker()
+{
+}
+
+LLDirPicker::~LLDirPicker()
+{
+ // nothing
+}
+
+BOOL LLDirPicker::getDir(LLString* filename)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+
+ // Modal, so pause agent
+ send_agent_pause();
+
+ BROWSEINFO bi;
+ memset(&bi, 0, sizeof(bi));
+
+ bi.ulFlags = BIF_USENEWUI;
+ bi.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());;
+ bi.lpszTitle = NULL;
+
+ ::OleInitialize(NULL);
+
+ LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);
+
+ if(pIDL != NULL)
+ {
+ WCHAR buffer[_MAX_PATH] = {'\0'};
+
+ if(::SHGetPathFromIDList(pIDL, buffer) != 0)
+ {
+ // Set the string value.
+
+ mDir = utf16str_to_utf8str(llutf16string(buffer));
+ success = TRUE;
+ }
+
+ // free the item id list
+ CoTaskMemFree(pIDL);
+ }
+
+ ::OleUninitialize();
+
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+LLString LLDirPicker::getDirName()
+{
+ return mDir;
+}
+
+/////////////////////////////////////////////DARWIN
+#elif LL_DARWIN
+
+LLDirPicker::LLDirPicker()
+{
+ reset();
+
+ memset(&mNavOptions, 0, sizeof(mNavOptions));
+ OSStatus error = NavGetDefaultDialogCreationOptions(&mNavOptions);
+ if (error == noErr)
+ {
+ mNavOptions.modality = kWindowModalityAppModal;
+ }
+}
+
+LLDirPicker::~LLDirPicker()
+{
+ // nothing
+}
+
+//static
+pascal void LLDirPicker::doNavCallbackEvent(NavEventCallbackMessage callBackSelector,
+ NavCBRecPtr callBackParms, void* callBackUD)
+{
+ switch(callBackSelector)
+ {
+ case kNavCBStart:
+ {
+ if (!sInstance.mFileName) break;
+
+ OSStatus error = noErr;
+ AEDesc theLocation = {typeNull, NULL};
+ FSSpec outFSSpec;
+
+ //Convert string to a FSSpec
+ FSRef myFSRef;
+
+ const char* filename=sInstance.mFileName->c_str();
+
+ error = FSPathMakeRef ((UInt8*)filename, &myFSRef, NULL);
+
+ if (error != noErr) break;
+
+ error = FSGetCatalogInfo (&myFSRef, kFSCatInfoNone, NULL, NULL, &outFSSpec, NULL);
+
+ if (error != noErr) break;
+
+ error = AECreateDesc(typeFSS, &outFSSpec, sizeof(FSSpec), &theLocation);
+
+ if (error != noErr) break;
+
+ error = NavCustomControl(callBackParms->context,
+ kNavCtlSetLocation, (void*)&theLocation);
+
+ }
+ }
+}
+
+OSStatus LLDirPicker::doNavChooseDialog()
+{
+ OSStatus error = noErr;
+ NavDialogRef navRef = NULL;
+ NavReplyRecord navReply;
+
+ memset(&navReply, 0, sizeof(navReply));
+
+ // NOTE: we are passing the address of a local variable here.
+ // This is fine, because the object this call creates will exist for less than the lifetime of this function.
+ // (It is destroyed by NavDialogDispose() below.)
+
+ error = NavCreateChooseFolderDialog(&mNavOptions, &doNavCallbackEvent, NULL, NULL, &navRef);
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ if (error == noErr)
+ error = NavDialogRun(navRef);
+
+ gViewerWindow->mWindow->afterDialog();
+
+ if (error == noErr)
+ error = NavDialogGetReply(navRef, &navReply);
+
+ if (navRef)
+ NavDialogDispose(navRef);
+
+ if (error == noErr && navReply.validRecord)
+ {
+ FSRef fsRef;
+ AEKeyword theAEKeyword;
+ DescType typeCode;
+ Size actualSize = 0;
+ char path[LL_MAX_PATH];
+
+ memset(&fsRef, 0, sizeof(fsRef));
+ error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
+
+ if (error == noErr)
+ error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path));
+
+ if (error == noErr)
+ mDir = path;
+ }
+
+ return error;
+}
+
+BOOL LLDirPicker::getDir(LLString* filename)
+{
+ if( mLocked ) return FALSE;
+ BOOL success = FALSE;
+ OSStatus error = noErr;
+
+ mFileName = filename;
+
+// mNavOptions.saveFileName
+
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavChooseDialog();
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mDir.length() > 0)
+ success = true;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+LLString LLDirPicker::getDirName()
+{
+ return mDir;
+}
+
+void LLDirPicker::reset()
+{
+ mLocked = FALSE;
+ mDir = NULL;
+}
+
+#else // not implemented
+
+LLDirPicker::LLDirPicker()
+{
+ reset();
+}
+
+LLDirPicker::~LLDirPicker()
+{
+}
+
+
+void LLDirPicker::reset()
+{
+}
+
+BOOL LLDirPicker::getDir(LLString* filename)
+{
+ return FALSE;
+}
+
+LLString LLDirPicker::getDirName()
+{
+ return "";
+}
+
+#endif
diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h
new file mode 100644
index 0000000000..70e124f6c4
--- /dev/null
+++ b/indra/newview/lldirpicker.h
@@ -0,0 +1,84 @@
+/**
+ * @dir lldirpicker.h
+ * @brief OS-specific dir picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// OS specific dir selection dialog. This is implemented as a
+// singleton class, so call the instance() method to get the working
+// instance.
+
+#ifndef LL_LLDIRPICKER_H
+#define LL_LLDIRPICKER_H
+
+#include "stdtypes.h"
+
+#if LL_DARWIN
+#include <Carbon/Carbon.h>
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+#include <vector>
+#include "llstring.h"
+
+#endif
+
+// Need commdlg.h for OPENDIRNAMEA
+#ifdef LL_WINDOWS
+#include <commdlg.h>
+#endif
+
+class LLDirPicker
+{
+public:
+ // calling this before main() is undefined
+ static LLDirPicker& instance( void ) { return sInstance; }
+
+ BOOL getDir(LLString* filename);
+ LLString getDirName();
+
+ // clear any lists of buffers or whatever, and make sure the dir
+ // picker isn't locked.
+ void reset();
+
+private:
+ enum
+ {
+ SINGLE_DIRNAME_BUFFER_SIZE = 1024,
+ //DIRNAME_BUFFER_SIZE = 65536
+ DIRNAME_BUFFER_SIZE = 65000
+ };
+
+ void buildDirname( void );
+
+#if LL_WINDOWS
+//FIXME
+#endif
+
+#if LL_DARWIN
+ NavDialogCreationOptions mNavOptions;
+ static pascal void doNavCallbackEvent(NavEventCallbackMessage callBackSelector,
+ NavCBRecPtr callBackParms, void* callBackUD);
+ OSStatus doNavChooseDialog();
+
+#endif
+
+ char mDirs[DIRNAME_BUFFER_SIZE];
+ LLString* mFileName;
+ LLString mDir;
+ BOOL mLocked;
+
+ static LLDirPicker sInstance;
+
+public:
+ // don't call these directly please.
+ LLDirPicker();
+ ~LLDirPicker();
+};
+
+#endif
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
new file mode 100644
index 0000000000..a198886d37
--- /dev/null
+++ b/indra/newview/lldrawable.cpp
@@ -0,0 +1,1320 @@
+/**
+ * @file lldrawable.cpp
+ * @brief LLDrawable class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawable.h"
+
+// library includes
+#include "material_codes.h"
+
+// viewer includes
+#include "llagparray.h"
+#include "llagparray.inl"
+#include "llcriticaldamp.h"
+#include "llface.h"
+#include "lllightconstants.h"
+#include "llsky.h"
+#include "llsurfacepatch.h"
+#include "llviewercamera.h"
+#include "llviewerregion.h"
+#include "llvolume.h"
+#include "llvoavatar.h"
+#include "llvovolume.h"
+#include "llvosurfacepatch.h" // for debugging
+#include "llworld.h"
+#include "pipeline.h"
+#include "llspatialpartition.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+
+const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f;
+const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f;
+const F32 OBJECT_DAMPING_TIME_CONSTANT = 0.06f;
+const F32 MIN_SHADOW_CASTER_RADIUS = 2.0f;
+
+////////////////////////
+//
+// Inline implementations.
+//
+//
+
+
+
+//////////////////////////////
+//
+// Drawable code
+//
+//
+
+// static
+U32 LLDrawable::sCurVisible = 0;
+U32 LLDrawable::sNumZombieDrawables = 0;
+F32 LLDrawable::sCurPixelAngle = 0;
+LLDynamicArrayPtr<LLPointer<LLDrawable> > LLDrawable::sDeadList;
+
+// static
+void LLDrawable::incrementVisible()
+{
+ sCurVisible++;
+ sCurPixelAngle = (F32) gViewerWindow->getWindowDisplayHeight()/gCamera->getView();
+}
+void LLDrawable::init()
+{
+ // mXform
+ mParent = NULL;
+ mRenderType = 0;
+ mCurrentScale = LLVector3(1,1,1);
+ mDistanceWRTCamera = 0.0f;
+ // mUVRect
+ mUVZ = 0.f;
+ // mLightSet
+ // mBlockSet
+ // mSavePos
+ mQuietCount = 0;
+
+ mState = 0;
+ mVObjp = NULL;
+ // mFaces
+ mSpatialGroupp = NULL;
+ mSpatialGroupOffset = -1;
+ mVisible = 0;
+ mRadius = 0.f;
+ mSunShadowFactor = 1.f;
+
+ mGeneration = -1;
+ mBinRadius = 1.f;
+ mSpatialBridge = NULL;
+}
+
+// static
+void LLDrawable::initClass()
+{
+}
+
+
+void LLDrawable::destroy()
+{
+ if (isDead())
+ {
+ sNumZombieDrawables--;
+ }
+
+ std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
+ mFaces.clear();
+
+ /*
+ if (!(sNumZombieDrawables % 10))
+ {
+ llinfos << "- Zombie drawables: " << sNumZombieDrawables << llendl;
+ }
+ */
+}
+
+void LLDrawable::markDead()
+{
+ if (isDead())
+ {
+ llwarns << "Warning! Marking dead multiple times!" << llendl;
+ return;
+ }
+
+ if (mSpatialBridge)
+ {
+ mSpatialBridge->markDead();
+ mSpatialBridge = NULL;
+ }
+
+ sNumZombieDrawables++;
+
+ // We're dead. Free up all of our references to other objects
+ setState(DEAD);
+ cleanupReferences();
+// sDeadList.put(this);
+}
+
+LLVOVolume* LLDrawable::getVOVolume() const
+{
+ LLViewerObject* objectp = mVObjp;
+ if ( !isDead() && objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
+ {
+ return ((LLVOVolume*)objectp);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+BOOL LLDrawable::isLight() const
+{
+ LLViewerObject* objectp = mVObjp;
+ if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME) && !isDead())
+ {
+ return ((LLVOVolume*)objectp)->getIsLight();
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLDrawable::clearLightSet()
+{
+ // Remove this object from any object which has it as a light
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ LLDrawable *targetp = *iter;
+ if (targetp != this && !targetp->isDead())
+ {
+ targetp->mLightSet.erase(this);
+ gPipeline.markRelight(targetp);
+ }
+ }
+ mLightSet.clear();
+}
+
+void LLDrawable::cleanupReferences()
+{
+ LLFastTimer t(LLFastTimer::FTM_PIPELINE);
+
+ std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
+ mFaces.clear();
+
+ clearLightSet();
+
+ gObjectList.removeDrawable(this);
+
+ mBlockSet.clear();
+
+ gPipeline.unlinkDrawable(this);
+
+ // Cleanup references to other objects
+ mVObjp = NULL;
+ mParent = NULL;
+}
+
+void LLDrawable::cleanupDeadDrawables()
+{
+ /*
+ S32 i;
+ for (i = 0; i < sDeadList.count(); i++)
+ {
+ if (sDeadList[i]->getNumRefs() > 1)
+ {
+ llwarns << "Dead drawable has " << sDeadList[i]->getNumRefs() << " remaining refs" << llendl;
+ gPipeline.findReferences(sDeadList[i]);
+ }
+ }
+ */
+ sDeadList.reset();
+}
+
+S32 LLDrawable::findReferences(LLDrawable *drawablep)
+{
+ S32 count = 0;
+ if (mLightSet.count(drawablep) > 0)
+ {
+ llinfos << this << ": lightset reference" << llendl;
+ count++;
+ }
+ if (mBlockSet.count(drawablep) > 0)
+ {
+ llinfos << this << ": blockset reference" << llendl;
+ count++;
+ }
+ if (mParent == drawablep)
+ {
+ llinfos << this << ": parent reference" << llendl;
+ count++;
+ }
+ return count;
+}
+
+#if 0
+// SJB: This is SLOW, so we don't want to allow it (we don't currently use it)
+void LLDrawable::removeFace(const S32 i)
+{
+ LLFace *face= mFaces[i];
+
+ if (face)
+ {
+ mFaces.erase(mFaces.begin() + i);
+ delete face;
+ }
+}
+#endif
+
+LLFace* LLDrawable::addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BOOL shared_geom)
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+
+ LLFace *face = new LLFace(this, mVObjp);
+
+ if (face)
+ {
+ mFaces.push_back(face);
+ face->setPool(poolp, texturep);
+
+ if (shared_geom)
+ {
+ face->setState(LLFace::SHARED_GEOM);
+ }
+ else if (!isVisible())
+ {
+ face->setState(LLFace::BACKLIST);
+ }
+ if (isState(UNLIT))
+ {
+ face->setState(LLFace::FULLBRIGHT);
+ }
+ }
+ return face;
+}
+
+void LLDrawable::setNumFaces(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
+{
+ if (newFaces == (S32)mFaces.size())
+ {
+ return;
+ }
+ else if (newFaces < (S32)mFaces.size())
+ {
+ std::for_each(mFaces.begin() + newFaces, mFaces.end(), DeletePointer());
+ mFaces.erase(mFaces.begin() + newFaces, mFaces.end());
+ }
+ else // (newFaces > mFaces.size())
+ {
+ mFaces.reserve(newFaces);
+ for (int i = mFaces.size(); i<newFaces; i++)
+ {
+ addFace(poolp, texturep);
+ }
+ }
+}
+
+void LLDrawable::setNumFacesFast(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
+{
+ if (newFaces <= (S32)mFaces.size() && newFaces >= (S32)mFaces.size()/2)
+ {
+ return;
+ }
+ else if (newFaces < (S32)mFaces.size())
+ {
+ std::for_each(mFaces.begin() + newFaces, mFaces.end(), DeletePointer());
+ mFaces.erase(mFaces.begin() + newFaces, mFaces.end());
+ }
+ else // (newFaces > mFaces.size())
+ {
+ mFaces.reserve(newFaces);
+ for (int i = mFaces.size(); i<newFaces; i++)
+ {
+ addFace(poolp, texturep);
+ }
+ }
+}
+
+void LLDrawable::mergeFaces(LLDrawable* src)
+{
+ U32 face_count = mFaces.size() + src->mFaces.size();
+
+ mFaces.reserve(face_count);
+ for (U32 i = 0; i < src->mFaces.size(); i++)
+ {
+ LLFace* facep = src->mFaces[i];
+ facep->setDrawable(this);
+ mFaces.push_back(facep);
+ }
+ src->mFaces.clear();
+}
+
+void LLDrawable::deleteFaces(S32 offset, S32 count)
+{
+ face_list_t::iterator face_begin = mFaces.begin() + offset;
+ face_list_t::iterator face_end = face_begin + count;
+ std::for_each(face_begin, face_end, DeletePointer());
+ mFaces.erase(face_begin, face_end);
+}
+
+void LLDrawable::update()
+{
+ llerrs << "Shouldn't be called!" << llendl;
+}
+
+
+void LLDrawable::updateMaterial()
+{
+}
+
+void LLDrawable::makeActive()
+{
+ if (!isState(ACTIVE)) // && mGeneration > 0)
+ {
+ setState(ACTIVE);
+
+ if (!isRoot() && !mParent->isActive())
+ {
+ mParent->makeActive();
+ }
+
+ gPipeline.setActive(this, TRUE);
+
+ //all child objects must also be active
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ getChild(i)->makeActive();
+ }
+
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ if (mVObjp->getVolume()->getPathType() == LL_PCODE_PATH_FLEXIBLE)
+ {
+ return;
+ }
+ }
+
+ clearState(LLDrawable::LIGHTING_BUILT);
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ }
+ mQuietCount = 0;
+}
+
+
+void LLDrawable::makeStatic()
+{
+ if (isState(ACTIVE))
+ {
+ clearState(ACTIVE);
+ gPipeline.setActive(this, FALSE);
+
+ if (mParent.notNull() && mParent->isActive())
+ {
+ llerrs << "Drawable became static with active parent!" << llendl;
+ }
+
+ S32 child_count = mVObjp->mChildList.size();
+ for (S32 child_num = 0; child_num < child_count; child_num++)
+ {
+ LLDrawable* child_drawable = mVObjp->mChildList[child_num]->mDrawable;
+ if (child_drawable)
+ {
+ child_drawable->makeStatic();
+ }
+ }
+
+ gPipeline.markRelight(this);
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ if (mSpatialBridge)
+ {
+ mSpatialBridge->markDead();
+ setSpatialBridge(NULL);
+ }
+ }
+}
+
+// Returns "distance" between target destination and resulting xfrom
+F32 LLDrawable::updateXform(BOOL undamped)
+{
+ BOOL damped = !undamped;
+
+ // Position
+ LLVector3 old_pos(mXform.getPosition());
+ LLVector3 target_pos;
+ if (mXform.isRoot())
+ {
+ // get root position in your agent's region
+ target_pos = mVObjp->getPositionAgent();
+ }
+ else
+ {
+ // parent-relative position
+ target_pos = mVObjp->getPosition();
+ }
+
+ // Rotation
+ LLQuaternion old_rot(mXform.getRotation());
+ LLQuaternion target_rot = mVObjp->getRotation();
+ //scaling
+ LLVector3 target_scale = mVObjp->getScale();
+ LLVector3 old_scale = mCurrentScale;
+ LLVector3 dest_scale = target_scale;
+
+ // Damping
+ F32 dist_squared = 0.f;
+ F32 scaled = 0.f;
+
+ if (damped && mDistanceWRTCamera > 0.0f)
+ {
+ F32 lerp_amt = llclamp(LLCriticalDamp::getInterpolant(OBJECT_DAMPING_TIME_CONSTANT), 0.f, 1.f);
+ LLVector3 new_pos = lerp(old_pos, target_pos, lerp_amt);
+ dist_squared = dist_vec_squared(new_pos, target_pos);
+
+ LLQuaternion new_rot = nlerp(lerp_amt, old_rot, target_rot);
+ dist_squared += (1.f - dot(new_rot, target_rot)) * 10.f;
+
+ LLVector3 new_scale = lerp(old_scale, target_scale, lerp_amt);
+ scaled = dist_vec_squared(new_scale, target_scale);
+
+ dist_squared += scaled;
+ F32 camdist2 = (mDistanceWRTCamera * mDistanceWRTCamera);
+ if ((dist_squared >= MIN_INTERPOLATE_DISTANCE_SQUARED * camdist2) &&
+ (dist_squared <= MAX_INTERPOLATE_DISTANCE_SQUARED))
+ {
+ // interpolate
+ target_pos = new_pos;
+ target_rot = new_rot;
+ target_scale = new_scale;
+
+ if (scaled >= MIN_INTERPOLATE_DISTANCE_SQUARED)
+ {
+ //scaling requires an immediate rebuild
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ }
+ else
+ {
+ // snap to final position
+ dist_squared = 0.0f;
+ }
+ }
+
+ // Update
+ mXform.setPosition(target_pos);
+ mXform.setRotation(target_rot);
+ mXform.setScale(LLVector3(1,1,1)); //no scale in drawable transforms (IT'S A RULE!)
+ mXform.updateMatrix();
+
+ mCurrentScale = target_scale;
+
+ if (!getVOVolume())
+ {
+ movePartition();
+ }
+ else if (mSpatialBridge)
+ {
+ gPipeline.markMoved(mSpatialBridge, FALSE);
+ }
+ else
+ {
+ //a child prim moved and needs its verts regenerated
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ return dist_squared;
+}
+
+void LLDrawable::setRadius(F32 radius)
+{
+ if (mRadius != radius)
+ {
+ mRadius = radius;
+ updateBinRadius();
+ if (!getVOVolume())
+ {
+ movePartition();
+ }
+ }
+}
+
+void LLDrawable::moveUpdatePipeline(BOOL moved)
+{
+ makeActive();
+
+ // Update the face centers.
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ getFace(i)->updateCenterAgent();
+ }
+
+ if (moved || !isState(LLDrawable::BUILT)) // moved since last frame
+ {
+ LLVector3 tmp = mSavePos - mXform.getPositionW();
+ F32 dist = tmp.magVecSquared(); // moved since last _update_
+
+ if (dist > 1.0f || !isState(LLDrawable::BUILT) || isLight())
+ {
+ mSavePos = mXform.getPositionW();
+ gPipeline.markRelight(this);
+ }
+ }
+}
+
+void LLDrawable::movePartition()
+{
+ if (getSpatialGroup() || getVOVolume())
+ {
+ LLSpatialPartition* part = getSpatialPartition();
+ if (part)
+ {
+ part->move(this, getSpatialGroup());
+ }
+ }
+}
+
+BOOL LLDrawable::updateMove()
+{
+ if (isDead())
+ {
+ llwarns << "Update move on dead drawable!" << llendl;
+ return TRUE;
+ }
+
+ if (mVObjp.isNull())
+ {
+ return FALSE;
+ }
+
+ makeActive();
+
+ BOOL done;
+
+ if (isState(MOVE_UNDAMPED))
+ {
+ done = updateMoveUndamped();
+ }
+ else
+ {
+ done = updateMoveDamped();
+ }
+ return done;
+}
+
+BOOL LLDrawable::updateMoveUndamped()
+{
+ F32 dist_squared = updateXform(TRUE);
+
+ mGeneration++;
+
+ if (!isState(LLDrawable::INVISIBLE))
+ {
+ BOOL moved = (dist_squared > 0.001f && dist_squared < 255.99f);
+ moveUpdatePipeline(moved);
+ mVObjp->updateText();
+ }
+
+ mVObjp->clearChanged(LLXform::MOVED);
+
+ return TRUE;
+}
+
+BOOL LLDrawable::updateMoveDamped()
+{
+ F32 dist_squared = updateXform(FALSE);
+
+ mGeneration++;
+
+ if (!isState(LLDrawable::INVISIBLE))
+ {
+ BOOL moved = (dist_squared > 0.001f && dist_squared < 128.0f);
+ moveUpdatePipeline(moved);
+ mVObjp->updateText();
+ }
+
+ BOOL done_moving = (dist_squared == 0.0f) ? TRUE : FALSE;
+
+ if (done_moving)
+ {
+ mVObjp->clearChanged(LLXform::MOVED);
+ }
+
+ return done_moving;
+}
+
+void LLDrawable::updateDistance(LLCamera& camera)
+{
+ if (mVObjp->isHUDAttachment())
+ {
+ mDistanceWRTCamera = 1.0f;
+ if (sCurVisible % 16 == 0)
+ {
+ mVObjp->updateLOD();
+ }
+ return;
+ }
+
+ LLVector3 pos(getPositionGroup());
+
+ pos -= camera.getOrigin();
+ mDistanceWRTCamera = pos.magVec();
+
+ //switch LOD with the spatial group to avoid artifacts
+ LLSpatialGroup* sg = getSpatialGroup();
+ if (!sg || sg->changeLOD())
+ {
+ mVObjp->updateLOD();
+ }
+}
+
+void LLDrawable::updateTexture()
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+
+ if (isDead())
+ {
+ llwarns << "Dead drawable updating texture!" << llendl;
+ return;
+ }
+
+ //FIXME: this updates textures on all faces in this drawable, not just the viewer object we care about
+ if (mVObjp->getNumTEs())
+ {
+ // For each face in this drawable, change the drawpool if necessary.
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ LLFace *facep = mFaces[i];
+ U32 pool_type = facep->getPool()->getType();
+
+ if ((pool_type == LLDrawPool::POOL_SIMPLE) ||
+ (pool_type == LLDrawPool::POOL_ALPHA) ||
+ (pool_type == LLDrawPool::POOL_HUD) ||
+ (pool_type == LLDrawPool::POOL_MEDIA) ||
+ (pool_type == LLDrawPool::POOL_BUMP))
+ {
+ LLViewerObject* objp = facep->getViewerObject();
+ S32 te_offset = facep->getTEOffset();
+
+ if (te_offset >= objp->getNumTEs()) // Shouldn't happen
+ {
+ llwarns << "TE offsets don't match!" << llendl;
+ facep->setTEOffset(-1);
+ continue;
+ }
+
+ LLDrawPool* poolp = NULL;
+ LLViewerImage* imagep = (te_offset >= 0) ? objp->getTEImage(te_offset) : facep->getTexture();
+ if (facep->isState(LLFace::HUD_RENDER))
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
+ }
+ else if (te_offset >= 0)
+ {
+ // This face actually uses texture entries...
+ const LLTextureEntry* te = facep->getTextureEntry();
+ poolp = LLPipeline::getPoolFromTE(te, imagep);
+ }
+ else
+ {
+ // No texture entry for this face.
+ if (!imagep)
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, NULL);
+ }
+ else if ((imagep->getComponents() == 4) || (imagep->getComponents() == 2))
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ }
+ else
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep);
+ }
+ }
+ facep->setPool(poolp, imagep);
+ }
+ }
+ }
+}
+
+
+BOOL LLDrawable::updateGeometry(BOOL priority)
+{
+ llassert(mVObjp.notNull());
+ BOOL res = mVObjp->updateGeometry(this);
+ if (isState(REBUILD_LIGHTING))
+ {
+ updateLighting(priority ? FALSE : TRUE); // only do actual lighting for non priority updates
+ if (priority)
+ {
+ gPipeline.markRelight(this); // schedule non priority update
+ }
+ else
+ {
+ clearState(REBUILD_LIGHTING);
+ }
+ }
+ return res;
+}
+
+void LLDrawable::shiftPos(const LLVector3 &shift_vector)
+{
+ if (isDead())
+ {
+ llwarns << "Shifting dead drawable" << llendl;
+ return;
+ }
+
+ if (mParent)
+ {
+ mXform.setPosition(mVObjp->getPosition());
+ }
+ else
+ {
+ mXform.setPosition(mVObjp->getPositionAgent());
+ }
+
+ mXform.setRotation(mVObjp->getRotation());
+ mXform.setScale(1,1,1);
+ mXform.updateMatrix();
+
+ if (isStatic() || //DP FIXME: don't know why this is happening, but
+ //some terrain patches are becoming active
+ //(earth quake, maybe?)
+ getRenderType() == LLPipeline::RENDER_TYPE_TERRAIN)
+ {
+ LLStrider<LLVector3> verticesp;
+
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ LLFace *facep = getFace(i);
+ facep->mCenterAgent += shift_vector;
+ facep->mExtents[0] += shift_vector;
+ facep->mExtents[1] += shift_vector;
+
+ if (facep->hasGeometry() && !facep->isState(LLFace::SHARED_GEOM))
+ {
+ S32 index = facep->getVertices(verticesp);
+ if (index >= 0)
+ {
+ S32 vertex_count = facep->getGeomCount();
+ for (S32 j = 0; j < vertex_count; j++)
+ {
+ *verticesp += shift_vector;
+ verticesp++;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // Update the face centers.
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ LLFace *facep = getFace(i);
+ facep->mCenterAgent += shift_vector;
+ }
+ }
+
+ //update spatial extents
+ if (!getVOVolume() || isStatic())
+ {
+ mExtents[0] += shift_vector;
+ mExtents[1] += shift_vector;
+ mPositionGroup += LLVector3d(shift_vector);
+ }
+ else if (mSpatialBridge)
+ {
+ mSpatialBridge->shiftPos(shift_vector);
+ }
+
+ mSavePos = mXform.getPositionW();
+
+ mVObjp->onShift(shift_vector);
+}
+
+const LLVector3& LLDrawable::getBounds(LLVector3& min, LLVector3& max) const
+{
+ mXform.getMinMax(min,max);
+ return mXform.getPositionW();
+}
+
+const LLVector3* LLDrawable::getSpatialExtents() const
+{
+ return mExtents;
+}
+
+void LLDrawable::setSpatialExtents(LLVector3 min, LLVector3 max)
+{
+ LLVector3 size = max - min;
+ mExtents[0] = min;
+ mExtents[1] = max;
+}
+
+void LLDrawable::setPositionGroup(const LLVector3d& pos)
+{
+ mPositionGroup.setVec(pos);
+}
+
+void LLDrawable::updateSpatialExtents()
+{
+ if (mVObjp)
+ {
+ mVObjp->updateSpatialExtents(mExtents[0], mExtents[1]);
+ }
+
+ if (mSpatialBridge.notNull())
+ {
+ mPositionGroup.setVec(0,0,0);
+ }
+}
+
+
+void LLDrawable::updateBinRadius()
+{
+ S32 binLOD = mVObjp ? mVObjp->getLOD() : 2;
+ static F64 detail_bins[] = { 8, 4, 2, 1 };
+ F32 radius = getVOVolume() && isStatic() ?
+ (mExtents[1]-mExtents[0]).magVec() : getRadius();
+ mBinRadius = detail_bins[binLOD] * llmax((F64) radius, (3-binLOD)*0.25);
+}
+
+void LLDrawable::updateLightSet()
+{
+ if (isDead())
+ {
+ llwarns << "Updating light set for dead drawable!" << llendl;
+ return;
+ }
+
+ LLVOVolume* light = getVOVolume();
+ if (isLight() && light)
+ {
+ // mLightSet points to lit objects
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ gPipeline.markRelight(*iter);
+ }
+ mLightSet.clear();
+ gPipeline.mObjectPartition->getObjects(getPositionAgent(), light->getLightRadius(), mLightSet);
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ gPipeline.markRelight(*iter);
+ }
+ }
+ else
+ {
+ // mLightSet points to nearby lights
+ mLightSet.clear();
+ gPipeline.mObjectPartition->getLights(getPositionAgent(), getRadius(), mLightSet);
+ const U32 max_lights = 16;
+ if (mLightSet.size() > max_lights)
+ {
+ typedef std::set<std::pair<F32,LLPointer<LLDrawable> > > sorted_pair_set_t;
+ sorted_pair_set_t sorted_set;
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ LLDrawable* drawable = *iter;
+ LLVector3 dvec = drawable->getPositionAgent() - getPositionAgent();
+ F32 dist2 = dvec.magVecSquared();
+ sorted_set.insert(std::make_pair(dist2, drawable));
+ }
+ mLightSet.clear();
+ S32 count = 0;
+ for (sorted_pair_set_t::iterator iter = sorted_set.begin(); iter != sorted_set.end(); iter++)
+ {
+ if (++count > 16)
+ break;
+ mLightSet.insert((*iter).second);
+ }
+ }
+ }
+}
+
+void LLDrawable::updateSpecialHoverCursor(BOOL enabled)
+{
+ // TODO: maintain a list of objects that have special
+ // hover cursors, then use that list for per-frame
+ // hover cursor selection. JC
+}
+
+BOOL LLDrawable::updateLighting(BOOL do_lighting)
+{
+ if (do_lighting)
+ {
+ if (gPipeline.getLightingDetail() >= 2 && (getLit() || isLight()))
+ {
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_LIGHTS);
+ updateLightSet();
+ do_lighting = isLight() ? FALSE : TRUE;
+ }
+ else
+ {
+ do_lighting = FALSE;
+ }
+ }
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEO_LIGHT);
+ if (mVObjp->updateLighting(do_lighting))
+ {
+ setState(LIGHTING_BUILT);
+ }
+ }
+
+ return TRUE;
+}
+
+void LLDrawable::applyLightsAsPoint(LLColor4& result)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ LLVector3 point_agent(getPositionAgent());
+ LLVector3 normal(-gCamera->getXAxis()); // make point agent face camera
+
+ F32 sun_int = normal * gPipeline.mSunDir;
+ LLColor4 color(gSky.getTotalAmbientColor());
+ color += gPipeline.mSunDiffuse * sun_int;
+
+ for (drawable_set_t::iterator iter = mLightSet.begin();
+ iter != mLightSet.end(); ++iter)
+ {
+ LLDrawable* drawable = *iter;
+ LLVOVolume* light = drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ LLColor4 light_color;
+ light->calcLightAtPoint(point_agent, normal, light_color);
+ color += light_color;
+ }
+
+ // Clamp the color...
+ color.mV[0] = llmax(color.mV[0], 0.f);
+ color.mV[1] = llmax(color.mV[1], 0.f);
+ color.mV[2] = llmax(color.mV[2], 0.f);
+
+ F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]);
+ if (max_color > 1.f)
+ {
+ color *= 1.f/max_color;
+ }
+
+ result = color;
+}
+
+F32 LLDrawable::getVisibilityRadius() const
+{
+ if (isDead())
+ {
+ return 0.f;
+ }
+ else if (isLight())
+ {
+ return llmax(getRadius(), getVOVolume()->getLightRadius());
+ }
+ else
+ {
+ return getRadius();
+ }
+}
+
+void LLDrawable::updateUVMinMax()
+{
+}
+
+void LLDrawable::setSpatialGroup(LLSpatialGroup *groupp, const S32 offset)
+{
+ mSpatialGroupp = groupp;
+
+ if (mSpatialGroupp)
+ {
+ mSpatialGroupOffset = offset;
+ }
+ else
+ {
+ mSpatialGroupOffset = -1;
+ }
+}
+
+LLSpatialPartition* LLDrawable::getSpatialPartition()
+{
+ LLSpatialPartition* retval = NULL;
+
+ if (mVObjp->isHUDAttachment())
+ { //HUD attachments don't get space partitioned
+ return NULL;
+ }
+
+ if (!mVObjp ||
+ !getVOVolume() ||
+ isStatic())
+ {
+ retval = gPipeline.mObjectPartition;
+ }
+
+ //must be an active volume
+ if (!retval && isRoot())
+ {
+ if (!mSpatialBridge)
+ {
+ setSpatialBridge(new LLSpatialBridge(this));
+ }
+ return mSpatialBridge->asPartition();
+ }
+ else if (!retval)
+ {
+ retval = getParent()->getSpatialPartition();
+ }
+
+ if (retval && mSpatialBridge.notNull())
+ {
+ mSpatialBridge->markDead();
+ setSpatialBridge(NULL);
+ }
+
+ return retval;
+}
+
+//=======================================
+// Spatial Partition Bridging Drawable
+//=======================================
+
+LLSpatialBridge::LLSpatialBridge(LLDrawable* root)
+{
+ mDrawable = root;
+ root->setSpatialBridge(this);
+
+ mRenderType = mDrawable->mRenderType; //w00! magic!
+
+ mOctree->balance();
+
+ gPipeline.mObjectPartition->put(this);
+}
+
+LLSpatialBridge::~LLSpatialBridge()
+{
+ if (getSpatialGroup())
+ {
+ gPipeline.mObjectPartition->remove(this, getSpatialGroup());
+ }
+}
+
+void LLSpatialBridge::updateSpatialExtents()
+{
+ LLSpatialGroup* root = (LLSpatialGroup*) mOctree->getListener(0);
+
+ if (mOctree->getChildCount() > 0)
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
+ root->rebound();
+ }
+
+ LLXformMatrix* mat = mDrawable->getXform();
+
+ LLVector3 offset = root->mBounds[0];
+ LLVector3 size = root->mBounds[1];
+
+ LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
+ LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
+
+ offset *= rotation;
+ center += offset;
+
+ LLVector3 v[4];
+ //get 4 corners of bounding box
+ v[0] = (size * rotation);
+ v[1] = (LLVector3(-size.mV[0], -size.mV[1], size.mV[2]) * rotation);
+ v[2] = (LLVector3(size.mV[0], -size.mV[1], -size.mV[2]) * rotation);
+ v[3] = (LLVector3(-size.mV[0], size.mV[1], -size.mV[2]) * rotation);
+
+ LLVector3& newMin = mExtents[0];
+ LLVector3& newMax = mExtents[1];
+
+ newMin = newMax = center;
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ for (U32 j = 0; j < 3; j++)
+ {
+ F32 delta = fabsf(v[i].mV[j]);
+ F32 min = center.mV[j] - delta;
+ F32 max = center.mV[j] + delta;
+
+ if (min < newMin.mV[j])
+ {
+ newMin.mV[j] = min;
+ }
+
+ if (max > newMax.mV[j])
+ {
+ newMax.mV[j] = max;
+ }
+ }
+ }
+
+ mPositionGroup.setVec((newMin + newMax) * 0.5f);
+ updateBinRadius();
+}
+
+void LLSpatialBridge::updateBinRadius()
+{
+ F32 rad = ((mExtents[1]-mExtents[0])*0.5f).magVec();
+ mBinRadius = llmax(rad, 2.f);
+ mRadius = rad;
+}
+
+LLCamera LLSpatialBridge::transformCamera(LLCamera& camera)
+{
+ LLCamera ret = camera;
+ LLXformMatrix* mat = mDrawable->getXform();
+ LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
+ //LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
+
+ //ret.rotate(~mat->getRotation());
+ LLVector3 delta = ret.getOrigin() - center;
+ delta *= ~mat->getRotation();
+ ret.setOrigin(delta);
+
+ return ret;
+}
+
+void LLDrawable::setVisible(LLCamera& camera, std::vector<LLDrawable*>* results, BOOL for_select)
+{
+ mVisible = sCurVisible;
+}
+
+void LLSpatialBridge::setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results, BOOL for_select)
+{
+ LLVector3 center = (mExtents[0] + mExtents[1]) * 0.5f;
+ LLVector3 size = (mExtents[1]-mExtents[0]) * 0.5f;
+
+ if (camera_in.AABBInFrustum(center, size))
+ {
+ LLVector3 lookAt = center - camera_in.getOrigin();
+ F32 distSqr = lookAt.magVecSquared();
+ F32 objRad = size.magVecSquared();
+
+ if (objRad/distSqr < SG_MIN_DIST_RATIO*4)
+ {
+ return;
+ }
+
+ LLDrawable::setVisible(camera_in);
+
+ if (for_select)
+ {
+ results->push_back(mDrawable);
+ for (U32 i = 0; i < mDrawable->getChildCount(); i++)
+ {
+ results->push_back(mDrawable->getChild(i));
+ }
+ }
+ else
+ {
+ const LLVector3* extents = mDrawable->getSpatialExtents();
+ objRad = mDrawable->getRadius();
+ objRad *= objRad;
+
+ if (objRad/distSqr > SG_MIN_DIST_RATIO)
+ {
+ gPipeline.markNotCulled(mDrawable, camera_in);
+ }
+
+ for (U32 i = 0; i < mDrawable->getChildCount(); i++)
+ {
+ LLDrawable* child = mDrawable->getChild(i);
+ extents = child->getSpatialExtents();
+ objRad = child->getRadius();
+ objRad *= objRad;
+
+ if (objRad/distSqr > SG_MIN_DIST_RATIO)
+ {
+ gPipeline.markNotCulled(mDrawable->getChild(i), camera_in);
+ }
+ }
+ }
+ }
+}
+
+void LLSpatialBridge::updateDistance(LLCamera& camera_in)
+{
+ LLCamera camera = transformCamera(camera_in);
+
+ mDrawable->updateDistance(camera);
+
+ for (U32 i = 0; i < mDrawable->getChildCount(); ++i)
+ {
+ mDrawable->getChild(i)->updateDistance(camera);
+ }
+}
+
+void LLSpatialBridge::makeActive()
+{ //it is an error to make a spatial bridge active (it's already active)
+ llerrs << "makeActive called on spatial bridge" << llendl;
+}
+
+void LLSpatialBridge::makeStatic()
+{
+ llerrs << "makeStatic called on spatial bridge" << llendl;
+}
+
+void LLSpatialBridge::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
+{
+ LLSpatialPartition::move(drawablep, curp, immediate);
+ gPipeline.markMoved(this, FALSE);
+}
+
+BOOL LLSpatialBridge::updateMove()
+{
+ mOctree->balance();
+ gPipeline.mObjectPartition->move(this, getSpatialGroup(), TRUE);
+ return TRUE;
+}
+
+void LLSpatialBridge::shiftPos(const LLVector3& vec)
+{
+ mExtents[0] += vec;
+ mExtents[1] += vec;
+ mPositionGroup += LLVector3d(vec);
+}
+
+void LLSpatialBridge::cleanupReferences()
+{
+ LLDrawable::cleanupReferences();
+ if (mDrawable)
+ {
+ mDrawable->setSpatialGroup(NULL, -1);
+ for (U32 i = 0; i < mDrawable->getChildCount(); i++)
+ {
+ LLDrawable* drawable = mDrawable->getChild(i);
+ if (drawable && drawable->getVOVolume())
+ {
+ drawable->setSpatialGroup(NULL, -1);
+ }
+ }
+
+ LLDrawable* drawablep = mDrawable;
+ mDrawable = NULL;
+ drawablep->setSpatialBridge(NULL);
+ }
+}
+
+const LLVector3 LLDrawable::getPositionAgent() const
+{
+ if (getVOVolume())
+ {
+ if (isActive())
+ {
+ if (isRoot())
+ {
+ return LLVector3(0,0,0) * getWorldMatrix();
+ }
+ else
+ {
+ return mVObjp->getPosition() * getParent()->getWorldMatrix();
+ }
+ }
+ else
+ {
+ return mVObjp->getPositionAgent();
+ }
+ }
+ else
+ {
+ return getWorldPosition();
+ }
+}
+
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
new file mode 100644
index 0000000000..cb9f970106
--- /dev/null
+++ b/indra/newview/lldrawable.h
@@ -0,0 +1,311 @@
+/**
+ * @file lldrawable.h
+ * @brief LLDrawable class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_DRAWABLE_H
+#define LL_DRAWABLE_H
+
+#include <vector>
+#include <map>
+
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "v4coloru.h"
+#include "llquaternion.h"
+#include "xform.h"
+#include "llprimitive.h"
+#include "llviewerimage.h"
+#include "lldarray.h"
+#include "llstat.h"
+#include "llviewerobject.h"
+#include "llrect.h"
+
+class LLDrawPool;
+class LLDrawable;
+class LLFace;
+class LLSpatialGroup;
+class LLSpatialBridge;
+class LLSpatialPartition;
+class LLVOVolume;
+
+extern F32 gFrameTimeSeconds;
+
+// Can have multiple silhouettes for each object
+const U32 SILHOUETTE_HIGHLIGHT = 0;
+
+
+// All data for new renderer goes into this class.
+class LLDrawable : public LLRefCount
+{
+public:
+ static void initClass();
+
+ LLDrawable() { init(); }
+ MEM_TYPE_NEW(LLMemType::MTYPE_DRAWABLE);
+
+ void markDead(); // Mark this drawable as dead
+ BOOL isDead() const { return isState(DEAD); }
+ BOOL isNew() const { return !isState(BUILT); }
+
+ BOOL isLight() const;
+
+ BOOL isVisible() const { return (mVisible == sCurVisible); }
+ virtual void setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results = NULL, BOOL for_select = FALSE);
+
+
+ const LLViewerRegion* getRegion() const { return mVObjp->getRegion(); }
+ const LLTextureEntry* getTextureEntry(U8 which) const { return mVObjp->getTE(which); }
+ LLViewerObject* getVObj() { return mVObjp; }
+ const LLViewerObject* getVObj() const { return mVObjp; }
+ LLVOVolume* getVOVolume() const; // cast mVObjp tp LLVOVolume if OK
+
+ const LLMatrix4& getWorldMatrix() const { return mXform.getWorldMatrix(); }
+ const LLMatrix4& getRenderMatrix() const { return isRoot() ? getWorldMatrix() : getParent()->getWorldMatrix(); }
+ const void setPosition(LLVector3 v) const { }
+ const LLVector3& getPosition() const { return mXform.getPosition(); }
+ const LLVector3& getWorldPosition() const { return mXform.getPositionW(); }
+ const LLVector3 getPositionAgent() const;
+ const LLVector3d& getPositionGroup() const { return mPositionGroup; }
+ const LLVector3& getScale() const { return mCurrentScale; }
+ const LLQuaternion& getWorldRotation() const { return mXform.getWorldRotation(); }
+ const LLQuaternion& getRotation() const { return mXform.getRotation(); }
+ const F32 getIntensity() const { return llmin(mXform.getScale().mV[0], 4.f); }
+ const S32 getLOD() const { return mVObjp ? mVObjp->getLOD() : 1; }
+ const F64 getBinRadius() const { return mBinRadius; }
+ void getMinMax(LLVector3& min,LLVector3& max) const { mXform.getMinMax(min,max); }
+ LLXformMatrix* getXform() { return &mXform; }
+
+ const U32 getState() const { return mState; }
+ const BOOL isState (U32 bits) const { return ((mState & bits) != 0); }
+ void setState (U32 bits) { mState |= bits; }
+ void clearState(U32 bits) { mState &= ~bits; }
+
+ BOOL isAvatar() const { return mVObjp.notNull() && mVObjp->isAvatar(); }
+ BOOL isRoot() const { return !mParent || mParent->isAvatar(); }
+ BOOL isSpatialRoot() const { return !mParent || mParent->isAvatar(); }
+ virtual BOOL isSpatialBridge() const { return FALSE; }
+ virtual LLSpatialPartition* asPartition() { return NULL; }
+ LLDrawable* getParent() const { return mParent; }
+ LLDrawable* getChild(U32 index) { return mVObjp->mChildList[index]->mDrawable; }
+ U32 getChildCount() { return mVObjp ? mVObjp->mChildList.size() : 0; }
+
+ // must set parent through LLViewerObject:: ()
+ //BOOL setParent(LLDrawable *parent);
+
+ inline LLFace* getFace(const S32 i) const;
+ inline S32 getNumFaces() const;
+
+ //void removeFace(const S32 i); // SJB: Avoid using this, it's slow
+ LLFace* addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BOOL shared_geom = FALSE);
+ void deleteFaces(S32 offset, S32 count);
+ void setNumFaces(const S32 numFaces, LLDrawPool *poolp, LLViewerImage *texturep);
+ void setNumFacesFast(const S32 numFaces, LLDrawPool *poolp, LLViewerImage *texturep);
+ void mergeFaces(LLDrawable* src);
+
+ void init();
+ void destroy();
+
+ void update();
+ F32 updateXform(BOOL undamped);
+
+ virtual void makeActive();
+ virtual void makeStatic();
+
+ BOOL isActive() const { return isState(ACTIVE); }
+ BOOL isStatic() const { return !isActive(); }
+ virtual BOOL updateMove();
+ virtual void movePartition();
+
+ void updateTexture();
+ void updateMaterial();
+ virtual void updateDistance(LLCamera& camera);
+ BOOL updateGeometry(BOOL priority);
+ BOOL updateLighting(BOOL priority);
+ void updateLightSet();
+
+ F32 getSunShadowFactor() const { return mSunShadowFactor; }
+ void setSunShadowFactor(F32 factor) { mSunShadowFactor = factor; }
+ void applyLightsAsPoint(LLColor4& result);
+ void updateSpecialHoverCursor(BOOL enabled);
+
+ virtual void shiftPos(const LLVector3 &shift_vector);
+
+ S32 getGeneration() const { return mGeneration; }
+
+ BOOL getLit() const { return isState(UNLIT) ? FALSE : TRUE; }
+ void setLit(BOOL lit) { lit ? clearState(UNLIT) : setState(UNLIT); }
+
+ void clearLightSet();
+ virtual void cleanupReferences();
+
+ void setRadius(const F32 radius);
+ F32 getRadius() const { return mRadius; }
+ F32 getVisibilityRadius() const;
+
+ void updateUVMinMax(); // Updates the cache of sun space bounding box.
+
+ const LLVector3& getBounds(LLVector3& min, LLVector3& max) const;
+ virtual void updateSpatialExtents();
+ virtual void updateBinRadius();
+ const LLVector3* getSpatialExtents() const;
+ void setSpatialExtents(LLVector3 min, LLVector3 max);
+ void setPositionGroup(const LLVector3d& pos);
+ void setPositionGroup(const LLVector3& pos) { setPositionGroup(LLVector3d(pos)); }
+
+ void setRenderType(S32 type) { mRenderType = type; }
+ BOOL isRenderType(S32 type) { return mRenderType == type; }
+ S32 getRenderType() { return mRenderType; }
+
+ // Debugging methods
+ S32 findReferences(LLDrawable *drawablep); // Not const because of @#$! iterators...
+
+ void setSpatialGroup(LLSpatialGroup *groupp, const S32 offset);
+ LLSpatialGroup *getSpatialGroup() const { return mSpatialGroupp; }
+ LLSpatialPartition* getSpatialPartition();
+
+ // Statics
+ static void incrementVisible();
+ static void cleanupDeadDrawables();
+
+protected:
+ virtual ~LLDrawable() { destroy(); }
+ void moveUpdatePipeline(BOOL moved);
+ BOOL updateMoveDamped();
+ BOOL updateMoveUndamped();
+
+public:
+ friend class LLPipeline;
+ friend class LLDrawPool;
+ friend class LLSpatialBridge;
+
+ typedef std::set<LLPointer<LLDrawable> > drawable_set_t;
+ typedef std::vector<LLPointer<LLDrawable> > drawable_vector_t;
+ typedef std::list<LLPointer<LLDrawable> > drawable_list_t;
+
+ struct CompareDistanceGreater
+ {
+ bool operator()(const LLDrawable* const& lhs, const LLDrawable* const& rhs)
+ {
+ return lhs->mDistanceWRTCamera < rhs->mDistanceWRTCamera; // farthest = last
+ }
+ };
+
+ struct CompareDistanceGreaterVisibleFirst
+ {
+ bool operator()(const LLDrawable* const& lhs, const LLDrawable* const& rhs)
+ {
+ if (lhs->isVisible() && !rhs->isVisible())
+ {
+ return TRUE; //visible things come first
+ }
+ else if (!lhs->isVisible() && rhs->isVisible())
+ {
+ return FALSE; //rhs is visible, comes first
+ }
+
+ return lhs->mDistanceWRTCamera < rhs->mDistanceWRTCamera; // farthest = last
+ }
+ };
+
+ typedef enum e_drawable_flags
+ {
+// TEXTURE = 0x00000001,
+ IN_REBUILD_Q1 = 0x00000002,
+ IN_REBUILD_Q2 = 0x00000004,
+ IN_LIGHT_Q = 0x00000008,
+ EARLY_MOVE = 0x00000010,
+ MOVE_UNDAMPED = 0x00000020,
+ ON_MOVE_LIST = 0x00000040,
+ USE_BACKLIGHT = 0x00000080,
+ UV = 0x00000100,
+ UNLIT = 0x00000200,
+ LIGHT = 0x00000400,
+ LIGHTING_BUILT = 0x00000800,
+ REBUILD_VOLUME = 0x00001000,
+ REBUILD_TCOORD = 0x00002000,
+ REBUILD_GEOMETRY= REBUILD_VOLUME|REBUILD_TCOORD,
+ REBUILD_LIGHTING= 0x00008000,
+ REBUILD_ALL = REBUILD_GEOMETRY|REBUILD_LIGHTING,
+ ON_SHIFT_LIST = 0x00100000,
+// NO_INTERP_COLOR = 0x00200000,
+ BLOCKER = 0x00400000,
+ ACTIVE = 0x00800000,
+ DEAD = 0x01000000,
+ INVISIBLE = 0x02000000, // stay invisible until flag is cleared
+ NEARBY_LIGHT = 0x04000000, // In gPipeline.mNearbyLightSet
+ BUILT = 0x08000000,
+ FORCE_INVISIBLE = 0x10000000, // stay invis until CLEAR_INVISIBLE is set (set of orphaned)
+ CLEAR_INVISIBLE = 0x20000000, // clear FORCE_INVISIBLE next draw frame
+ REBUILD_SHADOW = 0x40000000
+ } EDrawableFlags;
+
+ LLXformMatrix mXform;
+
+ // vis data
+ LLPointer<LLDrawable> mParent;
+
+ F32 mDistanceWRTCamera;
+
+ LLRectf mUVRect;
+ F32 mUVZ;
+
+ drawable_set_t mLightSet;
+ drawable_set_t mBlockSet;
+
+ LLVector3 mSavePos;
+ S32 mQuietCount;
+
+ static S32 getCurrentFrame() { return sCurVisible; }
+
+ void setSpatialBridge(LLSpatialBridge* bridge) { mSpatialBridge = (LLDrawable*) bridge; }
+ LLSpatialBridge* getSpatialBridge() { return (LLSpatialBridge*) (LLDrawable*) mSpatialBridge; }
+
+protected:
+ typedef std::vector<LLFace*> face_list_t;
+
+ U32 mState;
+ S32 mRenderType;
+ LLPointer<LLViewerObject> mVObjp;
+ face_list_t mFaces;
+ LLSpatialGroup* mSpatialGroupp;
+ LLPointer<LLDrawable> mSpatialBridge;
+ S32 mSpatialGroupOffset;
+
+ U32 mVisible;
+ F32 mRadius;
+ LLVector3 mExtents[2];
+ LLVector3d mPositionGroup;
+ F64 mBinRadius;
+ S32 mGeneration;
+
+ F32 mSunShadowFactor;
+
+ LLVector3 mCurrentScale;
+
+ static U32 sCurVisible; // Counter for what value of mVisible means currently visible
+ static F32 sCurPixelAngle; //current pixels per radian
+
+ static U32 sNumZombieDrawables;
+ static LLDynamicArrayPtr<LLPointer<LLDrawable> > sDeadList;
+};
+
+
+inline LLFace* LLDrawable::getFace(const S32 i) const
+{
+ llassert((U32)i < mFaces.size());
+ return mFaces[i];
+}
+
+
+inline S32 LLDrawable::getNumFaces()const
+{
+ return (S32)mFaces.size();
+}
+
+#endif
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
new file mode 100644
index 0000000000..0ef9eed7a3
--- /dev/null
+++ b/indra/newview/lldrawpool.cpp
@@ -0,0 +1,1402 @@
+/**
+ * @file lldrawpool.cpp
+ * @brief LLDrawPool class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpool.h"
+
+#include "llfasttimer.h"
+#include "llviewercontrol.h"
+
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolbump.h"
+#include "lldrawpoolclouds.h"
+#include "lldrawpoolground.h"
+#include "lldrawpoolmedia.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolsky.h"
+#include "lldrawpoolstars.h"
+#include "lldrawpooltree.h"
+#include "lldrawpooltreenew.h"
+#include "lldrawpoolterrain.h"
+#include "lldrawpoolwater.h"
+#include "lldrawpoolhud.h"
+#include "llface.h"
+#include "llviewerobjectlist.h" // For debug listing.
+#include "llvotreenew.h"
+#include "pipeline.h"
+
+#include "llagparray.inl"
+
+U32 LLDrawPool::sDataSizes[LLDrawPool::DATA_MAX_TYPES] =
+{
+ 12, // DATA_VERTICES
+ 8, // DATA_TEX_COORDS0
+ 8, // DATA_TEX_COORDS1
+ 8, // DATA_TEX_COORDS2
+ 8, // DATA_TEX_COORDS3
+ 12, // DATA_NORMALS
+ 4, // DATA_VERTEX_WEIGHTS,
+ 16, // DATA_CLOTHING_WEIGHTS
+ 12, // DATA_BINORMALS
+ 4, // DATA_COLORS
+};
+
+S32 LLDrawPool::sNumDrawPools = 0;
+
+LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerImage *tex0)
+{
+ LLDrawPool *poolp = NULL;
+ switch (type)
+ {
+ case POOL_SIMPLE:
+ poolp = new LLDrawPoolSimple(tex0);
+ break;
+ case POOL_ALPHA:
+ poolp = new LLDrawPoolAlpha();
+ break;
+ case POOL_AVATAR:
+ poolp = new LLDrawPoolAvatar();
+ break;
+ case POOL_TREE:
+ poolp = new LLDrawPoolTree(tex0);
+ break;
+ case POOL_TREE_NEW:
+ poolp = new LLDrawPoolTreeNew(tex0);
+ break;
+ case POOL_TERRAIN:
+ poolp = new LLDrawPoolTerrain(tex0);
+ break;
+ case POOL_SKY:
+ poolp = new LLDrawPoolSky();
+ break;
+ case POOL_STARS:
+ poolp = new LLDrawPoolStars();
+ break;
+ case POOL_CLOUDS:
+ poolp = new LLDrawPoolClouds();
+ break;
+ case POOL_WATER:
+ poolp = new LLDrawPoolWater();
+ break;
+ case POOL_GROUND:
+ poolp = new LLDrawPoolGround();
+ break;
+ case POOL_BUMP:
+ poolp = new LLDrawPoolBump(tex0);
+ break;
+ case POOL_MEDIA:
+ poolp = new LLDrawPoolMedia(tex0);
+ break;
+ case POOL_HUD:
+ poolp = new LLDrawPoolHUD();
+ break;
+ default:
+ llerrs << "Unknown draw pool type!" << llendl;
+ return NULL;
+ }
+
+ llassert(poolp->mType == type);
+ return poolp;
+}
+
+LLDrawPool::LLDrawPool(const U32 type, const U32 data_mask_il, const U32 data_mask_nil)
+{
+ llassert(data_mask_il & DATA_VERTICES_MASK);
+ S32 i;
+ mType = type;
+ sNumDrawPools++;
+ mId = sNumDrawPools;
+
+ mDataMaskIL = data_mask_il;
+ mDataMaskNIL = data_mask_nil;
+
+ U32 cur_mask = 0x01;
+ U32 cur_offset = 0;
+ for (i = 0; i < DATA_MAX_TYPES; i++)
+ {
+ mDataOffsets[i] = cur_offset;
+ if (cur_mask & mDataMaskIL)
+ {
+ cur_offset += sDataSizes[i];
+ }
+ cur_mask <<= 1;
+ }
+
+ mStride = cur_offset;
+
+ mCleanupUnused = FALSE;
+ mIndicesDrawn = 0;
+ mRebuildFreq = 128 + rand() % 5;
+ mRebuildTime = 0;
+ mGeneration = 1;
+ mSkippedVertices = 0;
+
+ resetDrawOrders();
+ resetVertexData(0);
+
+ if (gGLManager.mHasATIVAO && !gGLManager.mIsRadeon9700)
+ {
+ // ATI 8500 doesn't like indices > 15 bit.
+ mMaxVertices = DEFAULT_MAX_VERTICES/2;
+ }
+ else
+ {
+ mMaxVertices = DEFAULT_MAX_VERTICES;
+ }
+
+ // JC: This must happen last, as setUseAGP reads many of the
+ // above variables.
+ mUseAGP = FALSE;
+ setUseAGP(gPipeline.usingAGP());
+
+ for (i=0; i<NUM_BUCKETS; i++)
+ {
+ mFreeListGeomHead[i] = -1;
+ mFreeListIndHead[i] = -1;
+ }
+ mVertexShaderLevel = 0;
+}
+
+void LLDrawPool::destroy()
+{
+ if (!mReferences.empty())
+ {
+ llinfos << mReferences.size() << " references left on deletion of draw pool!" << llendl;
+ }
+}
+
+
+LLDrawPool::~LLDrawPool()
+{
+ destroy();
+
+ llassert( gPipeline.findPool( getType(), getTexture() ) == NULL );
+}
+
+BOOL LLDrawPool::setUseAGP(BOOL use_agp)
+{
+ BOOL ok = TRUE;
+ S32 vertex_count = mMemory.count() / mStride;
+ if (vertex_count > mMaxVertices && use_agp)
+ {
+#ifdef DEBUG_AGP
+ llwarns << "Allocating " << vertex_count << " vertices in pool type " << getType() << ", disabling AGP!" << llendl
+#endif
+ use_agp = FALSE;
+ ok = FALSE;
+ }
+
+ if (mUseAGP != use_agp)
+ {
+ mUseAGP = use_agp;
+
+ BOOL ok = TRUE;
+ ok &= mMemory.setUseAGP(use_agp);
+
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ ok &= mWeights.setUseAGP(use_agp);
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ ok &= mClothingWeights.setUseAGP(use_agp);
+ }
+
+ if (!ok)
+ {
+ // Disable AGP if any one of these doesn't have AGP, we don't want to try
+ // mixing AGP and non-agp arrays in a single pool.
+#ifdef DEBUG_AGP
+ llinfos << "Aborting using AGP because set failed on a mem block!" << llendl;
+#endif
+ setUseAGP(FALSE);
+ ok = FALSE;
+ }
+ }
+ return ok;
+}
+
+void LLDrawPool::flushAGP()
+{
+ mMemory.flushAGP();
+
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.flushAGP();
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.flushAGP();
+ }
+}
+
+void LLDrawPool::syncAGP()
+{
+ if (!getVertexCount())
+ {
+ return;
+ }
+ setUseAGP(gPipeline.usingAGP());
+
+ BOOL all_agp_on = TRUE;
+ mMemory.sync();
+ all_agp_on &= mMemory.isAGP();
+
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.sync();
+ all_agp_on &= mWeights.isAGP();
+ }
+
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.sync();
+ all_agp_on &= mClothingWeights.isAGP();
+ }
+
+ // Since sometimes AGP allocation is done during syncs, we need
+ // to make sure that if AGP allocation fails, we fallback to non-agp.
+ if (mUseAGP && !all_agp_on)
+ {
+#ifdef DEBUG_AGP
+ llinfos << "setUseAGP false because of AGP sync failure!" << llendl;
+#endif
+ setUseAGP(FALSE);
+ }
+}
+
+void LLDrawPool::dirtyTexture(const LLViewerImage *imagep)
+{
+}
+
+BOOL LLDrawPool::moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data)
+{
+ return TRUE;
+}
+
+// static
+S32 LLDrawPool::drawLoop(face_array_t& face_list, const U32* index_array)
+{
+ S32 res = 0;
+ if (!face_list.empty())
+ {
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ }
+ }
+ return res;
+}
+
+// static
+S32 LLDrawPool::drawLoopSetTex(face_array_t& face_list, const U32* index_array, S32 stage)
+{
+ S32 res = 0;
+ if (!face_list.empty())
+ {
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+ facep->bindTexture(stage);
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ }
+ }
+ return res;
+}
+
+void LLDrawPool::drawLoop()
+{
+ const U32* index_array = getRawIndices();
+ if (!mDrawFace.empty())
+ {
+ mIndicesDrawn += drawLoop(mDrawFace, index_array);
+ }
+}
+
+BOOL LLDrawPool::getVertexStrider(LLStrider<LLVector3> &vertices, const U32 index)
+{
+ llassert(mDataMaskIL & LLDrawPool::DATA_VERTICES_MASK);
+ vertices = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_VERTICES] + index * mStride);
+ vertices.setStride(mStride);
+ return TRUE;
+}
+
+BOOL LLDrawPool::getTexCoordStrider(LLStrider<LLVector2> &tex_coords, const U32 index, const U32 pass)
+{
+ llassert(mDataMaskIL & (LLDrawPool::DATA_TEX_COORDS0_MASK << pass));
+ tex_coords = (LLVector2*)(mMemory.getMem() + mDataOffsets[DATA_TEX_COORDS0 + pass] + index * mStride);
+ tex_coords.setStride(mStride);
+ return TRUE;
+}
+
+
+BOOL LLDrawPool::getVertexWeightStrider(LLStrider<F32> &vertex_weights, const U32 index)
+{
+ llassert(mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK);
+
+ vertex_weights = &mWeights[index];
+ vertex_weights.setStride( 0 );
+ return TRUE;
+}
+
+BOOL LLDrawPool::getClothingWeightStrider(LLStrider<LLVector4> &clothing_weights, const U32 index)
+{
+ llassert(mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK);
+
+ clothing_weights= &mClothingWeights[index];
+ clothing_weights.setStride( 0 );
+
+ return TRUE;
+}
+
+BOOL LLDrawPool::getNormalStrider(LLStrider<LLVector3> &normals, const U32 index)
+{
+ llassert((mDataMaskIL) & LLDrawPool::DATA_NORMALS_MASK);
+
+ normals = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_NORMALS] + index * mStride);
+
+ normals.setStride( mStride );
+
+ return TRUE;
+}
+
+
+BOOL LLDrawPool::getBinormalStrider(LLStrider<LLVector3> &binormals, const U32 index)
+{
+ llassert((mDataMaskIL) & LLDrawPool::DATA_BINORMALS_MASK);
+
+ binormals = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_BINORMALS] + index * mStride);
+
+ binormals.setStride( mStride );
+
+ return TRUE;
+}
+
+BOOL LLDrawPool::getColorStrider(LLStrider<LLColor4U> &colors, const U32 index)
+{
+ llassert((mDataMaskIL) & LLDrawPool::DATA_COLORS_MASK);
+
+ colors = (LLColor4U*)(mMemory.getMem() + mDataOffsets[DATA_COLORS] + index * mStride);
+
+ colors.setStride( mStride );
+
+ return TRUE;
+}
+
+//virtual
+void LLDrawPool::beginRenderPass( S32 pass )
+{
+}
+
+//virtual
+void LLDrawPool::endRenderPass( S32 pass )
+{
+ glDisableClientState ( GL_TEXTURE_COORD_ARRAY );
+ glDisableClientState ( GL_COLOR_ARRAY );
+ glDisableClientState ( GL_NORMAL_ARRAY );
+}
+void LLDrawPool::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+}
+
+void LLDrawPool::renderVisibility()
+{
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ // SJB: Note: This may be broken now. If you need it, fix it :)
+
+ glLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glTranslatef(-0.4f,-0.3f,0);
+
+ float table[7][3] = {
+ { 1,0,0 },
+ { 0,1,0 },
+ { 1,1,0 },
+ { 0,0,1 },
+ { 1,0,1 },
+ { 0,1,1 },
+ { 1,1,1 }
+ };
+
+ glColor4f(0,0,0,0.5);
+ glBegin(GL_POLYGON);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glEnd();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+
+ S32 geom_count = face->getGeomCount();
+ for (S32 j=0;j<geom_count;j++)
+ {
+ LLVector3 p1;
+ LLVector3 p2;
+
+ intptr_t p = ((intptr_t)face*13) % 7;
+ F32 r = table[p][0];
+ F32 g = table[p][1];
+ F32 b = table[p][2];
+
+ //p1.mV[1] = y;
+ //p2.mV[1] = y;
+
+ p1.mV[2] = 1.0;
+ p2.mV[2] = 1.0;
+
+ glColor4f(r,g,b,0.5f);
+
+ glBegin(GL_LINE_STRIP);
+ glVertex3fv(p1.mV);
+ glVertex3fv(p2.mV);
+ glEnd();
+
+ }
+ }
+
+ glColor4f(1,1,1,1);
+ glBegin(GL_LINE_STRIP);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glEnd();
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+}
+
+void LLDrawPool::enqueue(LLFace* facep)
+{
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ mMoveFace.put(facep);
+ }
+ else
+ {
+#if ENABLE_FACE_LINKING
+ facep->mSkipRender = FALSE;
+ facep->mNextFace = NULL;
+
+ if (mDrawFace.size() > 0)
+ {
+ LLFace* last_face = mDrawFace[mDrawFace.size()-1];
+ if (match(last_face, facep))
+ {
+ last_face->link(facep);
+ }
+ }
+#endif
+ mDrawFace.put(facep);
+ }
+}
+
+void LLDrawPool::bindGLVertexPointer()
+{
+ mMemory.bindGLVertexPointer(getStride(DATA_VERTICES), mDataOffsets[DATA_VERTICES]);
+}
+
+void LLDrawPool::bindGLTexCoordPointer(const U32 pass)
+{
+ mMemory.bindGLTexCoordPointer(getStride(DATA_TEX_COORDS0+pass), mDataOffsets[DATA_TEX_COORDS0+pass]);
+}
+
+void LLDrawPool::bindGLNormalPointer()
+{
+ mMemory.bindGLNormalPointer(getStride(DATA_NORMALS), mDataOffsets[DATA_NORMALS]);
+}
+
+void LLDrawPool::bindGLBinormalPointer(S32 index)
+{
+ mMemory.bindGLBinormalPointer(index, getStride(DATA_BINORMALS), mDataOffsets[DATA_BINORMALS]);
+}
+
+void LLDrawPool::bindGLColorPointer()
+{
+ mMemory.bindGLColorPointer(getStride(DATA_COLORS), mDataOffsets[DATA_COLORS]);
+}
+
+void LLDrawPool::bindGLVertexWeightPointer(S32 index)
+{
+ mWeights.bindGLVertexWeightPointer(index, 0, 0);
+}
+
+void LLDrawPool::bindGLVertexClothingWeightPointer(S32 index)
+{
+ mClothingWeights.bindGLVertexClothingWeightPointer(index, 0, 0);
+}
+
+
+U32* LLDrawPool::getIndices(S32 index)
+{
+ return &mIndices[index];
+}
+
+const LLVector3& LLDrawPool::getVertex(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_VERTICES_MASK);
+ llassert(index < mMemory.count());
+ llassert(mMemory.getMem());
+ return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_VERTICES] + index * mStride);
+}
+
+const LLVector2& LLDrawPool::getTexCoord(const S32 index, const U32 pass)
+{
+ llassert(mDataMaskIL & (LLDrawPool::DATA_TEX_COORDS0_MASK << pass));
+ llassert(index < mMemory.count());
+ return *(LLVector2*)(mMemory.getMem() + mDataOffsets[DATA_TEX_COORDS0 + pass] + index * mStride);
+}
+
+const LLVector3& LLDrawPool::getNormal(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_NORMALS_MASK);
+ llassert(index < mMemory.count());
+ return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_NORMALS] + index * mStride);
+}
+
+const LLVector3& LLDrawPool::getBinormal(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_BINORMALS_MASK);
+ llassert(index < mMemory.count());
+ return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_BINORMALS] + index * mStride);
+}
+
+const LLColor4U& LLDrawPool::getColor(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_COLORS_MASK);
+ llassert(index < mMemory.count());
+ return *(LLColor4U*)(mMemory.getMem() + mDataOffsets[DATA_COLORS] + index * mStride);
+}
+
+const F32& LLDrawPool::getVertexWeight(const S32 index)
+{
+ llassert(mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK);
+ llassert(index < mWeights.count());
+ llassert(mWeights.getMem());
+ return mWeights[index];
+}
+
+const LLVector4& LLDrawPool::getClothingWeight(const S32 index)
+{
+ llassert(mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK);
+ llassert(index < mClothingWeights.count());
+ llassert(mClothingWeights.getMem());
+ return mClothingWeights[index];
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define USE_FREE_LIST 0
+#define DEBUG_FREELIST 0
+
+struct tFreeListNode
+{
+ U32 count;
+ S32 next;
+};
+
+#if DEBUG_FREELIST
+static void check_list(U8 *pool, S32 stride, S32 head, S32 max)
+{
+ int count = 0;
+
+ while (head >= 0)
+ {
+ tFreeListNode *node = (tFreeListNode *)(pool + head*stride);
+ count++;
+ if ((count > max) || ((node->count>>20) != 0xabc) || ((node->count&0xfffff) < 2))
+ llerrs << "Bad Ind List" << llendl;
+ head = node->next;
+ }
+}
+#define CHECK_LIST(x) check_list##x
+#else
+#define CHECK_LIST(x)
+#endif
+
+// DEBUG!
+void LLDrawPool::CheckIntegrity()
+{
+#if DEBUG_FREELIST
+ int bucket;
+ for (bucket=0; bucket<NUM_BUCKETS; bucket++)
+ {
+ CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[bucket], mMemory.count() / mStride));
+ CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[bucket], mIndices.count()));
+ }
+#endif
+}
+
+int LLDrawPool::freeListBucket(U32 count)
+{
+ int bucket;
+
+ // llassert(NUM_BUCKETS == 8)
+
+ if (count & ~511) // >= 512
+ bucket = 7;
+ else if (count & 256) // 256-511
+ bucket = 6;
+ else if (count & 128)
+ bucket = 5;
+ else if (count & 64)
+ bucket = 4;
+ else if (count & 32)
+ bucket = 3;
+ else if (count & 16)
+ bucket = 2;
+ else if (count & 8) // 8-15
+ bucket = 1;
+ else // 0-7
+ bucket = 0;
+ return bucket;
+}
+
+void remove_node(int nodeidx, int pidx, U8 *membase, int stride, int *head)
+{
+ LLDrawPool::FreeListNode *node = (LLDrawPool::FreeListNode *)(membase + nodeidx*stride);
+ if (pidx >= 0)
+ {
+ LLDrawPool::FreeListNode *pnode = (LLDrawPool::FreeListNode *)(membase + pidx*stride);
+ pnode->next = node->next;
+ }
+ else
+ {
+ *head = node->next;
+ }
+}
+
+void LLDrawPool::freeListAddGeom(S32 index, U32 count)
+{
+#if USE_FREE_LIST
+ int i;
+ U8 *membase = (U8*)mMemory.getMem();
+ // See if next block or previous block is free, if so combine them
+ for (i=0; i<NUM_BUCKETS; i++)
+ {
+ int pidx = -1;
+ int nodeidx = mFreeListGeomHead[i];
+ while(nodeidx >= 0)
+ {
+ int change = 0;
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx*mStride);
+ int nodecount = node->count & 0xffff;
+ // Check for prev block
+ if (nodeidx + nodecount == index)
+ {
+ remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
+ // Combine nodes
+ index = nodeidx;
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ // Check for next block
+ if (nodeidx == index + count)
+ {
+ remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
+ // Combine nodes
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ if (change)
+ break;
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+ // Add (extended) block to free list
+ if (count >= 2) // need 2 words to store free list (theoreticly mStride could = 4)
+ {
+ CheckIntegrity();
+ if ((index + count)*mStride >= mMemory.count())
+ {
+ mMemory.shrinkTo(index*mStride);
+ }
+ else
+ {
+ int bucket = freeListBucket(count);
+ FreeListNode *node = (FreeListNode *)(membase + index*mStride);
+ node->count = count | (0xabc<<20);
+ node->next = mFreeListGeomHead[bucket];
+ mFreeListGeomHead[bucket] = index;
+ }
+ CheckIntegrity();
+ }
+#endif
+}
+
+void LLDrawPool::freeListAddInd(S32 index, U32 count)
+{
+#if USE_FREE_LIST
+ int i;
+ const U32 *membase = mIndices.getMem();
+ // See if next block or previous block is free, if so combine them
+ for (i=0; i<NUM_BUCKETS; i++)
+ {
+ int pidx = -1;
+ int nodeidx = mFreeListIndHead[i];
+ while(nodeidx >= 0)
+ {
+ int change = 0;
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx);
+ int nodecount = node->count & 0xffff;
+ // Check for prev block
+ if (nodeidx + nodecount == index)
+ {
+ remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
+ // Combine nodes
+ index = nodeidx;
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ // Check for next block
+ if (nodeidx == index + count)
+ {
+ remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
+ // Combine nodes
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ if (change)
+ break;
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+ // Add (extended) block to free list
+ if (count >= 2) // need 2 words to store free list
+ {
+ CheckIntegrity();
+ if (index + count >= mIndices.count())
+ {
+ mIndices.shrinkTo(index);
+ }
+ else
+ {
+ int bucket = freeListBucket(count);
+ FreeListNode *node = (FreeListNode *)(membase + index);
+ node->count = count | (0xabc<<20);
+ node->next = mFreeListIndHead[bucket];
+ mFreeListIndHead[bucket] = index;
+ }
+ CheckIntegrity();
+ }
+#endif
+}
+
+S32 LLDrawPool::freeListFindGeom(U32 count)
+{
+#if USE_FREE_LIST
+ int i, nodeidx, pidx;
+ int firstbucket = freeListBucket(count);
+ U8 *membase = (U8*)mMemory.getMem();
+ for (i=firstbucket; i<NUM_BUCKETS; i++)
+ {
+ pidx = -1;
+ nodeidx = mFreeListGeomHead[i];
+ CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[i], mMemory.count() / mStride));
+ while(nodeidx >= 0)
+ {
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx*mStride);
+ int nodecount = node->count & 0xffff;
+ llassert((node->count>>20) == 0xabc);
+ if (nodecount >= count)
+ {
+ remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
+#if 1
+ if (nodecount > count)
+ {
+ int leftover = nodecount - count;
+ freeListAddGeom(nodeidx + count, leftover);
+ }
+#endif
+ return nodeidx;
+ }
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+#endif // USE_FREE_LIST
+ return -1;
+}
+
+S32 LLDrawPool::freeListFindInd(U32 count)
+{
+#if USE_FREE_LIST
+ int i, nodeidx, pidx;
+ int firstbucket = freeListBucket(count);
+ U32 *membase = (U32 *)mIndices.getMem();
+ for (i=firstbucket; i<NUM_BUCKETS; i++)
+ {
+ pidx = -1;
+ nodeidx = mFreeListIndHead[i];
+ CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[i], mIndices.count()));
+ while(nodeidx >= 0)
+ {
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx);
+ int nodecount = node->count & 0xffff;
+ llassert((node->count>>20) == 0xabc);
+ if (nodecount >= count)
+ {
+ remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
+#if 1
+ if (nodecount > count)
+ {
+ int leftover = nodecount - count;
+ freeListAddInd(nodeidx + count, leftover);
+ }
+#endif
+ return nodeidx;
+ }
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+#endif // USE_FREE_LIST
+ return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+S32 LLDrawPool::reserveGeom(const U32 geom_count)
+{
+ LLFastTimer t(LLFastTimer::FTM_GEO_RESERVE);
+
+ S32 index;
+ index = freeListFindGeom(geom_count);
+ if (index < 0)
+ {
+ index = mMemory.count() / mStride;
+ if (!geom_count)
+ {
+ llwarns << "Attempting to reserve zero bytes!" << llendl;
+ return index;
+ }
+
+ S32 bytes = geom_count * mStride;
+
+ if ((index + (S32)geom_count) > (S32)mMaxVertices)
+ {
+ //
+ // Various drivers have issues with the number of indices being greater than a certain number.
+ // if you're using AGP. Disable AGP if we've got more vertices than in the pool.
+ //
+#ifdef DEBUG_AGP
+ llinfos << "setUseAGP false because of large vertex count in reserveGeom" << llendl;
+#endif
+ setUseAGP(FALSE);
+ }
+
+ mMemory.reserve_block(bytes);
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.reserve_block(geom_count);
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.reserve_block(geom_count);
+ }
+ }
+ CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[0], mMemory.count() / mStride));
+ return index;
+}
+
+S32 LLDrawPool::reserveInd(U32 indCount)
+{
+ S32 index;
+ index = freeListFindInd(indCount);
+ if (index < 0)
+ {
+ index = mIndices.count();
+
+ if (indCount)
+ {
+ mIndices.reserve_block(indCount);
+ }
+ }
+ for (U32 i=0;i<indCount;i++)
+ {
+ mIndices[index+i]=0;
+ }
+ CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[0], mIndices.count()));
+ return index;
+}
+
+S32 LLDrawPool::unReserveGeom(const S32 index, const U32 count)
+{
+ if (index < 0 || count == 0)
+ return -1;
+
+ freeListAddGeom(index, count);
+
+#if 0
+ int i;
+ S32 bytes,words;
+ U32 *memp;
+ // Fill mem with bad data (for testing only)
+ bytes = count * mStride;
+ bytes -= sizeof(FreeListNode);
+ memp = (U32*)(mMemory.getMem() + index * mStride);
+ memp += sizeof(FreeListNode)>>2;
+ words = bytes >> 2;
+ for (i=0; i<words; i++)
+ *memp++ = 0xffffffff;
+
+ words = count; // (sizeof each array is a word)
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ memp = (U32*)(&mWeights[index]);
+ for (i=0; i<words; i++)
+ *memp++ = 0xffffffff;
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ memp = (U32*)(&mClothingWeights[index]);
+ for (i=0; i<count; i++)
+ *memp++ = 0xffffffff;
+ }
+#endif
+ return -1;
+}
+
+S32 LLDrawPool::unReserveInd(const S32 index, const U32 count)
+{
+ if (index < 0 || count == 0)
+ return -1;
+
+ freeListAddInd(index, count);
+
+#if 0
+ int i;
+ U32 *memp = &mIndices[index];
+ for (i=0; i<count; i++)
+ *memp++ = 0xffffffff;
+#endif
+ return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+const U32 LLDrawPool::getIndexCount() const
+{
+ return mIndices.count();
+}
+
+const U32 LLDrawPool::getVertexCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+const U32 LLDrawPool::getTexCoordCount(U32 pass) const
+{
+ return mMemory.count() / mStride;
+}
+
+
+const U32 LLDrawPool::getNormalCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+
+const U32 LLDrawPool::getBinormalCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+const U32 LLDrawPool::getColorCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+const U32 LLDrawPool::getVertexWeightCount() const
+{
+ return mWeights.count();
+}
+
+// virtual
+BOOL LLDrawPool::addFace(LLFace *facep)
+{
+ addFaceReference(facep);
+ return TRUE;
+}
+
+// virtual
+BOOL LLDrawPool::removeFace(LLFace *facep)
+{
+ removeFaceReference(facep);
+
+ vector_replace_with_last(mDrawFace, facep);
+
+ facep->unReserve();
+
+ return TRUE;
+}
+
+// Not absolutely sure if we should be resetting all of the chained pools as well - djs
+void LLDrawPool::resetDrawOrders()
+{
+ mDrawFace.resize(0);
+}
+
+void LLDrawPool::resetIndices(S32 indices_count)
+{
+ mIndices.reset(indices_count);
+ for (S32 i=0; i<NUM_BUCKETS; i++)
+ mFreeListIndHead[i] = -1;
+}
+
+void LLDrawPool::resetVertexData(S32 reserve_count)
+{
+ mMemory.reset(reserve_count*mStride);
+
+ for (S32 i=0; i<NUM_BUCKETS; i++)
+ {
+ mFreeListGeomHead[i] = -1;
+ }
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.reset(reserve_count);
+ }
+
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.reset(reserve_count);
+ }
+}
+
+void LLDrawPool::resetAll()
+{
+ resetDrawOrders();
+ resetVertexData(0);
+ mGeneration++;
+
+}
+
+S32 LLDrawPool::rebuild()
+{
+ mRebuildTime++;
+
+ BOOL needs_rebuild = FALSE;
+ S32 rebuild_cost = 0;
+
+ if (mUseAGP)
+ {
+ if (getVertexCount() > 0.75f*DEFAULT_MAX_VERTICES)
+ {
+ if (mRebuildTime > 8)
+ {
+ needs_rebuild = TRUE;
+ }
+#ifdef DEBUG_AGP
+ llwarns << "More than " << DEFAULT_MAX_VERTICES << " in pool type " << (S32)mType << " at rebuild!" << llendl;
+#endif
+ }
+ }
+
+ // rebuild de-allocates 'stale' objects, so we still need to do a rebuild periodically
+ if (mRebuildFreq > 0 && mRebuildTime >= mRebuildFreq)
+ {
+ needs_rebuild = TRUE;
+ }
+
+ if (needs_rebuild)
+ {
+ mGeneration++;
+
+ if (mReferences.empty())
+ {
+ resetIndices(0);
+ resetVertexData(0);
+ }
+ else
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->hasGeometry() && !facep->isState(LLFace::BACKLIST | LLFace::SHARED_GEOM))
+ {
+ facep->backup();
+ }
+ }
+ S32 tot_verts = 0;
+ S32 tot_indices = 0;
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ tot_verts += facep->getGeomCount();
+ tot_indices += facep->getIndicesCount();
+ }
+ }
+ for (std::vector<LLFace*>::iterator iter = mMoveFace.begin();
+ iter != mMoveFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ tot_verts += facep->getGeomCount();
+ tot_indices += facep->getIndicesCount();
+ }
+ }
+
+ resetIndices(tot_indices);
+ flushAGP();
+ resetVertexData(tot_verts);
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ llassert(facep->getPool() == this);
+ facep->restore();
+ }
+ }
+ mRebuildTime = 0;
+ setDirty();
+ }
+
+ if (!mMoveFace.empty())
+ {
+ for (std::vector<LLFace*>::iterator iter = mMoveFace.begin();
+ iter != mMoveFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ facep->restore();
+ enqueue(facep);
+ }
+ setDirty();
+ mMoveFace.reset();
+ rebuild_cost++;
+ }
+ return rebuild_cost;
+}
+
+LLViewerImage *LLDrawPool::getTexture()
+{
+ return NULL;
+}
+
+LLViewerImage *LLDrawPool::getDebugTexture()
+{
+ return NULL;
+}
+
+void LLDrawPool::removeFaceReference(LLFace *facep)
+{
+ if (facep->getReferenceIndex() != -1)
+ {
+ if (facep->getReferenceIndex() != (S32)mReferences.size())
+ {
+ LLFace *back = mReferences.back();
+ mReferences[facep->getReferenceIndex()] = back;
+ back->setReferenceIndex(facep->getReferenceIndex());
+ }
+ mReferences.pop_back();
+ }
+ facep->setReferenceIndex(-1);
+}
+
+void LLDrawPool::addFaceReference(LLFace *facep)
+{
+ if (-1 == facep->getReferenceIndex())
+ {
+ facep->setReferenceIndex(mReferences.size());
+ mReferences.push_back(facep);
+ }
+}
+
+U32 LLDrawPool::getTrianglesDrawn() const
+{
+ return mIndicesDrawn / 3;
+}
+
+void LLDrawPool::resetTrianglesDrawn()
+{
+ mIndicesDrawn = 0;
+}
+
+void LLDrawPool::addIndicesDrawn(const U32 indices)
+{
+ mIndicesDrawn += indices;
+}
+
+BOOL LLDrawPool::verify() const
+{
+ BOOL ok = TRUE;
+ // Verify all indices in the pool are in the right range
+ const U32 *indicesp = getRawIndices();
+ for (U32 i = 0; i < getIndexCount(); i++)
+ {
+ if (indicesp[i] > getVertexCount())
+ {
+ ok = FALSE;
+ llinfos << "Bad index in tree pool!" << llendl;
+ }
+ }
+
+ for (std::vector<LLFace*>::const_iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ const LLFace* facep = *iter;
+ if (facep->getPool() != this)
+ {
+ llinfos << "Face in wrong pool!" << llendl;
+ facep->printDebugInfo();
+ ok = FALSE;
+ }
+ else if (!facep->verify())
+ {
+ ok = FALSE;
+ }
+ }
+
+ return ok;
+}
+
+void LLDrawPool::printDebugInfo() const
+{
+ llinfos << "Pool " << this << " Type: " << getType() << llendl;
+ llinfos << "--------------------" << llendl;
+ llinfos << "Vertex count: " << getVertexCount() << llendl;
+ llinfos << "Normal count: " << getNormalCount() << llendl;
+ llinfos << "Indices count: " << getIndexCount() << llendl;
+ llinfos << llendl;
+}
+
+
+S32 LLDrawPool::getMemUsage(const BOOL print)
+{
+ S32 mem_usage = 0;
+
+ mem_usage += sizeof(this);
+
+ // Usage beyond the pipeline allocated data (color and mMemory)
+ mem_usage += mIndices.getMax() * sizeof(U32);
+ mem_usage += mDrawFace.capacity() * sizeof(LLFace *);
+ mem_usage += mMoveFace.capacity() * sizeof(LLFace *);
+ mem_usage += mReferences.capacity() * sizeof(LLFace *);
+
+ mem_usage += mMemory.getSysMemUsage();
+ mem_usage += mWeights.getSysMemUsage();
+ mem_usage += mClothingWeights.getSysMemUsage();
+
+ return mem_usage;
+}
+
+LLColor3 LLDrawPool::getDebugColor() const
+{
+ return LLColor3(0.f, 0.f, 0.f);
+}
+
+void LLDrawPool::setDirty()
+{
+ mMemory.setDirty();
+ mWeights.setDirty();
+ mClothingWeights.setDirty();
+}
+
+BOOL LLDrawPool::LLOverrideFaceColor::sOverrideFaceColor = FALSE;
+
+void LLDrawPool::LLOverrideFaceColor::setColor(const LLColor4& color)
+{
+ if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
+ {
+ glVertexAttrib4fvARB(mPool->getMaterialAttribIndex(), color.mV);
+ }
+ else
+ {
+ glColor4fv(color.mV);
+ }
+}
+
+void LLDrawPool::LLOverrideFaceColor::setColor(const LLColor4U& color)
+{
+ if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
+ {
+ glVertexAttrib4ubvARB(mPool->getMaterialAttribIndex(), color.mV);
+ }
+ else
+ {
+ glColor4ubv(color.mV);
+ }
+}
+
+void LLDrawPool::LLOverrideFaceColor::setColor(F32 r, F32 g, F32 b, F32 a)
+{
+ if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
+ {
+ glVertexAttrib4fARB(mPool->getMaterialAttribIndex(), r,g,b,a);
+ }
+ else
+ {
+ glColor4f(r,g,b,a);
+ }
+}
+
+// virtual
+void LLDrawPool::enableShade()
+{ }
+
+// virtual
+void LLDrawPool::disableShade()
+{ }
+
+// virtual
+void LLDrawPool::setShade(F32 shade)
+{ }
diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h
new file mode 100644
index 0000000000..a66d7d14c7
--- /dev/null
+++ b/indra/newview/lldrawpool.h
@@ -0,0 +1,348 @@
+/**
+ * @file lldrawpool.h
+ * @brief LLDrawPool class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOL_H
+#define LL_LLDRAWPOOL_H
+
+#include "llagparray.h"
+#include "lldarray.h"
+#include "lldlinked.h"
+#include "llstrider.h"
+#include "llviewerimage.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "llstrider.h"
+
+class LLFace;
+class LLImageGL;
+class LLViewerImage;
+
+#define DEFAULT_MAX_VERTICES 65535
+
+class LLDrawPool
+{
+public:
+ typedef LLDynamicArray<LLFace*, 128> face_array_t;
+
+ enum
+ {
+ SHADER_LEVEL_SCATTERING = 2
+ };
+
+public:
+ LLDrawPool(const U32 type, const U32 data_mask_il, const U32 data_mask_nil);
+ virtual ~LLDrawPool();
+
+ static LLDrawPool* createPool(const U32 type, LLViewerImage *tex0 = NULL);
+
+ void flushAGP(); // Flush the AGP buffers so they can be repacked and reallocated.
+ void syncAGP();
+
+ virtual LLDrawPool *instancePool() = 0; // Create an empty new instance of the pool.
+ virtual void beginRenderPass( S32 pass );
+ virtual void endRenderPass( S32 pass );
+ virtual S32 getNumPasses() { return 1; }
+ virtual void render(S32 pass = 0) = 0;
+ virtual void renderForSelect() = 0;
+ virtual BOOL match(LLFace* last_face, LLFace* facep) { return FALSE; }
+ virtual void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+
+ virtual void prerender() = 0;
+ virtual S32 rebuild();
+
+ virtual S32 getMaterialAttribIndex() = 0;
+
+ virtual LLViewerImage *getTexture();
+ virtual LLViewerImage *getDebugTexture();
+ virtual void dirtyTexture(const LLViewerImage* texturep);
+
+ virtual void enqueue(LLFace *face);
+ virtual BOOL addFace(LLFace *face);
+ virtual BOOL removeFace(LLFace *face);
+
+ virtual BOOL verify() const; // Verify that all data in the draw pool is correct!
+ virtual LLColor3 getDebugColor() const; // For AGP debug display
+
+ virtual void resetDrawOrders();
+ virtual void resetVertexData(S32 reserve_count);
+ virtual void resetIndices(S32 num_indices);
+ void resetAll();
+
+ BOOL moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data = FALSE);
+
+
+ S32 getId() const { return mId; }
+ U32 getType() const { return mType; }
+
+ const U32 getStride() const;
+ inline const U32 getStride(const U32 data_type) const;
+ inline const U32 getOffset(const U32 data_type) const;
+
+ S32 reserveGeom(U32 count);
+ S32 reserveInd (U32 count);
+ S32 unReserveGeom(const S32 index, const U32 count);
+ S32 unReserveInd(const S32 index, const U32 count);
+
+ void bindGLVertexPointer();
+ void bindGLTexCoordPointer(const U32 pass=0);
+ void bindGLNormalPointer();
+ void bindGLBinormalPointer(S32 index);
+ void bindGLColorPointer();
+ void bindGLVertexWeightPointer(S32 index);
+ void bindGLVertexClothingWeightPointer(S32 index);
+
+ const U32 getIndexCount() const;
+ const U32 getTexCoordCount(const U32 pass=0) const;
+ const U32 getVertexCount() const;
+ const U32 getNormalCount() const;
+ const U32 getBinormalCount() const;
+ const U32 getColorCount() const;
+ const U32 getVertexWeightCount() const;
+
+ void setDirty();
+ void setDirtyMemory() { mMemory.setDirty(); }
+ void setDirtyWeights() { mWeights.setDirty(); }
+
+ const U32* getRawIndices() const { return mIndices.getMem(); }
+
+ U32 getIndex(const S32 index) { return mIndices[index]; } // Use to get one index
+ U32 *getIndices(const S32 index); // Used to get an array of indices for reading/writing
+ void CheckIntegrity(); // DEBUG
+
+ const LLVector3& getVertex(const S32 index);
+ const LLVector2& getTexCoord(const S32 index, const U32 pass);
+ const LLVector3& getNormal(const S32 index);
+ const LLVector3& getBinormal(const S32 index);
+ const LLColor4U& getColor(const S32 index);
+ const F32& getVertexWeight(const S32 index);
+ const LLVector4& getClothingWeight(const S32 index);
+
+ void setRebuild(const BOOL rebuild);
+
+ void destroy();
+
+ void buildEdges();
+
+ static S32 drawLoop(face_array_t& face_list, const U32* index_array);
+ static S32 drawLoopSetTex(face_array_t& face_list, const U32* index_array, S32 stage);
+ void drawLoop();
+
+ void renderVisibility();
+
+ void addFaceReference(LLFace *facep);
+ void removeFaceReference(LLFace *facep);
+ U32 getTrianglesDrawn() const;
+ void resetTrianglesDrawn();
+ void addIndicesDrawn(const U32 indices);
+
+ void printDebugInfo() const;
+ S32 getMemUsage(const BOOL print = FALSE);
+
+ BOOL setUseAGP(BOOL use_agp);
+ BOOL canUseAGP() const { return mMemory.isAGP(); } // Return TRUE if this pool can use AGP
+
+ S32 getMaxVertices() const { return mMaxVertices; }
+ S32 getVertexShaderLevel() const { return mVertexShaderLevel; }
+
+ friend class LLFace;
+ friend class LLPipeline;
+public:
+
+ enum
+ {
+ // Correspond to LLPipeline render type
+ POOL_SKY = 1,
+ POOL_STARS,
+ POOL_GROUND,
+ POOL_TERRAIN,
+ POOL_SIMPLE,
+ POOL_MEDIA,
+ POOL_BUMP,
+ POOL_AVATAR,
+ POOL_TREE,
+ POOL_TREE_NEW,
+ POOL_WATER,
+ POOL_CLOUDS,
+ POOL_ALPHA,
+ POOL_HUD,
+ };
+
+
+ // If you change the order or add params to these, you also need to adjust the sizes in the
+ // mDataSizes array defined in lldrawpool.cpp
+ typedef enum e_data_type
+ {
+ DATA_VERTICES = 0,
+ DATA_TEX_COORDS0 = 1,
+ DATA_TEX_COORDS1 = 2,
+ DATA_TEX_COORDS2 = 3,
+ DATA_TEX_COORDS3 = 4,
+ DATA_NORMALS = 5,
+ DATA_VERTEX_WEIGHTS = 6,
+ DATA_CLOTHING_WEIGHTS = 7,
+ DATA_BINORMALS = 8,
+ DATA_COLORS = 9,
+ DATA_MAX_TYPES = 10
+ } EDataType;
+
+ typedef enum e_data_mask
+ {
+ DATA_VERTICES_MASK = 1 << DATA_VERTICES,
+ DATA_TEX_COORDS0_MASK = 1 << DATA_TEX_COORDS0,
+ DATA_TEX_COORDS1_MASK = 1 << DATA_TEX_COORDS1,
+ DATA_TEX_COORDS2_MASK = 1 << DATA_TEX_COORDS2,
+ DATA_TEX_COORDS3_MASK = 1 << DATA_TEX_COORDS3,
+ DATA_NORMALS_MASK = 1 << DATA_NORMALS,
+ DATA_VERTEX_WEIGHTS_MASK = 1 << DATA_VERTEX_WEIGHTS,
+ DATA_CLOTHING_WEIGHTS_MASK = 1 << DATA_CLOTHING_WEIGHTS,
+ DATA_BINORMALS_MASK = 1 << DATA_BINORMALS,
+ DATA_COLORS_MASK = 1 << DATA_COLORS,
+
+ // Masks for standard types.
+ // IL for interleaved, NIL for non-interleaved.
+ DATA_SIMPLE_IL_MASK = DATA_VERTICES_MASK | DATA_TEX_COORDS0_MASK | DATA_NORMALS_MASK,
+ DATA_SIMPLE_NIL_MASK = 0,
+ DATA_BUMP_IL_MASK = DATA_SIMPLE_IL_MASK | DATA_BINORMALS_MASK | DATA_TEX_COORDS1_MASK,
+ } EDataMask;
+
+ face_array_t mDrawFace;
+ face_array_t mMoveFace;
+ face_array_t mReferences;
+
+ U32 mDataMaskIL; // Interleaved data
+ U32 mDataMaskNIL; // Non-interleaved data
+ U32 mDataOffsets[DATA_MAX_TYPES];
+ S32 mStride;
+
+ S32 mRebuildFreq;
+ S32 mRebuildTime;
+ S32 mGeneration;
+
+
+ S32 mSkippedVertices;
+
+ static U32 sDataSizes[DATA_MAX_TYPES];
+ static S32 sNumDrawPools;
+
+protected:
+ LLAGPArray<U8> mMemory;
+ LLAGPArray<F32> mWeights;
+ LLAGPArray<LLVector4> mClothingWeights;
+ LLAGPArray<U32> mIndices;
+
+public:
+
+ BOOL getVertexStrider (LLStrider<LLVector3> &vertices, const U32 index = 0);
+ BOOL getTexCoordStrider (LLStrider<LLVector2> &tex_coords, const U32 index = 0, const U32 pass=0);
+ BOOL getNormalStrider (LLStrider<LLVector3> &normals, const U32 index = 0);
+ BOOL getBinormalStrider (LLStrider<LLVector3> &binormals, const U32 index = 0);
+ BOOL getColorStrider (LLStrider<LLColor4U> &colors, const U32 index = 0);
+ BOOL getVertexWeightStrider(LLStrider<F32> &vertex_weights, const U32 index = 0);
+ BOOL getClothingWeightStrider(LLStrider<LLVector4> &clothing_weights, const U32 index = 0);
+
+public:
+ enum { NUM_BUCKETS = 8 }; // Need to change freeListBucket() if NUM_BUCKETS changes
+ struct FreeListNode
+ {
+ U32 count;
+ S32 next;
+ };
+protected:
+ int freeListBucket(U32 count);
+ void freeListAddGeom(S32 index, U32 count);
+ void freeListAddInd(S32 index, U32 count);
+ S32 freeListFindGeom(U32 count);
+ S32 freeListFindInd(U32 count);
+
+protected:
+ BOOL mUseAGP;
+ S32 mVertexShaderLevel;
+ S32 mId;
+ U32 mType; // Type of draw pool
+ S32 mMaxVertices;
+ S32 mIndicesDrawn;
+ BOOL mCleanupUnused; // Cleanup unused data when too full
+
+ S32 mFreeListGeomHead[8];
+ S32 mFreeListIndHead[8];
+
+public:
+ class LLOverrideFaceColor
+ {
+ public:
+ LLOverrideFaceColor(LLDrawPool* pool)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ }
+ LLOverrideFaceColor(LLDrawPool* pool, const LLColor4& color)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ setColor(color);
+ }
+ LLOverrideFaceColor(LLDrawPool* pool, const LLColor4U& color)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ setColor(color);
+ }
+ LLOverrideFaceColor(LLDrawPool* pool, F32 r, F32 g, F32 b, F32 a)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ setColor(r, g, b, a);
+ }
+ ~LLOverrideFaceColor()
+ {
+ sOverrideFaceColor = mOverride;
+ }
+ void setColor(const LLColor4& color);
+ void setColor(const LLColor4U& color);
+ void setColor(F32 r, F32 g, F32 b, F32 a);
+ BOOL mOverride;
+ LLDrawPool* mPool;
+ static BOOL sOverrideFaceColor;
+ };
+
+ virtual void enableShade();
+ virtual void disableShade();
+ virtual void setShade(F32 shade);
+
+};
+
+inline const U32 LLDrawPool::getStride() const
+{
+ return mStride;
+}
+
+inline const U32 LLDrawPool::getOffset(const U32 data_type) const
+{
+ return mDataOffsets[data_type];
+}
+
+inline const U32 LLDrawPool::getStride(const U32 data_type) const
+{
+ if (mDataMaskIL & (1 << data_type))
+ {
+ return mStride;
+ }
+ else if (mDataMaskNIL & (1 << data_type))
+ {
+ return 0;
+ }
+ else
+ {
+ llerrs << "Getting stride for unsupported data type " << data_type << llendl;
+ return 0;
+ }
+}
+
+#endif //LL_LLDRAWPOOL_H
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
new file mode 100644
index 0000000000..d34695a7be
--- /dev/null
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -0,0 +1,584 @@
+/**
+ * @file lldrawpoolalpha.cpp
+ * @brief LLDrawPoolAlpha class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolalpha.h"
+
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llfasttimer.h"
+
+#include "llagparray.h"
+#include "llcubemap.h"
+#include "llsky.h"
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h" // For debugging
+#include "llviewerobjectlist.h" // For debugging
+#include "llviewerwindow.h"
+#include "pipeline.h"
+
+const F32 MAX_DIST = 512.f;
+const F32 ALPHA_FALLOFF_START_DISTANCE = 0.8f;
+
+BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE;
+
+LLDrawPoolAlpha::LLDrawPoolAlpha() :
+ LLDrawPool(POOL_ALPHA,
+ DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK,
+ DATA_SIMPLE_NIL_MASK)
+{
+ mRebuiltLastFrame = FALSE;
+ mMinDistance = 0.f;
+ mMaxDistance = MAX_DIST;
+ mInvBinSize = NUM_ALPHA_BINS/(mMaxDistance - mMinDistance);
+ mCleanupUnused = TRUE;
+ //mRebuildFreq = -1 ; // Only rebuild if nearly full
+
+// for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+// {
+// mDistanceBins[i].realloc(200);
+// }
+}
+
+LLDrawPoolAlpha::~LLDrawPoolAlpha()
+{
+}
+
+LLDrawPool *LLDrawPoolAlpha::instancePool()
+{
+ llerrs << "Should never be calling instancePool on an alpha pool!" << llendl;
+ return NULL;
+}
+
+void LLDrawPoolAlpha::enqueue(LLFace *facep)
+{
+ if (!facep->isState(LLFace::GLOBAL))
+ {
+ facep->mCenterAgent = facep->mCenterLocal * facep->getRenderMatrix();
+ }
+ facep->mDistance = (facep->mCenterAgent - gCamera->getOrigin()) * gCamera->getAtAxis();
+
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ mMoveFace.put(facep);
+ }
+ else
+ {
+ mDrawFace.put(facep);
+ }
+
+ {
+ S32 dist_bin = lltrunc( (mMaxDistance - (facep->mDistance+32))*mInvBinSize );
+
+ if (dist_bin >= NUM_ALPHA_BINS)
+ {
+ mDistanceBins[NUM_ALPHA_BINS-1].put(facep);
+ //mDistanceBins[NUM_ALPHA_BINS-1].push(facep, (U32)(void*)facep->getTexture());
+ }
+ else if (dist_bin > 0)
+ {
+ mDistanceBins[dist_bin].put(facep);
+ //mDistanceBins[dist_bin].push(facep, (U32)(void*)facep->getTexture());
+ }
+ else
+ {
+ mDistanceBins[0].put(facep);
+ //mDistanceBins[0].push(facep, (U32)(void*)facep->getTexture());
+ }
+ }
+}
+
+BOOL LLDrawPoolAlpha::removeFace(LLFace *facep)
+{
+ BOOL removed = FALSE;
+
+ LLDrawPool::removeFace(facep);
+
+ {
+ for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ if (mDistanceBins[i].removeObj(facep) != -1)
+ {
+ if (removed)
+ {
+ llerrs << "Warning! " << "Face in multiple distance bins on removal" << llendl;
+ }
+ removed = TRUE;
+ }
+ }
+ }
+ if (removed)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLDrawPoolAlpha::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+void LLDrawPoolAlpha::beginRenderPass(S32 pass)
+{
+ if (mDrawFace.empty())
+ {
+ // No alpha objects, early exit.
+ return;
+ }
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+}
+
+
+void LLDrawPoolAlpha::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA);
+
+ if (mDrawFace.empty())
+ {
+ // No alpha objects, early exit.
+ return;
+ }
+
+ GLfloat shiny[4] =
+ {
+ 0.00f,
+ 0.25f,
+ 0.5f,
+ 0.75f
+ };
+
+ GLint specularIndex = (mVertexShaderLevel > 0) ?
+ gPipeline.mObjectAlphaProgram.mAttribute[LLPipeline::GLSL_SPECULAR_COLOR] : 0;
+
+ S32 diffTex = 0;
+ S32 envTex = -1;
+
+ if (mVertexShaderLevel > 0) //alpha pass uses same shader as shiny/bump
+ {
+ envTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (envTex >= 0 && cube_map)
+ {
+ cube_map->bind();
+ cube_map->setMatrix(1);
+ }
+
+ if (specularIndex > 0)
+ {
+ glVertexAttrib4fARB(specularIndex, 0, 0, 0, 0);
+ }
+
+ S32 scatterTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ diffTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ }
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ bindGLColorPointer();
+ }
+
+ S32 i, j;
+ glAlphaFunc(GL_GREATER,0.01f);
+ // This needs to be turned off or there will be lots of artifacting with the clouds - djs
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ LLDynamicArray<LLFace*>* distance_bins;
+ distance_bins = mDistanceBins;
+
+ S32 num_bins_no_alpha_test = ((gPickAlphaThreshold != 0.f) && gUsePickAlpha) ?
+ (NUM_ALPHA_BINS - llmax(2, (S32)(ALPHA_FALLOFF_START_DISTANCE * mInvBinSize))) :
+ NUM_ALPHA_BINS;
+
+ typedef std::vector<LLFace*> face_list_t;
+
+ for (i = 0; i < num_bins_no_alpha_test; i++)
+ {
+ S32 obj_count = distance_bins[i].count();
+
+ if (!obj_count)
+ {
+ continue;
+ }
+ else if (i > (NUM_ALPHA_BINS / 2) && obj_count < 100)
+ {
+ face_list_t pri_queue;
+ pri_queue.reserve(distance_bins[i].count());
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ pri_queue.push_back(distance_bins[i][j]);
+ }
+ std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
+
+ for (face_list_t::iterator iter = pri_queue.begin(); iter != pri_queue.end(); iter++)
+ {
+ const LLFace &face = *(*iter);
+ face.enableLights();
+ face.bindTexture(diffTex);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ else
+ {
+ S32 count = distance_bins[i].count();
+ for (j = 0; j < count; j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+ face.enableLights();
+ face.bindTexture(diffTex);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ }
+
+ GLfloat ogl_matrix[16];
+ gCamera->getOpenGLTransform(ogl_matrix);
+
+ for (i = num_bins_no_alpha_test; i < NUM_ALPHA_BINS; i++)
+ {
+ BOOL use_pri_queue = distance_bins[i].count() < 100;
+
+ face_list_t pri_queue;
+
+ if (use_pri_queue)
+ {
+ pri_queue.reserve(distance_bins[i].count());
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ pri_queue.push_back(distance_bins[i][j]);
+ }
+ std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
+ }
+
+ S32 count = distance_bins[i].count();
+ for (j = 0; j < count; j++)
+ {
+ const LLFace &face = use_pri_queue ? *pri_queue[j] : *distance_bins[i][j];
+ F32 fade_value = face.mAlphaFade * gPickAlphaThreshold;
+
+ face.enableLights();
+
+ if (fade_value < 1.f)
+ {
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ glAlphaFunc(GL_LESS, fade_value);
+ glBlendFunc(GL_ZERO, GL_ONE);
+ LLViewerImage::bindTexture(gPipeline.mAlphaSizzleImagep, diffTex);
+ LLVector4 s_params(ogl_matrix[2], ogl_matrix[6], ogl_matrix[10], ogl_matrix[14]);
+ LLVector4 t_params(ogl_matrix[1], ogl_matrix[5], ogl_matrix[9], ogl_matrix[13]);
+
+ LLGLEnable gls_texgen_s(GL_TEXTURE_GEN_S);
+ LLGLEnable gls_texgen_t(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_params.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_params.mV);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ }
+
+ {
+ // should get GL_GREATER to work, as it's faster
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LESS);
+ glAlphaFunc(GL_GEQUAL, fade_value);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ face.bindTexture(diffTex);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ }
+ }
+
+ // render opaque portion of actual texture
+ glAlphaFunc(GL_GREATER, 0.98f);
+
+ face.bindTexture(diffTex);
+ face.renderIndexed(getRawIndices());
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+
+ if (mVertexShaderLevel > 0) //single pass shader driven shiny/bump
+ {
+ gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (envTex >= 0 && cube_map)
+ {
+ cube_map->restoreMatrix();
+ }
+ gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+
+ if (sShowDebugAlpha)
+ {
+ gPipeline.disableLights();
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mHighlightProgram.bind();
+ }
+
+ LLViewerImage::sSmokeImagep->bind();
+ LLOverrideFaceColor override_color(this, 1.f, 0.f, 0.f, 1.f);
+ glColor4f(1.f, 0.f, 0.f, 1.f); // in case vertex shaders are enabled
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ if (distance_bins[i].count() < 100)
+ {
+ face_list_t pri_queue;
+ pri_queue.reserve(distance_bins[i].count());
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ pri_queue.push_back(distance_bins[i][j]);
+ }
+ std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
+
+ for (face_list_t::iterator iter = pri_queue.begin(); iter != pri_queue.end(); iter++)
+ {
+ const LLFace &face = *(*iter);
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ else
+ {
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ }
+
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mHighlightProgram.unbind();
+ }
+
+ }
+
+}
+
+void LLDrawPoolAlpha::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ // force faces on focus object to proper alpha cutoff based on object bbox distance
+ if (gAgent.getFocusObject())
+ {
+ LLDrawable* drawablep = gAgent.getFocusObject()->mDrawable;
+
+ if (drawablep)
+ {
+ const S32 num_faces = drawablep->getNumFaces();
+
+ for (S32 f = 0; f < num_faces; f++)
+ {
+ LLFace* facep = drawablep->getFace(f);
+ facep->mDistance = gAgent.getFocusObjectDist();
+ }
+ }
+ }
+
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ LLGLSObjectSelectAlpha gls_alpha;
+
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glAlphaFunc(gPickTransparent ? GL_GEQUAL : GL_GREATER, 0.f);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
+
+ LLDynamicArray<LLFace*>* distance_bins;
+ distance_bins = mDistanceBins;
+
+ S32 j;
+ S32 num_bins_no_alpha_test = (gPickAlphaThreshold != 0.f) ?
+ (NUM_ALPHA_BINS - llmax(2, (S32)(ALPHA_FALLOFF_START_DISTANCE * mInvBinSize))) :
+ NUM_ALPHA_BINS;
+
+ S32 i;
+ for (i = 0; i < num_bins_no_alpha_test; i++)
+ {
+ S32 distance_bin_size = distance_bins[i].count();
+ for (j = 0; j < distance_bin_size; j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+ if (face.getDrawable() && !face.getDrawable()->isDead() && (face.getViewerObject()->mGLName))
+ {
+ face.bindTexture();
+ face.renderForSelect();
+ }
+ }
+ }
+
+ for (i = num_bins_no_alpha_test; i < NUM_ALPHA_BINS; i++)
+ {
+ S32 distance_bin_size = distance_bins[i].count();
+ if (distance_bin_size)
+ {
+ for (j = 0; j < distance_bin_size; j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+
+ glAlphaFunc(GL_GEQUAL, face.mAlphaFade * gPickAlphaTargetThreshold);
+
+ if (face.getDrawable() && !face.getDrawable()->isDead() && (face.getViewerObject()->mGLName))
+ {
+ face.bindTexture();
+ face.renderForSelect();
+ }
+ }
+ }
+ }
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+}
+
+
+void LLDrawPoolAlpha::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ facep->renderSelected(image, color, index_offset, index_count);
+}
+
+
+void LLDrawPoolAlpha::resetDrawOrders()
+{
+ LLDrawPool::resetDrawOrders();
+
+ for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ mDistanceBins[i].resize(0);
+ }
+}
+
+BOOL LLDrawPoolAlpha::verify() const
+{
+ S32 i, j;
+ BOOL ok;
+ ok = LLDrawPool::verify();
+ for (i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ for (j = 0; j < mDistanceBins[i].count(); j++)
+ {
+ const LLFace &face = *mDistanceBins[i][j];
+ if (!face.verify())
+ {
+ ok = FALSE;
+ }
+ }
+ }
+ return ok;
+}
+
+LLViewerImage *LLDrawPoolAlpha::getDebugTexture()
+{
+ return LLViewerImage::sSmokeImagep;
+}
+
+
+LLColor3 LLDrawPoolAlpha::getDebugColor() const
+{
+ return LLColor3(1.f, 0.f, 0.f);
+}
+
+S32 LLDrawPoolAlpha::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectAlphaProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
+
+// virtual
+void LLDrawPoolAlpha::enableShade()
+{
+ glDisableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolAlpha::disableShade()
+{
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolAlpha::setShade(F32 shade)
+{
+ glColor4f(0,0,0,shade);
+}
diff --git a/indra/newview/lldrawpoolalpha.h b/indra/newview/lldrawpoolalpha.h
new file mode 100644
index 0000000000..6f24959e50
--- /dev/null
+++ b/indra/newview/lldrawpoolalpha.h
@@ -0,0 +1,64 @@
+/**
+ * @file lldrawpoolalpha.h
+ * @brief LLDrawPoolAlpha class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLALPHA_H
+#define LL_LLDRAWPOOLALPHA_H
+
+#include "lldrawpool.h"
+#include "llviewerimage.h"
+#include "llframetimer.h"
+
+class LLFace;
+class LLColor4;
+
+class LLDrawPoolAlpha: public LLDrawPool
+{
+public:
+ LLDrawPoolAlpha();
+ /*virtual*/ ~LLDrawPoolAlpha();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void beginRenderPass(S32 pass = 0);
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+
+ /*virtual*/ void enqueue(LLFace *face);
+ /*virtual*/ BOOL removeFace(LLFace *face);
+ /*virtual*/ void resetDrawOrders();
+
+ /*virtual*/ void enableShade();
+ /*virtual*/ void disableShade();
+ /*virtual*/ void setShade(F32 shade);
+
+
+ virtual S32 getMaterialAttribIndex();
+
+ BOOL mRebuiltLastFrame;
+ enum
+ {
+ NUM_ALPHA_BINS = 1024
+ };
+
+ /*virtual*/ BOOL verify() const;
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ static BOOL sShowDebugAlpha;
+protected:
+ F32 mMinDistance;
+ F32 mMaxDistance;
+ F32 mInvBinSize;
+
+ LLDynamicArray<LLFace*> mDistanceBins[NUM_ALPHA_BINS];
+};
+
+#endif // LL_LLDRAWPOOLALPHA_H
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
new file mode 100644
index 0000000000..dfe75084b5
--- /dev/null
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -0,0 +1,610 @@
+/**
+ * @file lldrawpoolavatar.cpp
+ * @brief LLDrawPoolAvatar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolavatar.h"
+
+#include "llvoavatar.h"
+#include "m3math.h"
+
+#include "llagparray.h"
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerregion.h"
+#include "noise.h"
+#include "pipeline.h"
+
+extern F32 gFrameDTClamped;
+extern BOOL gUseGLPick;
+
+F32 CLOTHING_GRAVITY_EFFECT = 0.7f;
+F32 CLOTHING_ACCEL_FORCE_FACTOR = 0.2f;
+const S32 NUM_TEST_AVATARS = 30;
+const S32 MIN_PIXEL_AREA_2_PASS_SKINNING = 500000000;
+
+// Format for gAGPVertices
+// vertex format for bumpmapping:
+// vertices 12
+// pad 4
+// normals 12
+// pad 4
+// texcoords0 8
+// texcoords1 8
+// total 48
+//
+// for no bumpmapping
+// vertices 12
+// texcoords 8
+// normals 12
+// total 32
+//
+
+S32 AVATAR_OFFSET_POS = 0;
+S32 AVATAR_OFFSET_NORMAL = 16;
+S32 AVATAR_OFFSET_TEX0 = 32;
+S32 AVATAR_OFFSET_TEX1 = 40;
+S32 AVATAR_VERTEX_BYTES = 48;
+
+
+BOOL gAvatarEmbossBumpMap = FALSE;
+
+LLDrawPoolAvatar::LLDrawPoolAvatar() :
+LLDrawPool(POOL_AVATAR,
+ DATA_SIMPLE_IL_MASK,
+ DATA_VERTEX_WEIGHTS_MASK | DATA_CLOTHING_WEIGHTS_MASK )
+{
+ mCleanupUnused = FALSE;
+
+ // Overide the data layout
+ mDataMaskIL = 0;
+ mStride = 0;
+ for (S32 i = 0; i < DATA_MAX_TYPES; i++)
+ {
+ mDataOffsets[i] = 0;
+ }
+
+ // Note: padding is to speed up SSE code
+ mDataMaskIL |= DATA_VERTICES_MASK;
+ mDataOffsets[DATA_VERTICES] = mStride;
+ mStride += sDataSizes[DATA_VERTICES];
+
+ mStride += 4;
+
+ mDataMaskIL |= DATA_NORMALS_MASK;
+ mDataOffsets[DATA_NORMALS] = mStride;
+ mStride += sDataSizes[DATA_NORMALS];
+
+ mStride += 4;
+
+ // Note: binormals are stripped off in software blending
+ mDataMaskIL |= DATA_BINORMALS_MASK;
+ mDataOffsets[DATA_BINORMALS] = mStride;
+ mStride += sDataSizes[DATA_BINORMALS];
+
+ mStride += 4; // To keep the structure 16-byte aligned (for SSE happiness)
+
+ mDataMaskIL |= DATA_TEX_COORDS0_MASK;
+ mDataOffsets[DATA_TEX_COORDS0] = mStride;
+ mStride += sDataSizes[DATA_TEX_COORDS0];
+
+ mDataMaskIL |= DATA_TEX_COORDS1_MASK;
+ mDataOffsets[DATA_TEX_COORDS1] = mStride;
+ mStride += sDataSizes[DATA_TEX_COORDS1];
+
+ //LLDebugVarMessageBox::show("acceleration", &CLOTHING_ACCEL_FORCE_FACTOR, 10.f, 0.1f);
+ //LLDebugVarMessageBox::show("gravity", &CLOTHING_GRAVITY_EFFECT, 10.f, 0.1f);
+
+}
+
+//-----------------------------------------------------------------------------
+// instancePool()
+//-----------------------------------------------------------------------------
+LLDrawPool *LLDrawPoolAvatar::instancePool()
+{
+ return new LLDrawPoolAvatar();
+}
+
+
+S32 LLDrawPoolAvatar::rebuild()
+{
+ mRebuildTime++;
+ if (mRebuildTime > mRebuildFreq)
+ {
+ flushAGP();
+
+ mRebuildTime = 0;
+ }
+
+ return 0;
+}
+
+BOOL gRenderAvatar = TRUE;
+
+void LLDrawPoolAvatar::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR);
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+void LLDrawPoolAvatar::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS);
+ renderAvatars(NULL); // render all avatars
+}
+
+void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, BOOL no_shaders)
+{
+ if (no_shaders)
+ {
+ mVertexShaderLevel = 0;
+ }
+
+ if (!gRenderAvatar)
+ {
+ return;
+ }
+
+ if (mDrawFace.empty() && !single_avatar)
+ {
+ return;
+ }
+
+ LLVOAvatar *avatarp;
+
+ if (single_avatar)
+ {
+ avatarp = single_avatar;
+ }
+ else
+ {
+ const LLFace *facep = mDrawFace[0];
+ if (!facep->getDrawable())
+ {
+ return;
+ }
+ avatarp = (LLVOAvatar *)(facep->getDrawable()->getVObj());
+ }
+
+ if (avatarp->isDead() || avatarp->mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLOverrideFaceColor color(this, 1.0f, 1.0f, 1.0f, 1.0f);
+
+ if( !single_avatar || (avatarp == single_avatar) )
+ {
+ if (LLVOAvatar::sShowCollisionVolumes)
+ {
+ LLGLSNoTexture no_texture;
+ avatarp->renderCollisionVolumes();
+ }
+
+ LLGLEnable normalize(GL_NORMALIZE);
+
+ if (avatarp->mIsSelf && LLAgent::sDebugDisplayTarget)
+ {
+ LLGLSNoTexture gls_no_texture;
+ LLVector3 pos = avatarp->getPositionAgent();
+
+ color.setColor(1.0f, 0.0f, 0.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ pos = avatarp->mDrawable->getPositionAgent();
+ color.setColor(1.0f, 0.0f, 0.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ pos = avatarp->mRoot.getWorldPosition();
+ color.setColor(1.0f, 1.0f, 1.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ pos = avatarp->mPelvisp->getWorldPosition();
+ color.setColor(0.0f, 0.0f, 1.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ color.setColor(1.0f, 1.0f, 1.0f, 1.0f);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLGLSLShader* vertex_program = &gPipeline.mAvatarProgram;
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.mAvatarMatrixParam = vertex_program->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
+ }
+
+ //--------------------------------------------------------------------------------
+ // this is where we first hit the software blending path
+ // if enabled, we need to set up the proper buffers and avoid setting other state
+ //--------------------------------------------------------------------------------
+ if (!(mVertexShaderLevel > 0))
+ {
+
+ // performance could be increased by better utilizing the buffers, for example, only using 1k buffers for lo-res
+ // avatars. But the only problem with using fewer buffers is that we're more likely to wait for a fence to complete
+
+ // vertex format:
+ // vertices 12
+ // texcoords 8
+ // normals 12
+ // binormals 12
+ // padding 4
+ // total 48
+
+ // Rotate to the next buffer, round-robin.
+ gPipeline.bufferRotate();
+
+ // Wait until the hardware is done reading the last set of vertices from the buffer before writing the next set.
+ gPipeline.bufferWaitFence();
+
+ // Need to do this because we may be rendering without AGP even in AGP mode
+ U8* buffer_offset_start = gPipeline.bufferGetScratchMemory();
+ glVertexPointer( 3, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_POS);
+ glTexCoordPointer(2, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_TEX0);
+ glNormalPointer( GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_NORMAL);
+
+ }
+
+ if ((mVertexShaderLevel > 0)) // for hardware blending
+ {
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer(0);
+ }
+
+ if ((mVertexShaderLevel > 0))
+ { //eyeballs render with the specular shader
+ gPipeline.mAvatarEyeballProgram.bind();
+ gPipeline.mMaterialIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+ gPipeline.mSpecularIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
+
+ S32 index = gPipeline.mAvatarEyeballProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gSky.mVOSkyp->getScatterMap()->bind(index);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+
+ if (avatarp->mSpecialRenderMode == 0) // normal
+ {
+ gPipeline.enableLightsAvatar(avatarp->mDrawable->getSunShadowFactor());
+ }
+ else if (avatarp->mSpecialRenderMode == 1) // anim preview
+ {
+ gPipeline.enableLightsAvatarEdit(LLColor4(0.7f, 0.6f, 0.3f, 1.f));
+ }
+ else // 2=image preview, 3=morph view
+ {
+ gPipeline.enableLightsAvatarEdit(LLColor4(.5f, .5f, .5f, 1.f));
+ }
+
+ // render rigid meshes (eyeballs) first
+ mIndicesDrawn += avatarp->renderRigid();
+
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mAvatarEyeballProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+
+ if (!gRenderForSelect && avatarp->mIsSelf && LLVOAvatar::sAvatarLoadTest)
+ {
+ LLVector3 orig_pos_root = avatarp->mRoot.getPosition();
+ LLVector3 next_pos_root = orig_pos_root;
+ for (S32 i = 0; i < NUM_TEST_AVATARS; i++)
+ {
+ next_pos_root.mV[VX] += 1.f;
+ if (i % 5 == 0)
+ {
+ next_pos_root.mV[VY] += 1.f;
+ next_pos_root.mV[VX] = orig_pos_root.mV[VX];
+ }
+
+ avatarp->mRoot.setPosition(next_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+
+ mIndicesDrawn += avatarp->renderRigid();
+ }
+ avatarp->mRoot.setPosition(orig_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+ }
+
+ if ((mVertexShaderLevel > 0)) // for hardware blending
+ {
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
+ {
+ bindGLTexCoordPointer(1);
+
+ bindGLBinormalPointer(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
+ gPipeline.mMaterialIndex = vertex_program->mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+ gPipeline.mSpecularIndex = vertex_program->mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
+ }
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ bindGLTexCoordPointer(0);
+ vertex_program->bind();
+ bindGLVertexWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ bindGLVertexClothingWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+ enable_cloth_weights(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+ }
+ enable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+
+ if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
+ {
+ enable_binormals(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
+ }
+
+ vertex_program->enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ S32 index = vertex_program->enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gSky.mVOSkyp->getScatterMap()->bind(index);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+
+ if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ LLMatrix4 rot_mat;
+ gCamera->getMatrixToLocal(rot_mat);
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ rot_mat *= cfr;
+
+ LLVector4 wind;
+ wind.setVec(avatarp->mWindVec);
+ wind.mV[VW] = 0;
+ wind = wind * rot_mat;
+ wind.mV[VW] = avatarp->mWindVec.mV[VW];
+
+ vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_WIND, wind.mV);
+ F32 phase = -1.f * (avatarp->mRipplePhase);
+
+ F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
+ LLVector4 sin_params(freq, freq, freq, phase);
+ vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_SINWAVE, sin_params.mV);
+
+ LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
+ gravity = gravity * rot_mat;
+ vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_GRAVITY, gravity.mV);
+ }
+
+ mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+
+ if (!gRenderForSelect && avatarp->mIsSelf && LLVOAvatar::sAvatarLoadTest)
+ {
+ LLVector3 orig_pos_root = avatarp->mRoot.getPosition();
+ LLVector3 next_pos_root = orig_pos_root;
+ for (S32 i = 0; i < NUM_TEST_AVATARS; i++)
+ {
+ next_pos_root.mV[VX] += 1.f;
+ if (i % 5 == 0)
+ {
+ next_pos_root.mV[VY] += 1.f;
+ next_pos_root.mV[VX] = orig_pos_root.mV[VX];
+ }
+
+ avatarp->mRoot.setPosition(next_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+
+ mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+ }
+ avatarp->mRoot.setPosition(orig_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+ }
+
+ // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+ if (!(mVertexShaderLevel > 0))
+ {
+ // want for the previously bound fence to finish
+ gPipeline.bufferSendFence();
+ }
+ else
+ {
+ vertex_program->disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ vertex_program->disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ disable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
+ {
+ disable_binormals(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
+ }
+ if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ disable_cloth_weights(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+ }
+
+ vertex_program->unbind();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// renderForSelect()
+//-----------------------------------------------------------------------------
+void LLDrawPoolAvatar::renderForSelect()
+{
+ if (gUseGLPick)
+ {
+ return;
+ }
+ //gGLSObjectSelectDepthAlpha.set();
+
+ if (!gRenderAvatar)
+ {
+ return;
+ }
+
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ const LLFace *facep = mDrawFace[0];
+ if (!facep->getDrawable())
+ {
+ return;
+ }
+ LLVOAvatar *avatarp = (LLVOAvatar *)(facep->getDrawable()->getVObj());
+
+ if (avatarp->isDead() || avatarp->mIsDummy || avatarp->mDrawable.isNull())
+ {
+ return;
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLGLSLShader* vertex_program = &gPipeline.mAvatarPickProgram;
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.mAvatarMatrixParam = vertex_program->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
+ }
+ glAlphaFunc(GL_GEQUAL, 0.2f);
+ glBlendFunc(GL_ONE, GL_ZERO);
+
+ //--------------------------------------------------------------------------------
+ // this is where we first hit the software blending path
+ // if enabled, we need to set up the proper buffers and avoid setting other state
+ //--------------------------------------------------------------------------------
+ if (!(mVertexShaderLevel > 0) || gUseGLPick)
+ {
+
+ // Rotate to the next buffer, round-robin.
+ gPipeline.bufferRotate();
+
+ // Wait until the hardware is done reading the last set of vertices from the buffer before writing the next set.
+ gPipeline.bufferWaitFence();
+
+ // Need to do this because we may be rendering without AGP even in AGP mode
+ U8* buffer_offset_start = gPipeline.bufferGetScratchMemory();
+ glVertexPointer( 3, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_POS);
+ glTexCoordPointer(2, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_TEX0);
+ glNormalPointer( GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_NORMAL);
+ }
+
+ S32 name = avatarp->mDrawable->getVObj()->mGLName;
+ LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name);
+ glColor4ubv(color.mV);
+
+ if ((mVertexShaderLevel > 0) && !gUseGLPick) // for hardware blending
+ {
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer(0);
+ }
+
+ // render rigid meshes (eyeballs) first
+ mIndicesDrawn += avatarp->renderRigid();
+
+ if ((mVertexShaderLevel > 0) && !gUseGLPick) // for hardware blending
+ {
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ bindGLTexCoordPointer(0);
+ vertex_program->bind();
+ bindGLVertexWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ /*if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ bindGLVertexClothingWeightPointer();
+ enable_cloth_weights();
+ }*/
+ enable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ }
+
+ mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+
+ // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+ if (!(mVertexShaderLevel > 0) || gUseGLPick)
+ {
+ // want for the previously bound fence to finish
+ gPipeline.bufferSendFence();
+ }
+ else
+ {
+ vertex_program->unbind();
+ disable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+
+ /*if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ disable_cloth_weights();
+ }*/
+ }
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // restore texture mode
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+//-----------------------------------------------------------------------------
+// getDebugTexture()
+//-----------------------------------------------------------------------------
+LLViewerImage *LLDrawPoolAvatar::getDebugTexture()
+{
+ if (mReferences.empty())
+ {
+ return NULL;
+ }
+ LLFace *face = mReferences[0];
+ if (!face->getDrawable())
+ {
+ return NULL;
+ }
+ const LLViewerObject *objectp = face->getDrawable()->getVObj();
+
+ // Avatar should always have at least 1 (maybe 3?) TE's.
+ return objectp->getTEImage(0);
+}
+
+
+LLColor3 LLDrawPoolAvatar::getDebugColor() const
+{
+ return LLColor3(0.f, 1.f, 0.f);
+}
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
new file mode 100644
index 0000000000..0d706b012b
--- /dev/null
+++ b/indra/newview/lldrawpoolavatar.h
@@ -0,0 +1,54 @@
+/**
+ * @file lldrawpoolavatar.h
+ * @brief LLDrawPoolAvatar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLAVATAR_H
+#define LL_LLDRAWPOOLAVATAR_H
+
+#include "lldrawpool.h"
+
+class LLVOAvatar;
+
+class LLDrawPoolAvatar : public LLDrawPool
+{
+protected:
+ S32 mNumFaces;
+public:
+ enum
+ {
+ SHADER_LEVEL_BUMP = 2,
+ SHADER_LEVEL_CLOTH = 3
+ };
+
+ LLDrawPoolAvatar();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ S32 rebuild();
+
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ virtual S32 getMaterialAttribIndex() { return 0; }
+
+ void renderAvatars(LLVOAvatar *single_avatar, BOOL no_shaders = FALSE); // renders only one avatar if single_avatar is not null.
+};
+
+
+
+extern S32 AVATAR_OFFSET_POS;
+extern S32 AVATAR_OFFSET_NORMAL;
+extern S32 AVATAR_OFFSET_TEX0;
+extern S32 AVATAR_OFFSET_TEX1;
+extern S32 AVATAR_VERTEX_BYTES;
+const S32 AVATAR_BUFFER_ELEMENTS = 8192; // Needs to be enough to store all avatar vertices.
+
+extern BOOL gAvatarEmbossBumpMap;
+#endif // LL_LLDRAWPOOLAVATAR_H
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
new file mode 100644
index 0000000000..b9d1cb17a0
--- /dev/null
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -0,0 +1,1135 @@
+/**
+ * @file lldrawpoolbump.cpp
+ * @brief LLDrawPoolBump class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolbump.h"
+
+#include "llstl.h"
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llimagegl.h"
+#include "m3math.h"
+#include "m4math.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llcubemap.h"
+#include "lldrawable.h"
+#include "lldrawpoolsimple.h"
+#include "llface.h"
+#include "llgl.h"
+#include "llsky.h"
+#include "lltextureentry.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "pipeline.h"
+
+
+//#include "llimagebmp.h"
+//#include "../tools/imdebug/imdebug.h"
+
+// static
+LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT];
+
+// static
+U32 LLStandardBumpmap::sStandardBumpmapCount = 0;
+
+// static
+LLBumpImageList gBumpImageList;
+
+const S32 STD_BUMP_LATEST_FILE_VERSION = 1;
+
+S32 LLDrawPoolBump::sBumpTex = -1;
+S32 LLDrawPoolBump::sDiffTex = -1;
+S32 LLDrawPoolBump::sEnvTex = -1;
+
+// static
+void LLStandardBumpmap::init()
+{
+ LLStandardBumpmap::restoreGL();
+}
+
+// static
+void LLStandardBumpmap::shutdown()
+{
+ LLStandardBumpmap::destroyGL();
+}
+
+// static
+void LLStandardBumpmap::restoreGL()
+{
+ llassert( LLStandardBumpmap::sStandardBumpmapCount == 0 );
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("None"); // BE_NO_BUMP
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Brightness"); // BE_BRIGHTNESS
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Darkness"); // BE_DARKNESS
+
+ std::string file_name = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "std_bump.ini" );
+ FILE* file = LLFile::fopen( file_name.c_str(), "rt" );
+ if( !file )
+ {
+ llwarns << "Could not open std_bump <" << file_name << ">" << llendl;
+ return;
+ }
+
+ S32 file_version = 0;
+
+ S32 fields_read = fscanf( file, "LLStandardBumpmap version %d", &file_version );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad LLStandardBumpmap header" << llendl;
+ return;
+ }
+
+ if( file_version > STD_BUMP_LATEST_FILE_VERSION )
+ {
+ llwarns << "LLStandardBumpmap has newer version (" << file_version << ") than viewer (" << STD_BUMP_LATEST_FILE_VERSION << ")" << llendl;
+ return;
+ }
+
+ while( !feof(file) && (LLStandardBumpmap::sStandardBumpmapCount < (U32)TEM_BUMPMAP_COUNT) )
+ {
+ char label[2048] = "";
+ char bump_file[2048] = "";
+ fields_read = fscanf( file, "\n%s %s", label, bump_file);
+ if( EOF == fields_read )
+ {
+ break;
+ }
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad LLStandardBumpmap entry" << llendl;
+ return;
+ }
+
+ llinfos << "Loading bumpmap: " << bump_file << " from viewerart" << llendl;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage = gImageList.getImage( LLUUID(gViewerArt.getString(bump_file)) );
+ LLStandardBumpmap::sStandardBumpmapCount++;
+ }
+
+ fclose( file );
+}
+
+// static
+void LLStandardBumpmap::destroyGL()
+{
+ for( U32 i = 0; i < LLStandardBumpmap::sStandardBumpmapCount; i++ )
+ {
+ gStandardBumpmapList[i].mLabel.assign("");
+ gStandardBumpmapList[i].mImage = NULL;
+ }
+ sStandardBumpmapCount = 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////
+
+LLDrawPoolBump::LLDrawPoolBump(LLViewerImage *texturep) :
+ LLDrawPool(POOL_BUMP, DATA_BUMP_IL_MASK | DATA_COLORS_MASK, DATA_SIMPLE_NIL_MASK),
+ mTexturep(texturep)
+{
+}
+
+LLDrawPool *LLDrawPoolBump::instancePool()
+{
+ return new LLDrawPoolBump(mTexturep);
+}
+
+
+void LLDrawPoolBump::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+BOOL LLDrawPoolBump::match(LLFace* last_face, LLFace* facep)
+{
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES) &&
+ !last_face->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ !facep->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ facep->getIndicesStart() == last_face->getIndicesStart()+last_face->getIndicesCount() &&
+ facep->getRenderColor() == last_face->getRenderColor() &&
+ facep->getTextureEntry()->getShiny() == last_face->getTextureEntry()->getShiny() &&
+ facep->getTextureEntry()->getBumpmap() == last_face->getTextureEntry()->getBumpmap())
+ {
+ if (facep->isState(LLFace::GLOBAL))
+ {
+ if (last_face->isState(LLFace::GLOBAL))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (!last_face->isState(LLFace::GLOBAL))
+ {
+ if (last_face->getRenderMatrix() == facep->getRenderMatrix())
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+// static
+S32 LLDrawPoolBump::numBumpPasses()
+{
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0)
+ {
+ return 1; // single pass for shaders
+ }
+ else
+ {
+ if (gSavedSettings.getBOOL("RenderObjectBump"))
+ return 3;
+ else
+ return 1;
+ }
+}
+
+S32 LLDrawPoolBump::getNumPasses()
+{
+ return numBumpPasses();
+}
+
+void LLDrawPoolBump::beginRenderPass(S32 pass)
+{
+ switch( pass )
+ {
+ case 0:
+ beginPass0(this);
+ break;
+ case 1:
+ beginPass1();
+ break;
+ case 2:
+ beginPass2();
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+void LLDrawPoolBump::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
+ if (!mTexturep)
+ {
+ return;
+ }
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ const U32* index_array = getRawIndices();
+
+ S32 indices = 0;
+ switch( pass )
+ {
+ case 0:
+ {
+ stop_glerror();
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ bindGLColorPointer();
+ }
+
+ stop_glerror();
+
+ LLGLState alpha_test(GL_ALPHA_TEST, FALSE);
+ LLGLState blend(GL_BLEND, FALSE);
+ LLViewerImage* tex = getTexture();
+ if (tex && tex->getPrimaryFormat() == GL_ALPHA)
+ {
+ // Enable Invisibility Hack
+ alpha_test.enable();
+ blend.enable();
+ }
+ indices += renderPass0(this, mDrawFace, index_array, mTexturep);
+ break;
+ }
+ case 1:
+ {
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ indices += renderPass1(mDrawFace, index_array, mTexturep);
+ break;
+ }
+ case 2:
+ {
+ bindGLVertexPointer();
+ // Texture unit 0
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ bindGLTexCoordPointer();
+ // Texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ bindGLTexCoordPointer(1);
+ indices += renderPass2(mDrawFace, index_array, mTexturep);
+ break;
+ }
+ default:
+ {
+ llassert(0);
+ break;
+ }
+ }
+ mIndicesDrawn += indices;
+}
+
+void LLDrawPoolBump::endRenderPass(S32 pass)
+{
+ switch( pass )
+ {
+ case 0:
+ endPass0(this);
+ break;
+ case 1:
+ endPass1();
+ break;
+ case 2:
+ endPass2();
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+//static
+void LLDrawPoolBump::beginPass0(LLDrawPool* pool)
+{
+ stop_glerror();
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ enable_binormals(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
+
+ sEnvTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (sEnvTex >= 0 && cube_map)
+ {
+ cube_map->bind();
+ cube_map->setMatrix(1);
+ }
+
+ sBumpTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ sDiffTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ S32 scatterTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+ }
+ stop_glerror();
+}
+
+//static
+S32 LLDrawPoolBump::renderPass0(LLDrawPool* pool, face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+{
+ if (!tex)
+ {
+ return 0;
+ }
+
+ if (face_list.empty())
+ {
+ return 0;
+ }
+
+ stop_glerror();
+
+ S32 res = 0;
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
+ pool->bindGLBinormalPointer(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
+
+ LLViewerImage::bindTexture(tex, sDiffTex);
+
+ //single pass shader driven shiny/bump
+ LLGLDisable(GL_ALPHA_TEST);
+
+ LLViewerImage::sWhiteImagep->bind(sBumpTex);
+
+ GLfloat alpha[4] =
+ {
+ 0.00f,
+ 0.25f,
+ 0.5f,
+ 0.75f
+ };
+
+ LLImageGL* last_bump = NULL;
+
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+ if (te)
+ {
+ U8 index = te->getShiny();
+ LLColor4 col = te->getColor();
+
+ gPipeline.mObjectBumpProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,
+ col.mV[0], col.mV[1], col.mV[2], alpha[index]);
+ gPipeline.mObjectBumpProgram.vertexAttrib4f(LLPipeline::GLSL_SPECULAR_COLOR,
+ alpha[index], alpha[index], alpha[index], alpha[index]);
+
+ LLImageGL* bump = getBumpMap(te, tex);
+ if (bump != last_bump)
+ {
+ if (bump)
+ {
+ bump->bind(sBumpTex);
+ }
+ else
+ {
+ LLViewerImage::sWhiteImagep->bind(sBumpTex);
+ }
+ }
+ last_bump = bump;
+
+ // Draw the geometry
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ stop_glerror();
+ }
+ else
+ {
+ llwarns << "DrawPoolBump has face with invalid texture entry." << llendl;
+ }
+ }
+ }
+ else
+ {
+ LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE);
+ LLViewerImage::bindTexture(tex);
+ res = LLDrawPool::drawLoop(face_list, index_array);
+ }
+ return res;
+}
+
+//static
+void LLDrawPoolBump::endPass0(LLDrawPool* pool)
+{
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (sEnvTex >= 0 && cube_map)
+ {
+ cube_map->restoreMatrix();
+ }
+
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ disable_binormals(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+}
+
+
+//static
+void LLDrawPoolBump::beginPass1()
+{
+ // Second pass: environment map
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if( cube_map )
+ {
+ cube_map->enable(0);
+ cube_map->setMatrix(0);
+ cube_map->bind();
+ }
+}
+
+//static
+S32 LLDrawPoolBump::renderPass1(face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0) //everything happens in pass0
+ {
+ return 0;
+ }
+
+ S32 res = 0;
+ if( gSky.mVOSkyp->getCubeMap() )
+ {
+ //LLGLSPipelineAlpha gls;
+ //LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_EQUAL);
+ LLGLEnable blend_enable(GL_BLEND);
+
+ GLfloat alpha[4] =
+ {
+ 0.00f,
+ 0.25f,
+ 0.5f,
+ 0.75f
+ };
+
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+ if (te)
+ {
+ U8 index = te->getShiny();
+ if( index > 0 )
+ {
+ LLOverrideFaceColor override_color(facep->getPool(), 1, 1, 1, alpha[index]);
+
+ // Draw the geometry
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ stop_glerror();
+ }
+ }
+ else
+ {
+ llwarns << "DrawPoolBump has face with invalid texture entry." << llendl;
+ }
+ }
+ }
+ return res;
+}
+
+//static
+void LLDrawPoolBump::endPass1()
+{
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if( cube_map )
+ {
+ cube_map->disable();
+ cube_map->restoreMatrix();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+}
+
+
+// static
+LLImageGL* LLDrawPoolBump::getBumpMap(const LLTextureEntry* te, LLViewerImage* tex)
+{
+ U32 bump_code = te->getBumpmap();
+ LLImageGL* bump = NULL;
+
+ switch( bump_code )
+ {
+ case BE_NO_BUMP:
+ bump = NULL;
+ break;
+ case BE_BRIGHTNESS:
+ case BE_DARKNESS:
+ if( tex )
+ {
+ bump = gBumpImageList.getBrightnessDarknessImage( tex, bump_code );
+ }
+ break;
+
+ default:
+ if( bump_code < LLStandardBumpmap::sStandardBumpmapCount )
+ {
+ bump = gStandardBumpmapList[bump_code].mImage;
+ }
+ break;
+ }
+
+ return bump;
+}
+
+//static
+void LLDrawPoolBump::beginPass2()
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
+ // Optional third pass: emboss bump map
+ stop_glerror();
+
+ // TEXTURE UNIT 0
+ // Output.rgb = texture at texture coord 0
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_ALPHA);
+
+ // Don't care about alpha output
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ // TEXTURE UNIT 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD_SIGNED_ARB);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Don't care about alpha output
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ // src = tex0 + (1 - tex1) - 0.5
+ // = (bump0/2 + 0.5) + (1 - (bump1/2 + 0.5)) - 0.5
+ // = (1 + bump0 - bump1) / 2
+
+
+ // Blend: src * dst + dst * src
+ // = 2 * src * dst
+ // = 2 * ((1 + bump0 - bump1) / 2) * dst [0 - 2 * dst]
+ // = (1 + bump0 - bump1) * dst.rgb
+ // = dst.rgb + dst.rgb * (bump0 - bump1)
+ glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+// glBlendFunc(GL_ONE, GL_ZERO); // temp
+
+ stop_glerror();
+}
+
+//static
+S32 LLDrawPoolBump::renderPass2(face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+{
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0) //everything happens in pass0
+ {
+ return 0;
+ }
+
+ LLGLDisable fog(GL_FOG);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_EQUAL);
+ LLGLEnable tex2d(GL_TEXTURE_2D);
+ LLGLEnable blend(GL_BLEND);
+ S32 res = 0;
+
+ LLImageGL* last_bump = NULL;
+
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+ LLOverrideFaceColor override_color(facep->getPool(), 1,1,1,1);
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+ LLImageGL* bump = getBumpMap(te, tex);
+
+ if( bump )
+ {
+ if( bump != last_bump )
+ {
+ last_bump = bump;
+
+ // Texture unit 0
+ bump->bind(0);
+ stop_glerror();
+
+ // Texture unit 1
+ bump->bind(1);
+ stop_glerror();
+ }
+
+ // Draw the geometry
+ res += facep->renderIndexed(index_array);
+ stop_glerror();
+ }
+ else
+ {
+// llwarns << "Skipping invalid bump code " << (S32) te->getBumpmap() << llendl;
+ }
+ }
+ return res;
+}
+
+//static
+void LLDrawPoolBump::endPass2()
+{
+ // Disable texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // Disable texture unit 0
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+}
+
+
+void LLDrawPoolBump::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+
+ bindGLVertexPointer();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->getDrawable() && !facep->getDrawable()->isDead() && (facep->getViewerObject()->mGLName))
+ {
+ facep->renderForSelect();
+ }
+ }
+}
+
+
+void LLDrawPoolBump::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ facep->renderSelected(image, color, index_offset, index_count);
+}
+
+
+void LLDrawPoolBump::dirtyTexture(const LLViewerImage *texturep)
+{
+ if (mTexturep == texturep)
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ gPipeline.markTextured(facep->getDrawable());
+ }
+ }
+}
+
+LLViewerImage *LLDrawPoolBump::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolBump::getDebugTexture()
+{
+ return mTexturep;
+}
+
+LLColor3 LLDrawPoolBump::getDebugColor() const
+{
+ return LLColor3(1.f, 1.f, 0.f);
+}
+
+////////////////////////////////////////////////////////////////
+// List of one-component bump-maps created from other texures.
+
+
+//const LLUUID TEST_BUMP_ID("3d33eaf2-459c-6f97-fd76-5fce3fc29447");
+
+void LLBumpImageList::init()
+{
+ llassert( mBrightnessEntries.size() == 0 );
+ llassert( mDarknessEntries.size() == 0 );
+
+ LLStandardBumpmap::init();
+}
+
+void LLBumpImageList::shutdown()
+{
+ mBrightnessEntries.clear();
+ mDarknessEntries.clear();
+ LLStandardBumpmap::shutdown();
+}
+
+void LLBumpImageList::destroyGL()
+{
+ mBrightnessEntries.clear();
+ mDarknessEntries.clear();
+ LLStandardBumpmap::destroyGL();
+}
+
+void LLBumpImageList::restoreGL()
+{
+ // Images will be recreated as they are needed.
+ LLStandardBumpmap::restoreGL();
+}
+
+
+LLBumpImageList::~LLBumpImageList()
+{
+ // Shutdown should have already been called.
+ llassert( mBrightnessEntries.size() == 0 );
+ llassert( mDarknessEntries.size() == 0 );
+}
+
+
+// Note: Does nothing for entries in gStandardBumpmapList that are not actually standard bump images (e.g. none, brightness, and darkness)
+void LLBumpImageList::addTextureStats(U8 bump, const LLUUID& base_image_id,
+ F32 pixel_area, F32 texel_area_ratio, F32 cos_center_angle)
+{
+ bump &= TEM_BUMP_MASK;
+ LLViewerImage* bump_image = gStandardBumpmapList[bump].mImage;
+ if( bump_image )
+ {
+ bump_image->addTextureStats(pixel_area, texel_area_ratio, cos_center_angle);
+ }
+}
+
+
+void LLBumpImageList::updateImages()
+{
+ for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); )
+ {
+ bump_image_map_t::iterator curiter = iter++;
+ LLImageGL* image = curiter->second;
+ if( image )
+ {
+ BOOL destroy = TRUE;
+ if( image->getHasGLTexture())
+ {
+ if( image->getBoundRecently() )
+ {
+ destroy = FALSE;
+ }
+ else
+ {
+ image->destroyGLTexture();
+ }
+ }
+
+ if( destroy )
+ {
+ //llinfos << "*** Destroying bright " << (void*)image << llendl;
+ mBrightnessEntries.erase(curiter); // deletes the image thanks to reference counting
+ }
+ }
+ }
+
+ for (bump_image_map_t::iterator iter = mDarknessEntries.begin(); iter != mDarknessEntries.end(); )
+ {
+ bump_image_map_t::iterator curiter = iter++;
+ LLImageGL* image = curiter->second;
+ if( image )
+ {
+ BOOL destroy = TRUE;
+ if( image->getHasGLTexture())
+ {
+ if( image->getBoundRecently() )
+ {
+ destroy = FALSE;
+ }
+ else
+ {
+ image->destroyGLTexture();
+ }
+ }
+
+ if( destroy )
+ {
+ //llinfos << "*** Destroying dark " << (void*)image << llendl;;
+ mDarknessEntries.erase(curiter); // deletes the image thanks to reference counting
+ }
+ }
+ }
+
+}
+
+
+// Note: the caller SHOULD NOT keep the pointer that this function returns. It may be updated as more data arrives.
+LLImageGL* LLBumpImageList::getBrightnessDarknessImage(LLViewerImage* src_image, U8 bump_code )
+{
+ llassert( (bump_code == BE_BRIGHTNESS) || (bump_code == BE_DARKNESS) );
+
+ LLImageGL* bump = NULL;
+ const F32 BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD = 1000;
+ if( src_image->mMaxVirtualSize > BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD )
+ {
+ bump_image_map_t* entries_list = NULL;
+ void (*callback_func)( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata ) = NULL;
+
+ switch( bump_code )
+ {
+ case BE_BRIGHTNESS:
+ entries_list = &mBrightnessEntries;
+ callback_func = LLBumpImageList::onSourceBrightnessLoaded;
+ break;
+ case BE_DARKNESS:
+ entries_list = &mDarknessEntries;
+ callback_func = LLBumpImageList::onSourceDarknessLoaded;
+ break;
+ default:
+ llassert(0);
+ return NULL;
+ }
+
+ bump_image_map_t::iterator iter = entries_list->find(src_image->getID());
+ if (iter != entries_list->end())
+ {
+ bump = iter->second;
+ }
+ else
+ {
+ LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,1);
+ raw->clear(0x77, 0x77, 0x77, 0xFF);
+ bump = new LLImageGL( raw, TRUE);
+ bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
+ (*entries_list)[src_image->getID()] = bump;
+
+ // Note: this may create an LLImageGL immediately
+ src_image->setLoadedCallback( callback_func, 0, TRUE, new LLUUID(src_image->getID()) );
+ bump = (*entries_list)[src_image->getID()]; // In case callback was called immediately and replaced the image
+
+// bump_total++;
+// llinfos << "*** Creating " << (void*)bump << " " << bump_total << llendl;
+ }
+ }
+
+ return bump;
+}
+
+
+// static
+void LLBumpImageList::onSourceBrightnessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ LLUUID* source_asset_id = (LLUUID*)userdata;
+ LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_BRIGHTNESS );
+ if( final )
+ {
+ delete source_asset_id;
+ }
+}
+
+// static
+void LLBumpImageList::onSourceDarknessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ LLUUID* source_asset_id = (LLUUID*)userdata;
+ LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_DARKNESS );
+ if( final )
+ {
+ delete source_asset_id;
+ }
+}
+
+
+// static
+void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump_code )
+{
+ if( success )
+ {
+ bump_image_map_t& entries_list(bump_code == BE_BRIGHTNESS ? gBumpImageList.mBrightnessEntries : gBumpImageList.mDarknessEntries );
+ bump_image_map_t::iterator iter = entries_list.find(source_asset_id);
+ if (iter != entries_list.end())
+ {
+ LLPointer<LLImageRaw> dst_image = new LLImageRaw(src->getWidth(), src->getHeight(), 1);
+ U8* dst_data = dst_image->getData();
+ S32 dst_data_size = dst_image->getDataSize();
+
+ U8* src_data = src->getData();
+ S32 src_data_size = src->getDataSize();
+
+ S32 src_components = src->getComponents();
+
+ // Convert to luminance and then scale and bias that to get ready for
+ // embossed bump mapping. (0-255 maps to 127-255)
+
+ // Convert to fixed point so we don't have to worry about precision/clamping.
+ const S32 FIXED_PT = 8;
+ const S32 R_WEIGHT = S32(0.2995f * (1<<FIXED_PT));
+ const S32 G_WEIGHT = S32(0.5875f * (1<<FIXED_PT));
+ const S32 B_WEIGHT = S32(0.1145f * (1<<FIXED_PT));
+
+ S32 minimum = 255;
+ S32 maximum = 0;
+
+ switch( src_components )
+ {
+ case 1:
+ case 2:
+ if( src_data_size == dst_data_size * src_components )
+ {
+ for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+ {
+ dst_data[i] = src_data[j];
+ if( dst_data[i] < minimum )
+ {
+ minimum = dst_data[i];
+ }
+ if( dst_data[i] > maximum )
+ {
+ maximum = dst_data[i];
+ }
+ }
+ }
+ else
+ {
+ llassert(0);
+ dst_image->clear();
+ }
+ break;
+ case 3:
+ case 4:
+ if( src_data_size == dst_data_size * src_components )
+ {
+ for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+ {
+ // RGB to luminance
+ dst_data[i] = (R_WEIGHT * src_data[j] + G_WEIGHT * src_data[j+1] + B_WEIGHT * src_data[j+2]) >> FIXED_PT;
+ //llassert( dst_data[i] <= 255 );true because it's 8bit
+ if( dst_data[i] < minimum )
+ {
+ minimum = dst_data[i];
+ }
+ if( dst_data[i] > maximum )
+ {
+ maximum = dst_data[i];
+ }
+ }
+ }
+ else
+ {
+ llassert(0);
+ dst_image->clear();
+ }
+ break;
+ default:
+ llassert(0);
+ dst_image->clear();
+ break;
+ }
+
+ if( maximum > minimum )
+ {
+ U8 bias_and_scale_lut[256];
+ F32 twice_one_over_range = 2.f / (maximum - minimum);
+ S32 i;
+
+ const F32 ARTIFICIAL_SCALE = 2.f; // Advantage: exagerates the effect in midrange. Disadvantage: clamps at the extremes.
+ if( BE_DARKNESS == bump_code )
+ {
+ for( i = minimum; i <= maximum; i++ )
+ {
+ F32 minus_one_to_one = F32(maximum - i) * twice_one_over_range - 1.f;
+ bias_and_scale_lut[i] = llclampb(llround(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128));
+ }
+ }
+ else
+ {
+ // BE_LIGHTNESS
+ for( i = minimum; i <= maximum; i++ )
+ {
+ F32 minus_one_to_one = F32(i - minimum) * twice_one_over_range - 1.f;
+ bias_and_scale_lut[i] = llclampb(llround(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128));
+ }
+ }
+
+ for( i = 0; i < dst_data_size; i++ )
+ {
+ dst_data[i] = bias_and_scale_lut[dst_data[i]];
+ }
+ }
+
+ LLImageGL* bump = new LLImageGL( TRUE);
+ bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
+ bump->createGLTexture(0, dst_image);
+ iter->second = bump; // derefs (and deletes) old image
+ }
+ else
+ {
+ // entry should have been added in LLBumpImageList::getImage().
+
+ // Not a legit assertion - the bump texture could have been flushed by the bump image manager
+ //llassert(0);
+ }
+ }
+}
+
+S32 LLDrawPoolBump::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
+
+// virtual
+void LLDrawPoolBump::enableShade()
+{
+ glDisableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolBump::disableShade()
+{
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolBump::setShade(F32 shade)
+{
+ glColor4f(0,0,0,shade);
+}
diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h
new file mode 100644
index 0000000000..b74acb4561
--- /dev/null
+++ b/indra/newview/lldrawpoolbump.h
@@ -0,0 +1,141 @@
+/**
+ * @file lldrawpoolbump.h
+ * @brief LLDrawPoolBump class definition
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLBUMP_H
+#define LL_LLDRAWPOOLBUMP_H
+
+#include "lldrawpool.h"
+#include "llstring.h"
+#include "lltextureentry.h"
+#include "lluuid.h"
+
+class LLImageRaw;
+
+class LLDrawPoolBump : public LLDrawPool
+{
+protected:
+ LLPointer<LLViewerImage> mTexturep; // The primary texture, not the bump texture
+
+public:
+ LLDrawPoolBump(LLViewerImage *texturep);
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void beginRenderPass( S32 pass );
+ /*virtual*/ void endRenderPass( S32 pass );
+ /*virtual*/ S32 getNumPasses();
+ /*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+ /*virtual*/ BOOL match(LLFace* last_face, LLFace* facep);
+
+ virtual S32 getMaterialAttribIndex();
+ static S32 numBumpPasses();
+
+ static void beginPass0(LLDrawPool* pool);
+ static S32 renderPass0(LLDrawPool* pool, face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
+ static void endPass0(LLDrawPool* pool);
+
+ static void beginPass1();
+ static S32 renderPass1(face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
+ static void endPass1();
+
+ static void beginPass2();
+ static S32 renderPass2(face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
+ static void endPass2();
+
+ /*virtual*/ void enableShade();
+ /*virtual*/ void disableShade();
+ /*virtual*/ void setShade(F32 shade);
+
+protected:
+ static LLImageGL* getBumpMap(const LLTextureEntry* te, LLViewerImage* tex);
+
+public:
+ static S32 sBumpTex;
+ static S32 sDiffTex;
+ static S32 sEnvTex;
+};
+
+enum EBumpEffect
+{
+ BE_NO_BUMP = 0,
+ BE_BRIGHTNESS = 1,
+ BE_DARKNESS = 2,
+ BE_STANDARD_0 = 3, // Standard must always be the last one
+ BE_COUNT = 4
+};
+
+////////////////////////////////////////////////////////////////
+// List of standard bumpmaps that are specificed by LLTextureEntry::mBump's lower bits
+
+class LLStandardBumpmap
+{
+public:
+ LLStandardBumpmap() : mLabel("") {}
+ LLStandardBumpmap( const char* label ) : mLabel(label) {}
+
+ LLString mLabel;
+ LLPointer<LLViewerImage> mImage;
+
+ static U32 sStandardBumpmapCount; // Number of valid values in gStandardBumpmapList[]
+
+ static void init();
+ static void shutdown();
+ static void restoreGL();
+ static void destroyGL();
+};
+
+extern LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT];
+
+////////////////////////////////////////////////////////////////
+// List of one-component bump-maps created from other texures.
+
+struct LLBumpImageEntry;
+
+class LLBumpImageList
+{
+public:
+ LLBumpImageList() {}
+ ~LLBumpImageList();
+
+ void init();
+ void shutdown();
+ void destroyGL();
+ void restoreGL();
+ void updateImages();
+
+
+ LLImageGL* getBrightnessDarknessImage(LLViewerImage* src_image, U8 bump_code);
+// LLImageGL* getTestImage();
+ void addTextureStats(U8 bump, const LLUUID& base_image_id,
+ F32 pixel_area, F32 texel_area_ratio, F32 cos_center_angle);
+
+ static void onSourceBrightnessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+ static void onSourceDarknessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+
+private:
+ static void onSourceLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump );
+
+private:
+ typedef std::map<LLUUID, LLPointer<LLImageGL> > bump_image_map_t;
+ bump_image_map_t mBrightnessEntries;
+ bump_image_map_t mDarknessEntries;
+};
+
+extern LLBumpImageList gBumpImageList;
+
+
+
+#endif // LL_LLDRAWPOOLBUMP_H
diff --git a/indra/newview/lldrawpoolclouds.cpp b/indra/newview/lldrawpoolclouds.cpp
new file mode 100644
index 0000000000..c279f085d5
--- /dev/null
+++ b/indra/newview/lldrawpoolclouds.cpp
@@ -0,0 +1,87 @@
+/**
+ * @file lldrawpoolclouds.cpp
+ * @brief LLDrawPoolClouds class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolclouds.h"
+
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llvoclouds.h"
+#include "pipeline.h"
+
+LLDrawPoolClouds::LLDrawPoolClouds() :
+ LLDrawPool(POOL_CLOUDS, DATA_SIMPLE_IL_MASK, 0)
+{
+}
+
+LLDrawPool *LLDrawPoolClouds::instancePool()
+{
+ return new LLDrawPoolClouds();
+}
+
+void LLDrawPoolClouds::enqueue(LLFace *facep)
+{
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ mMoveFace.put(facep);
+ }
+ else
+ {
+ mDrawFace.push_back(facep);
+ }
+ facep->mDistance = (facep->mCenterAgent - gCamera->getOrigin()) * gCamera->getAtAxis();
+}
+
+void LLDrawPoolClouds::beginRenderPass(S32 pass)
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+}
+
+void LLDrawPoolClouds::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+}
+
+void LLDrawPoolClouds::render(S32 pass)
+{
+ LLFastTimer ftm(LLFastTimer::FTM_RENDER_CLOUDS);
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
+ {
+ return;
+ }
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ glAlphaFunc(GL_GREATER,0.01f);
+
+ gPipeline.enableLightsFullbright(LLColor4(1.f,1.f,1.f));
+
+ mDrawFace[0]->bindTexture();
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+
+ std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater());
+
+ drawLoop();
+}
+
+
+void LLDrawPoolClouds::renderForSelect()
+{
+}
diff --git a/indra/newview/lldrawpoolclouds.h b/indra/newview/lldrawpoolclouds.h
new file mode 100644
index 0000000000..d333444400
--- /dev/null
+++ b/indra/newview/lldrawpoolclouds.h
@@ -0,0 +1,28 @@
+/**
+ * @file lldrawpoolclouds.h
+ * @brief LLDrawPoolClouds class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLCLOUDS_H
+#define LL_LLDRAWPOOLCLOUDS_H
+
+#include "lldrawpool.h"
+
+class LLDrawPoolClouds : public LLDrawPool
+{
+public:
+ LLDrawPoolClouds();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ LLDrawPool *instancePool();
+ /*virtual*/ void enqueue(LLFace *face);
+ /*virtual*/ void beginRenderPass(S32 pass);
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+ virtual S32 getMaterialAttribIndex() { return 0; }
+};
+
+#endif // LL_LLDRAWPOOLSKY_H
diff --git a/indra/newview/lldrawpoolground.cpp b/indra/newview/lldrawpoolground.cpp
new file mode 100644
index 0000000000..91e92bab6e
--- /dev/null
+++ b/indra/newview/lldrawpoolground.cpp
@@ -0,0 +1,85 @@
+/**
+ * @file lldrawpoolground.cpp
+ * @brief LLDrawPoolGround class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolground.h"
+
+#include "llviewercontrol.h"
+
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "pipeline.h"
+
+LLDrawPoolGround::LLDrawPoolGround() :
+ LLDrawPool(POOL_GROUND, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+{
+}
+
+LLDrawPool *LLDrawPoolGround::instancePool()
+{
+ return new LLDrawPoolGround();
+}
+
+void LLDrawPoolGround::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+}
+
+void LLDrawPoolGround::render(S32 pass)
+{
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ LLGLSPipelineSkyBox gls_skybox;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ glMatrixMode( GL_PROJECTION );
+
+ glPushMatrix();
+ gViewerWindow->setup3DRender();
+
+ glMatrixMode(GL_MODELVIEW);
+
+ LLGLState tex2d(GL_TEXTURE_2D, (mVertexShaderLevel > 0) ? TRUE : FALSE);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), 0);
+
+ LLFace *facep = mDrawFace[0];
+
+ if (!(mVertexShaderLevel > 0))
+ {
+ gPipeline.disableLights();
+ }
+
+ glColor4fv(facep->getFaceColor().mV);
+
+ facep->renderIndexed(getRawIndices());
+
+ glMatrixMode( GL_PROJECTION );
+ glPopMatrix();
+ glMatrixMode( GL_MODELVIEW );
+}
+
+void LLDrawPoolGround::renderForSelect()
+{
+}
+
diff --git a/indra/newview/lldrawpoolground.h b/indra/newview/lldrawpoolground.h
new file mode 100644
index 0000000000..8b2dbf4353
--- /dev/null
+++ b/indra/newview/lldrawpoolground.h
@@ -0,0 +1,28 @@
+/**
+ * @file lldrawpoolground.h
+ * @brief LLDrawPoolGround class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLGROUND_H
+#define LL_LLDRAWPOOLGROUND_H
+
+#include "lldrawpool.h"
+
+
+class LLDrawPoolGround : public LLDrawPool
+{
+public:
+ LLDrawPoolGround();
+
+ /*virtual*/ LLDrawPool *instancePool();
+ virtual S32 getMaterialAttribIndex() { return 0; }
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+};
+
+#endif // LL_LLDRAWPOOLGROUND_H
diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp
new file mode 100644
index 0000000000..600e4b1fc6
--- /dev/null
+++ b/indra/newview/lldrawpoolsimple.cpp
@@ -0,0 +1,227 @@
+/**
+ * @file lldrawpoolsimple.cpp
+ * @brief LLDrawPoolSimple class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolsimple.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "pipeline.h"
+
+S32 LLDrawPoolSimple::sDiffTex = 0;
+
+LLDrawPoolSimple::LLDrawPoolSimple(LLViewerImage *texturep) :
+ LLDrawPool(POOL_SIMPLE,
+ DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK,
+ DATA_SIMPLE_NIL_MASK), // ady temp
+ mTexturep(texturep)
+{
+}
+
+LLDrawPool *LLDrawPoolSimple::instancePool()
+{
+ return new LLDrawPoolSimple(mTexturep);
+}
+
+BOOL LLDrawPoolSimple::match(LLFace* last_face, LLFace* facep)
+{
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES) &&
+ !last_face->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ !facep->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ facep->getIndicesStart() == last_face->getIndicesStart()+last_face->getIndicesCount() &&
+ facep->getRenderColor() == last_face->getRenderColor())
+ {
+ if (facep->isState(LLFace::GLOBAL))
+ {
+ if (last_face->isState(LLFace::GLOBAL))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (!last_face->isState(LLFace::GLOBAL))
+ {
+ if (last_face->getRenderMatrix() == facep->getRenderMatrix())
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+void LLDrawPoolSimple::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+void LLDrawPoolSimple::beginRenderPass(S32 pass)
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+
+ if (mVertexShaderLevel > 0)
+ {
+ S32 scatterTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+ sDiffTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ }
+}
+
+
+void LLDrawPoolSimple::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE);
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ bindGLColorPointer();
+ }
+
+ LLViewerImage* tex = getTexture();
+ LLGLState alpha_test(GL_ALPHA_TEST, FALSE);
+ LLGLState blend(GL_BLEND, FALSE);
+
+ if (tex)
+ {
+ LLViewerImage::bindTexture(tex,sDiffTex);
+ if (tex->getPrimaryFormat() == GL_ALPHA)
+ {
+ // Enable Invisibility Hack
+ alpha_test.enable();
+ blend.enable();
+ }
+ }
+ else
+ {
+ LLImageGL::unbindTexture(sDiffTex, GL_TEXTURE_2D);
+ }
+
+ drawLoop();
+}
+
+void LLDrawPoolSimple::endRenderPass(S32 pass)
+{
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glDisableClientState(GL_COLOR_ARRAY);
+ }
+}
+
+void LLDrawPoolSimple::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+
+ bindGLVertexPointer();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ LLDrawable *drawable = facep->getDrawable();
+ if (drawable && !drawable->isDead() && (facep->getViewerObject()->mGLName))
+ {
+ facep->renderForSelect();
+ }
+ }
+}
+
+
+void LLDrawPoolSimple::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ facep->renderSelected(image, color, index_offset, index_count);
+}
+
+
+void LLDrawPoolSimple::dirtyTexture(const LLViewerImage *texturep)
+{
+ if (mTexturep == texturep)
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ gPipeline.markTextured(facep->getDrawable());
+ }
+ }
+}
+
+LLViewerImage *LLDrawPoolSimple::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolSimple::getDebugTexture()
+{
+ return mTexturep;
+}
+
+LLColor3 LLDrawPoolSimple::getDebugColor() const
+{
+ return LLColor3(1.f, 1.f, 1.f);
+}
+
+S32 LLDrawPoolSimple::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectSimpleProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
+
+// virtual
+void LLDrawPoolSimple::enableShade()
+{
+ glDisableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolSimple::disableShade()
+{
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolSimple::setShade(F32 shade)
+{
+ glColor4f(0,0,0,shade);
+}
diff --git a/indra/newview/lldrawpoolsimple.h b/indra/newview/lldrawpoolsimple.h
new file mode 100644
index 0000000000..41c4580cea
--- /dev/null
+++ b/indra/newview/lldrawpoolsimple.h
@@ -0,0 +1,74 @@
+/**
+ * @file lldrawpoolsimple.h
+ * @brief LLDrawPoolSimple class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLSIMPLE_H
+#define LL_LLDRAWPOOLSIMPLE_H
+
+#include "lldrawpool.h"
+
+class LLFRInfo
+{
+public:
+ U32 mPrimType;
+ U32 mGeomIndex;
+ U32 mGeomIndexEnd;
+ U32 mNumIndices;
+ U32 mIndicesStart;
+
+ LLFRInfo()
+ {
+ }
+
+ LLFRInfo(const U32 pt, const U32 gi, const U32 gc, const U32 ni, const U32 is) :
+ mPrimType(pt),
+ mGeomIndex(gi),
+ mGeomIndexEnd(gi+gc),
+ mNumIndices(ni),
+ mIndicesStart(is)
+ {
+ }
+};
+
+class LLDrawPoolSimple : public LLDrawPool
+{
+ LLPointer<LLViewerImage> mTexturep;
+public:
+ enum
+ {
+ SHADER_LEVEL_LOCAL_LIGHTS = 2
+ };
+
+ LLDrawPoolSimple(LLViewerImage *texturep);
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void beginRenderPass(S32 pass);
+ /*virtual*/ void endRenderPass(S32 pass);
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+ /*virtual*/ BOOL match(LLFace* last_face, LLFace* facep);
+
+ /*virtual*/ void enableShade();
+ /*virtual*/ void disableShade();
+ /*virtual*/ void setShade(F32 shade);
+
+ virtual S32 getMaterialAttribIndex();
+
+ static S32 sDiffTex;
+};
+
+#endif // LL_LLDRAWPOOLSIMPLE_H
diff --git a/indra/newview/lldrawpoolsky.cpp b/indra/newview/lldrawpoolsky.cpp
new file mode 100644
index 0000000000..96eb8ea721
--- /dev/null
+++ b/indra/newview/lldrawpoolsky.cpp
@@ -0,0 +1,180 @@
+/**
+ * @file lldrawpoolsky.cpp
+ * @brief LLDrawPoolSky class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolsky.h"
+
+#include "imageids.h"
+
+#include "llagparray.h"
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvosky.h"
+#include "llworld.h" // To get water height
+#include "pipeline.h"
+
+LLDrawPoolSky::LLDrawPoolSky() :
+ LLDrawPool(POOL_SKY, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+{
+}
+
+LLDrawPool *LLDrawPoolSky::instancePool()
+{
+ return new LLDrawPoolSky();
+}
+
+void LLDrawPoolSky::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+}
+
+void LLDrawPoolSky::render(S32 pass)
+{
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ LLVOSky *voskyp = gSky.mVOSkyp;
+ LLGLSPipelineSkyBox gls_skybox;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ if (gCamera->getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight())
+ //gWorldPointer->getWaterHeight())
+ {
+ //gGLSFog.set();
+ }
+
+ gPipeline.disableLights();
+
+ glMatrixMode( GL_PROJECTION );
+
+ glPushMatrix();
+ gViewerWindow->setup3DRender();
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ S32 face_count = (S32)mDrawFace.size();
+
+ for (S32 i = 0; i < llmin(6, face_count); ++i)
+ {
+ renderSkyCubeFace(i);
+ }
+
+ const LLFace *hbfaces[3];
+ hbfaces[0] = NULL;
+ hbfaces[1] = NULL;
+ hbfaces[2] = NULL;
+ for (S32 curr_face = 0; curr_face < face_count; curr_face++)
+ {
+ const LLFace* facep = mDrawFace[curr_face];
+ if (voskyp->isSameFace(LLVOSky::FACE_SUN, facep))
+ {
+ hbfaces[0] = facep;
+ }
+ if (voskyp->isSameFace(LLVOSky::FACE_MOON, facep))
+ {
+ hbfaces[1] = facep;
+ }
+ if (voskyp->isSameFace(LLVOSky::FACE_BLOOM, facep))
+ {
+ hbfaces[2] = facep;
+ }
+ }
+
+ LLGLEnable blend(GL_BLEND);
+
+ if (hbfaces[2])
+ {
+ renderSunHalo(hbfaces[2]);
+ }
+ if (hbfaces[0])
+ {
+ renderHeavenlyBody(0, hbfaces[0]);
+ }
+ if (hbfaces[1])
+ {
+ renderHeavenlyBody(1, hbfaces[1]);
+ }
+
+
+ glMatrixMode( GL_PROJECTION );
+ glPopMatrix();
+ glMatrixMode( GL_MODELVIEW );
+}
+
+void LLDrawPoolSky::renderSkyCubeFace(U8 side)
+{
+ const LLFace &face = *mDrawFace[LLVOSky::FACE_SIDE0 + side];
+ if (!face.getGeomCount())
+ {
+ return;
+ }
+
+ mSkyTex[side].bindTexture(TRUE);
+
+ face.renderIndexed(getRawIndices());
+
+ if (LLSkyTex::doInterpolate())
+ {
+ LLGLEnable blend(GL_BLEND);
+ mSkyTex[side].bindTexture(FALSE);
+ glColor4f(1, 1, 1, LLSkyTex::getInterpVal()); // lighting is disabled
+ face.renderIndexed(getRawIndices());
+ }
+
+ mIndicesDrawn += face.getIndicesCount();
+}
+
+void LLDrawPoolSky::renderHeavenlyBody(U8 hb, const LLFace* face)
+{
+ if ( !mHB[hb]->getDraw() ) return;
+ if (! face->getGeomCount()) return;
+
+ LLImageGL* tex = face->getTexture();
+ tex->bind();
+ LLColor4 color(mHB[hb]->getInterpColor());
+ LLOverrideFaceColor override(this, color);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+}
+
+
+
+void LLDrawPoolSky::renderSunHalo(const LLFace* face)
+{
+ if (! mHB[0]->getDraw()) return;
+ if (! face->getGeomCount()) return;
+
+ LLImageGL* tex = face->getTexture();
+ tex->bind();
+ LLColor4 color(mHB[0]->getInterpColor());
+ color.mV[3] = llclamp(mHB[0]->getHaloBrighness(), 0.f, 1.f);
+
+ LLOverrideFaceColor override(this, color);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+}
+
+
+void LLDrawPoolSky::renderForSelect()
+{
+}
+
diff --git a/indra/newview/lldrawpoolsky.h b/indra/newview/lldrawpoolsky.h
new file mode 100644
index 0000000000..881ce6d542
--- /dev/null
+++ b/indra/newview/lldrawpoolsky.h
@@ -0,0 +1,42 @@
+/**
+ * @file lldrawpoolsky.h
+ * @brief LLDrawPoolSky class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLSKY_H
+#define LL_LLDRAWPOOLSKY_H
+
+#include "lldrawpool.h"
+
+class LLSkyTex;
+class LLHeavenBody;
+
+class LLDrawPoolSky : public LLDrawPool
+{
+private:
+ LLSkyTex *mSkyTex;
+ LLHeavenBody *mHB[2]; // Sun and Moon
+
+public:
+ LLDrawPoolSky();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+ void setSkyTex(LLSkyTex* const st) { mSkyTex = st; }
+ void setSun(LLHeavenBody* sun) { mHB[0] = sun; }
+ void setMoon(LLHeavenBody* moon) { mHB[1] = moon; }
+
+ void renderSkyCubeFace(U8 side);
+ void renderHeavenlyBody(U8 hb, const LLFace* face);
+ void renderSunHalo(const LLFace* face);
+
+ virtual S32 getMaterialAttribIndex() { return 0; }
+};
+
+#endif // LL_LLDRAWPOOLSKY_H
diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp
new file mode 100644
index 0000000000..3e51e01699
--- /dev/null
+++ b/indra/newview/lldrawpoolterrain.cpp
@@ -0,0 +1,1091 @@
+/**
+ * @file lldrawpoolterrain.cpp
+ * @brief LLDrawPoolTerrain class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolterrain.h"
+
+#include "llfasttimer.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "llviewerparcelmgr.h" // for gRenderParcelOwnership
+#include "llviewerparceloverlay.h"
+#include "llvosurfacepatch.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h" // To get alpha gradients
+#include "llworld.h"
+#include "pipeline.h"
+
+const F32 DETAIL_SCALE = 1.f/16.f;
+int DebugDetailMap = 0;
+
+S32 LLDrawPoolTerrain::sDetailMode = 1;
+F32 LLDrawPoolTerrain::sDetailScale = DETAIL_SCALE;
+
+LLDrawPoolTerrain::LLDrawPoolTerrain(LLViewerImage *texturep) :
+ LLDrawPool(POOL_TERRAIN, DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK | DATA_TEX_COORDS1_MASK, DATA_SIMPLE_NIL_MASK),
+ mTexturep(texturep)
+{
+ // Hack!
+ sDetailScale = 1.f/gSavedSettings.getF32("RenderTerrainScale");
+ sDetailMode = gSavedSettings.getS32("RenderTerrainDetail");
+ mAlphaRampImagep = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("alpha_gradient.tga")),
+ TRUE, TRUE, GL_ALPHA8, GL_ALPHA);
+ mAlphaRampImagep->bind(0);
+ mAlphaRampImagep->setClamp(TRUE, TRUE);
+
+ m2DAlphaRampImagep = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("alpha_gradient_2d.tga")),
+ TRUE, TRUE, GL_ALPHA8, GL_ALPHA);
+ m2DAlphaRampImagep->bind(0);
+ m2DAlphaRampImagep->setClamp(TRUE, TRUE);
+
+ mTexturep->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+}
+
+LLDrawPoolTerrain::~LLDrawPoolTerrain()
+{
+ llassert( gPipeline.findPool( getType(), getTexture() ) == NULL );
+}
+
+
+LLDrawPool *LLDrawPoolTerrain::instancePool()
+{
+ return new LLDrawPoolTerrain(mTexturep);
+}
+
+
+void LLDrawPoolTerrain::prerender()
+{
+#if 0 // 1.9.2
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+#endif
+}
+
+void LLDrawPoolTerrain::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN);
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ if (!gGLManager.mHasMultitexture)
+ {
+ // No mulititexture, render simple land.
+ renderSimple(); // Render without multitexture
+ return;
+ }
+ // Render simplified land if video card can't do sufficient multitexturing
+ if (!gGLManager.mHasARBEnvCombine || (gGLManager.mNumTextureUnits < 2))
+ {
+ renderSimple(); // Render without multitexture
+ return;
+ }
+
+ LLGLSPipeline gls;
+ LLOverrideFaceColor override(this, 1.f, 1.f, 1.f, 1.f);
+
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.enableLightsDynamic(1.f);
+ renderFull4TUShader();
+ }
+ else
+ {
+ gPipeline.enableLightsStatic(1.f);
+ switch (sDetailMode)
+ {
+ case 0:
+ renderSimple();
+ break;
+ default:
+ if (gGLManager.mNumTextureUnits < 4)
+ {
+ renderFull2TU();
+ }
+ else
+ {
+ renderFull4TU();
+ }
+ break;
+ }
+ }
+
+ // Special-case for land ownership feedback
+ if (gSavedSettings.getBOOL("ShowParcelOwners"))
+ {
+ gPipeline.disableLights();
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mHighlightProgram.bind();
+ gPipeline.mHighlightProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,1,1,1,1);
+ renderOwnership();
+ gPipeline.mTerrainProgram.bind();
+ }
+ else
+ {
+ renderOwnership();
+ }
+ }
+}
+
+
+void LLDrawPoolTerrain::renderFull4TUShader()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ bindGLColorPointer();
+ }
+
+ glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+
+ // Hack! Get the region that this draw pool is rendering from!
+ LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+ LLVLComposition *compp = regionp->getComposition();
+ for (S32 i = 0; i < 4; i++)
+ {
+ compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+ compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
+ }
+
+ LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
+ LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
+ LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
+ LLViewerImage *detail_texture3p = compp->mDetailTextures[3];
+
+ static F32 dp = 0.f;
+ static LLFrameTimer timer;
+ dp += timer.getElapsedTimeAndResetF32();
+
+ LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal();
+
+ F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sDetailScale)*sDetailScale;
+ F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sDetailScale)*sDetailScale;
+
+ LLVector4 tp0, tp1;
+
+ tp0.setVec(sDetailScale, 0.0f, 0.0f, offset_x);
+ tp1.setVec(0.0f, sDetailScale, 0.0f, offset_y);
+
+ //----------------------------------------------------------------------------
+ // Pass 1/1
+
+ //
+ // Stage 0: detail texture 0
+ //
+
+ S32 detailTex0 = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_DETAIL0);
+ S32 detailTex1 = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_DETAIL1);
+ S32 rampTex = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_ALPHARAMP);
+ S32 scatterTex = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+
+ LLViewerImage::bindTexture(detail_texture0p,detailTex0);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ //
+ // Stage 1: Generate alpha ramp for detail0/detail1 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,rampTex);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ //
+ // Stage 2: Interpolate detail1 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture1p,detailTex1);
+
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ // Stage 4: Haze
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ //
+ // Stage 3: Modulate with primary color for lighting
+ //
+ //LLViewerImage::bindTexture(detail_texture1p,3); // bind any texture
+ //glEnable(GL_TEXTURE_2D); // Texture unit 3
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glActiveTextureARB(GL_TEXTURE3_ARB);
+ // GL_BLEND disabled by default
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Second pass
+
+ //
+ // Stage 0: Write detail3 into base
+ //
+ LLViewerImage::bindTexture(detail_texture2p,detailTex0);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ //
+ // Stage 1: Generate alpha ramp for detail2/detail3 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,rampTex);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ bindGLTexCoordPointer(1);
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-2.f, 0.f, 0.f);
+
+ //
+ // Stage 2: Interpolate detail2 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture3p,detailTex1);
+
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ //
+ // Stage 3: Generate alpha ramp for detail1/detail2 transition
+ //
+ //LLViewerImage::bindTexture(m2DAlphaRampImagep,3);
+
+ //glEnable(GL_TEXTURE_2D); // Texture unit 3
+
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glActiveTextureARB(GL_TEXTURE3_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-1.f, 0.f, 0.f);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ // Disable multitexture
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_ALPHARAMP);
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_DETAIL0);
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_DETAIL1);
+
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glActiveTextureARB(GL_TEXTURE3_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore blend state
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glEnable(GL_TEXTURE_2D);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+void LLDrawPoolTerrain::renderFull4TU()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+
+ // Hack! Get the region that this draw pool is rendering from!
+ LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+ LLVLComposition *compp = regionp->getComposition();
+ for (S32 i = 0; i < 4; i++)
+ {
+ compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+ compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
+ }
+
+ LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
+ LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
+ LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
+ LLViewerImage *detail_texture3p = compp->mDetailTextures[3];
+
+ LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal();
+ F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sDetailScale)*sDetailScale;
+ F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sDetailScale)*sDetailScale;
+
+ LLVector4 tp0, tp1;
+
+ tp0.setVec(sDetailScale, 0.0f, 0.0f, offset_x);
+ tp1.setVec(0.0f, sDetailScale, 0.0f, offset_y);
+
+ glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Pass 1/1
+
+ //
+ // Stage 0: detail texture 0
+ //
+ LLViewerImage::bindTexture(detail_texture0p,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ //
+ // Stage 1: Generate alpha ramp for detail0/detail1 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,1);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ //
+ // Stage 2: Interpolate detail1 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture1p,2);
+ glEnable(GL_TEXTURE_2D); // Texture unit 2
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);
+
+ //
+ // Stage 3: Modulate with primary (vertex) color for lighting
+ //
+ LLViewerImage::bindTexture(detail_texture1p,3); // bind any texture
+ glEnable(GL_TEXTURE_2D); // Texture unit 3
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+
+ // Set alpha texture and do lighting modulation
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ // GL_BLEND disabled by default
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Second pass
+
+ // Stage 0: Write detail3 into base
+ //
+ LLViewerImage::bindTexture(detail_texture3p,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+
+ //
+ // Stage 1: Generate alpha ramp for detail2/detail3 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,1);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-2.f, 0.f, 0.f);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ //
+ // Stage 2: Interpolate detail2 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture2p,2);
+ glEnable(GL_TEXTURE_2D); // Texture unit 2
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
+
+
+ //
+ // Stage 3: Generate alpha ramp for detail1/detail2 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,3);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 3
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-1.f, 0.f, 0.f);
+
+ // Set alpha texture and do lighting modulation
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ // Disable multitexture
+ LLImageGL::unbindTexture(3, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 3
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLImageGL::unbindTexture(2, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 2
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore blend state
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+void LLDrawPoolTerrain::renderFull2TU()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+
+ // Hack! Get the region that this draw pool is rendering from!
+ LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+ LLVLComposition *compp = regionp->getComposition();
+ for (S32 i = 0; i < 4; i++)
+ {
+ compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+ compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
+ }
+
+ LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
+ LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
+ LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
+ LLViewerImage *detail_texture3p = compp->mDetailTextures[3];
+
+ LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal();
+ F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sDetailScale)*sDetailScale;
+ F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sDetailScale)*sDetailScale;
+
+ LLVector4 tp0, tp1;
+
+ tp0.setVec(sDetailScale, 0.0f, 0.0f, offset_x);
+ tp1.setVec(0.0f, sDetailScale, 0.0f, offset_y);
+
+ glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Pass 1/4
+
+ //
+ // Stage 0: Render detail 0 into base
+ //
+ LLViewerImage::bindTexture(detail_texture0p,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Pass 2/4
+
+ //
+ // Stage 0: Generate alpha ramp for detail0/detail1 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ //
+ // Stage 1: Write detail1
+ //
+ LLViewerImage::bindTexture(detail_texture1p,1); // Texture unit 1
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+ //----------------------------------------------------------------------------
+ // Pass 3/4
+
+ //
+ // Stage 0: Generate alpha ramp for detail1/detail2 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-1.f, 0.f, 0.f);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ //
+ // Stage 1: Write detail2
+ //
+
+ LLViewerImage::bindTexture(detail_texture2p,1);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ //----------------------------------------------------------------------------
+ // Pass 4/4
+
+ //
+ // Stage 0: Generate alpha ramp for detail2/detail3 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-2.f, 0.f, 0.f);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ // Stage 1: Write detail3
+
+ LLViewerImage::bindTexture(detail_texture3p,1);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ // Restore blend state
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Disable multitexture
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+
+void LLDrawPoolTerrain::renderSimple()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+
+ LLVector4 tp0, tp1;
+
+ //----------------------------------------------------------------------------
+ // Pass 1/1
+
+ // Stage 0: Base terrain texture pass
+ mTexturep->addTextureStats(1024.f*1024.f);
+ mTexturep->bind(0);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D); // Texture unit 2
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ LLVector3 origin_agent = mDrawFace[0]->getDrawable()->getVObj()->getRegion()->getOriginAgent();
+ F32 tscale = 1.f/256.f;
+ tp0.setVec(tscale, 0.f, 0.0f, -1.f*(origin_agent.mV[0]/256.f));
+ tp1.setVec(0.f, tscale, 0.0f, -1.f*(origin_agent.mV[1]/256.f));
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+//============================================================================
+
+void LLDrawPoolTerrain::renderOwnership()
+{
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ llassert(!mDrawFace.empty());
+
+ // Each terrain pool is associated with a single region.
+ // We need to peek back into the viewer's data to find out
+ // which ownership overlay texture to use.
+ LLFace *facep = mDrawFace[0];
+ LLDrawable *drawablep = facep->getDrawable();
+ const LLViewerObject *objectp = drawablep->getVObj();
+ const LLVOSurfacePatch *vo_surface_patchp = (LLVOSurfacePatch *)objectp;
+ LLSurfacePatch *surface_patchp = vo_surface_patchp->getPatch();
+ LLSurface *surfacep = surface_patchp->getSurface();
+ LLViewerRegion *regionp = surfacep->getRegion();
+ LLViewerParcelOverlay *overlayp = regionp->getParcelOverlay();
+ LLImageGL *texturep = overlayp->getTexture();
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer(0);
+
+ LLViewerImage::bindTexture(texturep);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ // HACK: Because the region is 256 meters wide, but has 257 pixels, the
+ // texture coordinates for pixel 256x256 is not 1,1. This makes the
+ // ownership map not line up with the selection. Fix this with a texture
+ // matrix multiply.
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+
+ const F32 TEXTURE_FUDGE = 257.f / 256.f;
+ glScalef( TEXTURE_FUDGE, TEXTURE_FUDGE, 1.f );
+
+ const U32* index_array = getRawIndices();
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ facep->renderIndexed(index_array);
+ }
+
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+}
+
+
+void LLDrawPoolTerrain::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+
+ bindGLVertexPointer();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (!facep->getDrawable()->isDead() && (facep->getDrawable()->getVObj()->mGLName))
+ {
+ facep->renderForSelect();
+ }
+ }
+}
+
+void LLDrawPoolTerrain::dirtyTexture(const LLViewerImage *texturep)
+{
+ if (mTexturep == texturep)
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ gPipeline.markTextured(facep->getDrawable());
+ }
+ }
+}
+
+LLViewerImage *LLDrawPoolTerrain::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolTerrain::getDebugTexture()
+{
+ return mTexturep;
+}
+
+
+LLColor3 LLDrawPoolTerrain::getDebugColor() const
+{
+ return LLColor3(0.f, 0.f, 1.f);
+}
+
+S32 LLDrawPoolTerrain::getMaterialAttribIndex()
+{
+ return gPipeline.mTerrainProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h
new file mode 100644
index 0000000000..b5fe0a30fd
--- /dev/null
+++ b/indra/newview/lldrawpoolterrain.h
@@ -0,0 +1,49 @@
+/**
+ * @file lldrawpoolterrain.h
+ * @brief LLDrawPoolTerrain class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLTERRAIN_H
+#define LL_LLDRAWPOOLTERRAIN_H
+
+#include "lldrawpool.h"
+
+class LLDrawPoolTerrain : public LLDrawPool
+{
+ LLPointer<LLViewerImage> mTexturep;
+public:
+ LLDrawPoolTerrain(LLViewerImage *texturep);
+ virtual ~LLDrawPoolTerrain();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ LLPointer<LLViewerImage> mAlphaRampImagep;
+ LLPointer<LLViewerImage> m2DAlphaRampImagep;
+ LLPointer<LLViewerImage> mAlphaNoiseImagep;
+
+ virtual S32 getMaterialAttribIndex();
+
+ static S32 sDetailMode;
+ static F32 sDetailScale; // meters per texture
+protected:
+ void renderSimple();
+ void renderOwnership();
+
+ void renderFull2TU();
+ void renderFull4TU();
+ void renderFull4TUShader();
+};
+
+#endif // LL_LLDRAWPOOLSIMPLE_H
diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
new file mode 100644
index 0000000000..c41ceedac7
--- /dev/null
+++ b/indra/newview/lldrawpooltree.cpp
@@ -0,0 +1,342 @@
+/**
+ * @file lldrawpooltree.cpp
+ * @brief LLDrawPoolTree class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpooltree.h"
+
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewerwindow.h"
+#include "llvotree.h"
+#include "pipeline.h"
+#include "llviewercamera.h"
+
+S32 LLDrawPoolTree::sDiffTex = 0;
+
+LLDrawPoolTree::LLDrawPoolTree(LLViewerImage *texturep) :
+ LLDrawPool(POOL_TREE, DATA_SIMPLE_IL_MASK, 0),
+ mTexturep(texturep)
+{
+ mTexturep->bind(0);
+ mTexturep->setClamp(FALSE, FALSE);
+}
+
+LLDrawPool *LLDrawPoolTree::instancePool()
+{
+ return new LLDrawPoolTree(mTexturep);
+}
+
+void LLDrawPoolTree::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+void LLDrawPoolTree::beginRenderPass(S32 pass)
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ if ((mVertexShaderLevel > 0))
+ {
+ S32 scatterTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+ sDiffTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ }
+}
+
+void LLDrawPoolTree::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_TREES);
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ gPipeline.enableLightsDynamic(1.f);
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+
+ LLOverrideFaceColor color(this, 1.f, 1.f, 1.f, 1.f);
+
+ renderTree();
+
+}
+
+void LLDrawPoolTree::endRenderPass(S32 pass)
+{
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void LLDrawPoolTree::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ LLOverrideFaceColor color(this, 1.f, 1.f, 1.f, 1.f);
+
+ LLGLSObjectSelectAlpha gls_alpha;
+
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glAlphaFunc(gPickTransparent ? GL_GEQUAL : GL_GREATER, 0.f);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
+
+ renderTree(TRUE);
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+}
+
+void LLDrawPoolTree::renderTree(BOOL selecting)
+{
+ LLGLState normalize(GL_NORMALIZE, TRUE);
+
+ // Bind the texture for this tree.
+ LLViewerImage::bindTexture(mTexturep,sDiffTex);
+ if (mTexturep)
+ {
+ if (mTexturep->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ }
+ if (mTexturep->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ LLDrawable *drawablep = face->getDrawable();
+
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+
+ // Render each of the trees
+ LLVOTree *treep = (LLVOTree *)drawablep->getVObj();
+
+ LLColor4U color(255,255,255,255);
+
+ if (!selecting || treep->mGLName != 0)
+ {
+ if (selecting)
+ {
+ S32 name = treep->mGLName;
+
+ color = LLColor4U((U8)(name >> 16), (U8)(name >> 8), (U8)name, 255);
+ }
+
+ glPushMatrix();
+
+ // Translate to tree base HACK - adjustment in Z plants tree underground
+ const LLVector3 &pos_agent = treep->getPositionAgent();
+ glTranslatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ] - 0.1f);
+
+ // Rotate to tree position
+ F32 angle_radians, x, y, z;
+ treep->getRotation().getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ // Rotate and bend for current trunk/wind
+ // Note that trunk stiffness controls the amount of bend at the trunk as
+ // opposed to the crown of the tree
+ //
+ glRotatef(90.f, 0, 0, 1);
+ const F32 TRUNK_STIFF = 22.f;
+ glRotatef(treep->mTrunkBend.magVec()*TRUNK_STIFF, treep->mTrunkBend.mV[VX], treep->mTrunkBend.mV[VY], 0);
+
+ F32 radius = treep->getScale().magVec()*0.5f;
+ radius *= 0.1f;
+ glScalef(radius, radius, radius);
+
+ const F32 THRESH_ANGLE_FOR_BILLBOARD = 15.f;
+ const F32 BLEND_RANGE_FOR_BILLBOARD = 3.f;
+
+ F32 droop = treep->mDroop + 25.f*(1.f - treep->mTrunkBend.magVec());
+
+ S32 stop_depth = 0;
+ F32 app_angle = treep->getAppAngle()*LLVOTree::sTreeFactor;
+ F32 alpha = 1.0;
+ S32 trunk_LOD = 0;
+
+ for (S32 j = 0; j < 4; j++)
+ {
+
+ if (app_angle > LLVOTree::sLODAngles[j])
+ {
+ trunk_LOD = j;
+ break;
+ }
+ }
+
+ if (app_angle > (THRESH_ANGLE_FOR_BILLBOARD + BLEND_RANGE_FOR_BILLBOARD))
+ {
+ //
+ // Draw only the full geometry tree
+ //
+ //stop_depth = (app_angle < THRESH_ANGLE_FOR_RECURSION_REDUCTION);
+ glAlphaFunc(GL_GREATER, 0.5f);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
+ }
+ else if (app_angle < (THRESH_ANGLE_FOR_BILLBOARD - BLEND_RANGE_FOR_BILLBOARD))
+ {
+ //
+ // Draw only the billboard
+ //
+ // Only the billboard, can use closer to normal alpha func.
+ stop_depth = -1;
+ glAlphaFunc(GL_GREATER, 0.4f);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
+ }
+ else
+ {
+ //
+ // Draw a blended version including both billboard and full tree
+ //
+ alpha = (app_angle - THRESH_ANGLE_FOR_BILLBOARD)/BLEND_RANGE_FOR_BILLBOARD;
+ BOOL billboard_depth = TRUE; // billboard gets alpha
+ if (alpha > 0.5f)
+ {
+ billboard_depth = FALSE;
+ }
+ alpha = alpha/2.f + 0.5f;
+
+ glAlphaFunc(GL_GREATER, alpha*0.5f);
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, billboard_depth ? GL_FALSE : GL_TRUE);
+ color.mV[3] = (U8) (llclamp(alpha, 0.0f, 1.0f) * 255);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, 0, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
+ }
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, billboard_depth ? GL_TRUE : GL_FALSE);
+ glAlphaFunc(GL_GREATER, (1.f - alpha)*0.1f);
+ color.mV[3] = (U8) (llclamp(1.f-alpha, 0.0f, 1.0f) * 255);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, -1, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, 1.f - alpha);
+ }
+ }
+ glPopMatrix();
+ }
+ }
+
+ if (mTexturep)
+ {
+ if (mTexturep->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ }
+ if (mTexturep->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ }
+ glAlphaFunc(GL_GREATER, 0.01f);
+}
+
+
+S32 LLDrawPoolTree::rebuild()
+{
+ mRebuildTime++;
+ if (mRebuildTime > mRebuildFreq)
+ {
+ // Flush AGP to force an AGP realloc and reduce AGP fragmentation
+ flushAGP();
+ mRebuildTime = 0;
+ }
+
+ return 0;
+}
+
+BOOL LLDrawPoolTree::verify() const
+{
+ BOOL ok = TRUE;
+
+ // shared geometry. Just verify that it's there and correct.
+
+ // Verify all indices in the pool are in the right range
+ const U32 *indicesp = getRawIndices();
+ for (U32 i = 0; i < getIndexCount(); i++)
+ {
+ if (indicesp[i] > getVertexCount())
+ {
+ ok = FALSE;
+ llinfos << "Bad index in tree pool!" << llendl;
+ }
+ }
+
+ if (!ok)
+ {
+ printDebugInfo();
+ }
+ return ok;
+}
+
+LLViewerImage *LLDrawPoolTree::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolTree::getDebugTexture()
+{
+ return mTexturep;
+}
+
+
+LLColor3 LLDrawPoolTree::getDebugColor() const
+{
+ return LLColor3(1.f, 0.f, 1.f);
+}
+
+S32 LLDrawPoolTree::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectSimpleProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
diff --git a/indra/newview/lldrawpooltree.h b/indra/newview/lldrawpooltree.h
new file mode 100644
index 0000000000..228b11a981
--- /dev/null
+++ b/indra/newview/lldrawpooltree.h
@@ -0,0 +1,41 @@
+/**
+ * @file lldrawpooltree.h
+ * @brief LLDrawPoolTree class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLTREE_H
+#define LL_LLDRAWPOOLTREE_H
+
+#include "lldrawpool.h"
+
+class LLDrawPoolTree : public LLDrawPool
+{
+ LLPointer<LLViewerImage> mTexturep;
+public:
+ LLDrawPoolTree(LLViewerImage *texturep);
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void beginRenderPass( S32 pass );
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void endRenderPass( S32 pass );
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ S32 rebuild();
+ /*virtual*/ BOOL verify() const;
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ virtual S32 getMaterialAttribIndex();
+
+ static S32 sDiffTex;
+
+private:
+ void renderTree(BOOL selecting = FALSE);
+};
+
+#endif // LL_LLDRAWPOOLTREE_H
diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp
new file mode 100644
index 0000000000..108048efbc
--- /dev/null
+++ b/indra/newview/lldrawpoolwater.cpp
@@ -0,0 +1,729 @@
+/**
+ * @file lldrawpoolwater.cpp
+ * @brief LLDrawPoolWater class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfeaturemanager.h"
+#include "lldrawpoolwater.h"
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llerror.h"
+#include "m3math.h"
+
+#include "llagent.h" // for gAgent for getRegion for getWaterHeight
+#include "llagparray.h"
+#include "llcubemap.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h" // to get OGL_TO_CFR_ROTATION
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llvosky.h"
+#include "llvowater.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h" // gSunTextureID, gMoonTextureID
+
+const LLUUID WATER_TEST("2bfd3884-7e27-69b9-ba3a-3e673f680004");
+
+static float sTime;
+
+int nhpo2(int v)
+{
+ int r = 1;
+ while (r < v) {
+ r *= 2;
+ }
+ return r;
+}
+
+static GLuint sScreenTex = 0;
+
+LLDrawPoolWater::LLDrawPoolWater() :
+ LLDrawPool(POOL_WATER, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+{
+ mCleanupUnused = TRUE;
+ mHBTex[0] = gImageList.getImage(gSunTextureID, TRUE, TRUE);
+ mHBTex[0]->bind();
+ mHBTex[0]->setClamp(TRUE, TRUE);
+
+ mHBTex[1] = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
+ mHBTex[1]->bind();
+ mHBTex[1]->setClamp(TRUE, TRUE);
+
+ mWaterImagep = gImageList.getImage(WATER_TEST);
+ mWaterNormp = gImageList.getImage(LLUUID(gViewerArt.getString("water_normal.tga")));
+
+ restoreGL();
+}
+
+LLDrawPoolWater::~LLDrawPoolWater()
+{
+}
+
+//static
+void LLDrawPoolWater::restoreGL()
+{
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) >= SHADER_LEVEL_RIPPLE)
+ {
+ //build screen texture
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glGenTextures(1, &sScreenTex);
+ LLGLEnable gl_texture_2d(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, sScreenTex);
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ GLuint resX = nhpo2(viewport[2]);
+ GLuint resY = nhpo2(viewport[3]);
+
+ gImageList.updateMaxResidentTexMem(-1, resX*resY*3);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, resX, resY, 0, GL_RGB, GL_FLOAT, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+}
+
+
+LLDrawPool *LLDrawPoolWater::instancePool()
+{
+ llerrs << "Should never be calling instancePool on a water pool!" << llendl;
+ return NULL;
+}
+
+
+void LLDrawPoolWater::prerender()
+{
+#if 1 // 1.9.1
+ mVertexShaderLevel = gSavedSettings.getBOOL("RenderRippleWater") ?
+ gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) : 0;
+#else
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+#endif
+}
+
+extern LLColor4U MAX_WATER_COLOR;
+
+void LLDrawPoolWater::render(S32 pass)
+{
+ LLFastTimer ftm(LLFastTimer::FTM_RENDER_WATER);
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ LLGLSPipelineAlpha alphaState;
+
+ if ((mVertexShaderLevel >= SHADER_LEVEL_RIPPLE))
+ {
+ shade();
+ return;
+ }
+
+ if ((mVertexShaderLevel > 0))
+ {
+ renderShaderSimple();
+ return;
+ }
+
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ stop_glerror();
+
+ if (!gGLManager.mHasMultitexture)
+ {
+ // Ack! No multitexture! Bail!
+ return;
+ }
+
+ const LLFace* refl_face = voskyp->getReflFace();
+
+ gPipeline.disableLights();
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLGLDisable cullFace(GL_CULL_FACE);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer();
+
+ // Set up second pass first
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ mWaterImagep->addTextureStats(1024.f*1024.f);
+ mWaterImagep->bind(1);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+
+ LLVector3 camera_up = gCamera->getUpAxis();
+ F32 up_dot = camera_up * LLVector3::z_axis;
+
+ LLColor4 water_color;
+ if (gCamera->cameraUnderWater())
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.4f);
+ }
+ else
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.5f*(1.f + up_dot));
+ }
+
+ glColor4fv(water_color.mV);
+
+ // Automatically generate texture coords for detail map
+ glEnable(GL_TEXTURE_GEN_S); //texture unit 1
+ glEnable(GL_TEXTURE_GEN_T); //texture unit 1
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ // Slowly move over time.
+ F32 offset = fmod(gFrameTimeSeconds*2.f, 100.f);
+ F32 tp0[4] = {16.f/256.f, 0.0f, 0.0f, offset*0.01f};
+ F32 tp1[4] = {0.0f, 16.f/256.f, 0.0f, offset*0.01f};
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glClearStencil(1);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ LLGLEnable gls_stencil(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
+ glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ if (voskyp->isReflFace(face))
+ {
+ continue;
+ }
+ face->bindTexture();
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+
+ // Now, disable texture coord generation on texture state 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_GEN_S); //texture unit 1
+ glDisable(GL_TEXTURE_GEN_T); //texture unit 1
+
+ // Disable texture coordinate and color arrays
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ stop_glerror();
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ gSky.mVOSkyp->getCubeMap()->enable(0);
+ gSky.mVOSkyp->getCubeMap()->bind();
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ LLMatrix4 camera_mat = gCamera->getModelview();
+ LLMatrix4 camera_rot(camera_mat.getMat3());
+ camera_rot.invert();
+
+ glLoadMatrixf((F32 *)camera_rot.mMatrix);
+
+ glMatrixMode(GL_MODELVIEW);
+ LLOverrideFaceColor overrid(this, 1.f, 1.f, 1.f, 0.5f*up_dot);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ /*glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);*/
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ if (voskyp->isReflFace(face))
+ {
+ //refl_face = face;
+ continue;
+ }
+
+ if (face->getGeomCount() > 0)
+ {
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+ }
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ gSky.mVOSkyp->getCubeMap()->disable();
+ }
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glEnable(GL_TEXTURE_2D);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ }
+
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ if (refl_face)
+ {
+ glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF);
+ renderReflection(refl_face);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+}
+
+
+void LLDrawPoolWater::renderShaderSimple()
+{
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ stop_glerror();
+
+ if (!gGLManager.mHasMultitexture)
+ {
+ // Ack! No multitexture! Bail!
+ return;
+ }
+
+ const LLFace* refl_face = voskyp->getReflFace();
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLGLDisable cullFace(GL_CULL_FACE);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer();
+
+ // Set up second pass first
+ S32 bumpTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ mWaterImagep->addTextureStats(1024.f*1024.f);
+ mWaterImagep->bind(bumpTex);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLVector3 camera_up = gCamera->getUpAxis();
+ F32 up_dot = camera_up * LLVector3::z_axis;
+
+ LLColor4 water_color;
+ if (gCamera->cameraUnderWater())
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.4f);
+ }
+ else
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.5f*(1.f + up_dot));
+ }
+
+ glColor4fv(water_color.mV);
+
+ // Automatically generate texture coords for detail map
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnable(GL_TEXTURE_GEN_S); //texture unit 1
+ glEnable(GL_TEXTURE_GEN_T); //texture unit 1
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ // Slowly move over time.
+ F32 offset = fmod(gFrameTimeSeconds*2.f, 100.f);
+ F32 tp0[4] = {16.f/256.f, 0.0f, 0.0f, offset*0.01f};
+ F32 tp1[4] = {0.0f, 16.f/256.f, 0.0f, offset*0.01f};
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glClearStencil(1);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ LLGLEnable gls_stencil(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
+ glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
+
+ S32 envTex = -1;
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ envTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ gSky.mVOSkyp->getCubeMap()->bind();
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ LLMatrix4 camera_mat = gCamera->getModelview();
+ LLMatrix4 camera_rot(camera_mat.getMat3());
+ camera_rot.invert();
+
+ glLoadMatrixf((F32 *)camera_rot.mMatrix);
+
+ glMatrixMode(GL_MODELVIEW);
+ }
+
+ S32 scatterTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ S32 diffTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ gPipeline.mWaterProgram.bind();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ if (voskyp->isReflFace(face))
+ {
+ continue;
+ }
+ face->bindTexture(diffTex);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ }
+
+ // Now, disable texture coord generation on texture state 1
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ LLImageGL::unbindTexture(bumpTex, GL_TEXTURE_2D);
+
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisable(GL_TEXTURE_GEN_S); //texture unit 1
+ glDisable(GL_TEXTURE_GEN_T); //texture unit 1
+
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+
+ // Disable texture coordinate and color arrays
+ LLImageGL::unbindTexture(diffTex, GL_TEXTURE_2D);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ stop_glerror();
+
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ glUseProgramObjectARB(0);
+ gPipeline.disableLights();
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+
+ if (refl_face)
+ {
+ glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF);
+ renderReflection(refl_face);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+}
+
+void LLDrawPoolWater::renderReflection(const LLFace* face)
+{
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ if (!voskyp)
+ {
+ return;
+ }
+
+ if (!face->getGeomCount())
+ {
+ return;
+ }
+
+ S8 dr = voskyp->getDrawRefl();
+ if (dr < 0)
+ {
+ return;
+ }
+
+ LLGLSNoFog noFog;
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLViewerImage::bindTexture(mHBTex[dr]);
+
+ LLOverrideFaceColor override(this, face->getFaceColor().mV);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void bindScreenToTexture()
+{
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ GLuint resX = nhpo2(viewport[2]);
+ GLuint resY = nhpo2(viewport[3]);
+
+ glBindTexture(GL_TEXTURE_2D, sScreenTex);
+ GLint cResX;
+ GLint cResY;
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cResX);
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cResY);
+
+ if (cResX != (GLint)resX || cResY != (GLint)resY)
+ {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, resX, resY, 0, GL_RGB, GL_FLOAT, NULL);
+ gImageList.updateMaxResidentTexMem(-1, resX*resY*3);
+ }
+
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, viewport[0], viewport[1], 0, 0, viewport[2], viewport[3]);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ float scale[2];
+ scale[0] = (float) viewport[2]/resX;
+ scale[1] = (float) viewport[3]/resY;
+ glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_FBSCALE], 1, scale);
+
+ LLImageGL::sBoundTextureMemory += resX * resY * 3;
+}
+
+void LLDrawPoolWater::shade()
+{
+ static LLVector2 d1( 0.5f, -0.17f );
+ static LLVector2 d2( 0.58f, -0.67f );
+ static LLVector2 d3( 0.5f, 0.25f );
+
+ static LLVector3 wave1(1,0.42f,1);
+ static LLVector3 wave2(0.58f,0.42f,0.17f);
+ static LLVector3 wave3(0.42f,0.67f,0.33f);
+
+ /*static LLVector2 d1( 0.83f, -1 );
+ static LLVector2 d2( 0.58f, 1 );
+ static LLVector2 d3( 1, -0.88f );
+
+ static LLVector4 wave1(0.75f,0.08f,0.5f,0.67f);
+ static LLVector4 wave2(0.17f,0.33f,0.53f,0.62f);
+ static LLVector4 wave3(0.17f,0.6f,0.67f,1);*/
+
+ /*LLDebugVarMessageBox::show("Wave Direction 1", &d1, LLVector2(1,1), LLVector2(0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave Direction 2", &d2, LLVector2(1,1), LLVector2(0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave Direction 3", &d3, LLVector2(1,1), LLVector2(0.01f, 0.01f));
+
+ LLDebugVarMessageBox::show("Wave 1", &wave1, LLVector3(2,1,4), LLVector3(0.01f, 0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave 2", &wave2, LLVector3(2,1,4), LLVector3(0.01f, 0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave 3", &wave3, LLVector3(2,1,4), LLVector3(0.01f, 0.01f, 0.01f));*/
+
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ LLGLDisable blend(GL_BLEND);
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer();
+
+ LLColor3 light_diffuse(0,0,0);
+ F32 light_exp = 0.0f;
+ LLVector3 light_dir;
+
+ if (gSky.getSunDirection().mV[2] > NIGHTTIME_ELEVATION_COS)
+ {
+ light_dir = gSky.getSunDirection();
+ light_dir.normVec();
+ light_diffuse = gSky.mVOSkyp->getSun().getColorCached();
+ light_diffuse.normVec();
+ light_exp = light_dir * LLVector3(light_dir.mV[0], light_dir.mV[1], 0);
+ light_diffuse *= light_exp + 0.25f;
+ }
+ else
+ {
+ light_dir = gSky.getMoonDirection();
+ light_dir.normVec();
+ light_diffuse = gSky.mVOSkyp->getMoon().getColorCached();
+ light_diffuse.normVec();
+ light_diffuse *= 0.5f;
+ light_exp = light_dir * LLVector3(light_dir.mV[0], light_dir.mV[1], 0);
+ }
+
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= 512.f;
+ light_exp = light_exp > 32.f ? light_exp : 32.f;
+
+ sTime = (F32)LLFrameTimer::getElapsedSeconds()*0.5f;
+
+ LLCubeMap* skyMap = gSky.mVOSkyp->getCubeMap();
+
+ gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ skyMap->bind();
+
+ //bind normal map
+ S32 bumpTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ mWaterNormp->addTextureStats(1024.f*1024.f);
+ mWaterNormp->bind(bumpTex);
+
+ gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_WATER_SCREENTEX);
+
+ gPipeline.mWaterProgram.bind();
+
+ bindScreenToTexture();
+
+ S32 scatterTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ S32 diffTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_TIME], sTime);
+ glUniform3fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_SPECULAR], 1, light_diffuse.mV);
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_SPECULAR_EXP], light_exp);
+ glUniform3fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_EYEVEC], 1, gCamera->getOrigin().mV);
+ glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_WAVE_DIR1], 1, d1.mV);
+ glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_WAVE_DIR2], 1, d2.mV);
+ glUniform3fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_LIGHT_DIR], 1, light_dir.mV);
+
+ LLColor4 water_color;
+ LLVector3 camera_up = gCamera->getUpAxis();
+ F32 up_dot = camera_up * LLVector3::z_axis;
+ if (gCamera->cameraUnderWater())
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.4f);
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_REFSCALE], 0.25f);
+ }
+ else
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.5f*(1.f + up_dot));
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_REFSCALE], 0.01f);
+ }
+ if (water_color.mV[3] > 0.9f)
+ {
+ water_color.mV[3] = 0.9f;
+ }
+
+ glColor4fv(water_color.mV);
+
+ {
+ LLGLDisable cullface(GL_CULL_FACE);
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+
+ if (voskyp->isReflFace(face))
+ {
+ continue;
+ }
+
+ face->bindTexture(diffTex);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+ }
+
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_WATER_SCREENTEX);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ glUseProgramObjectARB(0);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ /*glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);*/
+}
+
+void LLDrawPoolWater::renderForSelect()
+{
+ // Can't select water!
+ return;
+}
+
+
+void LLDrawPoolWater::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ // Can't select water
+ return;
+}
+
+
+LLViewerImage *LLDrawPoolWater::getDebugTexture()
+{
+ return LLViewerImage::sSmokeImagep;
+}
+
+LLColor3 LLDrawPoolWater::getDebugColor() const
+{
+ return LLColor3(0.f, 1.f, 1.f);
+}
diff --git a/indra/newview/lldrawpoolwater.h b/indra/newview/lldrawpoolwater.h
new file mode 100644
index 0000000000..7e661b3718
--- /dev/null
+++ b/indra/newview/lldrawpoolwater.h
@@ -0,0 +1,57 @@
+/**
+ * @file lldrawpoolwater.h
+ * @brief LLDrawPoolWater class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLWATER_H
+#define LL_LLDRAWPOOLWATER_H
+
+#include "lldrawpool.h"
+
+
+class LLFace;
+class LLHeavenBody;
+class LLWaterSurface;
+
+class LLDrawPoolWater: public LLDrawPool
+{
+protected:
+ LLPointer<LLViewerImage> mHBTex[2];
+ LLPointer<LLViewerImage> mWaterImagep;
+ LLPointer<LLViewerImage> mWaterNormp;
+
+ const LLWaterSurface *mWaterSurface;
+public:
+ enum
+ {
+ SHADER_LEVEL_RIPPLE = 2,
+ };
+
+ LLDrawPoolWater();
+ /*virtual*/ ~LLDrawPoolWater();
+
+ /*virtual*/ LLDrawPool *instancePool();
+ static void restoreGL();
+
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ void renderReflection(const LLFace* face);
+ void shade();
+ void renderShaderSimple();
+
+ virtual S32 getMaterialAttribIndex() { return 0; }
+};
+
+void cgErrorCallback();
+
+#endif // LL_LLDRAWPOOLWATER_H
diff --git a/indra/newview/lldriverparam.cpp b/indra/newview/lldriverparam.cpp
new file mode 100644
index 0000000000..e0d1792e6c
--- /dev/null
+++ b/indra/newview/lldriverparam.cpp
@@ -0,0 +1,435 @@
+/**
+ * @file lldriverparam.cpp
+ * @brief A visual parameter that drives (controls) other visual parameters.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldriverparam.h"
+
+#include "llfasttimer.h"
+
+//-----------------------------------------------------------------------------
+// LLDriverParamInfo
+//-----------------------------------------------------------------------------
+
+LLDriverParamInfo::LLDriverParamInfo()
+{
+}
+
+BOOL LLDriverParamInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_driver" ) );
+
+ if( !LLViewerVisualParamInfo::parseXml( node ))
+ return FALSE;
+
+ LLXmlTreeNode* param_driver_node = node->getChildByName( "param_driver" );
+ if( !param_driver_node )
+ return FALSE;
+
+ for (LLXmlTreeNode* child = param_driver_node->getChildByName( "driven" );
+ child;
+ child = param_driver_node->getNextNamedChild())
+ {
+ S32 driven_id;
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ if( child->getFastAttributeS32( id_string, driven_id ) )
+ {
+ F32 min1 = mMinWeight;
+ F32 max1 = mMaxWeight;
+ F32 max2 = max1;
+ F32 min2 = max1;
+
+ // driven ________ //
+ // ^ /| |\ //
+ // | / | | \ //
+ // | / | | \ //
+ // | / | | \ //
+ // | / | | \ //
+ //-------|----|-------|----|-------> driver //
+ // | min1 max1 max2 min2
+
+ static LLStdStringHandle min1_string = LLXmlTree::addAttributeString("min1");
+ child->getFastAttributeF32( min1_string, min1 ); // optional
+ static LLStdStringHandle max1_string = LLXmlTree::addAttributeString("max1");
+ child->getFastAttributeF32( max1_string, max1 ); // optional
+ static LLStdStringHandle max2_string = LLXmlTree::addAttributeString("max2");
+ child->getFastAttributeF32( max2_string, max2 ); // optional
+ static LLStdStringHandle min2_string = LLXmlTree::addAttributeString("min2");
+ child->getFastAttributeF32( min2_string, min2 ); // optional
+
+ // Push these on the front of the deque, so that we can construct
+ // them in order later (faster)
+ mDrivenInfoList.push_front( LLDrivenEntryInfo( driven_id, min1, max1, max2, min2 ) );
+ }
+ else
+ {
+ llerrs << "<driven> Unable to resolve driven parameter: " << driven_id << llendl;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLDriverParam
+//-----------------------------------------------------------------------------
+
+LLDriverParam::LLDriverParam(LLVOAvatar *avatarp)
+ : mCurrentDistortionParam( NULL ), mAvatarp(avatarp)
+{
+}
+
+LLDriverParam::~LLDriverParam()
+{
+}
+
+BOOL LLDriverParam::setInfo(LLDriverParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLDriverParamInfo::entry_info_list_t::iterator iter;
+ mDriven.reserve(getInfo()->mDrivenInfoList.size());
+ for (iter = getInfo()->mDrivenInfoList.begin(); iter != getInfo()->mDrivenInfoList.end(); iter++)
+ {
+ LLDrivenEntryInfo *driven_info = &(*iter);
+ S32 driven_id = driven_info->mDrivenID;
+ LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarp->getVisualParam( driven_id );
+ if (param)
+ {
+ mDriven.push_back(LLDrivenEntry( param, driven_info ));
+ }
+ else
+ {
+ llerrs << "<driven> Unable to resolve driven parameter: " << driven_id << llendl;
+ mInfo = NULL;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#if 0 // obsolete
+BOOL LLDriverParam::parseData(LLXmlTreeNode* node)
+{
+ LLDriverParamInfo* info = new LLDriverParamInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+void LLDriverParam::setWeight(F32 weight, BOOL set_by_user)
+{
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ if (mIsAnimating)
+ {
+ // allow overshoot when animating
+ mCurWeight = weight;
+ }
+ else
+ {
+ mCurWeight = llclamp(weight, min_weight, max_weight);
+ }
+
+ // driven ________
+ // ^ /| |\ ^
+ // | / | | \ |
+ // | / | | \ |
+ // | / | | \ |
+ // | / | | \ |
+ //-------|----|-------|----|-------> driver
+ // | min1 max1 max2 min2
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ LLDrivenEntryInfo* info = driven->mInfo;
+
+ F32 driven_weight = 0.f;
+ F32 driven_min = driven->mParam->getMinWeight();
+ F32 driven_max = driven->mParam->getMaxWeight();
+
+ if (mIsAnimating)
+ {
+ // driven param doesn't interpolate (textures, for example)
+ if (!driven->mParam->getAnimating())
+ {
+ continue;
+ }
+ if( mCurWeight < info->mMin1 )
+ {
+ if (info->mMin1 == min_weight)
+ {
+ if (info->mMin1 == info->mMax1)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ //up slope extrapolation
+ F32 t = (mCurWeight - info->mMin1) / (info->mMax1 - info->mMin1 );
+ driven_weight = driven_min + t * (driven_max - driven_min);
+ }
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+
+ driven->mParam->setWeight( driven_weight, set_by_user );
+ continue;
+ }
+ else
+ if ( mCurWeight > info->mMin2 )
+ {
+ if (info->mMin2 == max_weight)
+ {
+ if (info->mMin2 == info->mMax2)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ //down slope extrapolation
+ F32 t = (mCurWeight - info->mMax2) / (info->mMin2 - info->mMax2 );
+ driven_weight = driven_max + t * (driven_min - driven_max);
+ }
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+
+ driven->mParam->setWeight( driven_weight, set_by_user );
+ continue;
+ }
+ }
+
+ driven_weight = getDrivenWeight(driven, mCurWeight);
+ driven->mParam->setWeight( driven_weight, set_by_user );
+ }
+}
+
+F32 LLDriverParam::getTotalDistortion()
+{
+ F32 sum = 0.f;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum += driven->mParam->getTotalDistortion();
+ }
+
+ return sum;
+}
+
+const LLVector3 &LLDriverParam::getAvgDistortion()
+{
+ // It's not actually correct to take the average of averages, but it good enough here.
+ LLVector3 sum;
+ S32 count = 0;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum += driven->mParam->getAvgDistortion();
+ count++;
+ }
+ sum /= (F32)count;
+
+ mDefaultVec = sum;
+ return mDefaultVec;
+}
+
+F32 LLDriverParam::getMaxDistortion()
+{
+ F32 max = 0.f;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ F32 param_max = driven->mParam->getMaxDistortion();
+ if( param_max > max )
+ {
+ max = param_max;
+ }
+ }
+
+ return max;
+}
+
+
+LLVector3 LLDriverParam::getVertexDistortion(S32 index, LLPolyMesh *poly_mesh)
+{
+ LLVector3 sum;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum += driven->mParam->getVertexDistortion( index, poly_mesh );
+ }
+ return sum;
+}
+
+const LLVector3* LLDriverParam::getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ mCurrentDistortionParam = NULL;
+ const LLVector3* v = NULL;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ v = driven->mParam->getFirstDistortion( index, poly_mesh );
+ if( v )
+ {
+ mCurrentDistortionParam = driven->mParam;
+ break;
+ }
+ }
+
+ return v;
+};
+
+const LLVector3* LLDriverParam::getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ llassert( mCurrentDistortionParam );
+ if( !mCurrentDistortionParam )
+ {
+ return NULL;
+ }
+
+ LLDrivenEntry* driven = NULL;
+ entry_list_t::iterator iter;
+
+ // Set mDriven iteration to the right point
+ for( iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ driven = &(*iter);
+ if( driven->mParam == mCurrentDistortionParam )
+ {
+ break;
+ }
+ }
+
+ // We're already in the middle of a param's distortions, so get the next one.
+ const LLVector3* v = driven->mParam->getNextDistortion( index, poly_mesh );
+ if( !v )
+ {
+ // This param is finished, so start the next param. It might not have any
+ // distortions, though, so we have to loop to find the next param that does.
+ for( iter++; iter != mDriven.end(); iter++ )
+ {
+ driven = &(*iter);
+ v = driven->mParam->getFirstDistortion( index, poly_mesh );
+ if( v )
+ {
+ mCurrentDistortionParam = driven->mParam;
+ break;
+ }
+ }
+ }
+
+ return v;
+};
+
+//-----------------------------------------------------------------------------
+// setAnimationTarget()
+//-----------------------------------------------------------------------------
+void LLDriverParam::setAnimationTarget( F32 target_value, BOOL set_by_user )
+{
+ LLVisualParam::setAnimationTarget(target_value, set_by_user);
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ F32 driven_weight = getDrivenWeight(driven, mTargetWeight);
+
+ // this isn't normally necessary, as driver params handle interpolation of their driven params
+ // but texture params need to know to assume their final value at beginning of interpolation
+ driven->mParam->setAnimationTarget(driven_weight, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// stopAnimating()
+//-----------------------------------------------------------------------------
+void LLDriverParam::stopAnimating(BOOL set_by_user)
+{
+ LLVisualParam::stopAnimating(set_by_user);
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ driven->mParam->setAnimating(FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getDrivenWeight()
+//-----------------------------------------------------------------------------
+F32 LLDriverParam::getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight)
+{
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ const LLDrivenEntryInfo* info = driven->mInfo;
+
+ F32 driven_weight = 0.f;
+ F32 driven_min = driven->mParam->getMinWeight();
+ F32 driven_max = driven->mParam->getMaxWeight();
+
+ if( input_weight <= info->mMin1 )
+ {
+ if( info->mMin1 == info->mMax1 &&
+ info->mMin1 <= min_weight)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+ }
+ else
+ if( input_weight <= info->mMax1 )
+ {
+ F32 t = (input_weight - info->mMin1) / (info->mMax1 - info->mMin1 );
+ driven_weight = driven_min + t * (driven_max - driven_min);
+ }
+ else
+ if( input_weight <= info->mMax2 )
+ {
+ driven_weight = driven_max;
+ }
+ else
+ if( input_weight <= info->mMin2 )
+ {
+ F32 t = (input_weight - info->mMax2) / (info->mMin2 - info->mMax2 );
+ driven_weight = driven_max + t * (driven_min - driven_max);
+ }
+ else
+ {
+ if (info->mMax2 >= max_weight)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+ }
+
+ return driven_weight;
+}
diff --git a/indra/newview/lldriverparam.h b/indra/newview/lldriverparam.h
new file mode 100644
index 0000000000..06850e3eaf
--- /dev/null
+++ b/indra/newview/lldriverparam.h
@@ -0,0 +1,90 @@
+/**
+ * @file lldriverparam.h
+ * @brief A visual parameter that drives (controls) other visual parameters.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRIVERPARAM_H
+#define LL_LLDRIVERPARAM_H
+
+#include "llvoavatar.h"
+#include "llviewervisualparam.h"
+
+//-----------------------------------------------------------------------------
+
+struct LLDrivenEntryInfo
+{
+ LLDrivenEntryInfo( S32 id, F32 min1, F32 max1, F32 max2, F32 min2 )
+ : mDrivenID( id ), mMin1( min1 ), mMax1( max1 ), mMax2( max2 ), mMin2( min2 ) {}
+ S32 mDrivenID;
+ F32 mMin1;
+ F32 mMax1;
+ F32 mMax2;
+ F32 mMin2;
+};
+
+struct LLDrivenEntry
+{
+ LLDrivenEntry( LLViewerVisualParam* param, LLDrivenEntryInfo *info )
+ : mParam( param ), mInfo( info ) {}
+ LLViewerVisualParam* mParam;
+ LLDrivenEntryInfo* mInfo;
+};
+
+//-----------------------------------------------------------------------------
+
+class LLDriverParamInfo : public LLViewerVisualParamInfo
+{
+ friend class LLDriverParam;
+public:
+ LLDriverParamInfo();
+ /*virtual*/ ~LLDriverParamInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ typedef std::deque<LLDrivenEntryInfo> entry_info_list_t;
+ entry_info_list_t mDrivenInfoList;
+};
+
+//-----------------------------------------------------------------------------
+
+class LLDriverParam : public LLViewerVisualParam
+{
+public:
+ LLDriverParam(LLVOAvatar *avatarp);
+ ~LLDriverParam();
+
+ // Special: These functions are overridden by child classes
+ LLDriverParamInfo* getInfo() const { return (LLDriverParamInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLDriverParamInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex ) {} // apply is called separately for each driven param.
+ /*virtual*/ void setWeight(F32 weight, BOOL set_by_user);
+ /*virtual*/ void setAnimationTarget( F32 target_value, BOOL set_by_user );
+ /*virtual*/ void stopAnimating(BOOL set_by_user);
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion();
+ /*virtual*/ const LLVector3& getAvgDistortion();
+ /*virtual*/ F32 getMaxDistortion();
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
+protected:
+ F32 getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight);
+
+
+ LLVector3 mDefaultVec; // temp holder
+ typedef std::vector<LLDrivenEntry> entry_list_t;
+ entry_list_t mDriven;
+ LLViewerVisualParam* mCurrentDistortionParam;
+ LLVOAvatar* mAvatarp;
+};
+
+#endif // LL_LLDRIVERPARAM_H
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
new file mode 100644
index 0000000000..49fcce4911
--- /dev/null
+++ b/indra/newview/lldynamictexture.cpp
@@ -0,0 +1,215 @@
+/**
+ * @file lldynamictexture.cpp
+ * @brief Implementation of LLDynamicTexture class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldynamictexture.h"
+#include "linked_lists.h"
+#include "llimagegl.h"
+#include "llglheaders.h"
+#include "llviewerwindow.h"
+#include "llviewercamera.h"
+#include "llviewercontrol.h"
+#include "llviewerimage.h"
+
+// static
+LLLinkedList<LLDynamicTexture> LLDynamicTexture::sInstances[ LLDynamicTexture::ORDER_COUNT ];
+S32 LLDynamicTexture::sNumRenders = 0;
+
+//-----------------------------------------------------------------------------
+// LLDynamicTexture()
+//-----------------------------------------------------------------------------
+LLDynamicTexture::LLDynamicTexture(S32 width, S32 height, S32 components, EOrder order, BOOL clamp) :
+ mWidth(width),
+ mHeight(height),
+ mComponents(components),
+ mTexture(NULL),
+ mLastBindTime(0),
+ mClamp(clamp)
+{
+ llassert((1 <= components) && (components <= 4));
+
+ generateGLTexture();
+
+ llassert( 0 <= order && order < ORDER_COUNT );
+ LLDynamicTexture::sInstances[ order ].addData(this);
+}
+
+//-----------------------------------------------------------------------------
+// LLDynamicTexture()
+//-----------------------------------------------------------------------------
+LLDynamicTexture::~LLDynamicTexture()
+{
+ for( S32 order = 0; order < ORDER_COUNT; order++ )
+ {
+ LLDynamicTexture::sInstances[order].removeData(this); // will fail in all but one case.
+ }
+}
+
+//-----------------------------------------------------------------------------
+// generateGLTexture()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::generateGLTexture()
+{
+ if (mComponents < 1 || mComponents > 4)
+ {
+ llerrs << "Bad number of components in dynamic texture: " << mComponents << llendl;
+ }
+
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw(mWidth, mHeight, mComponents);
+ mTexture = new LLImageGL(mWidth, mHeight, mComponents, FALSE);
+ mTexture->createGLTexture(0, raw_image);
+ mTexture->setClamp(mClamp, mClamp);
+}
+
+void LLDynamicTexture::generateGLTexture(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes)
+{
+ if (mComponents < 1 || mComponents > 4)
+ {
+ llerrs << "Bad number of components in dynamic texture: " << mComponents << llendl;
+ }
+
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw(mWidth, mHeight, mComponents);
+ mTexture = new LLImageGL(mWidth, mHeight, mComponents, FALSE);
+ mTexture->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes);
+ mTexture->createGLTexture(0, raw_image);
+ mTexture->setClamp(mClamp, mClamp);
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLDynamicTexture::render()
+{
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// preRender()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::preRender(BOOL clear_depth)
+{
+ {
+ // force rendering to on-screen portion of frame buffer
+ LLCoordScreen window_pos;
+ gViewerWindow->getWindow()->getPosition( &window_pos );
+ mOrigin.set(0, gViewerWindow->getWindowDisplayHeight() - mHeight); // top left corner
+
+ if (window_pos.mX < 0)
+ {
+ mOrigin.mX = -window_pos.mX;
+ }
+ if (window_pos.mY < 0)
+ {
+ mOrigin.mY += window_pos.mY;
+ }
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ // Set up camera
+ mCamera.setOrigin(*gCamera);
+ mCamera.setAxes(*gCamera);
+ mCamera.setAspect(gCamera->getAspect());
+ mCamera.setView(gCamera->getView());
+ mCamera.setNear(gCamera->getNear());
+
+ glViewport(mOrigin.mX, mOrigin.mY, mWidth, mHeight);
+ if (clear_depth)
+ {
+ glClear(GL_DEPTH_BUFFER_BIT);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// postRender()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::postRender(BOOL success)
+{
+ {
+ if (success)
+ {
+ success = mTexture->setSubImageFromFrameBuffer(0, 0, mOrigin.mX, mOrigin.mY, mWidth, mHeight);
+ }
+ }
+
+ // restore viewport
+ gViewerWindow->setupViewport();
+
+ // restore camera
+ gCamera->setOrigin(mCamera);
+ gCamera->setAxes(mCamera);
+ gCamera->setAspect(mCamera.getAspect());
+ gCamera->setView(mCamera.getView());
+ gCamera->setNear(mCamera.getNear());
+}
+
+//-----------------------------------------------------------------------------
+// bindTexture()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::bindTexture()
+{
+ LLViewerImage::bindTexture(mTexture,0);
+}
+
+void LLDynamicTexture::unbindTexture()
+{
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+}
+
+//-----------------------------------------------------------------------------
+// static
+// updateDynamicTextures()
+// Calls update on each dynamic texture. Calls each group in order: "first," then "middle," then "last."
+//-----------------------------------------------------------------------------
+BOOL LLDynamicTexture::updateAllInstances()
+{
+ sNumRenders = 0;
+ if (gGLManager.mIsDisabled)
+ {
+ return TRUE;
+ }
+
+ BOOL result = FALSE;
+ for( S32 order = 0; order < ORDER_COUNT; order++ )
+ {
+ for (LLDynamicTexture *dynamicTexture = LLDynamicTexture::sInstances[order].getFirstData();
+ dynamicTexture;
+ dynamicTexture = LLDynamicTexture::sInstances[order].getNextData())
+ {
+ if (dynamicTexture->needsRender())
+ {
+ dynamicTexture->preRender();
+ if (dynamicTexture->render())
+ {
+ result = TRUE;
+ gViewerWindow->finishFastFrame();
+ sNumRenders++;
+ }
+ dynamicTexture->postRender(result);
+ }
+ }
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// static
+// destroyGL()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::destroyGL()
+{
+}
+
+//-----------------------------------------------------------------------------
+// static
+// restoreGL()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::restoreGL()
+{
+}
diff --git a/indra/newview/lldynamictexture.h b/indra/newview/lldynamictexture.h
new file mode 100644
index 0000000000..6ff457e9a9
--- /dev/null
+++ b/indra/newview/lldynamictexture.h
@@ -0,0 +1,67 @@
+/**
+ * @file lldynamictexture.h
+ * @brief Implementation of LLDynamicTexture class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDYNAMICTEXTURE_H
+#define LL_LLDYNAMICTEXTURE_H
+
+#include "llgl.h"
+#include "linked_lists.h"
+#include "llcamera.h"
+#include "llcoord.h"
+#include "llimagegl.h"
+
+class LLDynamicTexture
+{
+public:
+ enum EOrder { ORDER_FIRST = 0, ORDER_MIDDLE = 1, ORDER_LAST = 2, ORDER_RESET = 3, ORDER_COUNT = 4 };
+
+ LLDynamicTexture(S32 width,
+ S32 height,
+ S32 components, // = 4,
+ EOrder order, // = ORDER_MIDDLE,
+ BOOL clamp);
+ virtual ~LLDynamicTexture();
+
+ S32 getOriginX() { return mOrigin.mX; }
+ S32 getOriginY() { return mOrigin.mY; }
+ S32 getWidth() { return mWidth; }
+ S32 getHeight() { return mHeight; }
+ S32 getComponents() { return mComponents; }
+ S32 getSize() { return mWidth * mHeight * mComponents; }
+
+ virtual BOOL needsRender() { return TRUE; }
+ virtual void preRender(BOOL clear_depth = TRUE);
+ virtual BOOL render();
+ virtual void postRender(BOOL success);
+ virtual void bindTexture();
+ virtual void unbindTexture();
+
+ static BOOL updateAllInstances();
+
+ static void destroyGL();
+ static void restoreGL();
+
+protected:
+ void generateGLTexture();
+ void generateGLTexture(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes = FALSE);
+
+protected:
+ S32 mWidth;
+ S32 mHeight;
+ S32 mComponents;
+ LLPointer<LLImageGL> mTexture;
+ F32 mLastBindTime;
+ BOOL mClamp;
+ LLCoordGL mOrigin;
+
+ LLCamera mCamera;
+ static LLLinkedList<LLDynamicTexture> LLDynamicTexture::sInstances[ LLDynamicTexture::ORDER_COUNT ];
+ static S32 sNumRenders;
+};
+
+#endif
diff --git a/indra/newview/llemote.cpp b/indra/newview/llemote.cpp
new file mode 100644
index 0000000000..108410b275
--- /dev/null
+++ b/indra/newview/llemote.cpp
@@ -0,0 +1,126 @@
+/**
+ * @file llemote.cpp
+ * @brief Implementation of LLEmote class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llemote.h"
+#include "llcharacter.h"
+#include "m3math.h"
+#include "llvoavatar.h"
+#include "llagent.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLEmote()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLEmote::LLEmote(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+
+ //RN: flag face joint as highest priority for now, until we implement a proper animation track
+ mJointSignature[0][LL_FACE_JOINT_NUM] = 0xff;
+ mJointSignature[1][LL_FACE_JOINT_NUM] = 0xff;
+ mJointSignature[2][LL_FACE_JOINT_NUM] = 0xff;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLEmote()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLEmote::~LLEmote()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLEmote::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLEmote::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEmote::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLEmote::onActivate()
+{
+ LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
+ if( default_param )
+ {
+ default_param->setWeight( default_param->getMaxWeight(), FALSE );
+ }
+
+ mParam = mCharacter->getVisualParam(mName.c_str());
+ if (mParam)
+ {
+ mParam->setWeight(0.f, FALSE);
+ mCharacter->updateVisualParams();
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEmote::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLEmote::onUpdate(F32 time, U8* joint_mask)
+{
+ if( mParam )
+ {
+ F32 weight = mParam->getMinWeight() + mPose.getWeight() * (mParam->getMaxWeight() - mParam->getMinWeight());
+ mParam->setWeight(weight, FALSE);
+
+ // Cross fade against the default parameter
+ LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
+ if( default_param )
+ {
+ F32 default_param_weight = default_param->getMinWeight() +
+ (1.f - mPose.getWeight()) * ( default_param->getMaxWeight() - default_param->getMinWeight() );
+
+ default_param->setWeight( default_param_weight, FALSE );
+ }
+
+ mCharacter->updateVisualParams();
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEmote::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLEmote::onDeactivate()
+{
+ if( mParam )
+ {
+ mParam->setWeight( mParam->getDefaultWeight(), FALSE );
+ }
+
+ LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
+ if( default_param )
+ {
+ default_param->setWeight( default_param->getMaxWeight(), FALSE );
+ }
+
+ mCharacter->updateVisualParams();
+}
+
+
+// End
diff --git a/indra/newview/llemote.h b/indra/newview/llemote.h
new file mode 100644
index 0000000000..c330d0bfc0
--- /dev/null
+++ b/indra/newview/llemote.h
@@ -0,0 +1,104 @@
+/**
+ * @file llemote.h
+ * @brief Definition of LLEmote class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEMOTE_H
+#define LL_LLEMOTE_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "lltimer.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_EMOTE 2000.f
+
+#define EMOTE_MORPH_FADEIN_TIME 0.3f
+#define EMOTE_MORPH_IN_TIME 1.1f
+#define EMOTE_MORPH_FADEOUT_TIME 1.4f
+
+class LLVisualParam;
+
+//-----------------------------------------------------------------------------
+// class LLEmote
+//-----------------------------------------------------------------------------
+class LLEmote :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLEmote(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLEmote();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLEmote(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return FALSE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return EMOTE_MORPH_FADEIN_TIME + EMOTE_MORPH_IN_TIME + EMOTE_MORPH_FADEOUT_TIME; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return EMOTE_MORPH_FADEIN_TIME; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return EMOTE_MORPH_FADEOUT_TIME; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_EMOTE; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ static BOOL getIndexFromName( const char* name, U32* index );
+
+protected:
+
+ LLCharacter* mCharacter;
+
+ LLVisualParam* mParam;
+};
+
+
+
+#endif // LL_LLEMOTE_H
+
+
diff --git a/indra/newview/lleventinfo.cpp b/indra/newview/lleventinfo.cpp
new file mode 100644
index 0000000000..8f63b61fa1
--- /dev/null
+++ b/indra/newview/lleventinfo.cpp
@@ -0,0 +1,134 @@
+/**
+ * @file lleventinfo.cpp
+ * @brief LLEventInfo class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "lleventinfo.h"
+
+#include "viewer.h" // for gPacificDaylightTime
+#include "lluuid.h"
+#include "message.h"
+
+LLEventInfo::cat_map LLEventInfo::sCategories;
+
+LLEventInfo::LLEventInfo(F32 global_x, F32 global_y,
+ const char* name,
+ U32 id,
+ S32 unix_time,
+ U32 event_flags)
+: mName( name ),
+ mID( id ),
+ mPosGlobal( global_x, global_y, 40.0 ),
+ mUnixTime( unix_time ),
+ mEventFlags(event_flags),
+ mSelected( FALSE )
+{
+ struct tm* timep;
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(unix_time, gPacificDaylightTime);
+
+ S32 display_hour = timep->tm_hour % 12;
+ if (display_hour == 0) display_hour = 12;
+
+ mTimeStr = llformat("% 2d/% 2d % 2d:%02d %s",
+ timep->tm_mon+1,
+ timep->tm_year-100,
+ display_hour,
+ timep->tm_min,
+ (timep->tm_hour < 12 ? "AM" : "PM") );
+}
+
+
+void LLEventInfo::unpack(LLMessageSystem *msg)
+{
+ const U32 MAX_DESC_LENGTH = 1024;
+
+ U32 event_id;
+ msg->getU32("EventData", "EventID", event_id);
+ mID = event_id;
+
+ char buffer[MAX_DESC_LENGTH];
+ msg->getString("EventData", "Name", MAX_DESC_LENGTH, buffer);
+ mName = buffer;
+
+ msg->getString("EventData", "Category", MAX_DESC_LENGTH, buffer);
+ mCategoryStr = buffer;
+
+ msg->getString("EventData", "Date", MAX_DESC_LENGTH, buffer);
+ // *FIX: evil hack to let users know that we don't localize
+ // time information. Hack! This is WRONG.
+ mTimeStr = buffer;
+
+ U32 duration;
+ msg->getU32("EventData","Duration",duration);
+ mDuration = duration;
+
+ msg->getU32("EventData", "DateUTC", mUnixTime);
+
+ msg->getString("EventData", "Desc", MAX_DESC_LENGTH, buffer);
+ mDesc = buffer;
+
+ msg->getString("EventData", "Creator", MAX_DESC_LENGTH, buffer);
+ mRunByID = LLUUID(buffer);
+
+ U32 foo;
+ msg->getU32("EventData", "Cover", foo);
+
+ mHasCover = foo ? TRUE : FALSE;
+ if (mHasCover)
+ {
+ U32 cover;
+ msg->getU32("EventData", "Amount", cover);
+ mCover = cover;
+ }
+
+ char sim_name[256];
+ msg->getString("EventData", "SimName", 256, sim_name);
+ mSimName.assign(sim_name);
+
+ msg->getVector3d("EventData", "GlobalPos", mPosGlobal);
+
+ // Mature content
+ U32 event_flags;
+ msg->getU32("EventData", "EventFlags", event_flags);
+ mEventFlags = event_flags;
+}
+
+// static
+void LLEventInfo::loadCategories(LLUserAuth::options_t event_options)
+{
+ LLUserAuth::options_t::iterator resp_it;
+ for (resp_it = event_options.begin();
+ resp_it != event_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+
+ LLUserAuth::response_t::const_iterator option_it;
+
+ S32 cat_id = 0;
+ option_it = response.find("category_id");
+ if (option_it != response.end())
+ {
+ cat_id = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ continue;
+ }
+
+ // Add the category id/name pair
+ option_it = response.find("category_name");
+ if (option_it != response.end())
+ {
+ LLEventInfo::sCategories[cat_id] = option_it->second;
+ }
+
+ }
+
+}
diff --git a/indra/newview/lleventinfo.h b/indra/newview/lleventinfo.h
new file mode 100644
index 0000000000..d8149a8e27
--- /dev/null
+++ b/indra/newview/lleventinfo.h
@@ -0,0 +1,50 @@
+/**
+ * @file lleventinfo.h
+ * @brief LLEventInfo class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTINFO_H
+#define LL_LLEVENTINFO_H
+
+#include <map>
+
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "lluserauth.h"
+
+class LLMessageSystem;
+
+class LLEventInfo
+{
+public:
+ LLEventInfo() {}
+ LLEventInfo(F32 global_x, F32 global_y, const char* name, U32 id, S32 unix_time, U32 event_flags);
+
+ void unpack(LLMessageSystem *msg);
+
+ static void loadCategories(LLUserAuth::options_t event_options);
+
+public:
+ std::string mName;
+ U32 mID;
+ std::string mDesc;
+ std::string mCategoryStr;
+ U32 mDuration;
+ std::string mTimeStr;
+ LLUUID mRunByID;
+ LLString mSimName;
+ LLVector3d mPosGlobal;
+ U32 mUnixTime; // seconds from 1970
+ BOOL mHasCover;
+ U32 mCover;
+ U32 mEventFlags;
+ BOOL mSelected;
+
+ typedef std::map<U32, std::string> cat_map;
+ static cat_map sCategories;
+};
+
+#endif // LL_LLEVENTINFO_H
diff --git a/indra/newview/lleventnotifier.cpp b/indra/newview/lleventnotifier.cpp
new file mode 100644
index 0000000000..72654b9a59
--- /dev/null
+++ b/indra/newview/lleventnotifier.cpp
@@ -0,0 +1,308 @@
+/**
+ * @file lleventnotifier.cpp
+ * @brief Viewer code for managing event notifications
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lleventnotifier.h"
+
+#include "message.h"
+
+#include "llnotify.h"
+#include "lleventinfo.h"
+#include "llfloaterdirectory.h"
+#include "llfloaterworldmap.h"
+#include "llagent.h"
+
+LLEventNotifier gEventNotifier;
+
+LLEventNotifier::LLEventNotifier()
+{
+}
+
+
+LLEventNotifier::~LLEventNotifier()
+{
+ en_map::iterator iter;
+
+ for (iter = mEventNotifications.begin();
+ iter != mEventNotifications.end();
+ iter++)
+ {
+ delete iter->second;
+ }
+}
+
+
+void LLEventNotifier::update()
+{
+ if (mNotificationTimer.getElapsedTimeF32() > 30.f)
+ {
+ // Check our notifications again and send out updates
+ // if they happen.
+
+ U32 alert_time = time_corrected() + 5 * 60;
+ en_map::iterator iter;
+ for (iter = mEventNotifications.begin();
+ iter != mEventNotifications.end();)
+ {
+ LLEventNotification *np = iter->second;
+
+ if (np->getEventDate() < (alert_time))
+ {
+ LLString::format_map_t args;
+ args["[NAME]"] = np->getEventName();
+ args["[DATE]"] = np->getEventDateStr();
+ LLNotifyBox::showXml("EventNotification", args,
+ notifyCallback, np);
+ mEventNotifications.erase(iter++);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+ mNotificationTimer.reset();
+ }
+}
+
+void LLEventNotifier::load(const LLUserAuth::options_t& event_options)
+{
+ LLUserAuth::options_t::const_iterator resp_it;
+ for (resp_it = event_options.begin();
+ resp_it != event_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+
+ LLEventNotification *new_enp = new LLEventNotification();
+
+ if (!new_enp->load(response))
+ {
+ delete new_enp;
+ continue;
+ }
+
+ mEventNotifications[new_enp->getEventID()] = new_enp;
+ }
+}
+
+
+BOOL LLEventNotifier::hasNotification(const U32 event_id)
+{
+ if (mEventNotifications.find(event_id) != mEventNotifications.end())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void LLEventNotifier::add(LLEventInfo &event_info)
+{
+ // We need to tell the simulator that we want to pay attention to
+ // this event, as well as add it to our list.
+
+ if (mEventNotifications.find(event_info.mID) != mEventNotifications.end())
+ {
+ // We already have a notification for this event, don't bother.
+ return;
+ }
+
+ // Push up a message to tell the server we have this notification.
+ gMessageSystem->newMessage("EventNotificationAddRequest");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlock("EventData");
+ gMessageSystem->addU32("EventID", event_info.mID);
+ gAgent.sendReliableMessage();
+
+ LLEventNotification *enp = new LLEventNotification;
+ enp->load(event_info);
+ mEventNotifications[event_info.mID] = enp;
+}
+
+void LLEventNotifier::remove(const U32 event_id)
+{
+ en_map::iterator iter;
+ iter = mEventNotifications.find(event_id);
+ if (iter == mEventNotifications.end())
+ {
+ // We don't have a notification for this event, don't bother.
+ return;
+ }
+
+ // Push up a message to tell the server to remove this notification.
+ gMessageSystem->newMessage("EventNotificationRemoveRequest");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlock("EventData");
+ gMessageSystem->addU32("EventID", event_id);
+ gAgent.sendReliableMessage();
+
+ delete iter->second;
+ mEventNotifications.erase(iter);
+}
+
+//static
+void LLEventNotifier::notifyCallback(S32 option, void *user_data)
+{
+ LLEventNotification *np = (LLEventNotification *)user_data;
+ if (!np)
+ {
+ llwarns << "Event notification callback without data!" << llendl;
+ return;
+ }
+ switch (option)
+ {
+ case 0:
+ gAgent.teleportViaLocation(np->getEventPosGlobal());
+ gFloaterWorldMap->trackLocation(np->getEventPosGlobal());
+ break;
+ case 1:
+ gDisplayEventHack = TRUE;
+ LLFloaterDirectory::showEvents(np->getEventID());
+ break;
+ case 2:
+ break;
+ }
+
+ // We could clean up the notification on the server now if we really wanted to.
+}
+
+
+
+LLEventNotification::LLEventNotification() :
+ mEventID(0),
+ mEventName("")
+{
+}
+
+
+LLEventNotification::~LLEventNotification()
+{
+}
+
+
+BOOL LLEventNotification::load(const LLUserAuth::response_t &response)
+{
+
+ LLUserAuth::response_t::const_iterator option_it;
+ BOOL event_ok = TRUE;
+ option_it = response.find("event_id");
+ if (option_it != response.end())
+ {
+ mEventID = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("event_name");
+ if (option_it != response.end())
+ {
+ llinfos << "Event: " << option_it->second << llendl;
+ mEventName = option_it->second;
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+
+ option_it = response.find("event_date");
+ if (option_it != response.end())
+ {
+ llinfos << "EventDate: " << option_it->second << llendl;
+ mEventDateStr = option_it->second;
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("event_date_ut");
+ if (option_it != response.end())
+ {
+ llinfos << "EventDate: " << option_it->second << llendl;
+ mEventDate = strtoul(option_it->second.c_str(), NULL, 10);
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ S32 grid_x = 0;
+ S32 grid_y = 0;
+ S32 x_region = 0;
+ S32 y_region = 0;
+
+ option_it = response.find("grid_x");
+ if (option_it != response.end())
+ {
+ llinfos << "GridX: " << option_it->second << llendl;
+ grid_x= atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("grid_y");
+ if (option_it != response.end())
+ {
+ llinfos << "GridY: " << option_it->second << llendl;
+ grid_y = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("x_region");
+ if (option_it != response.end())
+ {
+ llinfos << "RegionX: " << option_it->second << llendl;
+ x_region = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("y_region");
+ if (option_it != response.end())
+ {
+ llinfos << "RegionY: " << option_it->second << llendl;
+ y_region = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ mEventPosGlobal.mdV[VX] = grid_x * 256 + x_region;
+ mEventPosGlobal.mdV[VY] = grid_y * 256 + y_region;
+ mEventPosGlobal.mdV[VZ] = 0.f;
+
+ return event_ok;
+}
+
+BOOL LLEventNotification::load(const LLEventInfo &event_info)
+{
+
+ mEventID = event_info.mID;
+ mEventName = event_info.mName;
+ mEventDateStr = event_info.mTimeStr;
+ mEventDate = event_info.mUnixTime;
+ mEventPosGlobal = event_info.mPosGlobal;
+ return TRUE;
+}
+
diff --git a/indra/newview/lleventnotifier.h b/indra/newview/lleventnotifier.h
new file mode 100644
index 0000000000..90a11ed81d
--- /dev/null
+++ b/indra/newview/lleventnotifier.h
@@ -0,0 +1,68 @@
+/**
+ * @file lleventnotifier.h
+ * @brief Viewer code for managing event notifications
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTNOTIFIER_H
+#define LL_LLEVENTNOTIFIER_H
+
+#include "llframetimer.h"
+#include "lluserauth.h"
+#include "v3dmath.h"
+
+class LLEventInfo;
+class LLEventNotification;
+
+
+class LLEventNotifier
+{
+public:
+ LLEventNotifier();
+ virtual ~LLEventNotifier();
+
+ void update(); // Notify the user of the event if it's coming up
+
+ void load(const LLUserAuth::options_t& event_options); // In the format that it comes in from LLUserAuth
+ void add(LLEventInfo &event_info); // Add a new notification for an event
+ void remove(U32 event_id);
+
+ BOOL hasNotification(const U32 event_id);
+
+ typedef std::map<U32, LLEventNotification *> en_map;
+
+ static void notifyCallback(S32 option, void *user_data);
+protected:
+ en_map mEventNotifications;
+ LLFrameTimer mNotificationTimer;
+};
+
+
+class LLEventNotification
+{
+public:
+ LLEventNotification();
+ virtual ~LLEventNotification();
+
+ BOOL load(const LLUserAuth::response_t &en); // In the format it comes in from LLUserAuth
+ BOOL load(const LLEventInfo &event_info); // From existing event_info on the viewer.
+ //void setEventID(const U32 event_id);
+ //void setEventName(std::string &event_name);
+ U32 getEventID() const { return mEventID; }
+ const std::string &getEventName() const { return mEventName; }
+ U32 getEventDate() const { return mEventDate; }
+ const std::string &getEventDateStr() const { return mEventDateStr; }
+ LLVector3d getEventPosGlobal() const { return mEventPosGlobal; }
+protected:
+ U32 mEventID; // EventID for this event
+ std::string mEventName;
+ std::string mEventDateStr;
+ U32 mEventDate;
+ LLVector3d mEventPosGlobal;
+};
+
+extern LLEventNotifier gEventNotifier;
+
+#endif //LL_LLEVENTNOTIFIER_H
diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp
new file mode 100644
index 0000000000..5e12916975
--- /dev/null
+++ b/indra/newview/lleventpoll.cpp
@@ -0,0 +1,166 @@
+/**
+ * @file lleventpoll.cpp
+ * @brief Implementation of the LLEventPoll class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lleventpoll.h"
+
+#include "llhttpclient.h"
+#include "llhttpnode.h"
+#include "llsdserialize.h"
+
+
+
+class LLEventPoll::Impl : LLHTTPClient::Responder
+{
+public:
+ static Impl& start(
+ const std::string& pollURL, const LLHTTPNode& treeRoot)
+ {
+ Impl* i = new Impl(pollURL, treeRoot);
+ llinfos << "LLEventPoll::Impl::start <" << i->mCount << "> "
+ << pollURL << llendl;
+ return *i;
+ }
+
+ void stop()
+ {
+ llinfos << "LLEventPoll::Impl::stop <" << mCount << "> "
+ << mPollURL << llendl;
+ // there should be a way to stop a LLHTTPClient request in progress
+ mDone = true;
+ mPtr = NULL;
+ }
+
+private:
+ Impl(const std::string& pollURL, const LLHTTPNode& treeRoot)
+ : mPtr(NULL), mDone(false),
+ mPollURL(pollURL), mTreeRoot(treeRoot),
+ mCount(++sCount)
+ {
+ mPtr = this;
+ makeRequest();
+ }
+
+ ~Impl()
+ {
+ lldebugs << "LLEventPoll::Impl::~Impl <" << mCount << "> "
+ << mPollURL << llendl;
+ }
+
+
+ void makeRequest()
+ {
+ LLSD request;
+ request["ack"] = mAcknowledge;
+ request["done"] = mDone;
+
+ lldebugs << "LLEventPoll::Impl::makeRequest <" << mCount << "> ack = "
+ << LLSDXMLStreamer(mAcknowledge) << llendl;
+ LLHTTPClient::post(mPollURL, request, mPtr);
+ }
+
+ void handleMessage(const LLSD& content)
+ {
+ std::string message = content["message"];
+ if (message.empty())
+ {
+ llwarns << "LLEventPoll::Impl::handleMessage <" << mCount
+ << "> empty message name" << llendl;
+ return;
+ }
+
+ std::string path = "/message/" + message;
+
+ LLSD context;
+ const LLHTTPNode* handler = mTreeRoot.traverse(path, context);
+ if (!handler)
+ {
+ llwarns << "LLEventPoll::Impl::handleMessage <" << mCount
+ << "> no handler for " << path << llendl;
+ return;
+ }
+ LLPointer<LLSimpleResponse> responsep = LLSimpleResponse::create();
+ handler->post((LLHTTPNode::ResponsePtr)responsep, context, content["body"]);
+
+ lldebugs << "LLEventPoll::Impl::handleMessage handled <" << mCount << "> "
+ << message << ": " << *responsep << llendl;
+ }
+
+ virtual void error(U32 status, const std::string& reason)
+ {
+ lldebugs << "LLEventPoll::Impl::error <" << mCount << "> got "
+ << status << ": " << reason
+ << (mDone ? " -- done" : "") << llendl;
+
+ if (mDone) return;
+
+ if (status == 404)
+ {
+ // the capability has been revoked
+ stop();
+ return;
+ }
+
+ makeRequest();
+ }
+
+
+ virtual void result(const LLSD& content)
+ {
+ lldebugs << "LLEventPoll::Impl::result <" << mCount << ">"
+ << (mDone ? " -- done" : "") << llendl;
+
+ if (mDone) return;
+
+ mAcknowledge = content["id"];
+ LLSD events = content["events"];
+
+ lldebugs << "LLEventPoll::Impl::completed <" << mCount << "> ack = "
+ << LLSDXMLStreamer(mAcknowledge) << llendl;
+
+ LLSD::array_const_iterator i = events.beginArray();
+ LLSD::array_const_iterator end = events.endArray();
+ for (; i != end; ++i)
+ {
+ if (i->has("message"))
+ {
+ handleMessage(*i);
+ }
+ }
+
+ makeRequest();
+ }
+
+private:
+ typedef LLHTTPClient::ResponderPtr Ptr;
+
+ Ptr mPtr;
+ bool mDone;
+
+ std::string mPollURL;
+ const LLHTTPNode& mTreeRoot;
+
+ LLSD mAcknowledge;
+
+ // these are only here for debugging so we can see which poller is which
+ static int sCount;
+ int mCount;
+};
+
+int LLEventPoll::Impl::sCount = 0;
+
+
+LLEventPoll::LLEventPoll(const std::string& pollURL, const LLHTTPNode& treeRoot)
+ : impl(Impl::start(pollURL, treeRoot))
+ { }
+
+LLEventPoll::~LLEventPoll()
+{
+ impl.stop();
+}
diff --git a/indra/newview/lleventpoll.h b/indra/newview/lleventpoll.h
new file mode 100644
index 0000000000..c5024b2b95
--- /dev/null
+++ b/indra/newview/lleventpoll.h
@@ -0,0 +1,37 @@
+/**
+ * @file lleventpoll.h
+ * @brief LLEvDescription of the LLEventPoll class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTPOLL_H
+#define LL_LLEVENTPOLL_H
+
+class LLHTTPNode;
+
+
+class LLEventPoll
+ ///< implements the viewer side of server-to-viewer pushed events.
+{
+public:
+ LLEventPoll(const std::string& pollURL, const LLHTTPNode& treeRoot);
+ /**< Start polling the URL.
+
+ The object will automatically responde to events
+ by calling handlers in the tree.
+ */
+
+
+ virtual ~LLEventPoll();
+ ///< will stop polling, cancelling any poll in progress.
+
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+#endif // LL_LLEVENTPOLL_H
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
new file mode 100644
index 0000000000..a1b7300ec9
--- /dev/null
+++ b/indra/newview/llface.cpp
@@ -0,0 +1,1939 @@
+/**
+ * @file llface.cpp
+ * @brief LLFace class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawable.h" // lldrawable needs to be included before llface
+#include "llface.h"
+
+#include "llviewercontrol.h"
+#include "llvolume.h"
+#include "m3math.h"
+#include "v3color.h"
+
+#include "llagparray.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolbump.h"
+#include "llgl.h"
+#include "lllightconstants.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llvosky.h"
+#include "llvovolume.h"
+#include "pipeline.h"
+
+#include "llagparray.inl"
+
+#define LL_MAX_INDICES_COUNT 1000000
+
+extern BOOL gPickFaces;
+
+BOOL LLFace::sSafeRenderSelect = TRUE; // FALSE
+
+#define DOTVEC(a,b) (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2])
+
+
+/*
+For each vertex, given:
+ B - binormal
+ T - tangent
+ N - normal
+ P - position
+
+The resulting texture coordinate <u,v> is:
+
+ u = 2(B dot P)
+ v = 2(T dot P)
+*/
+void planarProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+{ //DONE!
+ LLVector3 binormal;
+ float d = vd.mNormal * LLVector3(1,0,0);
+ if (d >= 0.5f || d <= -0.5f)
+ {
+ binormal = LLVector3(0,1,0);
+ if (vd.mNormal.mV[0] < 0)
+ {
+ binormal = -binormal;
+ }
+ }
+ else
+ {
+ binormal = LLVector3(1,0,0);
+ if (vd.mNormal.mV[1] > 0)
+ {
+ binormal = -binormal;
+ }
+ }
+ LLVector3 tangent = binormal % vd.mNormal;
+
+ tc.mV[1] = -((tangent*vec)*2 - 0.5f);
+ tc.mV[0] = 1.0f+((binormal*vec)*2 - 0.5f);
+}
+
+void sphericalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+{ //BROKEN
+ /*tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/3.14159f;
+
+ tc.mV[1] = acosf(vd.mNormal * LLVector3(0,0,1))/6.284f;
+ if (vd.mNormal.mV[1] > 0)
+ {
+ tc.mV[1] = 1.0f-tc.mV[1];
+ }*/
+}
+
+void cylindricalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+{ //BROKEN
+ /*LLVector3 binormal;
+ float d = vd.mNormal * LLVector3(1,0,0);
+ if (d >= 0.5f || d <= -0.5f)
+ {
+ binormal = LLVector3(0,1,0);
+ }
+ else{
+ binormal = LLVector3(1,0,0);
+ }
+ LLVector3 tangent = binormal % vd.mNormal;
+
+ tc.mV[1] = -((tangent*vec)*2 - 0.5f);
+
+ tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/6.284f;
+
+ if (vd.mNormal.mV[1] < 0)
+ {
+ tc.mV[0] = 1.0f-tc.mV[0];
+ }*/
+}
+
+////////////////////
+//
+// LLFace implementation
+//
+
+void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
+{
+ mGeneration = DIRTY;
+ mState = GLOBAL;
+ mDrawPoolp = NULL;
+ mGeomIndex = -1;
+ mSkipRender = FALSE;
+ mNextFace = NULL;
+ // mCenterLocal
+ // mCenterAgent
+ mDistance = 0.f;
+
+ mPrimType = LLTriangles;
+ mGeomCount = 0;
+ mIndicesCount = 0;
+ mIndicesIndex = -1;
+ mTexture = NULL;
+ mTEOffset = -1;
+
+ mBackupMem = NULL;
+
+ setDrawable(drawablep);
+ mVObjp = objp;
+
+ mReferenceIndex = -1;
+ mAlphaFade = 0.f;
+
+ mFaceColor = LLColor4(1,0,0,1);
+}
+
+
+void LLFace::destroy()
+{
+ mDrawablep = NULL;
+ mVObjp = NULL;
+
+ if (mDrawPoolp)
+ {
+ mDrawPoolp->removeFace(this);
+ mDrawPoolp = NULL;
+ }
+
+ // Remove light and blocker list references
+
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+}
+
+
+// static
+void LLFace::initClass()
+{
+}
+
+void LLFace::setWorldMatrix(const LLMatrix4 &mat)
+{
+ llerrs << "Faces on this drawable are not independently modifiable\n" << llendl;
+}
+
+
+void LLFace::setDirty()
+{
+ mGeneration = DIRTY;
+}
+
+void LLFace::setPool(LLDrawPool* new_pool, LLViewerImage *texturep)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!new_pool)
+ {
+ llerrs << "Setting pool to null!" << llendl;
+ }
+
+ if (new_pool != mDrawPoolp)
+ {
+ // Remove from old pool
+ if (mDrawPoolp)
+ {
+ mDrawPoolp->removeFace(this);
+ mSkipRender = FALSE;
+ mNextFace = NULL;
+
+ // Invalidate geometry (will get rebuilt next frame)
+ setDirty();
+ if (mDrawablep)
+ {
+ gPipeline.markRebuild(mDrawablep, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ }
+ if (isState(BACKLIST))
+ {
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+ clearState(BACKLIST);
+ }
+ mGeomIndex = -1;
+
+ // Add to new pool
+ if (new_pool)
+ {
+ new_pool->addFace(this);
+ }
+ mDrawPoolp = new_pool;
+
+ }
+ mTexture = texturep;
+}
+
+
+void LLFace::setTEOffset(const S32 te_offset)
+{
+ mTEOffset = te_offset;
+}
+
+
+void LLFace::setFaceColor(const LLColor4& color)
+{
+ mFaceColor = color;
+ setState(USE_FACE_COLOR);
+}
+
+void LLFace::unsetFaceColor()
+{
+ clearState(USE_FACE_COLOR);
+}
+
+void LLFace::setDrawable(LLDrawable *drawable)
+{
+ mDrawablep = drawable;
+ mXform = &drawable->mXform;
+}
+
+S32 LLFace::allocBackupMem()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ S32 size = 0;
+ size += mIndicesCount * 4;
+ size += mGeomCount * mDrawPoolp->getStride();
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
+ {
+ size += mGeomCount * mDrawPoolp->sDataSizes[LLDrawPool::DATA_VERTEX_WEIGHTS];
+ }
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ size += mGeomCount * mDrawPoolp->sDataSizes[LLDrawPool::DATA_CLOTHING_WEIGHTS];
+ }
+
+ delete[] mBackupMem;
+ mBackupMem = new U8[size];
+ return size;
+}
+
+
+void LLFace::setSize(const S32 num_vertices, const S32 num_indices)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (getState() & SHARED_GEOM)
+ {
+ mGeomCount = num_vertices;
+ mIndicesCount = num_indices;
+ return; // Shared, don't allocate or do anything with memory
+ }
+ if (num_vertices != (S32)mGeomCount || num_indices != (S32)mIndicesCount)
+ {
+ setDirty();
+
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+ clearState(BACKLIST);
+
+ mGeomCount = num_vertices;
+ mIndicesCount = num_indices;
+ }
+
+}
+
+BOOL LLFace::reserveIfNeeded()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (getDirty())
+ {
+ if (isState(BACKLIST))
+ {
+ llwarns << "Reserve on backlisted object!" << llendl;
+ }
+
+ if (0 == mGeomCount)
+ {
+ //llwarns << "Reserving zero bytes for face!" << llendl;
+ mGeomCount = 0;
+ mIndicesCount = 0;
+ return FALSE;
+ }
+
+ mGeomIndex = mDrawPoolp->reserveGeom(mGeomCount);
+ // (reserveGeom() always returns a valid index)
+ mIndicesIndex = mDrawPoolp->reserveInd (mIndicesCount);
+ mGeneration = mDrawPoolp->mGeneration;
+ }
+
+ return TRUE;
+}
+
+void LLFace::unReserve()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!(isState(SHARED_GEOM)))
+ {
+ mGeomIndex = mDrawPoolp->unReserveGeom(mGeomIndex, mGeomCount);
+ mIndicesIndex = mDrawPoolp->unReserveInd(mIndicesIndex, mIndicesCount);
+ }
+}
+
+//============================================================================
+
+S32 LLFace::getGeometryAvatar(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &tex_coords,
+ LLStrider<F32> &vertex_weights,
+ LLStrider<LLVector4> &clothing_weights)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ llerrs << "No backup memory for backlist" << llendl;
+ }
+
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ binormals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
+ tex_coords = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ clothing_weights = (LLVector4*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_CLOTHING_WEIGHTS]);
+ vertex_weights = (F32*)(mBackupMem + (4 * mIndicesCount) + (mGeomCount * mDrawPoolp->getStride()));
+ tex_coords.setStride( mDrawPoolp->getStride());
+ vertices.setStride( mDrawPoolp->getStride());
+ normals.setStride( mDrawPoolp->getStride());
+ binormals.setStride( mDrawPoolp->getStride());
+ clothing_weights.setStride( mDrawPoolp->getStride());
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider (vertices, mGeomIndex);
+ mDrawPoolp->getNormalStrider (normals, mGeomIndex);
+ mDrawPoolp->getBinormalStrider (binormals, mGeomIndex);
+ mDrawPoolp->getTexCoordStrider (tex_coords, mGeomIndex);
+ mDrawPoolp->getVertexWeightStrider(vertex_weights, mGeomIndex);
+ mDrawPoolp->getClothingWeightStrider(clothing_weights, mGeomIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getGeometryTerrain(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLColor4U> &colors,
+ LLStrider<LLVector2> &texcoords0,
+ LLStrider<LLVector2> &texcoords1,
+ U32 *&indicesp)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ colors = (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_COLORS]);
+ texcoords0= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ texcoords1= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS1]);
+ texcoords0.setStride(mDrawPoolp->getStride());
+ texcoords1.setStride(mDrawPoolp->getStride());
+ vertices.setStride( mDrawPoolp->getStride());
+ normals.setStride( mDrawPoolp->getStride());
+ colors.setStride( mDrawPoolp->getStride());
+ indicesp = (U32*)mBackupMem;
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ llinfos << "Get geometry failed!" << llendl;
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ mDrawPoolp->getColorStrider(colors, mGeomIndex);
+ mDrawPoolp->getTexCoordStrider(texcoords0, mGeomIndex, 0);
+ mDrawPoolp->getTexCoordStrider(texcoords1, mGeomIndex, 1);
+
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getGeometry(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &tex_coords, U32 *&indicesp)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ tex_coords= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ tex_coords.setStride(mDrawPoolp->getStride());
+ vertices.setStride( mDrawPoolp->getStride());
+ normals.setStride( mDrawPoolp->getStride());
+ indicesp = (U32*)mBackupMem;
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_NORMALS_MASK)
+ {
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS0_MASK)
+ {
+ mDrawPoolp->getTexCoordStrider(tex_coords, mGeomIndex);
+ }
+
+ indicesp =mDrawPoolp->getIndices (mIndicesIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getGeometryColors(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &tex_coords, LLStrider<LLColor4U> &colors,
+ U32 *&indicesp)
+{
+ S32 res = getGeometry(vertices, normals, tex_coords, indicesp);
+ if (res >= 0)
+ {
+ getColors(colors);
+ }
+ return res;
+}
+
+S32 LLFace::getGeometryMultiTexture(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &tex_coords0,
+ LLStrider<LLVector2> &tex_coords1,
+ U32 *&indicesp)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ tex_coords0 = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ tex_coords0.setStride( mDrawPoolp->getStride() );
+ vertices.setStride( mDrawPoolp->getStride() );
+ normals.setStride( mDrawPoolp->getStride() );
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
+ {
+ binormals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
+ binormals.setStride( mDrawPoolp->getStride() );
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS1_MASK)
+ {
+ tex_coords1 = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS1]);
+ tex_coords1.setStride( mDrawPoolp->getStride() );
+ }
+ indicesp = (U32*)mBackupMem;
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_NORMALS_MASK)
+ {
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS0_MASK)
+ {
+ mDrawPoolp->getTexCoordStrider(tex_coords0, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
+ {
+ mDrawPoolp->getBinormalStrider(binormals, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS1_MASK)
+ {
+ mDrawPoolp->getTexCoordStrider(tex_coords1, mGeomIndex, 1);
+ }
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+void LLFace::updateCenterAgent()
+{
+ mCenterAgent = mCenterLocal * getRenderMatrix();
+}
+
+void LLFace::renderForSelect() const
+{
+ if(mGeomIndex < 0 || mDrawablep.isNull())
+ {
+ return;
+ }
+ if (mVObjp->mGLName)
+ {
+ S32 name = mVObjp->mGLName;
+
+ LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name);
+#if 0 // FIXME: Postponing this fix until we have texcoord pick info...
+ if (mTEOffset != -1)
+ {
+ color.mV[VALPHA] = (U8)(getTextureEntry()->getColor().mV[VALPHA] * 255.f);
+ }
+#endif
+ glColor4ubv(color.mV);
+
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ LLVOVolume *volp;
+ volp = (LLVOVolume *)(LLViewerObject*)mVObjp;
+ if (volp->getNumFaces() == 1 && !volp->getVolumeChanged())
+ {
+ // We need to special case the coalesced face model.
+ S32 num_vfs = volp->getVolume()->getNumFaces();
+ S32 offset = 0;
+ S32 i;
+
+ for (i = 0; i < num_vfs; i++)
+ {
+ if (gPickFaces)
+ {
+ // mask off high 4 bits (16 total possible faces)
+ color.mV[0] &= 0x0f;
+ color.mV[0] |= (i & 0x0f) << 4;
+ glColor4ubv(color.mV);
+ }
+ S32 count = volp->getVolume()->getVolumeFace(i).mIndices.size();
+ if (isState(GLOBAL))
+ {
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ glPopMatrix();
+ }
+ offset += count;
+ }
+ // We're done, return.
+ return;
+ }
+
+ // We don't have coalesced faces, do this the normal way.
+ }
+
+ if (gPickFaces && mTEOffset != -1)
+ {
+ // mask off high 4 bits (16 total possible faces)
+ color.mV[0] &= 0x0f;
+ color.mV[0] |= (mTEOffset & 0x0f) << 4;
+ glColor4ubv(color.mV);
+ }
+
+ if (mIndicesCount)
+ {
+ if (isState(GLOBAL))
+ {
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ glPopMatrix();
+ }
+ }
+ else if (mGeomCount > 0)
+ {
+ if (isState(GLOBAL))
+ {
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ glPopMatrix();
+ }
+ }
+ }
+}
+
+void LLFace::renderSelected(LLImageGL *imagep, const LLColor4& color, const S32 offset, const S32 count)
+{
+ if(mGeomIndex < 0 || mDrawablep.isNull())
+ {
+ return;
+ }
+ if (mGeomCount > 0)
+ {
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ glColor4fv(color.mV);
+
+ LLViewerImage::bindTexture(imagep);
+ if (!isState(GLOBAL))
+ {
+ // Apply the proper transform for non-global objects.
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ }
+
+ if (sSafeRenderSelect)
+ {
+ glBegin(mPrimType);
+ if (count)
+ {
+ for (S32 i = offset; i < offset + count; i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glNormal3fv(normal.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ else
+ {
+ for (U32 i = 0; i < getIndicesCount(); i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glNormal3fv(normal.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ glEnd();
+
+ if( gSavedSettings.getBOOL("ShowTangentBasis") )
+ {
+ S32 start;
+ S32 end;
+ if (count)
+ {
+ start = offset;
+ end = offset + count;
+ }
+ else
+ {
+ start = 0;
+ end = getIndicesCount();
+ }
+
+ LLGLSNoTexture gls_no_texture;
+ glColor4f(1, 1, 1, 1);
+ glBegin(GL_LINES);
+ for (S32 i = start; i < end; i++)
+ {
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv( (vertex + normal * 0.1f).mV );
+ }
+ glEnd();
+
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
+ {
+ glColor4f(0, 1, 0, 1);
+ glBegin(GL_LINES);
+ for (S32 i = start; i < end; i++)
+ {
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ LLVector3 binormal = mDrawPoolp->getBinormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv( (vertex + binormal * 0.1f).mV );
+ }
+ glEnd();
+ }
+ }
+ }
+ else
+ {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (count)
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ }
+ else
+ {
+ llerrs << "Rendering non-indexed volume face!" << llendl;
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ else
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ }
+ else
+ {
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ }
+
+ if (!isState(GLOBAL))
+ {
+ // Restore the tranform for non-global objects
+ glPopMatrix();
+ }
+ }
+}
+
+void LLFace::renderSelectedUV(const S32 offset, const S32 count)
+{
+ LLUUID uv_img_red_blue_id(gViewerArt.getString("uv_test1.tga"));
+ LLUUID uv_img_green_id(gViewerArt.getString("uv_test2.tga"));
+ LLViewerImage* red_blue_imagep = gImageList.getImage(uv_img_red_blue_id, TRUE, TRUE);
+ LLViewerImage* green_imagep = gImageList.getImage(uv_img_green_id, TRUE, TRUE);
+
+ LLGLSObjectSelect object_select;
+ LLGLEnable blend(GL_BLEND);
+ LLGLEnable texture(GL_TEXTURE_2D);
+
+ if (!mDrawPoolp || !getIndicesCount() || getIndicesStart() < 0)
+ {
+ return;
+ }
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ static F32 bias = 0.f;
+ static F32 factor = -10.f;
+ if (mGeomCount > 0)
+ {
+ glColor4fv(LLColor4::white.mV);
+
+ if (pass == 0)
+ {
+ LLViewerImage::bindTexture(red_blue_imagep);
+ }
+ else // pass == 1
+ {
+ glBlendFunc(GL_ONE, GL_ONE);
+ LLViewerImage::bindTexture(green_imagep);
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glScalef(256.f, 256.f, 1.f);
+ }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+
+ if (!isState(GLOBAL))
+ {
+ // Apply the proper transform for non-global objects.
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ }
+
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(factor, bias);
+ if (sSafeRenderSelect)
+ {
+ glBegin(mPrimType);
+ if (count)
+ {
+ for (S32 i = offset; i < offset + count; i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ else
+ {
+ for (U32 i = 0; i < getIndicesCount(); i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ glEnd();
+ }
+ else
+ {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ llassert(mGeomIndex >= 0);
+ if (count)
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ }
+ else
+ {
+ llerrs << "Rendering non-indexed volume face!" << llendl;
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ else
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ }
+ else
+ {
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ }
+
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ if (!isState(GLOBAL))
+ {
+ // Restore the tranform for non-global objects
+ glPopMatrix();
+ }
+ if (pass == 1)
+ {
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ }
+ }
+ }
+
+ //restore blend func
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+
+void LLFace::printDebugInfo() const
+{
+ LLDrawPool *poolp = getPool();
+ llinfos << "Object: " << getViewerObject()->mID << llendl;
+ if (getDrawable())
+ {
+ llinfos << "Type: " << LLPrimitive::pCodeToString(getDrawable()->getVObj()->getPCode()) << llendl;
+ }
+ if (getTexture())
+ {
+ llinfos << "Texture: " << getTexture() << " Comps: " << (U32)getTexture()->getComponents() << llendl;
+ }
+ else
+ {
+ llinfos << "No texture: " << llendl;
+ }
+
+ llinfos << "Face: " << this << llendl;
+ if (isState(BACKLIST))
+ {
+ llinfos << "Backlisted!" << llendl;
+ }
+ llinfos << "State: " << getState() << llendl;
+ llinfos << "Geom Index Data:" << llendl;
+ llinfos << "--------------------" << llendl;
+ llinfos << "GI: " << mGeomIndex << " Count:" << mGeomCount << llendl;
+ llinfos << "Face Index Data:" << llendl;
+ llinfos << "--------------------" << llendl;
+ llinfos << "II: " << mIndicesIndex << " Count:" << mIndicesCount << llendl;
+ llinfos << llendl;
+
+ poolp->printDebugInfo();
+
+ S32 pool_references = 0;
+ for (std::vector<LLFace*>::iterator iter = poolp->mReferences.begin();
+ iter != poolp->mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep == this)
+ {
+ llinfos << "Pool reference: " << pool_references << llendl;
+ pool_references++;
+ }
+ }
+
+ if (pool_references != 1)
+ {
+ llinfos << "Incorrect number of pool references!" << llendl;
+ }
+
+
+ llinfos << "Indices:" << llendl;
+ llinfos << "--------------------" << llendl;
+
+ const U32 *indicesp = getRawIndices();
+ S32 indices_count = getIndicesCount();
+ S32 geom_start = getGeomStart();
+
+ for (S32 i = 0; i < indices_count; i++)
+ {
+ llinfos << i << ":" << indicesp[i] << ":" << (S32)(indicesp[i] - geom_start) << llendl;
+ }
+ llinfos << llendl;
+
+ llinfos << "Vertices:" << llendl;
+ llinfos << "--------------------" << llendl;
+ for (S32 i = 0; i < mGeomCount; i++)
+ {
+ llinfos << mGeomIndex + i << ":" << poolp->getVertex(mGeomIndex + i) << llendl;
+ }
+ llinfos << llendl;
+}
+
+S32 LLFace::backup()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (isState(BACKLIST))
+ {
+ llwarns << "Face is already backed up in LLFace::backup!" << llendl;
+ return mGeomCount;
+ }
+ if (mGeomIndex < 0)
+ {
+ // flexible objects can cause this
+ //llwarns << "No geometry to back-up" << llendl;
+ return 0;
+ }
+
+ S32 size = 0;
+ if (!mBackupMem)
+ {
+ size = allocBackupMem();
+ }
+ else
+ {
+ llerrs << "Memory already backed up!" << llendl;
+ }
+
+ // Need to flag this, because we can allocate a non-zero backup mem if we have indices and no geometry.
+
+ if (mGeomCount || mIndicesCount)
+ {
+ setState(BACKLIST);
+#if !RELEASE_FOR_DOWNLOAD
+ if (mGeomIndex < 0 || mIndicesIndex < 0)
+ {
+ llerrs << "LLFace::backup" << llendl;
+ }
+#endif
+
+ U32 *backup = (U32*)mBackupMem;
+ S32 stride = mDrawPoolp->getStride();
+
+ U32 *index = mDrawPoolp->getIndices(mIndicesIndex);
+ for (U32 i=0;i<mIndicesCount;i++)
+ {
+ *backup++ = index[i] - mGeomIndex;
+ index[i] = 0;
+ }
+
+ if (!mGeomCount)
+ {
+ return mGeomCount;
+ }
+ //
+ // Don't change the order of these unles you change the corresponding getGeometry calls that read out of
+ // backup memory, and also the other of the backup/restore pair!
+ //
+ memcpy(backup, (mDrawPoolp->mMemory.getMem() + mGeomIndex * stride), mGeomCount * stride);
+ backup += mGeomCount * stride / 4;
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ memcpy(backup, &mDrawPoolp->getClothingWeight(mGeomIndex), mGeomCount * sizeof(LLVector4));
+ backup += mGeomCount*4;
+ }
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
+ {
+ memcpy(backup, &mDrawPoolp->getVertexWeight(mGeomIndex), mGeomCount * sizeof(F32));
+ backup += mGeomCount;
+ }
+
+ llassert((U8*)backup - mBackupMem == size);
+
+ unReserve();
+ }
+ return mGeomCount;
+}
+
+void LLFace::restore()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!isState(BACKLIST))
+ {
+ // flexible objects can cause this
+// printDebugInfo();
+// llwarns << "not backlisted for restore" << llendl;
+ return;
+ }
+ if (!mGeomCount || !mBackupMem)
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llwarns << "no backmem for restore" << llendl;
+ }
+
+ clearState(BACKLIST);
+ return;
+ }
+
+ S32 stride = mDrawPoolp->getStride();
+ mGeomIndex = mDrawPoolp->reserveGeom(mGeomCount);
+ mIndicesIndex = mDrawPoolp->reserveInd (mIndicesCount);
+ mGeneration = mDrawPoolp->mGeneration;
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ U32 *backup = (U32*)mBackupMem;
+ U32 *index = mDrawPoolp->getIndices(mIndicesIndex);
+
+ for (U32 i=0;i<mIndicesCount;i++)
+ {
+ S32 ind = mGeomIndex + *backup;
+ index[i] = ind;
+ backup++;
+ }
+
+ mDrawPoolp->mMemory.copyToMem(mGeomIndex * stride, (U8 *)backup, mGeomCount * stride);
+ backup += mGeomCount * stride / 4;
+
+ //
+ // Don't change the order of these unles you change the corresponding getGeometry calls that read out of
+ // backup memory, and also the other of the backup/restore pair!
+ //
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mDrawPoolp->mClothingWeights.copyToMem(mGeomIndex, (U8 *)backup, mGeomCount);
+ backup += mGeomCount*4;
+ }
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mDrawPoolp->mWeights.copyToMem(mGeomIndex, (U8 *)backup, mGeomCount);
+ backup += mGeomCount;
+ }
+
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+ clearState(BACKLIST);
+}
+
+// Transform the texture coordinates for this face.
+static void xform(LLVector2 &tex_coord, F32 cosAng, F32 sinAng, F32 offS, F32 offT, F32 magS, F32 magT)
+{
+ // New, good way
+ F32 s = tex_coord.mV[0];
+ F32 t = tex_coord.mV[1];
+
+ // Texture transforms are done about the center of the face.
+ s -= 0.5;
+ t -= 0.5;
+
+ // Handle rotation
+ F32 temp = s;
+ s = s * cosAng + t * sinAng;
+ t = -temp * sinAng + t * cosAng;
+
+ // Then scale
+ s *= magS;
+ t *= magT;
+
+ // Then offset
+ s += offS + 0.5f;
+ t += offT + 0.5f;
+
+ tex_coord.mV[0] = s;
+ tex_coord.mV[1] = t;
+}
+
+
+BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 f,
+ const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume)
+{
+ const LLVolumeFace &vf = volume.getVolumeFace(f);
+ S32 num_vertices = (S32)vf.mVertices.size();
+ S32 num_indices = (S32)vf.mIndices.size();
+ setSize(num_vertices, num_indices);
+
+ return genVolumeTriangles(volume, f, f, mat, inv_trans_mat, global_volume);
+}
+
+BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, const BOOL global_volume)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!mDrawablep)
+ {
+ return TRUE;
+ }
+
+ S32 index_offset;
+ F32 r, os, ot, ms, mt, cos_ang, sin_ang;
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector3> binormals;
+ LLStrider<LLVector2> tex_coords;
+ LLStrider<LLVector2> tex_coords2;
+ U32 *indicesp = NULL;
+
+ BOOL bump = mDrawPoolp && (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK);
+ BOOL is_static = mDrawablep->isStatic();
+ BOOL is_global = is_static;
+
+ if (bump)
+ {
+ index_offset = getGeometryMultiTexture(vertices, normals, binormals, tex_coords, tex_coords2, indicesp);
+ }
+ else
+ {
+ index_offset = getGeometry(vertices, normals, tex_coords, indicesp);
+ }
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ LLVector3 center_sum(0.f, 0.f, 0.f);
+
+ LLVector3 render_pos;
+
+ if (mDrawablep->isState(LLDrawable::REBUILD_TCOORD) &&
+ global_volume)
+ {
+ render_pos = mVObjp->getRenderPosition();
+ }
+
+ setPrimType(LLTriangles);
+
+ if (is_global)
+ {
+ setState(GLOBAL);
+ }
+ else
+ {
+ clearState(GLOBAL);
+ }
+
+ LLVector3 min, max;
+ LLVector2 tmin, tmax;
+
+ BOOL grab_first_vert = TRUE;
+ BOOL grab_first_tcoord = TRUE;
+
+ for (S32 vol_face = fstart; vol_face <= fend; vol_face++)
+ {
+ const LLVolumeFace &vf = volume.getVolumeFace(vol_face);
+ S32 num_vertices = (S32)vf.mVertices.size();
+ S32 num_indices = (S32)vf.mIndices.size();
+ llassert(num_indices > 0);
+
+ U8 bump_code;
+ const LLTextureEntry *tep = mVObjp->getTE(vol_face);
+
+ if (tep)
+ {
+ bump_code = tep->getBumpmap();
+ r = tep->getRotation();
+ os = tep->mOffsetS;
+ ot = tep->mOffsetT;
+ ms = tep->mScaleS;
+ mt = tep->mScaleT;
+ cos_ang = cos(r);
+ sin_ang = sin(r);
+ }
+ else
+ {
+ bump_code = 0;
+ cos_ang = 1.0f;
+ sin_ang = 0.0f;
+ os = 0.0f;
+ ot = 0.0f;
+ ms = 1.0f;
+ mt = 1.0f;
+ }
+
+ if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ // VERTICES & NORMALS
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector3 v;
+ v = vf.mVertices[i].mPosition * mat_vert;
+
+ LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
+ normal.normVec();
+ *normals++ = normal;
+
+ *vertices++ = v;
+
+ if (grab_first_vert)
+ {
+ grab_first_vert = FALSE;
+ min = max = v;
+ }
+ else
+ {
+ for (U32 j = 0; j < 3; j++)
+ {
+ if (v.mV[j] < min.mV[j])
+ {
+ min.mV[j] = v.mV[j];
+ }
+ if (v.mV[j] > max.mV[j])
+ {
+ max.mV[j] = v.mV[j];
+ }
+ }
+ }
+ }
+ for (S32 i = 0; i < num_indices; i++)
+ {
+ S32 index = vf.mIndices[i] + index_offset;
+ llassert(index >= 0 && (i != 1 || *(indicesp-1)!=(U32)index));
+ *indicesp++ = index;
+ }
+ }
+
+ if ((mDrawablep->isState(LLDrawable::REBUILD_TCOORD)) ||
+ ((bump || getTextureEntry()->getTexGen() != 0) && mDrawablep->isState(LLDrawable::REBUILD_VOLUME)))
+ {
+ // TEX COORDS AND BINORMALS
+ LLVector3 binormal_dir( -sin_ang, cos_ang, 0 );
+ LLVector3 bump_s_primary_light_ray;
+ LLVector3 bump_t_primary_light_ray;
+ if (bump)
+ {
+ F32 offset_multiple;
+ switch( bump_code )
+ {
+ case BE_NO_BUMP:
+ offset_multiple = 0.f;
+ break;
+ case BE_BRIGHTNESS:
+ case BE_DARKNESS:
+ if( mTexture.notNull() && mTexture->getHasGLTexture())
+ {
+ // Offset by approximately one texel
+ S32 cur_discard = mTexture->getDiscardLevel();
+ S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() );
+ max_size <<= cur_discard;
+ const F32 ARTIFICIAL_OFFSET = 2.f;
+ offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size;
+ }
+ else
+ {
+ offset_multiple = 1.f/256;
+ }
+ break;
+
+ default: // Standard bumpmap textures. Assumed to be 256x256
+ offset_multiple = 1.f / 256;
+ break;
+ }
+
+ F32 s_scale = 1.f;
+ F32 t_scale = 1.f;
+ if( tep )
+ {
+ tep->getScale( &s_scale, &t_scale );
+ }
+ LLVector3 sun_ray = gSky.getSunDirection();
+ LLVector3 moon_ray = gSky.getMoonDirection();
+ LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray;
+ bump_s_primary_light_ray = offset_multiple * s_scale * primary_light_ray;
+ bump_t_primary_light_ray = offset_multiple * t_scale * primary_light_ray;
+ }
+
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector2 tc = vf.mVertices[i].mTexCoord;
+
+ U8 texgen = getTextureEntry()->getTexGen();
+ if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
+ {
+
+ LLVector3 vec = vf.mVertices[i].mPosition; //-vf.mCenter;
+
+ if (global_volume)
+ {
+ vec -= render_pos;
+ }
+ else
+ {
+ vec.scaleVec(mVObjp->getScale());
+ }
+
+ switch (texgen)
+ {
+ case LLTextureEntry::TEX_GEN_PLANAR:
+ planarProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+ break;
+ case LLTextureEntry::TEX_GEN_SPHERICAL:
+ sphericalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+ break;
+ case LLTextureEntry::TEX_GEN_CYLINDRICAL:
+ cylindricalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+ break;
+ default:
+ break;
+ }
+ }
+ xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
+ *tex_coords++ = tc;
+ if (grab_first_tcoord)
+ {
+ grab_first_tcoord = FALSE;
+ tmin = tmax = tc;
+ }
+ else
+ {
+ for (U32 j = 0; j < 2; j++)
+ {
+ if (tmin.mV[j] > tc.mV[j])
+ {
+ tmin.mV[j] = tc.mV[j];
+ }
+ else if (tmax.mV[j] < tc.mV[j])
+ {
+ tmax.mV[j] = tc.mV[j];
+ }
+ }
+ }
+ if (bump)
+ {
+ LLVector3 tangent = vf.mVertices[i].mBinormal % vf.mVertices[i].mNormal;
+ LLMatrix3 tangent_to_object;
+ tangent_to_object.setRows(tangent, vf.mVertices[i].mBinormal, vf.mVertices[i].mNormal);
+ LLVector3 binormal = binormal_dir * tangent_to_object;
+
+ if (!global_volume)
+ {
+ binormal = binormal * mat_normal;
+ }
+ binormal.normVec();
+ tangent.normVec();
+
+ tc += LLVector2( bump_s_primary_light_ray * tangent, bump_t_primary_light_ray * binormal );
+ *tex_coords2++ = tc;
+
+ *binormals++ = binormal;
+ }
+ }
+ }
+
+ index_offset += num_vertices;
+
+ center_sum += vf.mCenter * mat_vert;
+ }
+
+ center_sum /= (F32)(fend-fstart+1);
+
+ if (is_static)
+ {
+ mCenterAgent = center_sum;
+ mCenterLocal = mCenterAgent - mDrawablep->getPositionAgent();
+ }
+ else
+ {
+ mCenterLocal = center_sum;
+ updateCenterAgent();
+ }
+
+ if (!grab_first_vert && mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ mExtents[0] = min;
+ mExtents[1] = max;
+ }
+
+ if (!grab_first_tcoord && mDrawablep->isState(LLDrawable::REBUILD_TCOORD))
+ {
+ mTexExtents[0] = tmin;
+ mTexExtents[1] = tmax;
+ }
+
+ return TRUE;
+}
+
+BOOL LLFace::genLighting(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL do_lighting)
+{
+ if (drawablep->isLight())
+ {
+ do_lighting = FALSE;
+ }
+
+ if (!((mDrawPoolp->mDataMaskIL) & LLDrawPool::DATA_COLORS_MASK))
+ {
+ return FALSE;
+ }
+ if (mGeomIndex < 0)
+ {
+ return FALSE; // no geometry
+ }
+ LLStrider<LLColor4U> colorsp;
+ S32 idx = getColors(colorsp);
+ if (idx < 0)
+ {
+ return FALSE;
+ }
+
+ for (S32 vol_face = fstart; vol_face <= fend; vol_face++)
+ {
+ const LLVolumeFace &vf = volume->getVolumeFace(vol_face);
+ S32 num_vertices = (S32)vf.mVertices.size();
+
+ if (isState(FULLBRIGHT) || !do_lighting)
+ {
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ (*colorsp++).setToBlack();
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector3 vertex = vf.mVertices[i].mPosition * mat_vert;
+ LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
+ normal.normVec();
+
+ LLColor4 color;
+ for (LLDrawable::drawable_set_t::const_iterator iter = drawablep->mLightSet.begin();
+ iter != drawablep->mLightSet.end(); ++iter)
+ {
+ LLDrawable* light_drawable = *iter;
+ LLVOVolume* light = light_drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ LLColor4 light_color;
+ light->calcLightAtPoint(vertex, normal, light_color);
+ color += light_color;
+ }
+
+ color.mV[3] = 1.0f;
+
+ (*colorsp++).setVecScaleClamp(color);
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLFace::genShadows(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL use_shadow_factor)
+{
+ if (drawablep->isLight())
+ {
+ return FALSE;
+ }
+
+ if (!((mDrawPoolp->mDataMaskIL) & LLDrawPool::DATA_COLORS_MASK))
+ {
+ return FALSE;
+ }
+ if (mGeomIndex < 0)
+ {
+ return FALSE; // no geometry
+ }
+ LLStrider<LLColor4U> colorsp;
+ S32 idx = getColors(colorsp);
+ if (idx < 0)
+ {
+ return FALSE;
+ }
+
+ for (S32 vol_face = fstart; vol_face <= fend; vol_face++)
+ {
+ const LLVolumeFace &vf = volume->getVolumeFace(vol_face);
+ S32 num_vertices = (S32)vf.mVertices.size();
+
+ if (isState(FULLBRIGHT))
+ {
+ continue;
+ }
+
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector3 vertex = vf.mVertices[i].mPosition * mat_vert;
+ LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
+ normal.normVec();
+
+ U8 shadow;
+
+ if (use_shadow_factor)
+ {
+ shadow = (U8) (drawablep->getSunShadowFactor() * 255);
+ }
+ else
+ {
+ shadow = 255;
+ }
+
+ (*colorsp++).mV[3] = shadow;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLFace::verify(const U32* indices_array) const
+{
+ BOOL ok = TRUE;
+ // First, check whether the face data fits within the pool's range.
+ if ((mGeomIndex < 0) || (mGeomIndex + mGeomCount) > (S32)getPool()->getVertexCount())
+ {
+ ok = FALSE;
+ llinfos << "Face not within pool range!" << llendl;
+ }
+
+ S32 indices_count = (S32)getIndicesCount();
+ S32 geom_start = getGeomStart();
+ S32 geom_count = mGeomCount;
+
+ if (!indices_count)
+ {
+ return TRUE;
+ }
+
+ if (indices_count > LL_MAX_INDICES_COUNT)
+ {
+ ok = FALSE;
+ llinfos << "Face has bogus indices count" << llendl;
+ }
+
+ const U32 *indicesp = indices_array ? indices_array + mIndicesIndex : getRawIndices();
+
+ for (S32 i = 0; i < indices_count; i++)
+ {
+ S32 delta = indicesp[i] - geom_start;
+ if (0 > delta)
+ {
+ llwarns << "Face index too low!" << llendl;
+ llinfos << "i:" << i << " Index:" << indicesp[i] << " GStart: " << geom_start << llendl;
+ ok = FALSE;
+ }
+ else if (delta >= geom_count)
+ {
+ llwarns << "Face index too high!" << llendl;
+ llinfos << "i:" << i << " Index:" << indicesp[i] << " GEnd: " << geom_start + geom_count << llendl;
+ ok = FALSE;
+ }
+ }
+
+ if (!ok)
+ {
+ printDebugInfo();
+ }
+ return ok;
+}
+
+
+void LLFace::setViewerObject(LLViewerObject* objp)
+{
+ mVObjp = objp;
+}
+
+void LLFace::enableLights() const
+{
+ if (isState(FULLBRIGHT|HUD_RENDER))
+ {
+ gPipeline.enableLightsFullbright(LLColor4::white);
+ }
+ else if (mDrawablep->isState(LLDrawable::LIGHTING_BUILT))
+ {
+ gPipeline.enableLightsStatic(1.f);
+ }
+ else
+ {
+ gPipeline.enableLightsDynamic(1.f);
+ }
+ if (isState(LIGHT))
+ {
+ const LLVOVolume* vovolume = (const LLVOVolume*)(mDrawablep->getVObj());
+ gPipeline.setAmbient(vovolume->getLightColor());
+ }
+}
+
+const LLColor4& LLFace::getRenderColor() const
+{
+ if (isState(USE_FACE_COLOR))
+ {
+ return mFaceColor; // Face Color
+ }
+ else
+ {
+ const LLTextureEntry* tep = getTextureEntry();
+ return (tep ? tep->getColor() : LLColor4::white);
+ }
+}
+
+void LLFace::renderSetColor() const
+{
+ if (!LLDrawPool::LLOverrideFaceColor::sOverrideFaceColor)
+ {
+ const LLColor4* color = &(getRenderColor());
+
+ if ((mDrawPoolp->mVertexShaderLevel > 0) && (mDrawPoolp->getMaterialAttribIndex() != 0))
+ {
+ glVertexAttrib4fvARB(mDrawPoolp->getMaterialAttribIndex(), color->mV);
+ }
+ else
+ {
+ glColor4fv(color->mV);
+ }
+ }
+}
+
+S32 LLFace::pushVertices(const U32* index_array) const
+{
+ U32 indices_count = mIndicesCount;
+ S32 ret = 0;
+#if ENABLE_FACE_LINKING
+ LLFace* next = mNextFace;
+#endif
+
+ if (mGeomCount < gGLManager.mGLMaxVertexRange && (S32) indices_count < gGLManager.mGLMaxIndexRange)
+ {
+ LLFace* current = (LLFace*) this;
+ S32 geom_count = mGeomCount;
+#if ENABLE_FACE_LINKING
+ while (current)
+ {
+ //chop up batch into implementation recommended sizes
+ while (next &&
+ (current == next ||
+ ((S32) (indices_count + next->mIndicesCount) < gGLManager.mGLMaxIndexRange &&
+ geom_count + next->mGeomCount < gGLManager.mGLMaxVertexRange)))
+ {
+ indices_count += next->mIndicesCount;
+ geom_count += next->mGeomCount;
+ next = next->mNextFace;
+ }
+#endif
+ if (indices_count)
+ {
+ glDrawRangeElements(mPrimType, current->mGeomIndex, current->mGeomIndex + geom_count, indices_count,
+ GL_UNSIGNED_INT, index_array + current->mIndicesIndex);
+ }
+ ret += (S32) indices_count;
+ indices_count = 0;
+ geom_count = 0;
+#if ENABLE_FACE_LINKING
+ current = next;
+ }
+#endif
+ }
+ else
+ {
+#if ENABLE_FACE_LINKING
+ while (next)
+ {
+ indices_count += next->mIndicesCount;
+ next = next->mNextFace;
+ }
+#endif
+ if (indices_count)
+ {
+ glDrawElements(mPrimType, indices_count, GL_UNSIGNED_INT, index_array + mIndicesIndex);
+ }
+ ret += (S32) indices_count;
+ }
+
+ return ret;
+
+}
+
+const LLMatrix4& LLFace::getRenderMatrix() const
+{
+ return mDrawablep->getRenderMatrix();
+}
+
+S32 LLFace::renderElements(const U32 *index_array) const
+{
+ S32 ret = 0;
+
+ if (isState(GLOBAL))
+ {
+ ret = pushVertices(index_array);
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ ret = pushVertices(index_array);
+ glPopMatrix();
+ }
+
+ return ret;
+}
+
+S32 LLFace::renderIndexed(const U32 *index_array) const
+{
+ if (mSkipRender)
+ {
+ return 0;
+ }
+
+ if(mGeomIndex < 0 || mDrawablep.isNull())
+ {
+ return 0;
+ }
+
+ renderSetColor();
+ return renderElements(index_array);
+}
+
+//============================================================================
+// From llface.inl
+
+S32 LLFace::getVertices(LLStrider<LLVector3> &vertices)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ vertices.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ if (mGeomIndex >= 0) // flexible objects may not have geometry
+ {
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ mDrawPoolp->setDirty();
+ }
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getColors(LLStrider<LLColor4U> &colors)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ llassert(mBackupMem);
+ colors = (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_COLORS]);
+ colors.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getColorStrider(colors, mGeomIndex);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getIndices(U32* &indicesp)
+{
+ if (isState(BACKLIST))
+ {
+ indicesp = (U32*)mBackupMem;
+ return 0;
+ }
+ else
+ {
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+ llassert(mGeomIndex >= 0 && indicesp[0] != indicesp[1]);
+ return mGeomIndex;
+ }
+}
+
+void LLFace::link(LLFace* facep)
+{
+#if ENABLE_FACE_LINKING
+ mNextFace = facep;
+ facep->mSkipRender = TRUE;
+#endif
+}
+
+LLVector3 LLFace::getPositionAgent() const
+{
+ if (mDrawablep->isStatic())
+ {
+ return mCenterAgent;
+ }
+ else
+ {
+ return mCenterLocal * getRenderMatrix();
+ }
+}
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
new file mode 100644
index 0000000000..0d017d6efe
--- /dev/null
+++ b/indra/newview/llface.h
@@ -0,0 +1,250 @@
+/**
+ * @file llface.h
+ * @brief LLFace class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFACE_H
+#define LL_LLFACE_H
+
+#include "llstrider.h"
+
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "v4coloru.h"
+#include "llquaternion.h"
+#include "xform.h"
+#include "lldarrayptr.h"
+#include "llpagemem.h"
+#include "llstat.h"
+#include "lldrawable.h"
+
+#define ENABLE_FACE_LINKING 1 // Causes problems with snapshot rendering
+
+class LLDrawPool;
+class LLVolume;
+class LLViewerImage;
+class LLTextureEntry;
+class LLVertexProgram;
+class LLViewerImage;
+
+class LLFace
+{
+public:
+
+ enum EMasks
+ {
+ SHARED_GEOM = 0x0001,
+ LIGHT = 0x0002,
+ REBUILD = 0x0004,
+ GLOBAL = 0x0008,
+ VISIBLE = 0x0010,
+ BACKLIST = 0x0020,
+ INTERP = 0x0040,
+ FULLBRIGHT = 0x0080,
+ HUD_RENDER = 0x0100,
+ USE_FACE_COLOR = 0x0200,
+
+ POINT_SPRITE = 0x10000,
+ BOARD_SPRITE = 0x20000,
+ FIXED_SPRITE = 0x40000,
+ DEPTH_SPRITE = 0x80000
+ };
+
+ enum EDirty
+ {
+ DIRTY = -2
+ };
+
+ static void initClass();
+
+public:
+ LLFace(LLDrawable* drawablep, LLViewerObject* objp) { init(drawablep, objp); }
+ ~LLFace() { destroy(); }
+
+ const LLMatrix4& getWorldMatrix() const { return mVObjp->getWorldMatrix(mXform); }
+ const LLMatrix4& getRenderMatrix() const;
+ const U32 getIndicesCount() const { return mIndicesCount; };
+ const S32 getIndicesStart() const { return mIndicesIndex; };
+ const S32 getGeomCount() const { return mGeomCount; } // vertex count for this face
+ const S32 getGeomIndex() const { return mGeomIndex; } // index into draw pool
+ const U32 getGeomStart() const { return mGeomIndex; } // index into draw pool
+ LLViewerImage* getTexture() const { return mTexture; }
+ LLXformMatrix* getXform() const { return mXform; }
+ BOOL hasGeometry() const { return mGeomCount > 0; }
+ LLVector3 getPositionAgent() const;
+ void setPrimType(U32 primType) { mPrimType = primType; }
+ const U32 getPrimType() const { return mPrimType; }
+
+ U32 getState() const { return mState; }
+ void setState(U32 state) { mState |= state; }
+ void clearState(U32 state) { mState &= ~state; }
+ BOOL isState(U32 state) const { return ((mState & state) != 0); }
+
+ void bindTexture(S32 stage = 0) const { LLViewerImage::bindTexture(mTexture, stage); }
+
+ void enableLights() const;
+ void renderSetColor() const;
+ S32 renderElements(const U32 *index_array) const;
+ S32 renderIndexed (const U32 *index_array) const;
+ S32 pushVertices(const U32* index_array) const;
+
+ void setWorldMatrix(const LLMatrix4& mat);
+ const LLTextureEntry* getTextureEntry() const { return mVObjp->getTE(mTEOffset); }
+
+ LLDrawPool* getPool() const { return mDrawPoolp; }
+ S32 getStride() const { return mDrawPoolp->getStride(); }
+ const U32* getRawIndices() const { return &mDrawPoolp->mIndices[mIndicesIndex]; }
+ LLDrawable* getDrawable() const { return mDrawablep; }
+ LLViewerObject* getViewerObject() const { return mVObjp; }
+
+ void clearDirty() { mGeneration = mDrawPoolp->mGeneration; };
+
+ S32 backup();
+ void restore();
+
+ void setViewerObject(LLViewerObject* object);
+ void setPool(LLDrawPool *pool, LLViewerImage *texturep);
+ void setDrawable(LLDrawable *drawable);
+ void setTEOffset(const S32 te_offset);
+ S32 getTEOffset() { return mTEOffset; }
+
+ void setFaceColor(const LLColor4& color); // override material color
+ void unsetFaceColor(); // switch back to material color
+ const LLColor4& getFaceColor() const { return mFaceColor; }
+ const LLColor4& getRenderColor() const;
+
+ void unReserve(); // Set Removed from pool
+
+ BOOL reserveIfNeeded(); // Reserves data if dirty.
+
+ // For avatar
+ S32 getGeometryAvatar(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &texCoords,
+ LLStrider<F32> &vertex_weights,
+ LLStrider<LLVector4> &clothing_weights);
+
+ // For terrain
+ S32 getGeometryTerrain(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLColor4U> &colors,
+ LLStrider<LLVector2> &texCoords0,
+ LLStrider<LLVector2> &texCoords1,
+ U32* &indices);
+
+ // For volumes, etc.
+ S32 getGeometry(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &texCoords,
+ U32* &indices);
+
+ S32 getGeometryColors(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &texCoords,
+ LLStrider<LLColor4U> &colors,
+ U32* &indices);
+
+ S32 getGeometryMultiTexture(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &texCoords0,
+ LLStrider<LLVector2> &texCoords1,
+ U32* &indices);
+
+
+ S32 getVertices (LLStrider<LLVector3> &vertices);
+ S32 getColors (LLStrider<LLColor4U> &colors);
+ S32 getIndices (U32* &indices);
+
+ void setSize(const S32 numVertices, const S32 num_indices = 0);
+ BOOL getDirty() const { return (mGeneration != mDrawPoolp->mGeneration); }
+
+ BOOL genVolumeTriangles(const LLVolume &volume, S32 f,
+ const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume = FALSE);
+ BOOL genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
+ const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume = FALSE);
+ BOOL genLighting(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL do_lighting);
+
+ BOOL genShadows(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL use_shadow_factor);
+
+ void init(LLDrawable* drawablep, LLViewerObject* objp);
+ void destroy();
+ void update();
+
+ void updateCenterAgent(); // Update center when xform has changed.
+ void renderSelectedUV(const S32 offset = 0, const S32 count = 0);
+
+ void renderForSelect() const;
+ void renderSelected(LLImageGL *image, const LLColor4 &color, const S32 offset = 0, const S32 count = 0);
+
+ F32 getKey() const { return mDistance; }
+
+ S32 getGeneration() const { return mGeneration; }
+ S32 getReferenceIndex() const { return mReferenceIndex; }
+ void setReferenceIndex(const S32 index) { mReferenceIndex = index; }
+
+ BOOL verify(const U32* indices_array = NULL) const;
+ void printDebugInfo() const;
+
+ void link(LLFace* facep);
+
+protected:
+ S32 allocBackupMem(); // Allocate backup memory based on the draw pool information.
+ void setDirty();
+
+public:
+ LLVector3 mCenterLocal;
+ LLVector3 mCenterAgent;
+ LLVector3 mExtents[2];
+ LLVector2 mTexExtents[2];
+ F32 mDistance;
+ F32 mAlphaFade;
+ LLFace* mNextFace;
+ BOOL mSkipRender;
+
+protected:
+ S32 mGeneration;
+ U32 mState;
+ LLDrawPool* mDrawPoolp;
+ S32 mGeomIndex; // index into draw pool
+ LLColor4 mFaceColor; // overrides material color if state |= USE_FACE_COLOR
+
+ U32 mPrimType;
+ S32 mGeomCount; // vertex count for this face
+ U32 mIndicesCount;
+ S32 mIndicesIndex; // index into draw pool for indices (yeah, I know!)
+ LLXformMatrix* mXform;
+ LLPointer<LLViewerImage> mTexture;
+
+ U8 *mBackupMem;
+
+ LLPointer<LLDrawable> mDrawablep;
+ LLPointer<LLViewerObject> mVObjp;
+ S32 mTEOffset;
+
+ S32 mReferenceIndex;
+
+protected:
+ static BOOL sSafeRenderSelect;
+
+public:
+ struct CompareDistanceGreater
+ {
+ bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+ {
+ return lhs->mDistance > rhs->mDistance; // farthest = first
+ }
+ };
+
+};
+
+#endif // LL_LLFACE_H
diff --git a/indra/newview/llface.inl b/indra/newview/llface.inl
new file mode 100644
index 0000000000..08beef5fd8
--- /dev/null
+++ b/indra/newview/llface.inl
@@ -0,0 +1,222 @@
+/**
+ * @file llface.inl
+ * @brief Inline functions for LLFace
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFACE_INL
+#define LL_LLFACE_INL
+
+#include "llglheaders.h"
+
+inline BOOL LLFace::getDirty() const
+{
+ return (mGeneration != mDrawPoolp->mGeneration);
+}
+
+inline void LLFace::clearDirty()
+{
+ mGeneration = mDrawPoolp->mGeneration;
+}
+
+inline const LLTextureEntry* LLFace::getTextureEntry() const
+{
+ return mVObjp->getTE(mTEOffset);
+}
+
+inline LLDrawPool* LLFace::getPool() const
+{
+ return mDrawPoolp;
+}
+
+inline S32 LLFace::getStride() const
+{
+ return mDrawPoolp->getStride();
+}
+
+inline LLDrawable* LLFace::getDrawable() const
+{
+ return mDrawablep;
+}
+
+inline LLViewerObject* LLFace::getViewerObject() const
+{
+ return mVObjp;
+}
+
+
+
+inline S32 LLFace::getVertices(LLStrider<LLVector3> &vertices)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ vertices.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getNormals(LLStrider<LLVector3> &normals)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ normals.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getBinormals(LLStrider<LLVector3> &binormals)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ binormals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
+ binormals.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getBinormalStrider(binormals, mGeomIndex);
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+
+inline S32 LLFace::getColors (LLStrider<LLColor4U> &colors)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ LLColor4U *colorp = NULL;
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ colorp = (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + (mGeomCount * mDrawPoolp->getStride()));
+ colors = colorp;
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ if (!mDrawPoolp->getColorStrider(colors, mGeomIndex))
+ {
+ printDebugInfo();
+ llerrs << "No color pointer for a color strider!" << llendl;
+ }
+ mDrawPoolp->setDirtyColors();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getTexCoords (LLStrider<LLVector2> &texCoords, S32 pass )
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ texCoords = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0 + pass]);
+ texCoords.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getTexCoordStrider(texCoords, mGeomIndex, pass );
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getIndices (U32* &indicesp)
+{
+ if (isState(BACKLIST))
+ {
+ indicesp = (U32*)mBackupMem;
+ return 0;
+ }
+ else
+ {
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+inline const U32* LLFace::getRawIndices() const
+{
+ llassert(!isState(BACKLIST));
+
+ return &mDrawPoolp->mIndices[mIndicesIndex];
+}
+
+
+inline void LLFace::bindTexture(S32 stage) const
+{
+ if (mTexture)
+ {
+ mTexture->bindTexture(stage);
+ }
+ else
+ {
+ LLImageGL::unbindTexture(stage, GL_TEXTURE_2D);
+ }
+}
+
+#endif
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
new file mode 100644
index 0000000000..c8c1d0d878
--- /dev/null
+++ b/indra/newview/llfasttimerview.cpp
@@ -0,0 +1,1084 @@
+/**
+ * @file llfasttimerview.cpp
+ * @brief LLFastTimerView class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "indra_constants.h"
+#include "llfasttimerview.h"
+#include "llviewerwindow.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llmath.h"
+#include "llfontgl.h"
+
+#include "viewer.h"
+#include "llviewerimagelist.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "llstat.h"
+
+#include "llfasttimer.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const S32 MAX_VISIBLE_HISTORY = 10;
+static const S32 LINE_GRAPH_HEIGHT = 240;
+
+struct ft_display_info {
+ int timer;
+ const char *desc;
+ LLColor4 *color;
+ S32 disabled; // initialized to 0
+ int level; // calculated based on desc
+ int parent; // calculated
+};
+
+static LLColor4 red0(0.5f, 0.0f, 0.0f, 1.0f);
+static LLColor4 green0(0.0f, 0.5f, 0.0f, 1.0f);
+static LLColor4 blue0(0.0f, 0.0f, 0.5f, 1.0f);
+static LLColor4 blue7(0.0f, 0.0f, 0.5f, 1.0f);
+
+static LLColor4 green7(0.6f, 1.0f, 0.4f, 1.0f);
+static LLColor4 green8(0.4f, 1.0f, 0.6f, 1.0f);
+static LLColor4 green9(0.6f, 1.0f, 0.6f, 1.0f);
+
+// green(6), blue, yellow, orange, pink(2), cyan
+// red (5) magenta (4)
+static struct ft_display_info ft_display_table[] =
+{
+ { LLFastTimer::FTM_FRAME, "Frame", &LLColor4::white, 0 },
+ { LLFastTimer::FTM_MESSAGES, " Messages", &LLColor4::grey1, 0 },
+ { LLFastTimer::FTM_SLEEP, " Sleep", &LLColor4::grey2, 0 },
+ { LLFastTimer::FTM_IDLE, " Idle", &blue0, 0 },
+ { LLFastTimer::FTM_INVENTORY, " Inventory Update", &LLColor4::purple6, 1 },
+ { LLFastTimer::FTM_AUTO_SELECT, " Open and Select", &LLColor4::red, 0 },
+ { LLFastTimer::FTM_FILTER, " Filter", &LLColor4::red2, 0 },
+ { LLFastTimer::FTM_ARRANGE, " Arrange", &LLColor4::red3, 0 },
+ { LLFastTimer::FTM_REFRESH, " Refresh", &LLColor4::red4, 0 },
+ { LLFastTimer::FTM_SORT, " Sort", &LLColor4::red5, 0 },
+ { LLFastTimer::FTM_RESET_DRAWORDER, " ResetDrawOrder", &LLColor4::pink1, 0 },
+ { LLFastTimer::FTM_WORLD_UPDATE, " World Update", &LLColor4::blue1, 1 },
+ { LLFastTimer::FTM_UPDATE_MOVE, " Move Objects", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_OCTREE_BALANCE, " Octree Balance", &LLColor4::red3, 0 },
+ { LLFastTimer::FTM_CULL, " Object Cull", &LLColor4::blue2, 0 },
+ { LLFastTimer::FTM_CULL_REBOUND, " Rebound", &LLColor4::blue3, 0 },
+ { LLFastTimer::FTM_HUD_EFFECTS, " HUD Effects", &LLColor4::orange1, 0 },
+ { LLFastTimer::FTM_HUD_UPDATE, " HUD Update", &LLColor4::orange2, 0 },
+ { LLFastTimer::FTM_OCCLUSION, " Object Occlude",&LLColor4::pink1, 0 },
+ { LLFastTimer::FTM_OCCLUSION_READBACK, " Occlusion Read",&LLColor4::red2, 0 },
+ { LLFastTimer::FTM_GEO_UPDATE, " Geo Update", &LLColor4::blue3, 0 },
+ { LLFastTimer::FTM_UPDATE_PRIMITIVES, " Volumes", &LLColor4::blue4, 0 },
+ { LLFastTimer::FTM_GEN_VOLUME, " Gen Volume", &LLColor4::yellow3, 0 },
+ { LLFastTimer::FTM_GEN_FLEX, " Flexible", &LLColor4::yellow4, 0 },
+ { LLFastTimer::FTM_GEN_TRIANGLES, " Triangles", &LLColor4::yellow5, 0 },
+ { LLFastTimer::FTM_GEO_LIGHT, " Lighting", &LLColor4::yellow1, 0 },
+ { LLFastTimer::FTM_GEO_SHADOW, " Shadow", &LLColor4::black, 0 },
+ { LLFastTimer::FTM_UPDATE_PARTICLES, " Particles", &LLColor4::blue5, 0 },
+ { LLFastTimer::FTM_GEO_RESERVE, " Reserve", &LLColor4::blue6, 0 },
+ { LLFastTimer::FTM_UPDATE_LIGHTS, " Lights", &LLColor4::yellow2, 0 },
+ { LLFastTimer::FTM_UPDATE_SKY, " Sky Update", &LLColor4::cyan1, 0 },
+ { LLFastTimer::FTM_OBJECTLIST_UPDATE, " Object Update", &LLColor4::purple1, 1 },
+ { LLFastTimer::FTM_AVATAR_UPDATE, " Avatars", &LLColor4::purple2, 0 },
+ { LLFastTimer::FTM_JOINT_UPDATE, " Joints", &LLColor4::purple3, 0 },
+ { LLFastTimer::FTM_ATTACHMENT_UPDATE, " Attachments", &LLColor4::purple4, 0 },
+ { LLFastTimer::FTM_UPDATE_ANIMATION, " Animation", &LLColor4::purple5, 0 },
+ { LLFastTimer::FTM_FLEXIBLE_UPDATE, " Flex Update", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_LOD_UPDATE, " LOD Update", &LLColor4::magenta1, 0 },
+ { LLFastTimer::FTM_REGION_UPDATE, " Region Update", &LLColor4::cyan2, 0 },
+ { LLFastTimer::FTM_NETWORK, " Network", &LLColor4::orange1, 1 },
+ { LLFastTimer::FTM_IDLE_NETWORK, " Decode Msgs", &LLColor4::orange2, 0 },
+ { LLFastTimer::FTM_PROCESS_MESSAGES, " Process Msgs", &LLColor4::orange3, 0 },
+ { LLFastTimer::FTM_PROCESS_OBJECTS, " Object Updates",&LLColor4::orange4, 0 },
+ { LLFastTimer::FTM_CREATE_OBJECT, " Create Obj", &LLColor4::orange5, 0 },
+// { LLFastTimer::FTM_LOAD_AVATAR, " Load Avatar", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_PROCESS_IMAGES, " Image Updates",&LLColor4::orange6, 0 },
+ { LLFastTimer::FTM_PIPELINE, " Pipeline", &LLColor4::magenta4, 0 },
+ { LLFastTimer::FTM_CLEANUP, " Cleanup", &LLColor4::cyan3, 0 },
+ { LLFastTimer::FTM_AUDIO_UPDATE, " Audio Update", &LLColor4::yellow3, 0 },
+ { LLFastTimer::FTM_IMAGE_UPDATE, " Image Update", &LLColor4::yellow4, 1 },
+ { LLFastTimer::FTM_IMAGE_CREATE, " Image CreateGL",&LLColor4::yellow5, 0 },
+ { LLFastTimer::FTM_IMAGE_DECODE, " Image Decode", &LLColor4::yellow6, 0 },
+ { LLFastTimer::FTM_VFILE_WAIT, " VFile Wait", &LLColor4::cyan6, 0 },
+// { LLFastTimer::FTM_IDLE_CB, " Callbacks", &LLColor4::pink1, 0 },
+ { LLFastTimer::FTM_RENDER, " Render", &green0, 0 },
+ { LLFastTimer::FTM_REBUILD, " Rebuild", &LLColor4::green1, 1 },
+ { LLFastTimer::FTM_STATESORT, " State Sort", &LLColor4::orange1, 1 },
+ { LLFastTimer::FTM_RENDER_GEOMETRY, " Geometry", &LLColor4::green2, 1 },
+ { LLFastTimer::FTM_POOLS, " Pools", &LLColor4::green3, 0 },
+ { LLFastTimer::FTM_POOLRENDER, " RenderPool", &LLColor4::green4, 0 },
+ { LLFastTimer::FTM_RENDER_TERRAIN, " Terrain", &LLColor4::green6, 0 },
+ { LLFastTimer::FTM_RENDER_CHARACTERS, " Avatars", &LLColor4::yellow1, 0 },
+ { LLFastTimer::FTM_RENDER_SIMPLE, " Simple", &LLColor4::yellow2, 0 },
+ { LLFastTimer::FTM_RENDER_SHINY, " Shiny", &LLColor4::yellow3, 0 },
+ { LLFastTimer::FTM_RENDER_BUMP, " Bump", &LLColor4::yellow4, 0 },
+ { LLFastTimer::FTM_RENDER_TREES, " Trees", &LLColor4::yellow8, 0 },
+ { LLFastTimer::FTM_RENDER_OCCLUSION, " Occlusion", &LLColor4::red1, 0 },
+ { LLFastTimer::FTM_RENDER_CLOUDS, " Clouds", &LLColor4::yellow5, 0 },
+ { LLFastTimer::FTM_RENDER_ALPHA, " Alpha", &LLColor4::yellow6, 0 },
+ { LLFastTimer::FTM_RENDER_HUD, " HUD", &LLColor4::yellow7, 0 },
+ { LLFastTimer::FTM_RENDER_WATER, " Water", &LLColor4::yellow9, 0 },
+ { LLFastTimer::FTM_RENDER_UI, " UI", &LLColor4::cyan4, 1 },
+ { LLFastTimer::FTM_RENDER_TIMER, " Timers", &LLColor4::cyan5, 1, 0 },
+// { LLFastTimer::FTM_RENDER_FONTS, " Fonts", &LLColor4::pink1, 0 },
+// { LLFastTimer::FTM_UPDATE_TEXTURES, " Textures", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_SWAP, " Swap", &LLColor4::pink1, 0 },
+
+// { LLFastTimer::FTM_TEMP1, " Temp1", &LLColor4::red1, 0 },
+// { LLFastTimer::FTM_TEMP2, " Temp2", &LLColor4::magenta1, 0 },
+// { LLFastTimer::FTM_TEMP3, " Temp3", &LLColor4::red2, 0 },
+// { LLFastTimer::FTM_TEMP4, " Temp4", &LLColor4::magenta2, 0 },
+// { LLFastTimer::FTM_TEMP5, " Temp5", &LLColor4::red3, 0 },
+// { LLFastTimer::FTM_TEMP6, " Temp6", &LLColor4::magenta3, 0 },
+// { LLFastTimer::FTM_TEMP7, " Temp7", &LLColor4::red4, 0 },
+// { LLFastTimer::FTM_TEMP8, " Temp8", &LLColor4::magenta4, 0 },
+
+ { LLFastTimer::FTM_OTHER, " Other", &red0 }
+};
+static int ft_display_didcalc = 0;
+static const int FTV_DISPLAY_NUM = (sizeof(ft_display_table)/sizeof(ft_display_table[0]));
+
+S32 ft_display_idx[FTV_DISPLAY_NUM]; // line of table entry for display purposes (for collapse)
+
+LLFastTimerView::LLFastTimerView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, TRUE)
+{
+ setVisible(FALSE);
+ mDisplayMode = 0;
+ mAvgCountTotal = 0;
+ mMaxCountTotal = 0;
+ mDisplayCenter = 1;
+ mDisplayCalls = 0;
+ mScrollIndex = 0;
+ mHoverIndex = -1;
+ mHoverBarIndex = -1;
+ mBarStart = new S32[(MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM];
+ memset(mBarStart, 0, (MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM * sizeof(S32));
+ mBarEnd = new S32[(MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM];
+ memset(mBarEnd, 0, (MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM * sizeof(S32));
+ mSubtractHidden = 0;
+ mPrintStats = -1;
+
+ // One-time setup
+ if (!ft_display_didcalc)
+ {
+ int pidx[FTV_DISPLAY_NUM];
+ int pdisabled[FTV_DISPLAY_NUM];
+ for (S32 i=0; i < FTV_DISPLAY_NUM; i++)
+ {
+ int level = 0;
+ const char *text = ft_display_table[i].desc;
+ while(text[0] == ' ')
+ {
+ text++;
+ level++;
+ }
+ llassert(level < FTV_DISPLAY_NUM);
+ ft_display_table[i].desc = text;
+ ft_display_table[i].level = level;
+ if (level > 0)
+ {
+ ft_display_table[i].parent = pidx[level-1];
+ if (pdisabled[level-1])
+ {
+ ft_display_table[i].disabled = 3;
+ }
+ }
+ else
+ {
+ ft_display_table[i].parent = -1;
+ }
+ ft_display_idx[i] = i;
+ pidx[level] = i;
+ pdisabled[level] = ft_display_table[i].disabled;
+ }
+ ft_display_didcalc = 1;
+ }
+}
+
+LLFastTimerView::~LLFastTimerView()
+{
+ delete[] mBarStart;
+ delete[] mBarEnd;
+}
+
+EWidgetType LLFastTimerView::getWidgetType() const
+{
+ return WIDGET_TYPE_FAST_TIMER_VIEW;
+}
+
+LLString LLFastTimerView::getWidgetTag() const
+{
+ return LL_FAST_TIMER_VIEW_TAG;
+}
+
+BOOL LLFastTimerView::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mBarRect.pointInRect(x, y))
+ {
+ S32 bar_idx = MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight());
+ bar_idx = llclamp(bar_idx, 0, MAX_VISIBLE_HISTORY);
+ mPrintStats = bar_idx;
+// return TRUE; // for now, pass all mouse events through
+ }
+ return FALSE;
+}
+
+S32 LLFastTimerView::getLegendIndex(S32 y)
+{
+ S32 idx = (mRect.getHeight() - y) / ((S32) LLFontGL::sMonospace->getLineHeight()+2) - 5;
+ if (idx >= 0 && idx < FTV_DISPLAY_NUM)
+ {
+ return ft_display_idx[idx];
+ }
+
+ return -1;
+}
+
+BOOL LLFastTimerView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (x < mBarRect.mLeft)
+ {
+ S32 legend_index = getLegendIndex(y);
+ if (legend_index >= 0 && legend_index < FTV_DISPLAY_NUM)
+ {
+ S32 disabled = ft_display_table[legend_index].disabled;
+ if (++disabled > 2)
+ disabled = 0;
+ ft_display_table[legend_index].disabled = disabled;
+ S32 level = ft_display_table[legend_index].level;
+
+ // propagate enable/disable to all children
+ legend_index++;
+ while (legend_index < FTV_DISPLAY_NUM && ft_display_table[legend_index].level > level)
+ {
+ ft_display_table[legend_index].disabled = disabled ? 3 : 0;
+ legend_index++;
+ }
+ }
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ if (++mDisplayMode > 3)
+ mDisplayMode = 0;
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ if (++mDisplayCenter > 2)
+ mDisplayCenter = 0;
+ }
+ else
+ {
+ // pause/unpause
+ LLFastTimer::sPauseHistory = !LLFastTimer::sPauseHistory;
+ // reset scroll to bottom when unpausing
+ if (!LLFastTimer::sPauseHistory)
+ {
+ mScrollIndex = 0;
+ }
+ }
+ // RN: for now, pass all mouse events through
+ return FALSE;
+}
+
+BOOL LLFastTimerView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return FALSE;
+}
+
+
+BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
+{
+ if(LLFastTimer::sPauseHistory && mBarRect.pointInRect(x, y))
+ {
+ mHoverIndex = -1;
+ mHoverBarIndex = MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight());
+ if (mHoverBarIndex == 0)
+ {
+ return TRUE;
+ }
+ else if (mHoverBarIndex == -1)
+ {
+ mHoverBarIndex = 0;
+ }
+ for (S32 i = 0; i < FTV_DISPLAY_NUM; i++)
+ {
+ if (x > mBarStart[mHoverBarIndex * FTV_DISPLAY_NUM + i] &&
+ x < mBarEnd[mHoverBarIndex * FTV_DISPLAY_NUM + i] &&
+ ft_display_table[i].disabled <= 1)
+ {
+ mHoverIndex = i;
+ }
+ }
+ }
+ else if (x < mBarRect.mLeft)
+ {
+ S32 legend_index = getLegendIndex(y);
+ if (legend_index >= 0 && legend_index < FTV_DISPLAY_NUM)
+ {
+ mHoverIndex = legend_index;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLFastTimerView::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (getVisible() && pointInView(x, y))
+ {
+ LLFastTimer::sPauseHistory = TRUE;
+ mScrollIndex = llclamp(mScrollIndex - clicks,
+ 0, llmin(LLFastTimer::sLastFrameIndex, (S32)LLFastTimer::FTM_HISTORY_NUM-MAX_VISIBLE_HISTORY));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLFastTimerView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ // Otherwise space key gets eaten from the rest of the UI. JC
+ if (getVisible())
+ {
+ switch (key)
+ {
+ case '=':
+ mDisplayCalls = !mDisplayCalls;
+ return TRUE;
+ case '-':
+ mSubtractHidden = !mSubtractHidden;
+ return TRUE;
+ case ' ':
+ // pause/unpause
+ LLFastTimer::sPauseHistory = !LLFastTimer::sPauseHistory;
+ // reset scroll to bottom when unpausing
+ if (!LLFastTimer::sPauseHistory)
+ {
+ mScrollIndex = 0;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void LLFastTimerView::draw()
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_TIMER);
+
+ std::string tdesc;
+
+ F64 clock_freq = (F64)LLFastTimer::countsPerSecond();
+ F64 iclock_freq = 1000.0 / clock_freq;
+
+ S32 margin = 10;
+ S32 height = (S32) (gViewerWindow->getVirtualWindowRect().getHeight()*0.75f);
+ S32 width = (S32) (gViewerWindow->getVirtualWindowRect().getWidth() * 0.75f);
+
+ mRect.setLeftTopAndSize(mRect.mLeft, mRect.mTop, width, height);
+
+ S32 left, top, right, bottom;
+ S32 x, y, barw, barh, dx, dy;
+ S32 texth, textw;
+ LLViewerImage* box_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
+
+ // Make sure all timers are accounted for
+ // Set 'FTM_OTHER' to unaccounted ticks last frame
+ {
+ S32 display_timer[LLFastTimer::FTM_NUM_TYPES];
+ S32 hidx = LLFastTimer::sLastFrameIndex % LLFastTimer::FTM_HISTORY_NUM;
+ for (S32 i=0; i < LLFastTimer::FTM_NUM_TYPES; i++)
+ {
+ display_timer[i] = 0;
+ }
+ for (S32 i=0; i < FTV_DISPLAY_NUM; i++)
+ {
+ S32 tidx = ft_display_table[i].timer;
+ display_timer[tidx] = 1;
+ }
+ LLFastTimer::sCountHistory[hidx][LLFastTimer::FTM_OTHER] = 0;
+ LLFastTimer::sCallHistory[hidx][LLFastTimer::FTM_OTHER] = 0;
+ for (S32 tidx = 0; tidx < LLFastTimer::FTM_NUM_TYPES; tidx++)
+ {
+ U64 counts = LLFastTimer::sCountHistory[hidx][tidx];
+ if (counts > 0 && display_timer[tidx] == 0)
+ {
+ LLFastTimer::sCountHistory[hidx][LLFastTimer::FTM_OTHER] += counts;
+ LLFastTimer::sCallHistory[hidx][LLFastTimer::FTM_OTHER] += 1;
+ }
+ }
+ LLFastTimer::sCountAverage[LLFastTimer::FTM_OTHER] = 0;
+ LLFastTimer::sCallAverage[LLFastTimer::FTM_OTHER] = 0;
+ for (S32 h = 0; h < LLFastTimer::FTM_HISTORY_NUM; h++)
+ {
+ LLFastTimer::sCountAverage[LLFastTimer::FTM_OTHER] += LLFastTimer::sCountHistory[h][LLFastTimer::FTM_OTHER];
+ LLFastTimer::sCallAverage[LLFastTimer::FTM_OTHER] += LLFastTimer::sCallHistory[h][LLFastTimer::FTM_OTHER];
+ }
+ LLFastTimer::sCountAverage[LLFastTimer::FTM_OTHER] /= LLFastTimer::FTM_HISTORY_NUM;
+ LLFastTimer::sCallAverage[LLFastTimer::FTM_OTHER] /= LLFastTimer::FTM_HISTORY_NUM;
+ }
+
+ // Draw the window background
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
+ }
+
+ S32 xleft = margin;
+ S32 ytop = margin;
+
+ // Draw some help
+ {
+
+ x = xleft;
+ y = height - ytop;
+ texth = (S32)LLFontGL::sMonospace->getLineHeight();
+
+ char modedesc[][32] = {
+ "2 x Average ",
+ "Max ",
+ "Recent Max ",
+ "100 ms "
+ };
+ char centerdesc[][32] = {
+ "Left ",
+ "Centered ",
+ "Ordered "
+ };
+
+ tdesc = llformat("Full bar = %s [Click to pause/reset] [SHIFT-Click to toggle]",modedesc[mDisplayMode]);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+ textw = LLFontGL::sMonospace->getWidth(tdesc);
+
+ x = xleft, y -= (texth + 2);
+ tdesc = llformat("Justification = %s [CTRL-Click to toggle]",centerdesc[mDisplayCenter]);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+ y -= (texth + 2);
+
+ LLFontGL::sMonospace->renderUTF8("[Right-click to log selected] [= to toggle counts] [- to subtract hidden]",
+ 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+ y -= (texth + 2);
+ }
+
+ // Calc the total ticks
+ S32 histmax = llmin(LLFastTimer::sLastFrameIndex+1, MAX_VISIBLE_HISTORY);
+ U64 ticks_sum[LLFastTimer::FTM_HISTORY_NUM+1][FTV_DISPLAY_NUM];
+ for (S32 j=-1; j<LLFastTimer::FTM_HISTORY_NUM; j++)
+ {
+ S32 hidx;
+ if (j >= 0)
+ hidx = (LLFastTimer::sLastFrameIndex+j) % LLFastTimer::FTM_HISTORY_NUM;
+ else
+ hidx = -1;
+
+ // calculate tick info by adding child ticks to parents
+ for (S32 i=0; i < FTV_DISPLAY_NUM; i++)
+ {
+ if (mSubtractHidden && ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+ // Get ticks
+ S32 tidx = ft_display_table[i].timer;
+ if (hidx >= 0)
+ ticks_sum[j+1][i] = LLFastTimer::sCountHistory[hidx][tidx];
+ else
+ ticks_sum[j+1][i] = LLFastTimer::sCountAverage[tidx];
+ S32 pidx = ft_display_table[i].parent;
+ // Add ticks to parents
+ while (pidx >= 0)
+ {
+ ticks_sum[j+1][pidx] += ticks_sum[j+1][i];
+ pidx = ft_display_table[pidx].parent;
+ }
+ }
+ }
+
+ // Draw the legend
+
+ S32 legendwidth = 0;
+ xleft = margin;
+ ytop = y;
+
+ y -= (texth + 2);
+
+ S32 cur_line = 0;
+ S32 display_line[FTV_DISPLAY_NUM];
+ for (S32 i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ S32 disabled = ft_display_table[i].disabled;
+ if (disabled == 3)
+ {
+ continue; // skip row
+ }
+ display_line[i] = cur_line;
+ ft_display_idx[cur_line] = i;
+ cur_line++;
+ S32 level = ft_display_table[i].level;
+ S32 parent = ft_display_table[i].parent;
+
+ x = xleft;
+
+ left = x; right = x + texth;
+ top = y; bottom = y - texth;
+ S32 scale_offset = 0;
+ if (i == mHoverIndex)
+ {
+ scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 2.f);
+ }
+ gl_rect_2d(left - scale_offset, top + scale_offset, right + scale_offset, bottom - scale_offset, *ft_display_table[i].color);
+
+ int tidx = ft_display_table[i].timer;
+ F32 ms = 0;
+ S32 calls = 0;
+ if (mHoverBarIndex > 0 && mHoverIndex >= 0)
+ {
+ S32 hidx = (LLFastTimer::sLastFrameIndex + (mHoverBarIndex - 1) - mScrollIndex) % LLFastTimer::FTM_HISTORY_NUM;
+ S32 bidx = LLFastTimer::FTM_HISTORY_NUM - mScrollIndex - mHoverBarIndex;
+ U64 ticks = ticks_sum[bidx+1][i]; // : LLFastTimer::sCountHistory[hidx][tidx];
+ ms = (F32)((F64)ticks * iclock_freq);
+ calls = (S32)LLFastTimer::sCallHistory[hidx][tidx];
+ }
+ else
+ {
+ U64 ticks = disabled >= 1 ? ticks_sum[0][i] : LLFastTimer::sCountAverage[tidx];
+ ms = (F32)((F64)ticks * iclock_freq);
+ calls = (S32)LLFastTimer::sCallAverage[tidx];
+ }
+ if (mDisplayCalls)
+ {
+ tdesc = llformat("%s (%d)",ft_display_table[i].desc,calls);
+ }
+ else
+ {
+ tdesc = llformat("%s [%.1f]",ft_display_table[i].desc,ms);
+ }
+ dx = (texth+4) + level*8;
+
+ LLColor4 color = disabled > 1 ? LLColor4::grey : LLColor4::white;
+ if (level > 0)
+ {
+ S32 line_start_y = (top + bottom) / 2;
+ S32 line_end_y = line_start_y + ((texth + 2) * (display_line[i] - display_line[parent])) - (texth / 2);
+ gl_line_2d(x + dx - 8, line_start_y, x + dx, line_start_y, color);
+ S32 line_x = x + (texth + 4) + ((level - 1) * 8);
+ gl_line_2d(line_x, line_start_y, line_x, line_end_y, color);
+ if (disabled == 1)
+ {
+ gl_line_2d(line_x+4, line_start_y-3, line_x+4, line_start_y+4, color);
+ }
+ }
+
+ x += dx;
+ BOOL is_child_of_hover_item = (i == mHoverIndex);
+ S32 next_parent = ft_display_table[i].parent;
+ while(!is_child_of_hover_item && next_parent >= 0)
+ {
+ is_child_of_hover_item = (mHoverIndex == next_parent);
+ next_parent = ft_display_table[next_parent].parent;
+ }
+
+ if (is_child_of_hover_item)
+ {
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, color, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::BOLD);
+ }
+ else
+ {
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, color, LLFontGL::LEFT, LLFontGL::TOP);
+ }
+ y -= (texth + 2);
+
+ textw = dx + LLFontGL::sMonospace->getWidth(ft_display_table[i].desc) + 40;
+ if (textw > legendwidth)
+ legendwidth = textw;
+ }
+ for (S32 i=cur_line; i<FTV_DISPLAY_NUM; i++)
+ {
+ ft_display_idx[i] = -1;
+ }
+ xleft += legendwidth + 8;
+ // ytop = ytop;
+
+ // update rectangle that includes timer bars
+ mBarRect.mLeft = xleft;
+ mBarRect.mRight = mRect.mRight - xleft;
+ mBarRect.mTop = ytop - ((S32)LLFontGL::sMonospace->getLineHeight() + 4);
+ mBarRect.mBottom = margin + LINE_GRAPH_HEIGHT;
+
+ y = ytop;
+ barh = (ytop - margin - LINE_GRAPH_HEIGHT) / (MAX_VISIBLE_HISTORY + 2);
+ dy = barh>>2; // spacing between bars
+ if (dy < 1) dy = 1;
+ barh -= dy;
+ barw = width - xleft - margin;
+
+ // Draw the history bars
+ if (LLFastTimer::sLastFrameIndex >= 0)
+ {
+ U64 totalticks;
+ if (!LLFastTimer::sPauseHistory)
+ {
+ U64 ticks = 0;
+ int hidx = (LLFastTimer::sLastFrameIndex - mScrollIndex) % LLFastTimer::FTM_HISTORY_NUM;
+ for (S32 i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (mSubtractHidden && ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+ int tidx = ft_display_table[i].timer;
+ ticks += LLFastTimer::sCountHistory[hidx][tidx];
+ }
+ if (LLFastTimer::sCurFrameIndex >= 10)
+ {
+ U64 framec = LLFastTimer::sCurFrameIndex;
+ U64 avg = (U64)mAvgCountTotal;
+ mAvgCountTotal = (avg*framec + ticks) / (framec + 1);
+ if (ticks > mMaxCountTotal)
+ {
+ mMaxCountTotal = ticks;
+ }
+ }
+#if 1
+ if (ticks < mAvgCountTotal/100 || ticks > mAvgCountTotal*100)
+ LLFastTimer::sResetHistory = 1;
+#endif
+ if (LLFastTimer::sCurFrameIndex < 10 || LLFastTimer::sResetHistory)
+ {
+ mAvgCountTotal = ticks;
+ mMaxCountTotal = ticks;
+ }
+ }
+
+ if (mDisplayMode == 0)
+ {
+ totalticks = mAvgCountTotal*2;
+ }
+ else if (mDisplayMode == 1)
+ {
+ totalticks = mMaxCountTotal;
+ }
+ else if (mDisplayMode == 2)
+ {
+ // Calculate the max total ticks for the current history
+ totalticks = 0;
+ for (S32 j=0; j<histmax; j++)
+ {
+ U64 ticks = 0;
+ for (S32 i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (mSubtractHidden && ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+ int tidx = ft_display_table[i].timer;
+ ticks += LLFastTimer::sCountHistory[j][tidx];
+ }
+ if (ticks > totalticks)
+ totalticks = ticks;
+ }
+ }
+ else
+ {
+ totalticks = (U64)(clock_freq * .1); // 100 ms
+ }
+
+ // Draw MS ticks
+ {
+ U32 ms = (U32)((F64)totalticks * iclock_freq) ;
+
+ tdesc = llformat("%.1f ms |", (F32)ms*.25f);
+ x = xleft + barw/4 - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ tdesc = llformat("%.1f ms |", (F32)ms*.50f);
+ x = xleft + barw/2 - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ tdesc = llformat("%.1f ms |", (F32)ms*.75f);
+ x = xleft + (barw*3)/4 - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ tdesc = llformat( "%d ms |", ms);
+ x = xleft + barw - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ LLRect graph_rect;
+ // Draw borders
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ glColor4f(0.5f,0.5f,0.5f,0.5f);
+
+ S32 by = y + 2;
+
+ y -= ((S32)LLFontGL::sMonospace->getLineHeight() + 4);
+
+ //heading
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, y+5, FALSE);
+
+ //tree view
+ gl_rect_2d(5, by, xleft-10, 5, FALSE);
+
+ by = y + 5;
+ //average bar
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, by-barh-dy-5, FALSE);
+
+ by -= barh*2+dy;
+
+ //current frame bar
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, by-barh-dy-2, FALSE);
+
+ by -= barh+dy+1;
+
+ //history bars
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, LINE_GRAPH_HEIGHT-barh-dy-2, FALSE);
+
+ by = LINE_GRAPH_HEIGHT-barh-dy-7;
+
+ //line graph
+ graph_rect = LLRect(xleft-5, by, mRect.getWidth()-5, 5);
+
+ gl_rect_2d(graph_rect, FALSE);
+ }
+
+ // Draw bars for each history entry
+ // Special: -1 = show running average
+ LLViewerImage::bindTexture(box_imagep);
+ for (S32 j=-1; j<histmax && y > LINE_GRAPH_HEIGHT; j++)
+ {
+ int sublevel_dx[FTV_DISPLAY_NUM+1];
+ int sublevel_left[FTV_DISPLAY_NUM+1];
+ int sublevel_right[FTV_DISPLAY_NUM+1];
+ S32 tidx;
+ if (j >= 0)
+ {
+ tidx = LLFastTimer::FTM_HISTORY_NUM - j - 1 - mScrollIndex;
+ }
+ else
+ {
+ tidx = -1;
+ }
+
+ x = xleft;
+
+ // draw the bars for each stat
+ int xpos[FTV_DISPLAY_NUM+1];
+ int deltax[FTV_DISPLAY_NUM+1];
+ xpos[0] = xleft;
+
+ for (S32 i = 0; i < FTV_DISPLAY_NUM; i++)
+ {
+ if (ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+
+ F32 frac = (F32)ticks_sum[tidx+1][i] / (F32)totalticks;
+
+ dx = llround(frac * (F32)barw);
+ deltax[i] = dx;
+
+ int level = ft_display_table[i].level;
+ int parent = ft_display_table[i].parent;
+ llassert(level < FTV_DISPLAY_NUM);
+ llassert(parent < FTV_DISPLAY_NUM);
+
+ left = xpos[level];
+
+ S32 prev_idx = i - 1;
+ while (prev_idx > 0)
+ {
+ if (ft_display_table[prev_idx].disabled <= 1)
+ {
+ break;
+ }
+ prev_idx--;
+ }
+ S32 next_idx = i + 1;
+ while (next_idx < FTV_DISPLAY_NUM)
+ {
+ if (ft_display_table[next_idx].disabled <= 1)
+ {
+ break;
+ }
+ next_idx++;
+ }
+
+ if (level == 0)
+ {
+ sublevel_left[level] = xleft;
+ sublevel_dx[level] = dx;
+ sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
+ }
+ else if (i==0 || ft_display_table[prev_idx].level < level)
+ {
+ // If we are the first entry at a new sublevel block, calc the
+ // total width of this sublevel and modify left to align block.
+ U64 sublevelticks = ticks_sum[tidx+1][i];
+ for (S32 k=i+1; k<FTV_DISPLAY_NUM; k++)
+ {
+ if (ft_display_table[k].level < level)
+ break;
+ if (ft_display_table[k].disabled <= 1 && ft_display_table[k].level == level)
+ sublevelticks += ticks_sum[tidx+1][k];
+ }
+ F32 subfrac = (F32)sublevelticks / (F32)totalticks;
+ sublevel_dx[level] = (int)(subfrac * (F32)barw + .5f);
+
+ if (mDisplayCenter == 1) // center aligned
+ {
+ left += (deltax[parent] - sublevel_dx[level])/2;
+ }
+ else if (mDisplayCenter == 2) // right aligned
+ {
+ left += (deltax[parent] - sublevel_dx[level]);
+ }
+
+ sublevel_left[level] = left;
+ sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
+ }
+
+ right = left + dx;
+ xpos[level] = right;
+ xpos[level+1] = left;
+
+ mBarStart[(j + 1) * FTV_DISPLAY_NUM + i] = left;
+ mBarEnd[(j + 1) * FTV_DISPLAY_NUM + i] = right;
+
+ top = y;
+ bottom = y - barh;
+
+ if (right > left)
+ {
+ //U32 rounded_edges = 0;
+ LLColor4 color = *ft_display_table[i].color;
+ S32 scale_offset = 0;
+
+ BOOL is_child_of_hover_item = (i == mHoverIndex);
+ S32 next_parent = ft_display_table[i].parent;
+ while(!is_child_of_hover_item && next_parent >= 0)
+ {
+ is_child_of_hover_item = (mHoverIndex == next_parent);
+ next_parent = ft_display_table[next_parent].parent;
+ }
+
+ if (i == mHoverIndex)
+ {
+ scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 3.f);
+ //color = lerp(color, LLColor4::black, -0.4f);
+ }
+ else if (mHoverIndex >= 0 && !is_child_of_hover_item)
+ {
+ color = lerp(color, LLColor4::grey, 0.8f);
+ }
+
+ glColor4fv(color.mV);
+ F32 start_fragment = llclamp((F32)(left - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
+ F32 end_fragment = llclamp((F32)(right - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
+ gl_segmented_rect_2d_fragment_tex(sublevel_left[level], top - level + scale_offset, sublevel_right[level], bottom + level - scale_offset, box_imagep->getWidth(), box_imagep->getHeight(), 16, start_fragment, end_fragment);
+
+ }
+
+ }
+ y -= (barh + dy);
+ if (j < 0)
+ y -= barh;
+ }
+
+ //draw line graph history
+ {
+ LLGLSNoTexture no_texture;
+ LLGLEnable scissor(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(graph_rect);
+
+ //normalize based on last frame's maximum
+ static U64 last_max = 0;
+ static F32 alpha_interp = 0.f;
+ U64 max_ticks = llmax(last_max, (U64) 1);
+ F32 ms = (F32)((F64)max_ticks * iclock_freq);
+
+ //display y-axis range
+ LLString tdesc = llformat("%4.2f ms", ms);
+ x = graph_rect.mRight - LLFontGL::sMonospace->getWidth(tdesc)-5;
+ y = graph_rect.mTop - ((S32)LLFontGL::sMonospace->getLineHeight());
+
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ //highlight visible range
+ {
+ S32 first_frame = LLFastTimer::FTM_HISTORY_NUM - mScrollIndex;
+ S32 last_frame = first_frame - MAX_VISIBLE_HISTORY;
+
+ F32 frame_delta = ((F32) (graph_rect.getWidth()))/(LLFastTimer::FTM_HISTORY_NUM-1);
+
+ F32 right = (F32) graph_rect.mLeft + frame_delta*first_frame;
+ F32 left = (F32) graph_rect.mLeft + frame_delta*last_frame;
+
+ glColor4f(0.5f,0.5f,0.5f,0.3f);
+ gl_rect_2d((S32) left, graph_rect.mTop, (S32) right, graph_rect.mBottom);
+
+ if (mHoverBarIndex >= 0)
+ {
+ S32 bar_frame = first_frame - mHoverBarIndex;
+ F32 bar = (F32) graph_rect.mLeft + frame_delta*bar_frame;
+
+ glColor4f(0.5f,0.5f,0.5f,1);
+
+ glBegin(GL_LINES);
+ glVertex2i((S32)bar, graph_rect.mBottom);
+ glVertex2i((S32)bar, graph_rect.mTop);
+ glEnd();
+ }
+ }
+
+ U64 cur_max = 0;
+ for (S32 idx = 0; idx < FTV_DISPLAY_NUM; ++idx)
+ {
+ if (ft_display_table[idx].disabled > 1)
+ { //skip disabled timers
+ continue;
+ }
+
+ //fatten highlighted timer
+ if (mHoverIndex == idx)
+ {
+ glLineWidth(3);
+ }
+
+ F32* col = ft_display_table[idx].color->mV;
+
+ F32 alpha = 1.f;
+
+ if (mHoverIndex >= 0 &&
+ idx != mHoverIndex)
+ { //fade out non-hihglighted timers
+ if (ft_display_table[idx].parent != mHoverIndex)
+ {
+ alpha = alpha_interp;
+ }
+ }
+
+ glColor4f(col[0], col[1], col[2], alpha);
+ glBegin(GL_LINE_STRIP);
+ for (U32 j = 0; j < LLFastTimer::FTM_HISTORY_NUM; j++)
+ {
+ U64 ticks = ticks_sum[j+1][idx];
+ if (alpha == 1.f)
+ { //normalize to highlighted timer
+ cur_max = llmax(cur_max, ticks);
+ }
+ F32 x = graph_rect.mLeft + ((F32) (graph_rect.getWidth()))/(LLFastTimer::FTM_HISTORY_NUM-1)*j;
+ F32 y = graph_rect.mBottom + (F32) graph_rect.getHeight()/max_ticks*ticks;
+ glVertex2f(x,y);
+ }
+ glEnd();
+
+ if (mHoverIndex == idx)
+ {
+ glLineWidth(1);
+ }
+ }
+
+ //interpolate towards new maximum
+ F32 dt = gFrameIntervalSeconds*3.f;
+ last_max = (U64) ((F32) last_max + ((F32) cur_max- (F32) last_max) * dt);
+ F32 alpha_target = last_max > cur_max ?
+ llmin((F32) last_max/ (F32) cur_max - 1.f,1.f) :
+ llmin((F32) cur_max/ (F32) last_max - 1.f,1.f);
+
+ alpha_interp = alpha_interp + (alpha_target-alpha_interp) * dt;
+
+ }
+ }
+
+ // Output stats for clicked bar to log
+ if (mPrintStats >= 0)
+ {
+ LLString legend_stat;
+ S32 stat_num;
+ S32 first = 1;
+ for (stat_num = 0; stat_num < FTV_DISPLAY_NUM; stat_num++)
+ {
+ if (ft_display_table[stat_num].disabled > 1)
+ continue;
+ if (!first)
+ legend_stat += ", ";
+ first=0;
+ legend_stat += ft_display_table[stat_num].desc;
+ }
+ llinfos << legend_stat << llendl;
+
+ LLString timer_stat;
+ first = 1;
+ for (stat_num = 0; stat_num < FTV_DISPLAY_NUM; stat_num++)
+ {
+ S32 disabled = ft_display_table[stat_num].disabled;
+ if (disabled > 1)
+ continue;
+ if (!first)
+ timer_stat += ", ";
+ first=0;
+ U64 ticks;
+ S32 tidx = ft_display_table[stat_num].timer;
+ if (mPrintStats > 0)
+ {
+ S32 hidx = (LLFastTimer::sLastFrameIndex+(mPrintStats-1)-mScrollIndex) % LLFastTimer::FTM_HISTORY_NUM;
+ ticks = disabled >= 1 ? ticks_sum[mPrintStats][stat_num] : LLFastTimer::sCountHistory[hidx][tidx];
+ }
+ else
+ {
+ ticks = disabled >= 1 ? ticks_sum[0][stat_num] : LLFastTimer::sCountAverage[tidx];
+ }
+ F32 ms = (F32)((F64)ticks * iclock_freq);
+
+ timer_stat += llformat("%.1f",ms);
+ }
+ llinfos << timer_stat << llendl;
+ mPrintStats = -1;
+ }
+
+ mHoverIndex = -1;
+ mHoverBarIndex = -1;
+
+ LLView::draw();
+}
+
+F64 LLFastTimerView::getTime(LLFastTimer::EFastTimerType tidx)
+{
+ // Find table index
+ S32 i;
+ for (i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (tidx == ft_display_table[i].timer)
+ {
+ break;
+ }
+ }
+ S32 table_idx = i;
+
+ // Add child ticks to parent
+ U64 ticks = LLFastTimer::sCountAverage[tidx];
+ S32 level = ft_display_table[table_idx].level;
+ for (i=table_idx+1; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (ft_display_table[i].level <= level)
+ {
+ break;
+ }
+ ticks += LLFastTimer::sCountAverage[ft_display_table[i].timer];
+ }
+
+ return (F64)ticks / (F64)LLFastTimer::countsPerSecond();
+}
diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h
new file mode 100644
index 0000000000..ea83c38a84
--- /dev/null
+++ b/indra/newview/llfasttimerview.h
@@ -0,0 +1,52 @@
+/**
+ * @file llfasttimerview.h
+ * @brief LLFastTimerView class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFASTTIMERVIEW_H
+#define LL_LLFASTTIMERVIEW_H
+
+#include "llview.h"
+#include "llframetimer.h"
+
+class LLFastTimerView : public LLView
+{
+public:
+ LLFastTimerView(const std::string& name, const LLRect& rect);
+ virtual ~LLFastTimerView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual void draw();
+
+ S32 getLegendIndex(S32 y);
+ F64 getTime(LLFastTimer::EFastTimerType tidx);
+
+private:
+ S32* mBarStart;
+ S32* mBarEnd;
+ S32 mDisplayMode;
+ S32 mDisplayCenter;
+ S32 mDisplayCalls;
+ U64 mAvgCountTotal;
+ U64 mMaxCountTotal;
+ LLRect mBarRect;
+ S32 mScrollIndex;
+ S32 mHoverIndex;
+ S32 mHoverBarIndex;
+ LLFrameTimer mHighlightTimer;
+ S32 mSubtractHidden;
+ S32 mPrintStats;
+};
+
+#endif
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
new file mode 100644
index 0000000000..be816deebc
--- /dev/null
+++ b/indra/newview/llfeaturemanager.cpp
@@ -0,0 +1,842 @@
+/**
+ * @file llfeaturemanager.cpp
+ * @brief LLFeatureManager class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iostream>
+#include <fstream>
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfeaturemanager.h"
+#include "lldir.h"
+
+#include "llsys.h"
+#include "llgl.h"
+#include "llsecondlifeurls.h"
+
+#include "llviewercontrol.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "lldrawpoolterrain.h"
+#include "llviewerimagelist.h"
+#include "llwindow.h"
+#include "llui.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+//
+// externs
+//
+extern LLMemoryInfo gSysMemory;
+extern LLCPUInfo gSysCPU;
+extern void write_debug(const char *str);
+extern void write_debug(const std::string& str);
+
+#if LL_DARWIN
+const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
+#else
+const char FEATURE_TABLE_FILENAME[] = "featuretable.txt";
+#endif
+
+const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
+
+LLFeatureManager *gFeatureManagerp = NULL;
+
+LLFeatureInfo::LLFeatureInfo(const char *name, const BOOL available, const S32 level) : mValid(TRUE)
+{
+ mName = name;
+ mAvailable = available;
+ mRecommendedLevel = level;
+}
+
+LLFeatureList::LLFeatureList(const char *name)
+{
+ mName = name;
+}
+
+LLFeatureList::~LLFeatureList()
+{
+}
+
+void LLFeatureList::addFeature(const char *name, const BOOL available, const S32 level)
+{
+ if (mFeatures.count(name))
+ {
+ llwarns << "LLFeatureList::Attempting to add preexisting feature " << name << llendl;
+ }
+
+ LLFeatureInfo fi(name, available, level);
+ mFeatures[name] = fi;
+}
+
+BOOL LLFeatureList::isFeatureAvailable(const char *name)
+{
+ if (mFeatures.count(name))
+ {
+ return mFeatures[name].mAvailable;
+ }
+
+ llwarns << "Feature " << name << " not on feature list!" << llendl;
+ return FALSE;
+}
+
+S32 LLFeatureList::getRecommendedLevel(const char *name)
+{
+ if (mFeatures.count(name))
+ {
+ return mFeatures[name].mRecommendedLevel;
+ }
+
+ llwarns << "Feature " << name << " not on feature list!" << llendl;
+ return -1;
+}
+
+BOOL LLFeatureList::maskList(LLFeatureList &mask)
+{
+ //llinfos << "Masking with " << mask.mName << llendl;
+ //
+ // Lookup the specified feature mask, and overlay it on top of the
+ // current feature mask.
+ //
+
+ LLFeatureInfo mask_fi;
+
+ feature_map_t::iterator feature_it;
+ for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
+ {
+ mask_fi = feature_it->second;
+ //
+ // Look for the corresponding feature
+ //
+ if (!mFeatures.count(mask_fi.mName))
+ {
+ llwarns << "Feature " << mask_fi.mName << " in mask not in top level!" << llendl;
+ continue;
+ }
+
+ LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
+ if (mask_fi.mAvailable && !cur_fi.mAvailable)
+ {
+ llwarns << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << llendl;
+ continue;
+ }
+ cur_fi.mAvailable = mask_fi.mAvailable;
+ cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llinfos << "Feature mask " << mask.mName
+ << " Feature " << mask_fi.mName
+ << " Mask: " << mask_fi.mRecommendedLevel
+ << " Now: " << cur_fi.mRecommendedLevel << llendl;
+#endif
+ }
+
+#if 0 && !LL_RELEASE_FOR_DOWNLOAD
+ llinfos << "After appling mask " << mask.mName << llendl;
+ dump();
+#endif
+ return TRUE;
+}
+
+void LLFeatureList::dump()
+{
+ llinfos << "Feature list: " << mName << llendl;
+ llinfos << "--------------" << llendl;
+
+ LLFeatureInfo fi;
+ feature_map_t::iterator feature_it;
+ for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
+ {
+ fi = feature_it->second;
+ llinfos << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << llendl;
+ }
+ llinfos << llendl;
+}
+
+LLFeatureList *LLFeatureManager::findMask(const char *name)
+{
+ if (mMaskList.count(name))
+ {
+ return mMaskList[name];
+ }
+
+ return NULL;
+}
+
+BOOL LLFeatureManager::maskFeatures(const char *name)
+{
+ LLFeatureList *maskp = findMask(name);
+ if (!maskp)
+ {
+ llwarns << "Unknown feature mask " << name << llendl;
+ return FALSE;
+ }
+ llinfos << "Applying Feature Mask: " << name << llendl;
+ return maskList(*maskp);
+}
+
+BOOL LLFeatureManager::loadFeatureTables()
+{
+ std::string data_path = gDirUtilp->getAppRODataDir();
+
+ data_path += gDirUtilp->getDirDelimiter();
+
+ data_path += FEATURE_TABLE_FILENAME;
+
+
+ char name[MAX_STRING+1];
+
+ llifstream file;
+ U32 version;
+
+ file.open(data_path.c_str());
+
+ if (!file)
+ {
+ llwarns << "Unable to open feature table!" << llendl;
+ return FALSE;
+ }
+
+ // Check file version
+ file >> name;
+ file >> version;
+ if (strcmp(name, "version"))
+ {
+ llwarns << data_path << " does not appear to be a valid feature table!" << llendl;
+ return FALSE;
+ }
+
+ mTableVersion = version;
+
+ LLFeatureList *flp = NULL;
+ while (!file.eof())
+ {
+ char buffer[MAX_STRING];
+ name[0] = 0;
+
+ file >> name;
+
+ if (strlen(name) >= 2 &&
+ name[0] == '/' &&
+ name[1] == '/')
+ {
+ // This is a comment.
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ if (strlen(name) == 0)
+ {
+ // This is a blank line
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ if (!strcmp(name, "list"))
+ {
+ if (flp)
+ {
+ //flp->dump();
+ }
+ // It's a new mask, create it.
+ file >> name;
+ if (mMaskList.count(name))
+ {
+ llerrs << "Overriding mask " << name << ", this is invalid!" << llendl;
+ }
+
+ if (!flp)
+ {
+ //
+ // The first one is always the default
+ //
+ flp = this;
+ }
+ else
+ {
+ flp = new LLFeatureList(name);
+ mMaskList[name] = flp;
+ }
+
+ }
+ else
+ {
+ if (!flp)
+ {
+ llerrs << "Specified parameter before <list> keyword!" << llendl;
+ }
+ S32 available, recommended;
+ file >> available >> recommended;
+ flp->addFeature(name, available, recommended);
+ }
+ }
+ file.close();
+ //flp->dump();
+
+ return TRUE;
+}
+
+S32 LLFeatureManager::getGPUClass()
+{
+ return mGPUClass;
+}
+
+S32 LLFeatureManager::loadGPUClass()
+{
+ std::string data_path = gDirUtilp->getAppRODataDir();
+
+ data_path += gDirUtilp->getDirDelimiter();
+
+ data_path += GPU_TABLE_FILENAME;
+
+ llifstream file;
+
+ file.open(data_path.c_str());
+
+ if (!file)
+ {
+ llwarns << "Unable to open GPU table: " << data_path << "!" << llendl;
+ return 0;
+ }
+
+ std::string renderer = gGLManager.getRawGLString();
+ for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
+ {
+ *i = tolower(*i);
+ }
+
+ while (!file.eof())
+ {
+ char buffer[MAX_STRING];
+ buffer[0] = 0;
+
+ file.getline(buffer, MAX_STRING);
+
+ if (strlen(buffer) >= 2 &&
+ buffer[0] == '/' &&
+ buffer[1] == '/')
+ {
+ // This is a comment.
+ continue;
+ }
+
+ if (strlen(buffer) == 0)
+ {
+ // This is a blank line
+ continue;
+ }
+
+ char* cls, *label, *expr;
+
+ label = strtok(buffer, "\t");
+ expr = strtok(NULL, "\t");
+ cls = strtok(NULL, "\t");
+
+ if (label == NULL || expr == NULL || cls == NULL)
+ {
+ continue;
+ }
+
+ for (U32 i = 0; i < strlen(expr); i++)
+ {
+ expr[i] = tolower(expr[i]);
+ }
+
+ char* ex = strtok(expr, ".*");
+ char* rnd = (char*) renderer.c_str();
+
+ while (ex != NULL && rnd != NULL)
+ {
+ rnd = strstr(rnd, ex);
+ ex = strtok(NULL, ".*");
+ }
+
+ if (rnd != NULL)
+ {
+ file.close();
+ llinfos << "GPU is " << label << llendl;
+ return (S32) strtol(cls, NULL, 10);
+ }
+ }
+ file.close();
+ //flp->dump();
+
+ llwarns << "Couldn't match GPU to a class: " << gGLManager.getRawGLString() << llendl;
+ return 0;
+}
+
+void LLFeatureManager::cleanupFeatureTables()
+{
+ std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
+ mMaskList.clear();
+}
+
+
+void LLFeatureManager::initCPUFeatureMasks()
+{
+ if (gSysMemory.getPhysicalMemory() <= 256*1024*1024)
+ {
+ maskFeatures("RAM256MB");
+ }
+ else if (gSysMemory.getPhysicalMemory() <= 512*1024*1024)
+ {
+ //maskFeatures("RAM512MB");
+ }
+
+ if (gSysCPU.getMhz() < 1100)
+ {
+ maskFeatures("CPUSlow");
+ }
+ if (isSafe())
+ {
+ maskFeatures("safe");
+ }
+}
+
+void LLFeatureManager::initGraphicsFeatureMasks()
+{
+ mGPUClass = loadGPUClass();
+
+ if (mGPUClass >= 0 && mGPUClass < 4)
+ {
+ const char* class_table[] =
+ {
+ "Class0",
+ "Class1",
+ "Class2",
+ "Class3"
+ };
+
+ llinfos << "Setting GPU Class to " << class_table[mGPUClass] << llendl;
+ maskFeatures(class_table[mGPUClass]);
+ }
+
+ if (!gGLManager.mHasFragmentShader)
+ {
+ maskFeatures("NoPixelShaders");
+ }
+ if (!gGLManager.mHasVertexShader)
+ {
+ maskFeatures("NoVertexShaders");
+ }
+ if (gGLManager.mIsNVIDIA)
+ {
+ maskFeatures("NVIDIA");
+ }
+ if (gGLManager.mIsGF2or4MX)
+ {
+ maskFeatures("GeForce2");
+ }
+ if (gGLManager.mIsATI)
+ {
+ maskFeatures("ATI");
+ }
+ if (gGLManager.mIsRadeon8500)
+ {
+ maskFeatures("Radeon8500");
+ }
+ if (gGLManager.mIsRadeon9700)
+ {
+ maskFeatures("Radeon9700");
+ }
+ if (gGLManager.mIsGFFX)
+ {
+ maskFeatures("GeForceFX");
+ }
+ if (gGLManager.mIsIntel)
+ {
+ maskFeatures("Brookdale");
+ }
+
+ if (gGLManager.mIsMobilityRadeon9000)
+ {
+ maskFeatures("MobilityRadeon9000");
+ }
+ if (isSafe())
+ {
+ maskFeatures("safe");
+ }
+}
+
+extern LLOSInfo gSysOS;
+
+
+BOOL bad_hardware_dialog(const LLString &info_str, const LLString &url)
+{
+ if (!gSavedSettings.getWarning("AboutBadPCI"))
+ {
+ return FALSE;
+ }
+
+ // XUI:translate
+ std::string msg = llformat(
+ "[SECOND_LIFE] has detected that there may be a problem with.\n"
+ "hardware or drivers on your computer. Often resolving these\n"
+ "issues can result in enhanced stability and performance.\n"
+ " \n"
+ "%s\n"
+ " \n"
+ "Would you like to view a web page with more detailed\n"
+ "information on this problem?\n", info_str.c_str());
+
+ // Warn them that runnin without DirectX 9 will
+ // not allow us to tell them about driver issues
+ S32 button = OSMessageBox(msg.c_str(),
+ "Warning",
+ OSMB_YESNO);
+ if (OSBTN_YES== button)
+ {
+ llinfos << "User quitting after detecting bad drivers" << llendl;
+ spawn_web_browser(url.c_str());
+ return TRUE;
+ }
+ else
+ {
+ // Don't warn about bad PCI stuff again, they've clicked past it.
+ gSavedSettings.setWarning("AboutBadPCI", FALSE);
+ }
+ return FALSE;
+}
+
+BOOL LLFeatureManager::initPCIFeatureMasks()
+{
+#if LL_WINDOWS
+ BOOL exit_after_bad = FALSE;
+
+ BOOL is_2000 = FALSE;
+ BOOL is_xp = FALSE;
+
+ if (gSysOS.mMajorVer != 5)
+ {
+ // Unknown windows version number, exit!"
+ llwarns << "Unknown Windows major version " << gSysOS.mMajorVer << ", aborting detection!" << llendl;
+ return FALSE;
+ }
+ if (gSysOS.mMinorVer == 0)
+ {
+ is_2000 = TRUE;
+ }
+ else if (gSysOS.mMinorVer == 1)
+ {
+ is_xp = TRUE;
+ }
+ else
+ {
+ llwarns << "Unknown Windows minor version " << gSysOS.mMinorVer << ", aborting detection!" << llendl;
+ return FALSE;
+ }
+
+ // This only works on Win32, as it relies on DX9 hardware detection
+ // The PCI masks are actually the inverse of the normal masks
+ // We actually look through the masks,and see if any hardware matches it.
+ // This is because the masks encode logic about
+
+ // Check for the broken AMD AGP controllers (751, 761, 762)
+
+ // Horrible cruddy fixed lookup table.
+ // Figure out what OS we're on, the version numbers are different. Sigh...
+
+ LLDXDriverFile *dfilep = NULL;
+ LLDXDevice *devp = NULL;
+
+ // AMD(1022) AGP controllers
+ // 7007 AMD-751 AGP Controller
+ // 700F AMD-761 AGP Controller
+ // 700D AMD-762 AGP Controller
+ devp = gDXHardware.findDevice("VEN_1022", "DEV_7007|DEV_700F|DEV_700D");
+ if (devp)
+ {
+ // We're just pretty much screwed here, there are big problems with this hardware
+ // We've got trouble with the most recent nVidia drivers. Check for this and warn.
+
+ // Note: Need to detect that we're running with older nVidia hardware, probably
+ exit_after_bad |= bad_hardware_dialog("AMD AGP Controller",
+ AMD_AGP_URL);
+ }
+
+ // VIA(1106) AGP Controllers
+ // These need upgrading on both Win2K and WinXP
+ //
+ // 8305 VT8363/8365 CPU to AGP - Apollo KT133/KM133
+ // 8598 VT82C598MVP/694X CPU to AGP - Apollo MVP3/Pro133A
+ // 8605 VT8605 CPU to AGP - Apollo PM133
+ // B091 VT8633 CPU to AGP - Apollo Pro 266
+ // B099 VT8366/A/T CPU to AGP - Apollo KT266/A/333
+ // B168 VT8235 CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X333 chipset
+ // B188 VT8237 CPU to AGP (AGP 2.0/3.0) - K8T800
+ // B198 VT8237 CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X600 chipset
+
+ devp = gDXHardware.findDevice("VEN_1106",
+ "DEV_8305|DEV_8598|DEV_8605|DEV_B091|"
+ "DEV_B099|DEV_B168|DEV_B188|DEV_B198");
+ if (devp)
+ {
+ BOOL out_of_date = FALSE;
+ // Wanted driver: VIAAGP1.SYS
+ // Version Format: M.mm.0000.vvvv
+ // M.mm - Major/minor OS version (5.0 for Win2000, 5.1 for WinXP)
+ // vvvv - driver version number
+ //
+ // Notes:
+ // 3442 is most recent as of 2/25/04, probably want at least 3430 (seems to be a common version)
+
+ // These are DELIBERATE assignments inside if statements, blech.
+ if (dfilep = devp->findDriver("pci.sys"))
+ {
+ // Old driver: pci.sys
+ // Version: 5.01.2600.xxxx
+ //
+ // Notes:
+ // Default WinXP driver for B168, B198?
+
+ // Old driver: pci.sys
+ // Version: 5.01.2195.xxxx
+ //
+ // Notes:
+ // Default Win2K driver for 8305?
+
+ llwarns << "Detected pci.sys" << llendl;
+ write_debug("Old driver (pci.sys) for VIA detected!");
+ out_of_date = TRUE;
+ }
+ else if (dfilep = devp->findDriver("VIAAGP.SYS"))
+ {
+ // Old driver: VIAAGP.SYS
+ // Version: 5.01.2600.xxxx
+ //
+ // Notes:
+ // Default WinXP driver for B09x?
+
+ llwarns << "Detected VIAAGP.SYS" << llendl;
+ write_debug("Old driver (VIAAGP.SYS) for VIA detected!");
+ out_of_date = TRUE;
+ }
+ else if (dfilep = devp->findDriver("VIAAGP1.SYS"))
+ {
+ if (dfilep->mVersion.getField(3) < 3430)
+ {
+ // They're using a pretty old version of the VIA AGP drivers
+ // Maybe they want to upgrade?
+ llwarns << "Detected VIAAGP1.SYS" << llendl;
+ write_debug("Old driver (VIAAGP1.SYS) for VIA detected!");
+ out_of_date = TRUE;
+ }
+ }
+ if (out_of_date)
+ {
+ exit_after_bad |= bad_hardware_dialog("Out of date VIA AGP chipset driver",
+ VIA_URL);
+ }
+ }
+
+ // Intel(8086) AGP controllers (Win2K)
+ // These particular controllers only may need drivers on Win2K
+ //
+ // 1A31 82845[MP|MZ] Processor to AGP Controller
+ // 2532 82850/860 Processor to AGP Controller
+ if (is_2000)
+ {
+ devp = gDXHardware.findDevice("VEN_8086",
+ "DEV_1A31");
+ if (devp)
+ {
+ if (dfilep = devp->findDriver("pci.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ llwarns << "Detected pci.sys" << llendl;
+ write_debug("Old driver (pci.sys) for Intel 82845/850 on Win2K detected!");
+ exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver",
+ INTEL_CHIPSET_URL);
+ }
+ }
+ }
+
+ /* Removed 4/3/2006 by JC
+ After talking with Doug, we don't know what the proper driver
+ and/or version number should be for Intel 865. Regardless, this
+ code would _always_ complain if you had that chipset.
+
+ // Intel(8086) AGP controllers (All)
+ // These particular controllers may need drivers on both Win2K and WinXP
+ //
+ // 2561 82845G/GL/GE/PE/GV Processor to AGP Controller
+ // 2571 82865G/PE/P/GV/28248P Processor to AGP Controller
+ devp = gDXHardware.findDevice("VEN_8086",
+ "DEV_2571");
+ if (devp)
+ {
+ // Wanted driver: AGP440.SYS(?)
+ //
+ // Notes:
+ // Not sure, need to verify with an actual 82865/75 (Dell 8300?)
+
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver",
+ INTEL_CHIPSET_URL);
+ }
+ */
+
+
+ // SiS(1039) AGP controllers (All)
+ // These particular controllers may need drivers on both Win2K and WinXP
+ //
+ // 0001 SiS 530
+ // 0002 SiS SG86C202(???)
+ // 0003 SiS 648FX
+ devp = gDXHardware.findDevice("VEN_1039",
+ "DEV_0001|DEV_0002|DEV_0003");
+ if (devp)
+ {
+ BOOL out_of_date = FALSE;
+ // Wanted driver: SISAGPX.SYS
+ //
+ // Notes:
+ // Not sure, need to verify with an actual 82865/75 (Dell 8300?)
+
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ if (dfilep = devp->findDriver("pci.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ llwarns << "Detected pci.sys" << llendl;
+ write_debug("Old driver (pci.sys) for SiS detected!");
+ out_of_date = TRUE;
+ }
+
+ if (dfilep = devp->findDriver("sisagp.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ llwarns << "Detected sisagp.sys" << llendl;
+ write_debug("Old driver (sisagp.sys) for SiS detected!");
+ out_of_date = TRUE;
+ }
+
+ if (dfilep = devp->findDriver("sisagpx.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 7.02.0000.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ if (dfilep->mVersion.getField(3) < 1160)
+ {
+ out_of_date = TRUE;
+ llwarns << "Detected sisagpx.sys" << llendl;
+ write_debug("Old driver (sisagpx.sys) for SiS detected!");
+ }
+ }
+ if (out_of_date)
+ {
+ exit_after_bad |= bad_hardware_dialog("Out of date SiS chipset driver",
+ SIS_CHIPSET_URL);
+ }
+ }
+
+ return exit_after_bad;
+#else
+ return TRUE;
+#endif
+}
+
+void LLFeatureManager::applyRecommendedFeatures()
+{
+ // see featuretable.txt
+
+ llinfos << "Applying Recommended Features" << llendl;
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ dump();
+#endif
+
+ // Enabling AGP
+ if (getRecommendedLevel("RenderAGP"))
+ {
+ gSavedSettings.setBOOL("RenderUseAGP", TRUE);
+ }
+ else
+ {
+ gSavedSettings.setBOOL("RenderUseAGP", FALSE);
+ }
+
+ // Anisotropic rendering
+ BOOL aniso = getRecommendedLevel("RenderAniso");
+ LLImageGL::sGlobalUseAnisotropic = aniso;
+ gSavedSettings.setBOOL("RenderAnisotropic", LLImageGL::sGlobalUseAnisotropic);
+
+ // Render Avatar Mode
+ BOOL avatar_vp = getRecommendedLevel("RenderAvatarVP");
+ S32 avatar_mode = getRecommendedLevel("RenderAvatarMode");
+ if (avatar_vp == FALSE)
+ avatar_mode = 0;
+ gSavedSettings.setBOOL("RenderAvatarVP", avatar_vp);
+ gSavedSettings.setS32("RenderAvatarMode", avatar_mode);
+
+ // Render Distance
+ S32 far_clip = getRecommendedLevel("RenderDistance");
+ gSavedSettings.setF32("RenderFarClip", (F32)far_clip);
+
+ // Lighting
+ S32 lighting = getRecommendedLevel("RenderLighting");
+ gSavedSettings.setS32("RenderLightingDetail", lighting);
+
+ // ObjectBump
+ BOOL bump = getRecommendedLevel("RenderObjectBump");
+ gSavedSettings.setBOOL("RenderObjectBump", bump);
+
+ // Particle Count
+ S32 max_parts = getRecommendedLevel("RenderParticleCount");
+ gSavedSettings.setS32("RenderMaxPartCount", max_parts);
+ LLViewerPartSim::setMaxPartCount(max_parts);
+
+ // RippleWater
+ BOOL ripple = getRecommendedLevel("RenderRippleWater");
+ gSavedSettings.setBOOL("RenderRippleWater", ripple);
+
+ // Vertex Shaders
+ S32 shaders = getRecommendedLevel("VertexShaderEnable");
+ gSavedSettings.setBOOL("VertexShaderEnable", shaders);
+
+ // Terrain
+ S32 terrain = getRecommendedLevel("RenderTerrainDetail");
+ gSavedSettings.setS32("RenderTerrainDetail", terrain);
+ LLDrawPoolTerrain::sDetailMode = terrain;
+
+ // Set the amount of VRAM we have available
+ if (isSafe())
+ {
+ gSavedSettings.setS32("GraphicsCardMemorySetting", 1); // 32 MB in 'safe' mode
+ }
+ else
+ {
+ S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting");
+ // -1 indicates use default (max), don't change
+ if (idx != -1)
+ {
+ idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting
+ gSavedSettings.setS32("GraphicsCardMemorySetting", idx);
+ }
+ }
+}
diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h
new file mode 100644
index 0000000000..98f9317b01
--- /dev/null
+++ b/indra/newview/llfeaturemanager.h
@@ -0,0 +1,98 @@
+/**
+ * @file llfeaturemanager.h
+ * @brief LLFeatureManager class definition
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFEATUREMANAGER_H
+#define LL_LLFEATUREMANAGER_H
+
+#include "stdtypes.h"
+
+#include "llstring.h"
+#include "llskipmap.h"
+#include <map>
+
+class LLFeatureInfo
+{
+public:
+ LLFeatureInfo() : mValid(FALSE), mAvailable(FALSE), mRecommendedLevel(-1) {}
+ LLFeatureInfo(const char *name, const BOOL available, const S32 level);
+
+ BOOL isValid() const { return mValid; };
+
+public:
+ BOOL mValid;
+ LLString mName;
+ BOOL mAvailable;
+ S32 mRecommendedLevel;
+};
+
+
+class LLFeatureList
+{
+public:
+ LLFeatureList(const char *name = "default");
+ virtual ~LLFeatureList();
+
+ BOOL isFeatureAvailable(const char *name);
+ S32 getRecommendedLevel(const char *name);
+
+ void setFeatureAvailable(const char *name, const BOOL available);
+ void setRecommendedLevel(const char *name, const S32 level);
+
+ BOOL loadFeatureList(FILE *fp);
+
+ BOOL maskList(LLFeatureList &mask);
+
+ void addFeature(const char *name, const BOOL available, const S32 level);
+
+ void dump();
+protected:
+ LLString mName;
+ typedef std::map<LLString, LLFeatureInfo> feature_map_t;
+ feature_map_t mFeatures;
+};
+
+
+class LLFeatureManager : public LLFeatureList
+{
+public:
+ LLFeatureManager() : mInited(FALSE), mTableVersion(0), mSafe(FALSE), mGPUClass(0) {}
+
+ void maskCurrentList(const char *name); // Mask the current feature list with the named list
+
+ BOOL loadFeatureTables();
+ S32 getGPUClass();
+ S32 loadGPUClass();
+
+ void cleanupFeatureTables();
+
+ S32 getVersion() const { return mTableVersion; }
+ void setSafe(const BOOL safe) { mSafe = safe; }
+ BOOL isSafe() const { return mSafe; }
+
+ LLFeatureList *findMask(const char *name);
+ BOOL maskFeatures(const char *name);
+
+
+ void initCPUFeatureMasks();
+ void initGraphicsFeatureMasks();
+ BOOL initPCIFeatureMasks();
+
+ void applyRecommendedFeatures();
+protected:
+ void initBaseMask();
+
+ std::map<LLString, LLFeatureList *> mMaskList;
+ BOOL mInited;
+ S32 mTableVersion;
+ BOOL mSafe; // Reinitialize everything to the "safe" mask
+ S32 mGPUClass;
+};
+
+extern LLFeatureManager *gFeatureManagerp;
+
+#endif // LL_LLFEATUREMANAGER_H
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
new file mode 100644
index 0000000000..776a2e4dc2
--- /dev/null
+++ b/indra/newview/llfilepicker.cpp
@@ -0,0 +1,1339 @@
+/**
+ * @file llfilepicker.cpp
+ * @brief OS-specific file picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfilepicker.h"
+//#include "viewer.h"
+//#include "llviewermessage.h"
+#include "llworld.h"
+#include "llviewerwindow.h"
+#include "llkeyboard.h"
+#include "lldir.h"
+#include "llframetimer.h"
+
+//
+// Globals
+//
+
+LLFilePicker LLFilePicker::sInstance;
+
+#if LL_WINDOWS
+#define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0"
+#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg)\0*.tga;*.bmp;*.jpg;*.jpeg\0"
+#define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0"
+#ifdef _CORY_TESTING
+#define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0"
+#endif
+#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
+#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
+#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
+#endif
+
+//
+// Implementation
+//
+#if LL_WINDOWS
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+
+ mOFN.lStructSize = sizeof(OPENFILENAMEW);
+ mOFN.hwndOwner = NULL; // Set later
+ mOFN.hInstance = NULL;
+ mOFN.lpstrCustomFilter = NULL;
+ mOFN.nMaxCustFilter = 0;
+ mOFN.lpstrFile = NULL; // set in open and close
+ mOFN.nMaxFile = LL_MAX_PATH;
+ mOFN.lpstrFileTitle = NULL;
+ mOFN.nMaxFileTitle = 0;
+ mOFN.lpstrInitialDir = NULL;
+ mOFN.lpstrTitle = NULL;
+ mOFN.Flags = 0; // set in open and close
+ mOFN.nFileOffset = 0;
+ mOFN.nFileExtension = 0;
+ mOFN.lpstrDefExt = NULL;
+ mOFN.lCustData = 0L;
+ mOFN.lpfnHook = NULL;
+ mOFN.lpTemplateName = NULL;
+}
+
+LLFilePicker::~LLFilePicker()
+{
+ // nothing
+}
+
+BOOL LLFilePicker::setupFilter(ELoadFilter filter)
+{
+ BOOL res = TRUE;
+ switch (filter)
+ {
+ case FFLOAD_ALL:
+ mOFN.lpstrFilter = L"All Files (*.*)\0*.*\0" \
+ SOUND_FILTER \
+ IMAGE_FILTER \
+ ANIM_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_WAV:
+ mOFN.lpstrFilter = SOUND_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_IMAGE:
+ mOFN.lpstrFilter = IMAGE_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_ANIM:
+ mOFN.lpstrFilter = ANIM_FILTER \
+ L"\0";
+ break;
+#ifdef _CORY_TESTING
+ case FFLOAD_GEOMETRY:
+ mOFN.lpstrFilter = GEOMETRY_FILTER \
+ L"\0";
+ break;
+#endif
+ case FFLOAD_XML:
+ mOFN.lpstrFilter = XML_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_SLOBJECT:
+ mOFN.lpstrFilter = SLOBJECT_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_RAW:
+ mOFN.lpstrFilter = RAW_FILTER \
+ L"\0";
+ break;
+ default:
+ res = FALSE;
+ break;
+ }
+ return res;
+}
+
+BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+ mMultiFile = FALSE;
+
+ // don't provide default file selection
+ mFilesW[0] = '\0';
+
+ mOFN.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());
+ mOFN.lpstrFile = mFilesW;
+ mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
+ mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ;
+ mOFN.nFilterIndex = 1;
+
+ setupFilter(filter);
+
+ // Modal, so pause agent
+ send_agent_pause();
+ // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
+ success = GetOpenFileName(&mOFN);
+ if (success)
+ {
+ LLString tstr = utf16str_to_utf8str(llutf16string(mFilesW));
+ memcpy(mFiles, tstr.c_str(), tstr.size()+1);
+ mCurrentFile = mFiles;
+ }
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+ mMultiFile = FALSE;
+
+ // don't provide default file selection
+ mFilesW[0] = '\0';
+
+ mOFN.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());
+ mOFN.lpstrFile = mFilesW;
+ mOFN.nFilterIndex = 1;
+ mOFN.nMaxFile = FILENAME_BUFFER_SIZE;
+ mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR |
+ OFN_EXPLORER | OFN_ALLOWMULTISELECT;
+
+ setupFilter(filter);
+
+ // Modal, so pause agent
+ send_agent_pause();
+ // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
+ success = GetOpenFileName(&mOFN); // pauses until ok or cancel.
+ if( success )
+ {
+ // The getopenfilename api doesn't tell us if we got more than
+ // one file, so we have to test manually by checking string
+ // lengths.
+ if( wcslen(mOFN.lpstrFile) > mOFN.nFileOffset )
+ {
+ mMultiFile = FALSE;
+ mCurrentFile = mFiles;
+ LLString tstr = utf16str_to_utf8str(llutf16string(mFilesW));
+ memcpy(mFiles, tstr.c_str(), tstr.size()+1);
+ }
+ else
+ {
+ mMultiFile = TRUE;
+ mCurrentFile = 0;
+ mLocked = TRUE;
+ WCHAR* tptrw = mFilesW;
+ char* tptr = mFiles;
+ memset( mFiles, 0, FILENAME_BUFFER_SIZE );
+ while(1)
+ {
+ if (*tptrw == 0 && *(tptrw+1) == 0) // double '\0'
+ break;
+ if (*tptrw == 0 && !mCurrentFile)
+ mCurrentFile = tptr+1;
+ S32 tlen16,tlen8;
+ tlen16 = utf16chars_to_utf8chars(tptrw, tptr, &tlen8);
+ tptrw += tlen16;
+ tptr += tlen8;
+ }
+ }
+ }
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const char* filename)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+ mMultiFile = FALSE;
+
+ mOFN.lpstrFile = mFilesW;
+ if (filename)
+ {
+ llutf16string tstring = utf8str_to_utf16str(filename);
+ wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); }
+ else
+ {
+ mFilesW[0] = '\0';
+ }
+ mOFN.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());
+
+ switch( filter )
+ {
+ case FFSAVE_ALL:
+ mOFN.lpstrDefExt = NULL;
+ mOFN.lpstrFilter =
+ L"All Files (*.*)\0*.*\0" \
+ L"WAV Sounds (*.wav)\0*.wav\0" \
+ L"Targa, Bitmap Images (*.tga; *.bmp)\0*.tga;*.bmp\0" \
+ L"\0";
+ break;
+ case FFSAVE_WAV:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.wav", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"wav";
+ L"WAV Sounds (*.wav)\0*.wav\0" \
+ L"\0";
+ break;
+ case FFSAVE_TGA:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.tga", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"tga";
+ mOFN.lpstrFilter =
+ L"Targa Images (*.tga)\0*.tga\0" \
+ L"\0";
+ break;
+ case FFSAVE_BMP:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.bmp", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"bmp";
+ mOFN.lpstrFilter =
+ L"Bitmap Images (*.bmp)\0*.bmp\0" \
+ L"\0";
+ break;
+ case FFSAVE_AVI:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.avi", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"avi";
+ mOFN.lpstrFilter =
+ L"AVI Movie File (*.avi)\0*.avi\0" \
+ L"\0";
+ break;
+ case FFSAVE_ANIM:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.xaf", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"xaf";
+ mOFN.lpstrFilter =
+ L"XAF Anim File (*.xaf)\0*.xaf\0" \
+ L"\0";
+ break;
+#ifdef _CORY_TESTING
+ case FFSAVE_GEOMETRY:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.slg", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"slg";
+ mOFN.lpstrFilter =
+ L"SLG SL Geometry File (*.slg)\0*.slg\0" \
+ L"\0";
+ break;
+#endif
+ case FFSAVE_XML:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.xml", FILENAME_BUFFER_SIZE);
+ }
+
+ mOFN.lpstrDefExt = L"xml";
+ mOFN.lpstrFilter =
+ L"XML File (*.xml)\0*.xml\0" \
+ L"\0";
+ break;
+ case FFSAVE_COLLADA:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.collada", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"collada";
+ mOFN.lpstrFilter =
+ L"COLLADA File (*.collada)\0*.collada\0" \
+ L"\0";
+ break;
+ case FFSAVE_RAW:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.raw", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"raw";
+ mOFN.lpstrFilter = RAW_FILTER \
+ L"\0";
+ break;
+ default:
+ return FALSE;
+ }
+
+
+ mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
+ mOFN.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
+
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
+ success = GetSaveFileName(&mOFN);
+ if (success)
+ {
+ LLString tstr = utf16str_to_utf8str(llutf16string(mFilesW));
+ memcpy(mFiles, tstr.c_str(), tstr.size()+1);
+ mCurrentFile = mFiles;
+ }
+ gKeyboard->resetKeys();
+ }
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ if(mMultiFile)
+ {
+ buildFilename();
+ return mFilename;
+ }
+ return mFiles;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ if(mMultiFile)
+ {
+ mCurrentFile += strlen(mCurrentFile) + 1;
+ if( '\0' != mCurrentFile[0] )
+ {
+ buildFilename();
+ return mFilename;
+ }
+ else
+ {
+ mLocked = FALSE;
+ }
+ }
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ if( '\0' != mCurrentFile[0] )
+ {
+ return mCurrentFile;
+ }
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+ mLocked = FALSE;
+ memset( mFiles, 0, FILENAME_BUFFER_SIZE );
+ memset( mFilename, 0, LL_MAX_PATH );
+ mCurrentFile = mFiles;
+}
+
+void LLFilePicker::buildFilename( void )
+{
+ strncpy( mFilename, mFiles, LL_MAX_PATH );
+ S32 len = strlen( mFilename );
+
+ strcat(mFilename,gDirUtilp->getDirDelimiter().c_str());
+ len += strlen(gDirUtilp->getDirDelimiter().c_str());
+
+// mFilename[len++] = '\\';
+ LLString::copy( mFilename + len, mCurrentFile, LL_MAX_PATH - len );
+}
+
+#elif LL_DARWIN
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+
+ memset(&mNavOptions, 0, sizeof(mNavOptions));
+ OSStatus error = NavGetDefaultDialogCreationOptions(&mNavOptions);
+ if (error == noErr)
+ {
+ mNavOptions.modality = kWindowModalityAppModal;
+ }
+ mFileIndex = 0;
+}
+
+LLFilePicker::~LLFilePicker()
+{
+ // nothing
+}
+
+Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode)
+{
+ Boolean result = true;
+ ELoadFilter filter = *((ELoadFilter*) callBackUD);
+ OSStatus error = noErr;
+
+ if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS))
+ {
+ // navInfo is only valid for typeFSRef and typeFSS
+ NavFileOrFolderInfo *navInfo = (NavFileOrFolderInfo*) info;
+ if (!navInfo->isFolder)
+ {
+ AEDesc desc;
+ error = AECoerceDesc(theItem, typeFSRef, &desc);
+ if (error == noErr)
+ {
+ FSRef fileRef;
+ error = AEGetDescData(&desc, &fileRef, sizeof(fileRef));
+ if (error == noErr)
+ {
+ LSItemInfoRecord fileInfo;
+ error = LSCopyItemInfoForRef(&fileRef, kLSRequestExtension | kLSRequestTypeCreator, &fileInfo);
+ if (error == noErr)
+ {
+ if (filter == FFLOAD_IMAGE)
+ {
+ if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' &&
+ fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' &&
+ fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+ else if (filter == FFLOAD_WAV)
+ {
+ if (fileInfo.filetype != 'WAVE' && fileInfo.filetype != 'WAV ' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("wave"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("wav"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+ else if (filter == FFLOAD_ANIM)
+ {
+ if (fileInfo.filetype != 'BVH ' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("bvh"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+#ifdef _CORY_TESTING
+ else if (filter == FFLOAD_GEOMETRY)
+ {
+ if (fileInfo.filetype != 'SLG ' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("slg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+#endif
+ else if (filter == FFLOAD_SLOBJECT)
+ {
+ llwarns << "*** navOpenFilterProc: FFLOAD_SLOBJECT NOT IMPLEMENTED ***" << llendl;
+ }
+ else if (filter == FFLOAD_RAW)
+ {
+ if (fileInfo.filetype != '\?\?\?\?' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+
+ if (fileInfo.extension)
+ {
+ CFRelease(fileInfo.extension);
+ }
+ }
+ }
+ AEDisposeDesc(&desc);
+ }
+ }
+ }
+ return result;
+}
+
+OSStatus LLFilePicker::doNavChooseDialog(ELoadFilter filter)
+{
+ OSStatus error = noErr;
+ NavDialogRef navRef = NULL;
+ NavReplyRecord navReply;
+
+ memset(&navReply, 0, sizeof(navReply));
+ mFiles[0] = '\0';
+ mFileVector.clear();
+
+ // NOTE: we are passing the address of a local variable here.
+ // This is fine, because the object this call creates will exist for less than the lifetime of this function.
+ // (It is destroyed by NavDialogDispose() below.)
+ error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef);
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ if (error == noErr)
+ error = NavDialogRun(navRef);
+
+ gViewerWindow->mWindow->afterDialog();
+
+ if (error == noErr)
+ error = NavDialogGetReply(navRef, &navReply);
+
+ if (navRef)
+ NavDialogDispose(navRef);
+
+ if (error == noErr && navReply.validRecord)
+ {
+ SInt32 count = 0;
+ SInt32 index;
+
+ // AE indexes are 1 based...
+ error = AECountItems(&navReply.selection, &count);
+ for (index = 1; index <= count; index++)
+ {
+ FSRef fsRef;
+ AEKeyword theAEKeyword;
+ DescType typeCode;
+ Size actualSize = 0;
+ char path[MAX_PATH];
+
+ memset(&fsRef, 0, sizeof(fsRef));
+ error = AEGetNthPtr(&navReply.selection, index, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
+
+ if (error == noErr)
+ error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path));
+
+ if (error == noErr)
+ mFileVector.push_back(LLString(path));
+ }
+ }
+
+ return error;
+}
+
+OSStatus LLFilePicker::doNavSaveDialog(ESaveFilter filter, const char* filename)
+{
+ OSStatus error = noErr;
+ NavDialogRef navRef = NULL;
+ NavReplyRecord navReply;
+
+ memset(&navReply, 0, sizeof(navReply));
+ mFiles[0] = '\0';
+ mFileVector.clear();
+
+ // Setup the type, creator, and extension
+ OSType type, creator;
+ CFStringRef extension = NULL;
+ switch (filter)
+ {
+ case FFSAVE_WAV:
+ type = 'WAVE';
+ creator = 'TVOD';
+ extension = CFSTR(".wav");
+ break;
+
+ case FFSAVE_TGA:
+ type = 'TPIC';
+ creator = 'prvw';
+ extension = CFSTR(".tga");
+ break;
+
+ case FFSAVE_BMP:
+ type = 'BMPf';
+ creator = 'prvw';
+ extension = CFSTR(".bmp");
+ break;
+
+ case FFSAVE_AVI:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".mov");
+ break;
+
+ case FFSAVE_ANIM:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".xaf");
+ break;
+
+#ifdef _CORY_TESTING
+ case FFSAVE_GEOMETRY:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".slg");
+ break;
+#endif
+ case FFSAVE_RAW:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".raw");
+ break;
+
+ case FFSAVE_ALL:
+ default:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR("");
+ break;
+ }
+
+ // Create the dialog
+ error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef);
+ if (error == noErr)
+ {
+ CFStringRef nameString = NULL;
+ bool hasExtension = true;
+
+ // Create a CFString of the initial file name
+ if (filename)
+ nameString = CFStringCreateWithCString(NULL, filename, kCFStringEncodingUTF8);
+ else
+ nameString = CFSTR("Untitled");
+
+ // Add the extension if one was not provided
+ if (nameString && !CFStringHasSuffix(nameString, extension))
+ {
+ CFStringRef tempString = nameString;
+ hasExtension = false;
+ nameString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), tempString, extension);
+ CFRelease(tempString);
+ }
+
+ // Set the name in the dialog
+ if (nameString)
+ {
+ error = NavDialogSetSaveFileName(navRef, nameString);
+ CFRelease(nameString);
+ }
+ else
+ {
+ error = paramErr;
+ }
+ }
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ // Run the dialog
+ if (error == noErr)
+ error = NavDialogRun(navRef);
+
+ gViewerWindow->mWindow->afterDialog();
+
+ if (error == noErr)
+ error = NavDialogGetReply(navRef, &navReply);
+
+ if (navRef)
+ NavDialogDispose(navRef);
+
+ if (error == noErr && navReply.validRecord)
+ {
+ SInt32 count = 0;
+
+ // AE indexes are 1 based...
+ error = AECountItems(&navReply.selection, &count);
+ if (count > 0)
+ {
+ // Get the FSRef to the containing folder
+ FSRef fsRef;
+ AEKeyword theAEKeyword;
+ DescType typeCode;
+ Size actualSize = 0;
+
+ memset(&fsRef, 0, sizeof(fsRef));
+ error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
+
+ if (error == noErr)
+ {
+ char path[PATH_MAX];
+ char newFileName[SINGLE_FILENAME_BUFFER_SIZE];
+
+ error = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
+ if (error == noErr)
+ {
+ if (CFStringGetCString(navReply.saveFileName, newFileName, sizeof(newFileName), kCFStringEncodingUTF8))
+ {
+ mFileVector.push_back(LLString(path) + LLString("/") + LLString(newFileName));
+ }
+ else
+ {
+ error = paramErr;
+ }
+ }
+ else
+ {
+ error = paramErr;
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
+{
+ if( mLocked ) return FALSE;
+ mMultiFile = FALSE;
+ BOOL success = FALSE;
+
+ OSStatus error = noErr;
+
+ mFileVector.clear();
+ mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavChooseDialog(filter);
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mFileVector.size())
+ success = true;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
+{
+ if( mLocked ) return FALSE;
+ mMultiFile = TRUE;
+ BOOL success = FALSE;
+
+ OSStatus error = noErr;
+
+ mFileVector.clear();
+ mNavOptions.optionFlags |= kNavAllowMultipleFiles;
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavChooseDialog(filter);
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mFileVector.size())
+ success = true;
+ if (mFileVector.size() > 1)
+ mLocked = TRUE;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+void LLFilePicker::getFilePath(SInt32 index)
+{
+ mFiles[0] = 0;
+ if (mFileVector.size())
+ strcpy(mFiles, mFileVector[index].c_str());
+}
+
+void LLFilePicker::getFileName(SInt32 index)
+{
+ mFilename[0] = 0;
+ if (mFileVector.size())
+ {
+ char *start = strrchr(mFileVector[index].c_str(), '/');
+ if (start && ((start + 1 - mFileVector[index].c_str()) < (mFileVector[index].size())))
+ strcpy(mFilename, start + 1);
+ }
+}
+
+BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const char* filename)
+{
+ if( mLocked ) return FALSE;
+ BOOL success = FALSE;
+ OSStatus error = noErr;
+
+ mFileVector.clear();
+ mMultiFile = FALSE;
+ mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
+
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavSaveDialog(filter, filename);
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mFileVector.size())
+ success = true;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ mFileIndex = 0;
+ getFilePath(mFileIndex);
+ return mFiles;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ if(mMultiFile)
+ {
+ mFileIndex++;
+ if (mFileIndex < mFileVector.size())
+ {
+ getFilePath(mFileIndex);
+ return mFiles;
+ }
+ else
+ {
+ mLocked = FALSE;
+ }
+ }
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ if (mFileIndex < mFileVector.size())
+ {
+ getFileName(mFileIndex);
+ return mFilename;
+ }
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+ mLocked = FALSE;
+ memset( mFiles, 0, FILENAME_BUFFER_SIZE );
+ memset( mFilename, 0, LL_MAX_PATH );
+ mCurrentFile = mFiles;
+
+ mFileIndex = 0;
+ mFileVector.clear();
+}
+
+#elif LL_LINUX
+
+# if LL_GTK
+LLFilePicker::LLFilePicker()
+{
+ reset();
+}
+
+LLFilePicker::~LLFilePicker()
+{
+}
+
+static void store_filenames(GtkWidget *widget, gpointer user_data) {
+ StoreFilenamesStruct *sfs = (StoreFilenamesStruct*) user_data;
+ GtkWidget *win = sfs->win;
+
+ llinfos <<"store_filesnames: marker A" << llendl;
+
+ // get NULL-terminated list of strings allocated for us by GTK
+ gchar** string_list =
+ gtk_file_selection_get_selections(GTK_FILE_SELECTION(win));
+
+ llinfos <<"store_filesnames: marker B" << llendl;
+
+ int idx = 0;
+ while (string_list[idx])
+ {
+ // platform-string to utf8
+ gchar* filename_utf8 = g_filename_from_utf8(string_list[idx],
+ -1, NULL,
+ NULL,
+ NULL);
+ sfs->fileVector.push_back(LLString(filename_utf8));
+ ++idx;
+ }
+
+ llinfos <<"store_filesnames: marker C" << llendl;
+
+ g_strfreev(string_list);
+
+ llinfos <<"store_filesnames: marker D" << llendl;
+
+ llinfos << sfs->fileVector.size() << " filename(s) selected:" << llendl;
+ U32 x;
+ for (x=0; x<sfs->fileVector.size(); ++x)
+ llinfos << x << ":" << sfs->fileVector[x] << llendl;
+}
+
+GtkWindow* LLFilePicker::buildFilePicker(void)
+{
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL) &&
+ ! gViewerWindow->getWindow()->getFullscreen())
+ {
+ GtkWidget *win = NULL;
+
+ win = gtk_file_selection_new(NULL);
+ mStoreFilenames.win = win;
+
+# if LL_X11
+ // Make GTK tell the window manager to associate this
+ // dialog with our non-GTK raw X11 window, which should try
+ // to keep it on top etc.
+ Window *XWindowID_ptr = (Window*) gViewerWindow->
+ getWindow()->getPlatformWindow();
+ if (XWindowID_ptr && None != *XWindowID_ptr)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(*XWindowID_ptr);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+ else
+ {
+ llwarns << "Hmm, couldn't get xwid from LLWindow." << llendl;
+ }
+# endif //LL_X11
+
+ g_signal_connect (G_OBJECT(win), "destroy",
+ G_CALLBACK(gtk_main_quit), NULL);
+
+ // on 'ok', grab the file selection list
+ g_signal_connect (GTK_FILE_SELECTION(win)->ok_button,
+ "clicked",
+ G_CALLBACK(store_filenames),
+ &mStoreFilenames);
+
+ // both 'ok' and 'cancel' will end the dialog
+ g_signal_connect_swapped (G_OBJECT(GTK_FILE_SELECTION(win)->
+ ok_button),
+ "clicked",
+ G_CALLBACK(gtk_widget_destroy),
+ G_OBJECT(win));
+ g_signal_connect_swapped (G_OBJECT(GTK_FILE_SELECTION(win)->
+ cancel_button),
+ "clicked",
+ G_CALLBACK(gtk_widget_destroy),
+ G_OBJECT(win));
+
+ gtk_file_selection_show_fileop_buttons(GTK_FILE_SELECTION(win));
+ gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(win),
+ FALSE);
+
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+
+ return GTK_WINDOW(win);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const char* filename )
+{
+ BOOL rtn = FALSE;
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ reset();
+ GtkWindow* picker = buildFilePicker();
+
+ if (picker)
+ {
+ std::string suggest_name = "untitled";
+ std::string suggest_ext = "";
+ std::string caption = "Save ";
+ switch (filter)
+ {
+ case FFSAVE_WAV:
+ caption += "Sounds (*.wav)";
+ suggest_ext += ".wav";
+ break;
+ case FFSAVE_TGA:
+ caption += "Targa Images (*.tga)";
+ suggest_ext += ".tga";
+ break;
+ case FFSAVE_BMP:
+ caption += "Bitmap Images (*.bmp)";
+ suggest_ext += ".bmp";
+ break;
+ case FFSAVE_AVI:
+ caption += "AVI Movie File (*.avi)";
+ suggest_ext += ".avi";
+ break;
+ case FFSAVE_ANIM:
+ caption += "XAF Anim File (*.xaf)";
+ suggest_ext += ".xaf";
+ break;
+ case FFSAVE_XML:
+ caption += "XML File (*.xml)";
+ suggest_ext += ".xml";
+ break;
+ case FFSAVE_RAW:
+ caption += "RAW File (*.raw)";
+ suggest_ext += ".raw";
+ break;
+ default:;
+ break;
+ }
+
+ gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
+
+ if (!filename)
+ {
+ suggest_name += suggest_ext;
+
+ gtk_file_selection_set_filename
+ (GTK_FILE_SELECTION(picker),
+ g_filename_from_utf8(suggest_name.c_str(),
+ -1, NULL,
+ NULL,
+ NULL));
+ gtk_editable_select_region(GTK_EDITABLE(GTK_FILE_SELECTION(picker)->selection_entry), 0, suggest_name.length() - suggest_ext.length() );
+ }
+ else
+ {
+ gtk_file_selection_set_filename
+ (GTK_FILE_SELECTION(picker),
+ g_filename_from_utf8(filename,
+ -1, NULL,
+ NULL,
+ NULL));
+ gtk_editable_select_region(GTK_EDITABLE(GTK_FILE_SELECTION(picker)->selection_entry), 0, -1 );
+ }
+
+ gtk_widget_show_all(GTK_WIDGET(picker));
+ gtk_main();
+ rtn = (mStoreFilenames.fileVector.size() == 1);
+ }
+
+ gViewerWindow->mWindow->afterDialog();
+
+ return rtn;
+}
+
+BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
+{
+ BOOL rtn = FALSE;
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ reset();
+ GtkWindow* picker = buildFilePicker();
+
+ if (picker)
+ {
+ std::string caption = "Load ";
+ switch (filter)
+ {
+ case FFLOAD_WAV:
+ caption += "Sounds (*.wav)"; break;
+ case FFLOAD_ANIM:
+ caption += "Animations (*.bvh)"; break;
+ case FFLOAD_IMAGE:
+ caption += "Images (*.tga; *.bmp; *.jpg; *.jpeg)"; break;
+ default:;
+ break;
+ }
+
+ gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
+
+ gtk_widget_show_all(GTK_WIDGET(picker));
+ gtk_main();
+ rtn = (mStoreFilenames.fileVector.size() == 1);
+ }
+
+ gViewerWindow->mWindow->afterDialog();
+
+ return rtn;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
+{
+ BOOL rtn = FALSE;
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ reset();
+ GtkWindow* picker = buildFilePicker();
+
+ if (picker)
+ {
+ gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(picker),
+ TRUE);
+
+ gtk_window_set_title(GTK_WINDOW(picker), "Load Files");
+
+ gtk_widget_show_all(GTK_WIDGET(picker));
+ gtk_main();
+ rtn = !mStoreFilenames.fileVector.empty();
+ }
+
+ gViewerWindow->mWindow->afterDialog();
+
+ return rtn;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ mNextFileIndex = 0;
+ return getNextFile();
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ if (mStoreFilenames.fileVector.size() > mNextFileIndex)
+ return mStoreFilenames.fileVector[mNextFileIndex++].c_str();
+ else
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ // getDirname is badly named... it really means getBasename.
+ S32 index = mNextFileIndex - 1; // want index before the 'next' cursor
+ if (index >= 0 && index < (S32)mStoreFilenames.fileVector.size())
+ {
+ // we do this using C strings so we don't have to
+ // convert a LLString/std::string character offset into a
+ // byte-offset for the return (which is a C string anyway).
+ const char* dirsep = gDirUtilp->getDirDelimiter().c_str();
+ const char* fullpath = mStoreFilenames.fileVector[index].c_str();
+ const char* finalpart = NULL;
+ const char* thispart = fullpath;
+ // walk through the string looking for the final dirsep, i.e. /
+ do
+ {
+ thispart = strstr(thispart, dirsep);
+ if (NULL != thispart)
+ finalpart = thispart = &thispart[1];
+ }
+ while (NULL != thispart);
+ return finalpart;
+ }
+ else
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+ llinfos << "GTK LLFilePicker::reset()" << llendl;
+ mNextFileIndex = 0;
+ mStoreFilenames.win = NULL;
+ mStoreFilenames.fileVector.clear();
+}
+
+# else // LL_GTK
+
+// Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with
+// static results, when we don't have a real filepicker.
+
+static LLString hackyfilename;
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+}
+
+LLFilePicker::~LLFilePicker()
+{
+}
+
+BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const char* filename )
+{
+ llinfos << "getSaveFile suggested filename is [" << filename
+ << "]" << llendl;
+ if (filename && filename[0])
+ {
+ hackyfilename.assign(gDirUtilp->getLindenUserDir());
+ hackyfilename += gDirUtilp->getDirDelimiter();
+ hackyfilename += filename;
+ return TRUE;
+ }
+ hackyfilename.clear();
+ return FALSE;
+}
+
+BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
+{
+ // HACK: Static filenames for 'open' until we implement filepicker
+ hackyfilename.assign(gDirUtilp->getLindenUserDir());
+ hackyfilename += gDirUtilp->getDirDelimiter();
+ hackyfilename += "upload";
+ switch (filter)
+ {
+ case FFLOAD_WAV: hackyfilename += ".wav"; break;
+ case FFLOAD_IMAGE: hackyfilename += ".tga"; break;
+ case FFLOAD_ANIM: hackyfilename += ".bvh"; break;
+ default: break;
+ }
+ llinfos << "getOpenFile: Will try to open file: " << hackyfilename
+ << llendl;
+ return TRUE;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
+{
+ hackyfilename.clear();
+ return FALSE;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ if (!hackyfilename.empty())
+ {
+ return hackyfilename.c_str();
+ }
+ return NULL;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ hackyfilename.clear();
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+}
+#endif // LL_GTK
+
+#else // not implemented
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+}
+
+LLFilePicker::~LLFilePicker()
+{
+}
+
+BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const char* filename )
+{
+ return FALSE;
+}
+
+BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
+{
+ return FALSE;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
+{
+ return FALSE;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ return NULL;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+}
+
+#endif
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
new file mode 100644
index 0000000000..718af39c5d
--- /dev/null
+++ b/indra/newview/llfilepicker.h
@@ -0,0 +1,167 @@
+/**
+ * @file llfilepicker.h
+ * @brief OS-specific file picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// OS specific file selection dialog. This is implemented as a
+// singleton class, so call the instance() method to get the working
+// instance. When you call getMultipleOpenFile(), it locks the picker
+// until you iterate to the end of the list of selected files with
+// getNextFile() or call reset().
+
+#ifndef LL_LLFILEPICKER_H
+#define LL_LLFILEPICKER_H
+
+#include "stdtypes.h"
+
+#if LL_DARWIN
+#include <Carbon/Carbon.h>
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+#include <vector>
+#include "llstring.h"
+
+#endif
+
+// Need commdlg.h for OPENFILENAMEA
+#ifdef LL_WINDOWS
+#include <commdlg.h>
+#endif
+
+// mostly for Linux, possible on others
+#if LL_GTK
+# include "gtk/gtk.h"
+#endif // LL_GTK
+
+// also mostly for Linux, for some X11-specific filepicker usability tweaks
+#if LL_X11
+#include "SDL/SDL_syswm.h"
+#endif
+
+#if LL_GTK
+// we use an aggregate structure so we can pass its pointer through a C callback
+typedef struct {
+ GtkWidget *win;
+ std::vector<LLString> fileVector;
+} StoreFilenamesStruct;
+#endif // LL_GTK
+
+class LLFilePicker
+{
+public:
+ // calling this before main() is undefined
+ static LLFilePicker& instance( void ) { return sInstance; }
+
+ enum ELoadFilter
+ {
+ FFLOAD_ALL = 1,
+ FFLOAD_WAV = 2,
+ FFLOAD_IMAGE = 3,
+ FFLOAD_ANIM = 4,
+#ifdef _CORY_TESTING
+ FFLOAD_GEOMETRY = 5,
+#endif
+ FFLOAD_XML = 6,
+ FFLOAD_SLOBJECT = 7,
+ FFLOAD_RAW = 8,
+ };
+
+ enum ESaveFilter
+ {
+ FFSAVE_ALL = 1,
+ FFSAVE_WAV = 3,
+ FFSAVE_TGA = 4,
+ FFSAVE_BMP = 5,
+ FFSAVE_AVI = 6,
+ FFSAVE_ANIM = 7,
+#ifdef _CORY_TESTING
+ FFSAVE_GEOMETRY = 8,
+#endif
+ FFSAVE_XML = 9,
+ FFSAVE_COLLADA = 10,
+ FFSAVE_RAW = 11,
+ };
+
+ // open the dialog. This is a modal operation
+ BOOL getSaveFile( ESaveFilter filter = FFSAVE_ALL, const char* filename = NULL );
+ BOOL getOpenFile( ELoadFilter filter = FFLOAD_ALL );
+ BOOL getMultipleOpenFiles( ELoadFilter filter = FFLOAD_ALL );
+
+ // Get the filename(s) found. getFirstFile() sets the pointer to
+ // the start of the structure and allows the start of iteration.
+ const char* getFirstFile();
+
+ // getNextFile() increments the internal representation and
+ // returns the next file specified by the user. Returns NULL when
+ // no more files are left. Further calls to getNextFile() are
+ // undefined.
+ const char* getNextFile();
+
+ // This utility function extracts the directory name but doesn't
+ // do any incrementing. This is currently only supported when
+ // you're opening multiple files.
+ const char* getDirname();
+
+ // clear any lists of buffers or whatever, and make sure the file
+ // picker isn't locked.
+ void reset();
+
+private:
+ enum
+ {
+ SINGLE_FILENAME_BUFFER_SIZE = 1024,
+ //FILENAME_BUFFER_SIZE = 65536
+ FILENAME_BUFFER_SIZE = 65000
+ };
+
+ void buildFilename( void );
+
+#if LL_WINDOWS
+ OPENFILENAMEW mOFN; // for open and save dialogs
+ char *mOpenFilter;
+ WCHAR mFilesW[FILENAME_BUFFER_SIZE];
+
+ BOOL setupFilter(ELoadFilter filter);
+#endif
+
+#if LL_DARWIN
+ NavDialogCreationOptions mNavOptions;
+ std::vector<LLString> mFileVector;
+ UInt32 mFileIndex;
+
+ OSStatus doNavChooseDialog(ELoadFilter filter);
+ OSStatus doNavSaveDialog(ESaveFilter filter, const char* filename);
+ void getFilePath(SInt32 index);
+ void getFileName(SInt32 index);
+ static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode);
+#endif
+
+#if LL_GTK
+ StoreFilenamesStruct mStoreFilenames;
+
+ GtkWindow* buildFilePicker(void);
+ U32 mNextFileIndex;
+#endif
+
+ char mFiles[FILENAME_BUFFER_SIZE];
+ char mFilename[LL_MAX_PATH];
+ char* mCurrentFile;
+ BOOL mLocked;
+ BOOL mMultiFile;
+
+ static LLFilePicker sInstance;
+
+public:
+ // don't call these directly please.
+ LLFilePicker();
+ ~LLFilePicker();
+};
+
+#endif
diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp
new file mode 100644
index 0000000000..4a4e1a9ed4
--- /dev/null
+++ b/indra/newview/llfirstuse.cpp
@@ -0,0 +1,196 @@
+/**
+ * @file llfirstuse.cpp
+ * @brief Methods that spawn "first-use" dialogs
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfirstuse.h"
+
+// library includes
+#include "indra_constants.h"
+
+// viewer includes
+#include "llnotify.h"
+#include "llviewercontrol.h"
+#include "llui.h"
+#include "viewer.h"
+
+// static
+std::set<LLString> LLFirstUse::sConfigVariables;
+
+// static
+void LLFirstUse::addConfigVariable(const LLString& var)
+{
+ gSavedSettings.addWarning(var);
+ sConfigVariables.insert(var);
+}
+
+// static
+void LLFirstUse::disableFirstUse()
+{
+ // Set all first-use warnings to disabled
+ for (std::set<LLString>::iterator iter = sConfigVariables.begin();
+ iter != sConfigVariables.end(); ++iter)
+ {
+ gSavedSettings.setWarning(*iter, FALSE);
+ }
+}
+
+// Called whenever the viewer detects that your balance went up
+void LLFirstUse::useBalanceIncrease(S32 delta)
+{
+ if (gSavedSettings.getWarning("FirstBalanceIncrease"))
+ {
+ gSavedSettings.setWarning("FirstBalanceIncrease", FALSE);
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d",delta);
+ LLNotifyBox::showXml("FirstBalanceIncrease", args);
+ }
+}
+
+
+// Called whenever the viewer detects your balance went down
+void LLFirstUse::useBalanceDecrease(S32 delta)
+{
+ if (gSavedSettings.getWarning("FirstBalanceDecrease"))
+ {
+ gSavedSettings.setWarning("FirstBalanceDecrease", FALSE);
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d",-delta);
+ LLNotifyBox::showXml("FirstBalanceDecrease", args);
+ }
+}
+
+
+// static
+void LLFirstUse::useSit()
+{
+ if (gSavedSettings.getWarning("FirstSit"))
+ {
+ gSavedSettings.setWarning("FirstSit", FALSE);
+
+ LLNotifyBox::showXml("FirstSit");
+ }
+}
+
+// static
+void LLFirstUse::useMap()
+{
+ if (gSavedSettings.getWarning("FirstMap"))
+ {
+ gSavedSettings.setWarning("FirstMap", FALSE);
+
+ LLNotifyBox::showXml("FirstMap");
+ }
+}
+
+// static
+void LLFirstUse::useGoTo()
+{
+ // nothing for now JC
+}
+
+// static
+void LLFirstUse::useBuild()
+{
+ if (gSavedSettings.getWarning("FirstBuild"))
+ {
+ gSavedSettings.setWarning("FirstBuild", FALSE);
+
+ LLNotifyBox::showXml("FirstBuild");
+ }
+}
+
+// static
+void LLFirstUse::useLeftClickNoHit()
+{
+ if (gSavedSettings.getWarning("FirstLeftClickNoHit"))
+ {
+ gSavedSettings.setWarning("FirstLeftClickNoHit", FALSE);
+
+ LLNotifyBox::showXml("FirstLeftClickNoHit");
+ }
+}
+
+// static
+void LLFirstUse::useTeleport()
+{
+ if (gSavedSettings.getWarning("FirstTeleport"))
+ {
+ gSavedSettings.setWarning("FirstTeleport", FALSE);
+
+ LLNotifyBox::showXml("FirstTeleport");
+ }
+}
+
+// static
+void LLFirstUse::useOverrideKeys()
+{
+ if (gSavedSettings.getWarning("FirstOverrideKeys"))
+ {
+ gSavedSettings.setWarning("FirstOverrideKeys", FALSE);
+
+ LLNotifyBox::showXml("FirstOverrideKeys");
+ }
+}
+
+// static
+void LLFirstUse::useAttach()
+{
+ // nothing for now
+}
+
+// static
+void LLFirstUse::useAppearance()
+{
+ if (gSavedSettings.getWarning("FirstAppearance"))
+ {
+ gSavedSettings.setWarning("FirstAppearance", FALSE);
+
+ LLNotifyBox::showXml("FirstAppearance");
+ }
+}
+
+// static
+void LLFirstUse::useInventory()
+{
+ if (gSavedSettings.getWarning("FirstInventory"))
+ {
+ gSavedSettings.setWarning("FirstInventory", FALSE);
+
+ LLNotifyBox::showXml("FirstInventory");
+ }
+}
+
+
+// static
+void LLFirstUse::useSandbox()
+{
+ if (gSavedSettings.getWarning("FirstSandbox"))
+ {
+ gSavedSettings.setWarning("FirstSandbox", FALSE);
+
+ LLString::format_map_t args;
+ args["[HOURS]"] = llformat("%d",SANDBOX_CLEAN_FREQ);
+ args["[TIME]"] = llformat("%d",SANDBOX_FIRST_CLEAN_HOUR);
+ LLNotifyBox::showXml("FirstSandbox", args);
+ }
+}
+
+// static
+void LLFirstUse::useFlexible()
+{
+ if (gSavedSettings.getWarning("FirstFlexible"))
+ {
+ gSavedSettings.setWarning("FirstFlexible", FALSE);
+
+ LLNotifyBox::showXml("FirstFlexible");
+ }
+}
+
diff --git a/indra/newview/llfirstuse.h b/indra/newview/llfirstuse.h
new file mode 100644
index 0000000000..af545a05a9
--- /dev/null
+++ b/indra/newview/llfirstuse.h
@@ -0,0 +1,83 @@
+/**
+ * @file llfirstuse.h
+ * @brief Methods that spawn "first-use" dialogs.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFIRSTUSE_H
+#define LL_LLFIRSTUSE_H
+
+#include <vector>
+#include "llstring.h"
+
+/*
+1. On first use of 'sit here', explain how to get up and rotate view.
+
+2. On first use of map, explain dbl-click = telport, how hubs/beacons work,
+click-drag to move map
+
+3. First use of pie 'Go To', explain other ways to move around
+
+4. First use of 'Create' or 'Edit', explain build toolbar, that you can create
+things if build enabled, edit things you own, and that you can ESC to exit it.
+
+5. First use of 'Talk to' explain difference between that and regular chat,
+reduced range, how to leave conversation, arrow keys to orbit.
+
+6. First lft-click that does nothing (land,object): Explain that rgt-click
+gives menu, lft-click interacts or moves if physical
+
+7. On first receipt of money (not rez/derez) explain that objects or people may
+give you money, and how to give someone or something money ('Pay...').
+
+8. After first teleporting and being sent to nearest hub, a dialog explaining
+how to find and move toward the beacon.
+
+9. On first accept/auto-accept permissions, explain that some objects may be
+activated by entering mouselook 'M', or may override your movement keys with
+other functions.
+
+10. FIrst use of 'wear' or drag object from inventory onto self: 'You can
+attach objects to your body by dragging ontl yourelf of rgt-clk->wear from
+object or from inventory.
+
+11. FIrst time you run the client on a system without QuickTime installed.
+
+12. First time you create a flexible object.
+*/
+
+class LLFirstUse
+{
+public:
+ // Add a config variable to be reset on resetFirstUse()
+ static void addConfigVariable(const LLString& var);
+
+ // Sets all controls back to show the dialogs.
+ static void disableFirstUse();
+
+ // These methods are called each time the appropriate action is
+ // taken. The functions themselves handle only showing the dialog
+ // the first time, or subsequent times if the user wishes.
+ static void useBalanceIncrease(S32 delta);
+ static void useBalanceDecrease(S32 delta);
+ static void useSit();
+ static void useMap();
+ static void useGoTo();
+ static void useBuild();
+ static void useLeftClickNoHit();
+ static void useTeleport();
+ static void useOverrideKeys();
+ static void useAttach();
+ static void useAppearance();
+ static void useInventory();
+ static void useSandbox();
+ static void useFlexible();
+
+protected:
+ static std::set<LLString> sConfigVariables;
+};
+
+#endif
+// EOF
diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp
new file mode 100644
index 0000000000..4f821a7978
--- /dev/null
+++ b/indra/newview/llflexibleobject.cpp
@@ -0,0 +1,832 @@
+/**
+ * @file llflexibleobject.cpp
+ * @brief Flexible object implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "pipeline.h"
+#include "lldrawpoolbump.h"
+#include "llface.h"
+#include "llflexibleobject.h"
+#include "llglheaders.h"
+#include "llsphere.h"
+#include "llviewerobject.h"
+#include "llimagegl.h"
+#include "llagent.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+
+/*static*/ LLVolumeImplFlexible::lodset_t LLVolumeImplFlexible::sLODBins[ FLEXIBLE_OBJECT_MAX_LOD ];
+/*static*/ U64 LLVolumeImplFlexible::sCurrentUpdateFrame = 0;
+/*static*/ U32 LLVolumeImplFlexible::sDebugInserted = 0;
+/*static*/ U32 LLVolumeImplFlexible::sDebugVisible = 0;
+
+/*static*/ F32 LLVolumeImplFlexible::sUpdateFactor = 1.0f;
+
+// LLFlexibleObjectData::pack/unpack now in llprimitive.cpp
+
+//-----------------------------------------------
+// constructor
+//-----------------------------------------------
+LLVolumeImplFlexible::LLVolumeImplFlexible(LLViewerObject* vo, LLFlexibleObjectData* attributes) :
+ mVO(vo), mAttributes(attributes)
+{
+ mInitialized = FALSE;
+ mUpdated = FALSE;
+ mJustShifted = FALSE;
+ mInitializedRes = -1;
+ mSimulateRes = 0;
+ mFrameNum = 0;
+ mLastUpdate = 0;
+
+}//-----------------------------------------------
+
+LLVector3 LLVolumeImplFlexible::getFramePosition() const
+{
+ return mVO->getRenderPosition();
+}
+
+LLQuaternion LLVolumeImplFlexible::getFrameRotation() const
+{
+ return mVO->getRenderRotation();
+}
+
+void LLVolumeImplFlexible::onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin)
+{
+ if (param_type == LLNetworkData::PARAMS_FLEXIBLE)
+ {
+ mAttributes = (LLFlexibleObjectData*)data;
+ setAttributesOfAllSections();
+ }
+}
+
+void LLVolumeImplFlexible::onShift(const LLVector3 &shift_vector)
+{
+ for (int section = 0; section < (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1; ++section)
+ {
+ mSection[section].mPosition += shift_vector;
+ }
+ mVO->getVolume()->mBounds[0] += shift_vector;
+}
+
+//-----------------------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setParentPositionAndRotationDirectly( LLVector3 p, LLQuaternion r )
+{
+ mParentPosition = p;
+ mParentRotation = r;
+
+}//-----------------------------------------------------------------------------------------------------
+
+void LLVolumeImplFlexible::remapSections(LLFlexibleObjectSection *source, S32 source_sections,
+ LLFlexibleObjectSection *dest, S32 dest_sections)
+{
+ S32 num_output_sections = 1<<dest_sections;
+ LLVector3 scale = mVO->mDrawable->getScale();
+ F32 source_section_length = scale.mV[VZ] / (F32)(1<<source_sections);
+ F32 section_length = scale.mV[VZ] / (F32)num_output_sections;
+ if (source_sections == -1)
+ {
+ // Generate all from section 0
+ dest[0] = source[0];
+ for (S32 section=0; section<num_output_sections; ++section)
+ {
+ dest[section+1] = dest[section];
+ dest[section+1].mPosition += dest[section].mDirection * section_length;
+ dest[section+1].mVelocity.setVec( LLVector3::zero );
+ }
+ }
+ else if (source_sections > dest_sections)
+ {
+ // Copy, skipping sections
+
+ S32 num_steps = 1<<(source_sections-dest_sections);
+
+ // Copy from left to right since it may be an in-place computation
+ for (S32 section=0; section<num_output_sections; ++section)
+ {
+ dest[section+1] = source[(section+1)*num_steps];
+ }
+ dest[0] = source[0];
+ }
+ else if (source_sections < dest_sections)
+ {
+ // Interpolate section info
+ // Iterate from right to left since it may be an in-place computation
+ S32 step_shift = dest_sections-source_sections;
+ S32 num_steps = 1<<step_shift;
+ for (S32 section=num_output_sections-num_steps; section>=0; section -= num_steps)
+ {
+ LLFlexibleObjectSection *last_source_section = &source[section>>step_shift];
+ LLFlexibleObjectSection *source_section = &source[(section>>step_shift)+1];
+
+ // Cubic interpolation of position
+ // At^3 + Bt^2 + Ct + D = f(t)
+ LLVector3 D = last_source_section->mPosition;
+ LLVector3 C = last_source_section->mdPosition * source_section_length;
+ LLVector3 Y = source_section->mdPosition * source_section_length - C; // Helper var
+ LLVector3 X = (source_section->mPosition - D - C); // Helper var
+ LLVector3 A = Y - 2*X;
+ LLVector3 B = X - A;
+
+ F32 t_inc = 1.f/F32(num_steps);
+ F32 t = t_inc;
+ for (S32 step=1; step<num_steps; ++step)
+ {
+ dest[section+step].mScale =
+ lerp(last_source_section->mScale, source_section->mScale, t);
+ dest[section+step].mAxisRotation =
+ slerp(t, last_source_section->mAxisRotation, source_section->mAxisRotation);
+
+ // Evaluate output interpolated values
+ F32 t_sq = t*t;
+ dest[section+step].mPosition = t_sq*(t*A + B) + t*C + D;
+ dest[section+step].mRotation =
+ slerp(t, last_source_section->mRotation, source_section->mRotation);
+ dest[section+step].mVelocity = lerp(last_source_section->mVelocity, source_section->mVelocity, t);
+ dest[section+step].mDirection = lerp(last_source_section->mDirection, source_section->mDirection, t);
+ dest[section+step].mdPosition = lerp(last_source_section->mdPosition, source_section->mdPosition, t);
+ dest[section+num_steps] = *source_section;
+ t += t_inc;
+ }
+ }
+ dest[0] = source[0];
+ }
+ else
+ {
+ // numbers are equal. copy info
+ for (S32 section=0; section <= num_output_sections; ++section)
+ {
+ dest[section] = source[section];
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+void LLVolumeImplFlexible::setAttributesOfAllSections()
+{
+ LLVector2 bottom_scale, top_scale;
+ F32 begin_rot = 0, end_rot = 0;
+ if (mVO->getVolume())
+ {
+ const LLPathParams &params = mVO->getVolume()->getParams().getPathParams();
+ bottom_scale = params.getBeginScale();
+ top_scale = params.getEndScale();
+ begin_rot = F_PI * params.getTwistBegin();
+ end_rot = F_PI * params.getTwist();
+ }
+
+ S32 num_sections = 1 << mSimulateRes;
+
+
+ LLVector3 scale = mVO->mDrawable->getScale();
+
+ mSection[0].mPosition = getAnchorPosition();
+ mSection[0].mDirection = LLVector3::z_axis * getFrameRotation();
+ mSection[0].mdPosition = mSection[0].mDirection;
+ mSection[0].mScale.setVec(scale.mV[VX]*bottom_scale.mV[0], scale.mV[VY]*bottom_scale.mV[1]);
+ mSection[0].mVelocity.setVec(0,0,0);
+ mSection[0].mAxisRotation.setQuat(begin_rot,0,0,1);
+
+ LLVector3 parentSectionPosition = mSection[0].mPosition;
+ LLVector3 last_direction = mSection[0].mDirection;
+
+ remapSections(mSection, mInitializedRes, mSection, mSimulateRes);
+ mInitializedRes = mSimulateRes;
+
+ F32 t_inc = 1.f/F32(num_sections);
+ F32 t = t_inc;
+ for ( int i=1; i<= num_sections; i++)
+ {
+ mSection[i].mAxisRotation.setQuat(lerp(begin_rot,end_rot,t),0,0,1);
+ mSection[i].mScale = LLVector2(
+ scale.mV[VX] * lerp(bottom_scale.mV[0], top_scale.mV[0], t),
+ scale.mV[VY] * lerp(bottom_scale.mV[1], top_scale.mV[1], t));
+ t += t_inc;
+ }
+ mLastUpdate = 0;
+}//-----------------------------------------------------------------------------------
+
+
+void LLVolumeImplFlexible::onSetVolume(const LLVolumeParams &volume_params, const S32 detail)
+{
+ doIdleUpdate(gAgent, *gWorldp, 0.0);
+ if (mVO && mVO->mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mVO->mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ gPipeline.markMoved(mVO->mDrawable);
+ }
+}
+
+//---------------------------------------------------------------------------------
+// This calculates the physics of the flexible object. Note that it has to be 0
+// updated every time step. In the future, perhaps there could be an
+// optimization similar to what Havok does for objects that are stationary.
+//---------------------------------------------------------------------------------
+BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mVO->mDrawable.isNull())
+ {
+ // Don't do anything until we have a drawable
+ return TRUE;
+ }
+
+ //flexible objects never go static
+ mVO->mDrawable->mQuietCount = 0;
+ if (!mVO->mDrawable->isRoot())
+ {
+ mVO->mDrawable->getParent()->mQuietCount = 0;
+ }
+
+ if (((LLVOVolume*)mVO)->mLODChanged ||
+ mVO->mDrawable->isState(LLDrawable::IN_REBUILD_Q1))
+ {
+ mLastUpdate = 0; // Force an immediate update
+ }
+ // Relegate invisible objects to the lowest priority bin
+ S32 lod = 0;
+ F32 app_angle = mVO->getAppAngle()*DEG_TO_RAD/gCamera->getView();
+ if (mVO->mDrawable->isVisible())
+ {
+ sDebugVisible++;
+ if (mVO->isSelected())
+ {
+ // Force selected objects to update *every* frame
+ lod = FLEXIBLE_OBJECT_MAX_LOD-1;
+ }
+ else
+ {
+ if (app_angle > 0)
+ {
+ lod = 5 - (S32)(1.0f/sqrtf(app_angle));
+ if (lod < 1)
+ {
+ lod = 1;
+ }
+ }
+
+ if (mVO->isAttachment())
+ {
+ lod += 3;
+ }
+ }
+ }
+
+ S32 new_res = mAttributes->getSimulateLOD();
+ // Rendering sections increases with visible angle on the screen
+ mRenderRes = (S32)(FLEXIBLE_OBJECT_MAX_SECTIONS*4*app_angle);
+ if (mRenderRes > FLEXIBLE_OBJECT_MAX_SECTIONS)
+ {
+ mRenderRes = FLEXIBLE_OBJECT_MAX_SECTIONS;
+ }
+ // Bottom cap at 1/4 the original number of sections
+ if (mRenderRes < mAttributes->getSimulateLOD()-1)
+ {
+ mRenderRes = mAttributes->getSimulateLOD()-1;
+ }
+ // Throttle back simulation of segments we're not rendering
+ if (mRenderRes < new_res)
+ {
+ new_res = mRenderRes;
+ }
+
+ if (!mInitialized || (mSimulateRes != new_res))
+ {
+ mSimulateRes = new_res;
+ setAttributesOfAllSections();
+ mInitialized = TRUE;
+ }
+
+ sLODBins[lod].insert(this);
+ sDebugInserted++;
+ return TRUE;
+}
+
+// static
+void LLVolumeImplFlexible::resetUpdateBins()
+{
+ U32 lod;
+ for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
+ {
+ sLODBins[lod].clear();
+ }
+ ++sCurrentUpdateFrame;
+ sDebugInserted = 0;
+ sDebugVisible = 0;
+}
+
+inline S32 log2(S32 x)
+{
+ S32 ret = 0;
+ while (x > 1)
+ {
+ ++ret;
+ x >>= 1;
+ }
+ return ret;
+}
+
+// static
+void LLVolumeImplFlexible::doFlexibleUpdateBins()
+{
+ U32 lod;
+ U32 updated = 0;
+ U32 regen = 0;
+ U32 newflexies = 0;
+ F32 time_alloc[FLEXIBLE_OBJECT_MAX_LOD];
+ F32 total_time_alloc = 0;
+
+ bool new_objects_only = false;
+
+ if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE))
+ {
+ new_objects_only = true;
+ }
+
+ for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
+ {
+ int count = sLODBins[lod].size();
+ if (count > 0)
+ {
+ time_alloc[lod] = (F32)((lod+1)*(log2(count)));
+ }
+ else
+ {
+ time_alloc[lod] = 0;
+ }
+ total_time_alloc += time_alloc[lod];
+ }
+ total_time_alloc = FLEXIBLE_OBJECT_TIMESLICE * (sUpdateFactor+0.01f) / total_time_alloc;
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_FLEXIBLE_UPDATE);
+ LLTimer timer;
+ for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
+ {
+ LLVolumeImplFlexible::lodset_t::iterator itor = sLODBins[lod].begin();
+ int bin_count = 0;
+ if (!new_objects_only)
+ {
+ timer.reset();
+ double end_time = time_alloc[lod] * total_time_alloc;
+ for (; itor!=sLODBins[lod].end(); ++itor)
+ {
+
+ (*itor)->doFlexibleUpdate();
+ ++updated;
+ (*itor)->doFlexibleRebuild();
+ ++bin_count;
+ ++regen;
+ if (timer.getElapsedTimeF64() > end_time)
+ {
+ break;
+ }
+ }
+ }
+ for (; itor != sLODBins[lod].end(); ++itor)
+ {
+ if ((*itor)->getLastUpdate() == 0)
+ {
+ // *Always* update newly-created objects, or objects which have changed LOD
+ (*itor)->doFlexibleUpdate();
+ (*itor)->doFlexibleRebuild();
+ ++newflexies;
+ }
+ }
+ }
+ }
+}
+
+void LLVolumeImplFlexible::doFlexibleUpdate()
+{
+ LLPath *path = &mVO->getVolume()->getPath();
+ S32 num_sections = 1 << mSimulateRes;
+
+ F32 secondsThisFrame = mTimer.getElapsedTimeAndResetF32();
+ if (secondsThisFrame > 0.2f)
+ {
+ secondsThisFrame = 0.2f;
+ }
+
+ LLVector3 BasePosition = getFramePosition();
+ LLQuaternion BaseRotation = getFrameRotation();
+ LLQuaternion parentSegmentRotation = BaseRotation;
+ LLVector3 anchorDirectionRotated = LLVector3::z_axis * parentSegmentRotation;
+ LLVector3 anchorScale = mVO->mDrawable->getScale();
+
+ F32 section_length = anchorScale.mV[VZ] / (F32)num_sections;
+ F32 inv_section_length = 1.f / section_length;
+
+ S32 i;
+
+ // ANCHOR position is offset from BASE position (centroid) by half the length
+ LLVector3 AnchorPosition = BasePosition - (anchorScale.mV[VZ]/2 * anchorDirectionRotated);
+
+ mSection[0].mPosition = AnchorPosition;
+ mSection[0].mDirection = anchorDirectionRotated;
+ mSection[0].mRotation = BaseRotation;
+
+ LLQuaternion deltaRotation;
+
+ LLVector3 lastPosition;
+
+ // Coefficients which are constant across sections
+ F32 t_factor = mAttributes->getTension() * 0.1f;
+ t_factor = t_factor*(1 - pow(0.85f, secondsThisFrame*30));
+ if ( t_factor > FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE )
+ {
+ t_factor = FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE;
+ }
+
+ F32 friction_coeff = (mAttributes->getAirFriction()*2+1);
+ friction_coeff = pow(10.f, friction_coeff*secondsThisFrame);
+ friction_coeff = (friction_coeff > 1) ? friction_coeff : 1;
+ F32 momentum = 1.0f / friction_coeff;
+
+ F32 wind_factor = (mAttributes->getWindSensitivity()*0.1f) * section_length * secondsThisFrame;
+ F32 max_angle = atan(section_length*2.f);
+
+ F32 force_factor = section_length * secondsThisFrame;
+
+ // Update simulated sections
+ for (i=1; i<=num_sections; ++i)
+ {
+ LLVector3 parentSectionVector;
+ LLVector3 parentSectionPosition;
+ LLVector3 parentDirection;
+
+ //---------------------------------------------------
+ // save value of position as lastPosition
+ //---------------------------------------------------
+ lastPosition = mSection[i].mPosition;
+
+ //------------------------------------------------------------------------------------------
+ // gravity
+ //------------------------------------------------------------------------------------------
+ mSection[i].mPosition.mV[2] -= mAttributes->getGravity() * force_factor;
+
+ //------------------------------------------------------------------------------------------
+ // wind force
+ //------------------------------------------------------------------------------------------
+ if (mAttributes->getWindSensitivity() > 0.001f)
+ {
+ mSection[i].mPosition += gAgent.getRegion()->mWind.getVelocity( mSection[i].mPosition ) * wind_factor;
+ }
+
+ //------------------------------------------------------------------------------------------
+ // user-defined force
+ //------------------------------------------------------------------------------------------
+ mSection[i].mPosition += mAttributes->getUserForce() * force_factor;
+
+ //---------------------------------------------------
+ // tension (rigidity, stiffness)
+ //---------------------------------------------------
+ parentSectionPosition = mSection[i-1].mPosition;
+ parentDirection = mSection[i-1].mDirection;
+
+ if ( i == 1 )
+ {
+ parentSectionVector = mSection[0].mDirection;
+ }
+ else
+ {
+ parentSectionVector = mSection[i-2].mDirection;
+ }
+
+ LLVector3 currentVector = mSection[i].mPosition - parentSectionPosition;
+
+ LLVector3 difference = (parentSectionVector*section_length) - currentVector;
+ LLVector3 tensionForce = difference * t_factor;
+
+ mSection[i].mPosition += tensionForce;
+
+ //------------------------------------------------------------------------------------------
+ // sphere collision, currently not used
+ //------------------------------------------------------------------------------------------
+ /*if ( mAttributes->mUsingCollisionSphere )
+ {
+ LLVector3 vectorToCenterOfCollisionSphere = mCollisionSpherePosition - mSection[i].mPosition;
+ if ( vectorToCenterOfCollisionSphere.magVecSquared() < mCollisionSphereRadius * mCollisionSphereRadius )
+ {
+ F32 distanceToCenterOfCollisionSphere = vectorToCenterOfCollisionSphere.magVec();
+ F32 penetration = mCollisionSphereRadius - distanceToCenterOfCollisionSphere;
+
+ LLVector3 normalToCenterOfCollisionSphere;
+
+ if ( distanceToCenterOfCollisionSphere > 0.0f )
+ {
+ normalToCenterOfCollisionSphere = vectorToCenterOfCollisionSphere / distanceToCenterOfCollisionSphere;
+ }
+ else // rare
+ {
+ normalToCenterOfCollisionSphere = LLVector3::x_axis; // arbitrary
+ }
+
+ // push the position out to the surface of the collision sphere
+ mSection[i].mPosition -= normalToCenterOfCollisionSphere * penetration;
+ }
+ }*/
+
+ //------------------------------------------------------------------------------------------
+ // inertia
+ //------------------------------------------------------------------------------------------
+ mSection[i].mPosition += mSection[i].mVelocity * momentum;
+
+ //------------------------------------------------------------------------------------------
+ // clamp length & rotation
+ //------------------------------------------------------------------------------------------
+ mSection[i].mDirection = mSection[i].mPosition - parentSectionPosition;
+ mSection[i].mDirection.normVec();
+ deltaRotation.shortestArc( parentDirection, mSection[i].mDirection );
+
+ F32 angle;
+ LLVector3 axis;
+ deltaRotation.getAngleAxis(&angle, axis);
+ if (angle > F_PI) angle -= 2.f*F_PI;
+ if (angle < -F_PI) angle += 2.f*F_PI;
+ if (angle > max_angle)
+ {
+ //angle = 0.5f*(angle+max_angle);
+ deltaRotation.setQuat(max_angle, axis);
+ } else if (angle < -max_angle)
+ {
+ //angle = 0.5f*(angle-max_angle);
+ deltaRotation.setQuat(-max_angle, axis);
+ }
+ LLQuaternion segment_rotation = parentSegmentRotation * deltaRotation;
+ parentSegmentRotation = segment_rotation;
+
+ mSection[i].mDirection = (parentDirection * deltaRotation);
+ mSection[i].mPosition = parentSectionPosition + mSection[i].mDirection * section_length;
+ mSection[i].mRotation = segment_rotation;
+
+ if (i > 1)
+ {
+ // Propogate half the rotation up to the parent
+ LLQuaternion halfDeltaRotation(angle/2, axis);
+ mSection[i-1].mRotation = mSection[i-1].mRotation * halfDeltaRotation;
+ }
+
+ //------------------------------------------------------------------------------------------
+ // calculate velocity
+ //------------------------------------------------------------------------------------------
+ mSection[i].mVelocity = mSection[i].mPosition - lastPosition;
+ }
+
+ // Calculate derivatives (not necessary until normals are automagically generated)
+ mSection[0].mdPosition = (mSection[1].mPosition - mSection[0].mPosition) * inv_section_length;
+ // i = 1..NumSections-1
+ for (i=1; i<num_sections; ++i)
+ {
+ // Quadratic numerical derivative of position
+
+ // f(-L1) = aL1^2 - bL1 + c = f1
+ // f(0) = c = f2
+ // f(L2) = aL2^2 + bL2 + c = f3
+ // f = ax^2 + bx + c
+ // d/dx f = 2ax + b
+ // d/dx f(0) = b
+
+ // c = f2
+ // a = [(f1-c)/L1 + (f3-c)/L2] / (L1+L2)
+ // b = (f3-c-aL2^2)/L2
+
+ LLVector3 a = (mSection[i-1].mPosition-mSection[i].mPosition +
+ mSection[i+1].mPosition-mSection[i].mPosition) * 0.5f * inv_section_length * inv_section_length;
+ LLVector3 b = (mSection[i+1].mPosition-mSection[i].mPosition - a*(section_length*section_length));
+ b *= inv_section_length;
+
+ mSection[i].mdPosition = b;
+ }
+
+ // i = NumSections
+ mSection[i].mdPosition = (mSection[i].mPosition - mSection[i-1].mPosition) * inv_section_length;
+
+ // Create points
+ S32 num_render_sections = 1<<mRenderRes;
+ path->resizePath(num_render_sections+1);
+
+ LLPath::PathPt *new_point;
+
+ LLFlexibleObjectSection newSection[ (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1 ];
+ remapSections(mSection, mSimulateRes, newSection, mRenderRes);
+
+ for (i=0; i<=num_render_sections; ++i)
+ {
+ new_point = &path->mPath[i];
+ new_point->mPos = newSection[i].mPosition;
+ new_point->mRot = mSection[i].mAxisRotation * newSection[i].mRotation;
+ new_point->mScale = newSection[i].mScale;
+ new_point->mTexT = ((F32)i)/(num_render_sections);
+ }
+
+ mLastSegmentRotation = parentSegmentRotation;
+}
+
+void LLVolumeImplFlexible::doFlexibleRebuild()
+{
+ mVO->getVolume()->regen();
+
+ mVO->markForUpdate(TRUE);
+
+ mUpdated = TRUE;
+
+ mLastUpdate = sCurrentUpdateFrame;
+}//------------------------------------------------------------------
+
+void LLVolumeImplFlexible::onSetScale(const LLVector3& scale, BOOL damped)
+{
+ setAttributesOfAllSections();
+}
+
+BOOL LLVolumeImplFlexible::doUpdateGeometry(LLDrawable *drawable)
+{
+ BOOL compiled = FALSE;
+
+ LLVOVolume *volume = (LLVOVolume*)mVO;
+
+ if (volume->mDrawable.isNull()) // Not sure why this is happening, but it is...
+ {
+ return TRUE; // No update to complete
+ }
+
+ volume->calcAllTEsSame();
+
+ if (volume->mVolumeChanged || volume->mFaceMappingChanged)
+ {
+ compiled = TRUE;
+ volume->regenFaces();
+ }
+ else if (volume->mLODChanged)
+ {
+ LLPointer<LLVolume> old_volumep, new_volumep;
+ F32 old_lod, new_lod;
+
+ old_volumep = volume->getVolume();
+ old_lod = old_volumep->getDetail();
+
+ LLVolumeParams volume_params = volume->getVolume()->getParams();
+ volume->setVolume(volume_params, 0);
+ doFlexibleUpdate();
+ volume->getVolume()->regen();
+
+ new_volumep = volume->getVolume();
+ new_lod = new_volumep->getDetail();
+
+ if (new_lod != old_lod)
+ {
+ compiled = TRUE;
+ if (new_volumep->getNumFaces() != old_volumep->getNumFaces())
+ {
+ volume->regenFaces();
+ }
+ }
+ }
+
+ if (mUpdated)
+ {
+ compiled = TRUE;
+ mUpdated = FALSE;
+ }
+
+ if(compiled)
+ {
+ volume->updateRelativeXform(isVolumeGlobal());
+ volume->genTriangles(isVolumeGlobal());
+ LLPipeline::sCompiles++;
+ }
+
+ volume->mVolumeChanged = FALSE;
+ volume->mLODChanged = FALSE;
+ volume->mFaceMappingChanged = FALSE;
+
+ // clear UV flag
+ drawable->clearState(LLDrawable::UV);
+
+ drawable->movePartition();
+
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setCollisionSphere( LLVector3 p, F32 r )
+{
+ mCollisionSpherePosition = p;
+ mCollisionSphereRadius = r;
+
+}//------------------------------------------------------------------
+
+
+//----------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setUsingCollisionSphere( bool u )
+{
+}//------------------------------------------------------------------
+
+
+//----------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setRenderingCollisionSphere( bool r )
+{
+}//------------------------------------------------------------------
+
+//------------------------------------------------------------------
+LLVector3 LLVolumeImplFlexible::getEndPosition()
+{
+ S32 num_sections = 1 << mAttributes->getSimulateLOD();
+ return mSection[ num_sections ].mPosition;
+
+}//------------------------------------------------------------------
+
+
+//------------------------------------------------------------------
+LLVector3 LLVolumeImplFlexible::getNodePosition( int nodeIndex )
+{
+ S32 num_sections = 1 << mAttributes->getSimulateLOD();
+ if ( nodeIndex > num_sections - 1 )
+ {
+ nodeIndex = num_sections - 1;
+ }
+ else if ( nodeIndex < 0 )
+ {
+ nodeIndex = 0;
+ }
+
+ return mSection[ nodeIndex ].mPosition;
+
+}//------------------------------------------------------------------
+
+LLVector3 LLVolumeImplFlexible::getPivotPosition() const
+{
+ return getAnchorPosition();
+}
+
+//------------------------------------------------------------------
+LLVector3 LLVolumeImplFlexible::getAnchorPosition() const
+{
+ LLVector3 BasePosition = getFramePosition();
+ LLQuaternion parentSegmentRotation = getFrameRotation();
+ LLVector3 anchorDirectionRotated = LLVector3::z_axis * parentSegmentRotation;
+ LLVector3 anchorScale = mVO->mDrawable->getScale();
+ return BasePosition - (anchorScale.mV[VZ]/2 * anchorDirectionRotated);
+
+}//------------------------------------------------------------------
+
+
+//------------------------------------------------------------------
+LLQuaternion LLVolumeImplFlexible::getEndRotation()
+{
+ return mLastSegmentRotation;
+
+}//------------------------------------------------------------------
+
+
+void LLVolumeImplFlexible::updateRelativeXform(BOOL global_volume)
+{
+ LLVOVolume* vo = (LLVOVolume*) mVO;
+
+ LLVector3 delta_scale = LLVector3(1,1,1);
+ LLVector3 delta_pos;
+ LLQuaternion delta_rot;
+
+ if (!mVO->mDrawable->isRoot())
+ { //global to parent relative
+ LLViewerObject* parent = (LLViewerObject*) vo->getParent();
+ delta_rot = ~parent->getRenderRotation();
+ delta_pos = -parent->getRenderPosition()*delta_rot;
+ }
+ else
+ { //global to local
+ delta_rot = ~getFrameRotation();
+ delta_pos = -getFramePosition()*delta_rot;
+ }
+
+ // Vertex transform (4x4)
+ LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
+ LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
+
+ vo->mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(delta_pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ vo->mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+
+}
+
+const LLMatrix4& LLVolumeImplFlexible::getWorldMatrix(LLXformMatrix* xform) const
+{
+ return xform->getWorldMatrix();
+}
diff --git a/indra/newview/llflexibleobject.h b/indra/newview/llflexibleobject.h
new file mode 100644
index 0000000000..0d782d96ac
--- /dev/null
+++ b/indra/newview/llflexibleobject.h
@@ -0,0 +1,154 @@
+/**
+ * @file llflexibleobject.h
+ * @author JJ Ventrella, Andrew Meadows, Tom Yedwab
+ * @brief Flexible object definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * This is for specifying objects in the world that are animated and
+ * rendered locally - on the viewer. Flexible Objects are linear arrays
+ * of positions, which stay at a fixed distance from each other. One
+ * position is fixed as an "anchor" and is attached to some other object
+ * in the world, determined by the server. All the other positions are
+ * updated according to local physics.
+ */
+
+#ifndef LL_LLFLEXIBLEOBJECT_H
+#define LL_LLFLEXIBLEOBJECT_H
+
+#include "llmemory.h"
+#include "llprimitive.h"
+#include "llvovolume.h"
+#include "llwind.h"
+
+// 10 ms for the whole thing!
+const F32 FLEXIBLE_OBJECT_TIMESLICE = 0.003f;
+const U32 FLEXIBLE_OBJECT_MAX_LOD = 10;
+
+// See llprimitive.h for LLFlexibleObjectData and DEFAULT/MIN/MAX values
+
+//-------------------------------------------------------------------
+
+struct LLFlexibleObjectSection
+{
+ // Input parameters
+ LLVector2 mScale;
+ LLQuaternion mAxisRotation;
+ // Simulated state
+ LLVector3 mPosition;
+ LLVector3 mVelocity;
+ LLVector3 mDirection;
+ LLQuaternion mRotation;
+ // Derivatives (Not all currently used, will come back with LLVolume changes to automagically generate normals)
+ LLVector3 mdPosition;
+ //LLMatrix4 mRotScale;
+ //LLMatrix4 mdRotScale;
+};
+
+//---------------------------------------------------------
+// The LLVolumeImplFlexible class
+//---------------------------------------------------------
+class LLVolumeImplFlexible : public LLVolumeInterface
+{
+ public:
+ LLVolumeImplFlexible(LLViewerObject* volume, LLFlexibleObjectData* attributes);
+
+ // Implements LLVolumeInterface
+ LLVector3 getFramePosition() const;
+ LLQuaternion getFrameRotation() const;
+ LLVolumeInterfaceType getInterfaceType() const { return INTERFACE_FLEXIBLE; }
+ BOOL doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ BOOL doUpdateGeometry(LLDrawable *drawable);
+ LLVector3 getPivotPosition() const;
+ void onSetVolume(const LLVolumeParams &volume_params, const S32 detail);
+ void onSetScale(const LLVector3 &scale, BOOL damped);
+ void onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin);
+ void onShift(const LLVector3 &shift_vector);
+ bool isVolumeUnique() const { return true; }
+ bool isVolumeGlobal() const { return true; }
+ bool isActive() const { return true; }
+ const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
+ void updateRelativeXform(BOOL global_volume = FALSE);
+ void doFlexibleUpdate(); // Called to update the simulation
+ void doFlexibleRebuild(); // Called to rebuild the geometry
+ static void resetUpdateBins();
+ static void doFlexibleUpdateBins();
+
+ //void setAttributes( LLFlexibleObjectData );
+ void setParentPositionAndRotationDirectly( LLVector3 p, LLQuaternion r );
+ void setUsingCollisionSphere( bool u );
+ void setCollisionSphere( LLVector3 position, F32 radius );
+ void setRenderingCollisionSphere( bool r);
+
+ LLVector3 getEndPosition();
+ LLQuaternion getEndRotation();
+ LLVector3 getNodePosition( int nodeIndex );
+ LLVector3 getAnchorPosition() const;
+
+ private:
+ //--------------------------------------
+ // private members
+ //--------------------------------------
+ LLViewerObject* mVO;
+ LLTimer mTimer;
+ LLVector3 mAnchorPosition;
+ LLVector3 mParentPosition;
+ LLQuaternion mParentRotation;
+ LLQuaternion mInitialAxisRotation;
+ LLQuaternion mLastSegmentRotation;
+ BOOL mInitialized;
+ BOOL mUpdated;
+ LLFlexibleObjectData* mAttributes;
+ LLFlexibleObjectSection mSection [ (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1 ];
+ S32 mInitializedRes;
+ S32 mSimulateRes;
+ S32 mRenderRes;
+ U32 mFrameNum;
+ LLVector3 mCollisionSpherePosition;
+ F32 mCollisionSphereRadius;
+
+ U64 mLastUpdate;
+
+ BOOL mJustShifted;
+
+ //--------------------------------------
+ // private methods
+ //--------------------------------------
+ void setAttributesOfAllSections ();
+
+ void remapSections(LLFlexibleObjectSection *source, S32 source_sections,
+ LLFlexibleObjectSection *dest, S32 dest_sections);
+
+ U64 getLastUpdate() const { return mLastUpdate; }
+
+ // LOD Bins
+ struct FlexCompare
+ {
+ bool operator()(LLVolumeImplFlexible* a, LLVolumeImplFlexible* b) const
+ {
+ U64 a_update = a->getLastUpdate();
+ U64 b_update = b->getLastUpdate();
+ if (a_update == b_update)
+ {
+ return a < b; // compare pointers
+ }
+ return a_update < b_update;
+ }
+ };
+ typedef std::set<LLVolumeImplFlexible*, FlexCompare> lodset_t;
+ static lodset_t sLODBins[ FLEXIBLE_OBJECT_MAX_LOD ];
+ static U64 sCurrentUpdateFrame;
+ static U32 sDebugInserted;
+ static U32 sDebugVisible;
+
+public:
+ // Global setting for update rate
+ static F32 sUpdateFactor;
+
+};// end of class definition
+
+
+#endif // LL_LLFLEXIBLEOBJECT_H
diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp
new file mode 100644
index 0000000000..d3c927a11c
--- /dev/null
+++ b/indra/newview/llfloaterabout.cpp
@@ -0,0 +1,167 @@
+/**
+ * @file llfloaterabout.cpp
+ * @author James Cook
+ * @brief The about box from Help->About
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterabout.h"
+
+#include "llsys.h"
+#include "llgl.h"
+#include "llui.h" // for tr()
+#include "v3dmath.h"
+
+#include "llviewertexteditor.h"
+#include "llviewercontrol.h"
+#include "llagent.h"
+#include "llviewerstats.h"
+#include "llviewerregion.h"
+#include "llversion.h"
+#include "llviewerbuild.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h" // for gViewerDigest
+
+#include "llmozlib.h"
+#include "llglheaders.h"
+
+extern LLCPUInfo gSysCPU;
+extern LLMemoryInfo gSysMemory;
+extern LLOSInfo gSysOS;
+extern U32 gPacketsIn;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLFloaterAbout* LLFloaterAbout::sInstance = NULL;
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterAbout
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFloaterAbout::LLFloaterAbout()
+: LLFloater("floater_about", "FloaterAboutRect", "")
+{
+ gUICtrlFactory->buildFloater(this, "floater_about.xml");
+
+ // Support for changing product name.
+ LLString title("About ");
+ title += gSecondLife;
+ setTitle(title);
+
+ LLString support;
+
+ // Version string
+ LLString version = gSecondLife
+ + llformat(" %d.%d.%d (%d) %s %s",
+ LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VIEWER_BUILD,
+ __DATE__, __TIME__);
+ support.append(version);
+ support.append("\n\n");
+
+ // Position
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ //XUI:translate
+ const LLVector3d &pos = gAgent.getPositionGlobal();
+ LLString pos_text = llformat("You are at %.1f, %.1f, %.1f ",
+ pos.mdV[VX], pos.mdV[VY], pos.mdV[VZ]);
+ support.append(pos_text);
+
+ LLString region_text = llformat("in %s located at ",
+ gAgent.getRegion()->getName().c_str());
+ support.append(region_text);
+
+ char buffer[MAX_STRING];
+ gAgent.getRegion()->getHost().getHostName(buffer, MAX_STRING);
+ support.append(buffer);
+ support.append(" (");
+ gAgent.getRegion()->getHost().getString(buffer, MAX_STRING);
+ support.append(buffer);
+ support.append(")\n\n");
+ }
+
+ // CPU
+ support.append("CPU: ");
+ support.append( gSysCPU.getCPUStringTerse() );
+ support.append("\n");
+
+ U32 memory = gSysMemory.getPhysicalMemory() / 1024 / 1024;
+ // For some reason, the reported amount of memory is always wrong by one meg
+ memory++;
+
+ LLString mem_text = llformat("Memory: %u MB\n", memory );
+ support.append(mem_text);
+
+ support.append("OS Version: ");
+ support.append( gSysOS.getOSString().c_str() );
+ support.append("\n");
+
+ support.append("Graphics Card Vendor: ");
+ support.append( (const char*) glGetString(GL_VENDOR) );
+ support.append("\n");
+
+ support.append("Graphics Card: ");
+ support.append( (const char*) glGetString(GL_RENDERER) );
+ support.append("\n");
+
+ support.append("OpenGL Version: ");
+ support.append( (const char*) glGetString(GL_VERSION) );
+ support.append("\n");
+
+#if LL_LIBXUL_ENABLED
+ support.append("LLMozLib Version: ");
+ support.append( (const char*) LLMozLib::getInstance()->getVersion().c_str() );
+ support.append("\n");
+#endif // LL_LIBXUL_ENABLED
+
+ if (gViewerStats
+ && gPacketsIn > 0)
+ {
+ LLString packet_loss = llformat("Packets Lost: %.0f/%.0f (%.1f%%)",
+ gViewerStats->mPacketsLostStat.getCurrent(),
+ F32(gPacketsIn),
+ 100.f*gViewerStats->mPacketsLostStat.getCurrent() / F32(gPacketsIn) );
+ support.append(packet_loss);
+ support.append("\n");
+ }
+
+ // MD5 digest of executable
+ support.append("Viewer Digest: ");
+ char viewer_digest_string[UUID_STR_LENGTH];
+ gViewerDigest.toString( viewer_digest_string );
+ support.append(viewer_digest_string);
+
+ // Fix views
+ childDisable("credits_editor");
+ childDisable("support_editor");
+ childSetText("support_editor", support);
+
+ center();
+
+ sInstance = this;
+}
+
+// Destroys the object
+LLFloaterAbout::~LLFloaterAbout()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterAbout::show(void*)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterAbout();
+ }
+
+ sInstance->open();
+}
diff --git a/indra/newview/llfloaterabout.h b/indra/newview/llfloaterabout.h
new file mode 100644
index 0000000000..f7d73e34d6
--- /dev/null
+++ b/indra/newview/llfloaterabout.h
@@ -0,0 +1,28 @@
+/**
+ * @file llfloaterabout.h
+ * @brief The about box from Help -> About
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERABOUT_H
+#define LL_LLFLOATERABOUT_H
+
+#include "llfloater.h"
+
+class LLFloaterAbout
+: public LLFloater
+{
+public:
+ LLFloaterAbout();
+ virtual ~LLFloaterAbout();
+
+ static void show(void*);
+
+private:
+ static LLFloaterAbout* sInstance;
+};
+
+
+#endif // LL_LLFLOATERABOUT_H
diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp
new file mode 100644
index 0000000000..5df1acac53
--- /dev/null
+++ b/indra/newview/llfloateranimpreview.cpp
@@ -0,0 +1,1139 @@
+/**
+ * @file llfloateranimpreview.cpp
+ * @brief LLFloaterAnimPreview class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateranimpreview.h"
+
+#include "llbvhloader.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+#include "llvfile.h"
+#include "llapr.h"
+
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "llkeyframemotion.h"
+#include "lllineeditor.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llviewermenu.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+
+S32 LLFloaterAnimPreview::sUploadAmount = 10;
+
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREF_BUTTON_HEIGHT = 16;
+const S32 PREVIEW_TEXTURE_HEIGHT = 300;
+
+const F32 PREVIEW_CAMERA_DISTANCE = 4.f;
+
+const F32 MIN_CAMERA_ZOOM = 0.5f;
+const F32 MAX_CAMERA_ZOOM = 10.f;
+
+//-----------------------------------------------------------------------------
+// LLFloaterAnimPreview()
+//-----------------------------------------------------------------------------
+LLFloaterAnimPreview::LLFloaterAnimPreview(const char* filename) :
+ LLFloaterNameDesc(filename)
+{
+ mLastSliderValue = 0.f;
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+
+ mIDList["Standing"] = ANIM_AGENT_STAND;
+ mIDList["Walking"] = ANIM_AGENT_FEMALE_WALK;
+ mIDList["Sitting"] = ANIM_AGENT_SIT_FEMALE;
+ mIDList["Flying"] = ANIM_AGENT_HOVER;
+
+ mIDList["[None]"] = LLUUID::null;
+ mIDList["Aaaaah"] = ANIM_AGENT_EXPRESS_OPEN_MOUTH;
+ mIDList["Afraid"] = ANIM_AGENT_EXPRESS_AFRAID;
+ mIDList["Angry"] = ANIM_AGENT_EXPRESS_ANGER;
+ mIDList["Big Smile"] = ANIM_AGENT_EXPRESS_TOOTHSMILE;
+ mIDList["Bored"] = ANIM_AGENT_EXPRESS_BORED;
+ mIDList["Cry"] = ANIM_AGENT_EXPRESS_CRY;
+ mIDList["Disdain"] = ANIM_AGENT_EXPRESS_DISDAIN;
+ mIDList["Embarrassed"] = ANIM_AGENT_EXPRESS_EMBARRASSED;
+ mIDList["Frown"] = ANIM_AGENT_EXPRESS_FROWN;
+ mIDList["Kiss"] = ANIM_AGENT_EXPRESS_KISS;
+ mIDList["Laugh"] = ANIM_AGENT_EXPRESS_LAUGH;
+ mIDList["Plllppt"] = ANIM_AGENT_EXPRESS_TONGUE_OUT;
+ mIDList["Repulsed"] = ANIM_AGENT_EXPRESS_REPULSED;
+ mIDList["Sad"] = ANIM_AGENT_EXPRESS_SAD;
+ mIDList["Shrug"] = ANIM_AGENT_EXPRESS_SHRUG;
+ mIDList["Smile"] = ANIM_AGENT_EXPRESS_SMILE;
+ mIDList["Surprise"] = ANIM_AGENT_EXPRESS_SURPRISE;
+ mIDList["Wink"] = ANIM_AGENT_EXPRESS_WINK;
+ mIDList["Worry"] = ANIM_AGENT_EXPRESS_WORRY;
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::postBuild()
+{
+ LLRect r;
+ LLKeyframeMotion* motionp = NULL;
+ LLBVHLoader* loaderp = NULL;
+
+ if (!LLFloaterNameDesc::postBuild())
+ {
+ return FALSE;
+ }
+
+ childSetCommitCallback("name_form", onCommitName, this);
+
+ childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount));
+ childSetAction("ok_btn", onBtnOK, this);
+ setDefaultBtn();
+
+ mPreviewRect.set(PREVIEW_HPAD,
+ PREVIEW_TEXTURE_HEIGHT,
+ getRect().getWidth() - PREVIEW_HPAD,
+ PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f);
+
+ S32 y = mPreviewRect.mTop + BTN_HEIGHT;
+ S32 btn_left = PREVIEW_HPAD;
+
+ r.set( btn_left, y, btn_left + 32, y - BTN_HEIGHT );
+ mPlayButton = LLViewerUICtrlFactory::getButtonByName(this, "play_btn");
+ if (!mPlayButton)
+ {
+ mPlayButton = new LLButton("play_btn", LLRect(0,0,0,0));
+ }
+ mPlayButton->setClickedCallback(onBtnPlay);
+ mPlayButton->setCallbackUserData(this);
+
+ mPlayButton->setImages("button_anim_play.tga",
+ "button_anim_play_selected.tga");
+ mPlayButton->setDisabledImages("","");
+
+ mPlayButton->setScaleImage(TRUE);
+ mPlayButton->setFixedBorder(0, 0);
+
+ mStopButton = LLViewerUICtrlFactory::getButtonByName(this, "stop_btn");
+ if (!mStopButton)
+ {
+ mStopButton = new LLButton("stop_btn", LLRect(0,0,0,0));
+ }
+ mStopButton->setClickedCallback(onBtnStop);
+ mStopButton->setCallbackUserData(this);
+
+ mStopButton->setImages("button_anim_stop.tga",
+ "button_anim_stop_selected.tga");
+ mStopButton->setDisabledImages("","");
+
+ mStopButton->setScaleImage(TRUE);
+ mStopButton->setFixedBorder(0, 0);
+
+ r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT);
+ childSetCommitCallback("playback_slider", onSliderMove, this);
+
+ childHide("bad_animation_text");
+
+ childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this);
+ childSetValue("preview_base_anim", "Standing");
+
+ childSetCommitCallback("priority", onCommitPriority, this);
+ childSetCommitCallback("loop_check", onCommitLoop, this);
+ childSetCommitCallback("loop_in_point", onCommitLoopIn, this);
+ childSetValidate("loop_in_point", validateLoopIn);
+ childSetCommitCallback("loop_out_point", onCommitLoopOut, this);
+ childSetValidate("loop_out_point", validateLoopOut);
+
+ childSetCommitCallback("hand_pose_combo", onCommitHandPose, this);
+
+ childSetCommitCallback("emote_combo", onCommitEmote, this);
+ childSetValue("emote_combo", "[None]");
+
+ childSetCommitCallback("ease_in_time", onCommitEaseIn, this);
+ childSetValidate("ease_in_time", validateEaseIn);
+ childSetCommitCallback("ease_out_time", onCommitEaseOut, this);
+ childSetValidate("ease_out_time", validateEaseOut);
+
+ if (!stricmp(strrchr(mFilename.c_str(), '.'), ".bvh"))
+ {
+ // loading a bvh file
+
+ // now load bvh file
+ S32 file_size;
+ apr_file_t* fp = ll_apr_file_open(mFilenameAndPath, LL_APR_RB, &file_size);
+
+ if (!fp)
+ {
+ llwarns << "Can't open BVH file:" << mFilename << llendl;
+ }
+ else
+ {
+ char* file_buffer;
+
+ file_buffer = new char[file_size + 1];
+
+ if (file_size == ll_apr_file_read(fp, file_buffer, file_size))
+ {
+ file_buffer[file_size] = '\0';
+ llinfos << "Loading BVH file " << mFilename << llendl;
+ loaderp = new LLBVHLoader(file_buffer);
+ }
+
+ apr_file_close(fp);
+ delete file_buffer;
+ }
+ }
+
+ if (loaderp && loaderp->isInitialized() && loaderp->getDuration() <= MAX_ANIM_DURATION)
+ {
+ // generate unique id for this motion
+ mTransactionID.generate();
+ mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+
+ mAnimPreview = new LLPreviewAnimation(256, 256);
+
+ // motion will be returned, but it will be in a load-pending state, as this is a new motion
+ // this motion will not request an asset transfer until next update, so we have a chance to
+ // load the keyframe data locally
+ motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID);
+
+ // create data buffer for keyframe initialization
+ S32 buffer_size = loaderp->getOutputSize();
+ U8* buffer = new U8[buffer_size];
+
+ LLDataPackerBinaryBuffer dp(buffer, buffer_size);
+
+ // pass animation data through memory buffer
+ loaderp->serialize(dp);
+ dp.reset();
+ BOOL success = motionp->deserialize(dp);
+
+ delete []buffer;
+
+ if (motionp && success)
+ {
+ const LLBBoxLocal &pelvis_bbox = motionp->getPelvisBBox();
+
+ LLVector3 temp = pelvis_bbox.getCenter();
+ // only consider XY?
+ //temp.mV[VZ] = 0.f;
+ F32 pelvis_offset = temp.magVec();
+
+ temp = pelvis_bbox.getExtent();
+ //temp.mV[VZ] = 0.f;
+ F32 pelvis_max_displacement = pelvis_offset + (temp.magVec() * 0.5f) + 1.f;
+
+ F32 camera_zoom = gCamera->getDefaultFOV() / (2.f * atan(pelvis_max_displacement / PREVIEW_CAMERA_DISTANCE));
+
+ mAnimPreview->setZoom(camera_zoom);
+
+ motionp->setName(childGetValue("name_form").asString());
+ mAnimPreview->getDummyAvatar()->startMotion(mMotionID);
+ childSetMinValue("playback_slider", 0.0);
+ childSetMaxValue("playback_slider", 1.0);
+
+ childSetValue("loop_check", LLSD(motionp->getLoop()));
+ childSetValue("loop_in_point", LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f));
+ childSetValue("loop_out_point", LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f));
+ childSetValue("priority", LLSD((F32)motionp->getPriority()));
+ childSetValue("hand_pose_combo", LLHandMotion::getHandPoseName(motionp->getHandPose()));
+ childSetValue("ease_in_time", LLSD(motionp->getEaseInDuration()));
+ childSetValue("ease_out_time", LLSD(motionp->getEaseOutDuration()));
+ mEnabled = TRUE;
+ char seconds_string[128];
+ sprintf(seconds_string, " - %.2f seconds", motionp->getDuration());
+
+ setTitle(mFilename + LLString(seconds_string));
+ }
+ else
+ {
+ delete mAnimPreview;
+ mAnimPreview = NULL;
+ mMotionID.setNull();
+ // XUI:translate
+ childSetValue("bad_animation_text", LLSD("Failed to initialize motion."));
+ mEnabled = FALSE;
+ }
+ }
+ else
+ {
+ if ( loaderp )
+ {
+ if (loaderp->getDuration() > MAX_ANIM_DURATION)
+ {
+ char output_str[256];
+
+ sprintf(output_str, "Animation file is %.1f seconds in length.\n\nMaximum animation length is %.1f seconds.\n",
+ loaderp->getDuration(), MAX_ANIM_DURATION);
+ childSetValue("bad_animation_text", LLSD(output_str));
+ }
+ else
+ {
+ char* status = loaderp->getStatus();
+ LLString error_string("Unable to read animation file.\n\n");
+ error_string += LLString(status);
+ childSetValue("bad_animation_text", LLSD(error_string));
+ }
+ }
+
+ mEnabled = FALSE;
+ mMotionID.setNull();
+ mAnimPreview = NULL;
+ }
+
+ refresh();
+
+ delete loaderp;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterAnimPreview()
+//-----------------------------------------------------------------------------
+LLFloaterAnimPreview::~LLFloaterAnimPreview()
+{
+ delete mAnimPreview;
+ mAnimPreview = NULL;
+
+ mEnabled = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::draw()
+{
+ LLFloater::draw();
+ LLRect r = getRect();
+
+ refresh();
+
+ if (mMotionID.notNull() && mAnimPreview)
+ {
+ glColor3f(1.f, 1.f, 1.f);
+ mAnimPreview->bindTexture();
+
+ glBegin( GL_QUADS );
+ {
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ }
+ glEnd();
+
+ mAnimPreview->unbindTexture();
+
+ LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar();
+ if (!avatarp->areAnimationsPaused())
+ {
+ mAnimPreview->requestUpdate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resetMotion()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::resetMotion()
+{
+ LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar();
+ BOOL paused = avatarp->areAnimationsPaused();
+
+ mPauseRequest = NULL;
+
+ LLUUID anim_id = mIDList[childGetValue("preview_base_anim").asString()];
+ avatarp->stopMotion(anim_id, TRUE);
+ avatarp->stopMotion(mMotionID, TRUE);
+ avatarp->startMotion(anim_id, 5.f);
+ avatarp->startMotion(mMotionID);
+ childSetValue("playback_slider", 0.0);
+ mLastSliderValue = 0.0f;
+
+ if (paused)
+ {
+ mPauseRequest = avatarp->requestPause();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseDown()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mPreviewRect.pointInRect(x, y))
+ {
+ bringToFront( x, y );
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ gViewerWindow->hideCursor();
+ mLastMouseX = x;
+ mLastMouseY = y;
+ return TRUE;
+ }
+
+ return LLFloater::handleMouseDown(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseUp()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->setMouseCapture(FALSE, NULL);
+ gViewerWindow->showCursor();
+ return LLFloater::handleMouseUp(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleHover()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleHover(S32 x, S32 y, MASK mask)
+{
+ MASK local_mask = mask & ~MASK_ALT;
+
+ if (mAnimPreview && gViewerWindow->hasMouseCapture(this))
+ {
+ if (local_mask == MASK_PAN)
+ {
+ // pan here
+ mAnimPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAnimPreview->rotate(yaw_radians, pitch_radians);
+ }
+ else
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAnimPreview->rotate(yaw_radians, 0.f);
+ mAnimPreview->zoom(zoom_amt);
+ }
+
+ mAnimPreview->requestUpdate();
+
+ LLUI::setCursorPositionLocal(this, mLastMouseX, mLastMouseY);
+ }
+
+ if (!mPreviewRect.pointInRect(x, y) || !mAnimPreview)
+ {
+ return LLFloater::handleHover(x, y, mask);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+ }
+ else if (local_mask == MASK_PAN)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ mAnimPreview->zoom((F32)clicks * -0.2f);
+ mAnimPreview->requestUpdate();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// onMouseCaptureLost()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onMouseCaptureLost(LLMouseHandler* handler)
+{
+ gViewerWindow->showCursor();
+}
+
+//-----------------------------------------------------------------------------
+// onBtnPlay()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onBtnPlay(void* user_data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data;
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mMotionID.notNull() && previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+
+ if(!avatarp->isMotionActive(previewp->mMotionID))
+ {
+ previewp->resetMotion();
+ previewp->mPauseRequest = NULL;
+ }
+ else
+ {
+ if (avatarp->areAnimationsPaused())
+ {
+ previewp->mPauseRequest = NULL;
+ }
+ else
+ {
+ previewp->mPauseRequest = avatarp->requestPause();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onBtnStop()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onBtnStop(void* user_data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data;
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mMotionID.notNull() && previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+
+ // is the motion looping and have we passed the loop in point?
+ if (previewp->childGetValue("loop_check").asBoolean() &&
+ (F32)previewp->childGetValue("loop_in_point").asReal() <= (F32)previewp->childGetValue("playback_slider").asReal() * 100.f)
+ {
+ avatarp->stopMotion(previewp->mMotionID, FALSE);
+ }
+ else
+ {
+ avatarp->stopMotion(previewp->mMotionID, FALSE);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onSliderMove()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onSliderMove(LLUICtrl* ctrl, void*user_data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data;
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLMotion* motionp = avatarp->findMotion(previewp->mMotionID);
+ LLMotion* base_motionp =
+ avatarp->findMotion(previewp->mIDList[previewp->childGetValue("preview_base_anim").asString()]);
+
+ if (motionp && base_motionp)
+ {
+ if (!avatarp->isMotionActive(previewp->mMotionID))
+ {
+ previewp->resetMotion();
+ }
+
+ previewp->mPauseRequest = avatarp->requestPause();
+ F32 original_activation_time = motionp->mActivationTimestamp;
+ motionp->mActivationTimestamp -= ((F32)previewp->childGetValue("playback_slider").asReal() - previewp->mLastSliderValue) *
+ motionp->getDuration();
+ base_motionp->mActivationTimestamp -= ((F32)previewp->childGetValue("playback_slider").asReal() - previewp->mLastSliderValue) *
+ base_motionp->getDuration();
+
+ if (motionp->mSendStopTimestamp != F32_MIN)
+ {
+ motionp->mSendStopTimestamp = motionp->mSendStopTimestamp - original_activation_time + motionp->mActivationTimestamp;
+ }
+
+ if (motionp->mStopTimestamp != F32_MIN)
+ {
+ motionp->mStopTimestamp = motionp->mStopTimestamp - original_activation_time + motionp->mActivationTimestamp;
+ }
+ previewp->refresh();
+ }
+ }
+
+ previewp->mLastSliderValue = (F32)previewp->childGetValue("playback_slider").asReal();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitBaseAnim()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitBaseAnim(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+
+ BOOL paused = avatarp->areAnimationsPaused();
+
+ // stop all other possible base motions
+ avatarp->stopMotion(ANIM_AGENT_STAND, TRUE);
+ avatarp->stopMotion(ANIM_AGENT_WALK, TRUE);
+ avatarp->stopMotion(ANIM_AGENT_SIT, TRUE);
+ avatarp->stopMotion(ANIM_AGENT_HOVER, TRUE);
+
+ previewp->resetMotion();
+
+ if (!paused)
+ {
+ previewp->mPauseRequest = NULL;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitLoop()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitLoop(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return;
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setLoop(previewp->childGetValue("loop_check").asBoolean());
+ motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() * 0.01f * motionp->getDuration());
+ motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitLoopIn()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitLoopIn(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() / 100.f);
+ previewp->resetMotion();
+ previewp->childSetValue("loop_check", LLSD(TRUE));
+ onCommitLoop(ctrl, data);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitLoopOut()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitLoopOut(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration());
+ previewp->resetMotion();
+ previewp->childSetValue("loop_check", LLSD(TRUE));
+ onCommitLoop(ctrl, data);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitName()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitName(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setName(previewp->childGetValue("name_form").asString());
+ }
+
+ LLFloaterNameDesc::doCommit(ctrl, data);
+}
+
+//-----------------------------------------------------------------------------
+// onCommitHandPose()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitHandPose(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setHandPose(LLHandMotion::getHandPose(previewp->childGetValue("hand_pose_combo").asString()));
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitEmote()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitEmote(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setEmote(previewp->mIDList[previewp->childGetValue("emote_combo").asString()]);
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitPriority()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitPriority(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setPriority(llfloor((F32)previewp->childGetValue("priority").asReal()));
+}
+
+//-----------------------------------------------------------------------------
+// onCommitEaseIn()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitEaseIn(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setEaseIn((F32)previewp->childGetValue("ease_in_time").asReal());
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitEaseOut()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitEaseOut(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setEaseOut((F32)previewp->childGetValue("ease_out_time").asReal());
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// validateEaseIn()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateEaseIn(LLUICtrl* spin, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (!motionp->getLoop())
+ {
+ F32 new_ease_in = llclamp((F32)previewp->childGetValue("ease_in_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration());
+ previewp->childSetValue("ease_in_time", LLSD(new_ease_in));
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// validateEaseOut()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateEaseOut(LLUICtrl* spin, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (!motionp->getLoop())
+ {
+ F32 new_ease_out = llclamp((F32)previewp->childGetValue("ease_out_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration());
+ previewp->childSetValue("ease_out_time", LLSD(new_ease_out));
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// validateLoopIn()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateLoopIn(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal();
+ F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal();
+
+ if (loop_in_value < 0.f)
+ {
+ loop_in_value = 0.f;
+ }
+ else if (loop_in_value > 100.f)
+ {
+ loop_in_value = 100.f;
+ }
+ else if (loop_in_value > loop_out_value)
+ {
+ loop_in_value = loop_out_value;
+ }
+
+ previewp->childSetValue("loop_in_point", LLSD(loop_in_value));
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// validateLoopOut()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateLoopOut(LLUICtrl* spin, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal();
+ F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal();
+
+ if (loop_out_value < 0.f)
+ {
+ loop_out_value = 0.f;
+ }
+ else if (loop_out_value > 100.f)
+ {
+ loop_out_value = 100.f;
+ }
+ else if (loop_out_value < loop_in_value)
+ {
+ loop_out_value = loop_in_value;
+ }
+
+ previewp->childSetValue("loop_out_point", LLSD(loop_out_value));
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// refresh()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::refresh()
+{
+ if (!mAnimPreview)
+ {
+ childShow("bad_animation_text");
+ mPlayButton->setEnabled(FALSE);
+ mStopButton->setEnabled(FALSE);
+ childDisable("ok_btn");
+ }
+ else
+ {
+ childHide("bad_animation_text");
+ mPlayButton->setEnabled(TRUE);
+ LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar();
+ if (avatarp->isMotionActive(mMotionID))
+ {
+ mStopButton->setEnabled(TRUE);
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID);
+ if (avatarp->areAnimationsPaused())
+ {
+
+ mPlayButton->setImages("button_anim_play.tga",
+ "button_anim_play_selected.tga");
+
+ }
+ else
+ {
+ if (motionp)
+ {
+ F32 fraction_complete;
+ fraction_complete = motionp->getLastUpdateTime() / motionp->getDuration();
+
+ childSetValue("playback_slider", fraction_complete);
+ mLastSliderValue = fraction_complete;
+ }
+ mPlayButton->setImages("button_anim_pause.tga",
+ "button_anim_pause_selected.tga");
+
+ }
+ }
+ else
+ {
+ mPauseRequest = avatarp->requestPause();
+ mPlayButton->setImages("button_anim_play.tga",
+ "button_anim_play_selected.tga");
+
+ mStopButton->setEnabled(FALSE);
+ }
+ childEnable("ok_btn");
+ mAnimPreview->requestUpdate();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onBtnOK()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onBtnOK(void* userdata)
+{
+ LLFloaterAnimPreview* floaterp = (LLFloaterAnimPreview*)userdata;
+ if (!floaterp->mEnabled) return;
+
+ if (floaterp->mAnimPreview)
+ {
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)floaterp->mAnimPreview->getDummyAvatar()->findMotion(floaterp->mMotionID);
+
+ S32 file_size = motionp->getFileSize();
+ U8* buffer = new U8[file_size];
+
+ LLDataPackerBinaryBuffer dp(buffer, file_size);
+ if (motionp->serialize(dp))
+ {
+ LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND);
+
+ S32 size = dp.getCurrentSize();
+ file.setMaxSize(size);
+ if (file.write((U8*)buffer, size))
+ {
+ std::string name = floaterp->childGetValue("name_form").asString();
+ std::string desc = floaterp->childGetValue("description_form").asString();
+ upload_new_resource(floaterp->mTransactionID, // tid
+ LLAssetType::AT_ANIMATION,
+ name,
+ desc,
+ 0,
+ LLAssetType::AT_NONE,
+ LLInventoryType::IT_ANIMATION,
+ PERM_NONE,
+ name);
+ }
+ else
+ {
+ llwarns << "Failure writing animation data." << llendl;
+ gViewerWindow->alertXml("WriteAnimationFail");
+ }
+ }
+
+ delete [] buffer;
+ // clear out cache for motion data
+ floaterp->mAnimPreview->getDummyAvatar()->removeMotion(floaterp->mMotionID);
+ LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID);
+ }
+
+ floaterp->onClose(false);
+}
+
+//-----------------------------------------------------------------------------
+// LLPreviewAnimation
+//-----------------------------------------------------------------------------
+LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE)
+{
+ mNeedsUpdate = TRUE;
+ mCameraDistance = PREVIEW_CAMERA_DISTANCE;
+ mCameraYaw = 0.f;
+ mCameraPitch = 0.f;
+ mCameraZoom = 1.f;
+
+ mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion());
+ mDummyAvatar->createDrawable(&gPipeline);
+ mDummyAvatar->mIsDummy = TRUE;
+ mDummyAvatar->mSpecialRenderMode = 1;
+ mDummyAvatar->setPositionAgent(LLVector3::zero);
+ mDummyAvatar->slamPosition();
+ mDummyAvatar->updateJointLODs();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ mDummyAvatar->startMotion(ANIM_AGENT_STAND, 5.f);
+ mDummyAvatar->mSkirtLOD.setVisible(FALSE, TRUE);
+ gPipeline.markVisible(mDummyAvatar->mDrawable);
+
+ // stop extraneous animations
+ mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE );
+ mDummyAvatar->stopMotion( ANIM_AGENT_EYE, TRUE );
+ mDummyAvatar->stopMotion( ANIM_AGENT_BODY_NOISE, TRUE );
+ mDummyAvatar->stopMotion( ANIM_AGENT_BREATHE_ROT, TRUE );
+}
+
+//-----------------------------------------------------------------------------
+// LLPreviewAnimation()
+//-----------------------------------------------------------------------------
+LLPreviewAnimation::~LLPreviewAnimation()
+{
+ mDummyAvatar->markDead();
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+BOOL LLPreviewAnimation::render()
+{
+ mNeedsUpdate = FALSE;
+ LLVOAvatar* avatarp = mDummyAvatar;
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSUIDefault def;
+ LLGLSNoTexture gls_no_texture;
+ glColor4f(0.15f, 0.2f, 0.3f, 1.f);
+
+ gl_rect_2d_simple( mWidth, mHeight );
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ LLVector3 target_pos = avatarp->mRoot.getWorldPosition();
+
+ LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
+ LLQuaternion(mCameraYaw, LLVector3::z_axis);
+
+ LLQuaternion av_rot = avatarp->mRoot.getWorldRotation() * camera_rot;
+ gCamera->setOriginAndLookAt(
+ target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera
+ LLVector3::z_axis, // up
+ target_pos + (mCameraOffset * av_rot) ); // point of interest
+
+ gCamera->setView(gCamera->getDefaultFOV() / mCameraZoom);
+ gCamera->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mWidth, mHeight, FALSE);
+
+ mCameraRelPos = gCamera->getOrigin() - avatarp->mHeadp->getWorldPosition();
+
+ //avatarp->setAnimationData("LookAtPoint", (void *)&mCameraRelPos);
+
+ //RN: timestep must be zero, because paused animations will never initialize
+ // av skeleton otherwise
+ avatarp->setTimeStep(0.f);
+ if (avatarp->areAnimationsPaused())
+ {
+ avatarp->updateMotion(TRUE);
+ }
+ else
+ {
+ avatarp->updateMotion();
+ }
+
+ avatarp->mRoot.updateWorldMatrixChildren();
+
+ stop_glerror();
+
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ if (avatarp->mDrawable.notNull())
+ {
+ LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
+ gPipeline.unbindAGP();
+ avatarPoolp->syncAGP();
+ if (avatarPoolp->canUseAGP() && gPipeline.usingAGP())
+ {
+ gPipeline.bindAGP();
+ }
+ avatarPoolp->renderAvatars(avatarp, TRUE); // renders only one avatar (no shaders)
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// requestUpdate()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::requestUpdate()
+{
+ mNeedsUpdate = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::rotate(F32 yaw_radians, F32 pitch_radians)
+{
+ mCameraYaw = mCameraYaw + yaw_radians;
+
+ mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
+}
+
+//-----------------------------------------------------------------------------
+// zoom()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::zoom(F32 zoom_delta)
+{
+ setZoom(mCameraZoom + zoom_delta);
+}
+
+//-----------------------------------------------------------------------------
+// setZoom()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::setZoom(F32 zoom_amt)
+{
+ mCameraZoom = llclamp(zoom_amt, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM);
+}
+
+//-----------------------------------------------------------------------------
+// pan()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::pan(F32 right, F32 up)
+{
+ mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f);
+ mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f);
+}
+
diff --git a/indra/newview/llfloateranimpreview.h b/indra/newview/llfloateranimpreview.h
new file mode 100644
index 0000000000..40ec96f73d
--- /dev/null
+++ b/indra/newview/llfloateranimpreview.h
@@ -0,0 +1,108 @@
+/**
+ * @file llfloateranimpreview.h
+ * @brief LLFloaterAnimPreview class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERANIMPREVIEW_H
+#define LL_LLFLOATERANIMPREVIEW_H
+
+#include "llfloaternamedesc.h"
+#include "lldynamictexture.h"
+#include "llcharacter.h"
+#include "llquaternion.h"
+
+class LLVOAvatar;
+class LLViewerJointMesh;
+
+class LLPreviewAnimation : public LLDynamicTexture
+{
+public:
+ LLPreviewAnimation(S32 width, S32 height);
+ virtual ~LLPreviewAnimation();
+
+ BOOL render();
+ void requestUpdate();
+ void rotate(F32 yaw_radians, F32 pitch_radians);
+ void zoom(F32 zoom_delta);
+ void setZoom(F32 zoom_amt);
+ void pan(F32 right, F32 up);
+ virtual BOOL needsUpdate() { return mNeedsUpdate; }
+
+ LLVOAvatar* getDummyAvatar() { return mDummyAvatar; }
+
+protected:
+ BOOL mNeedsUpdate;
+ F32 mCameraDistance;
+ F32 mCameraYaw;
+ F32 mCameraPitch;
+ F32 mCameraZoom;
+ LLVector3 mCameraOffset;
+ LLVector3 mCameraRelPos;
+ LLVOAvatar* mDummyAvatar;
+};
+
+class LLFloaterAnimPreview : public LLFloaterNameDesc
+{
+public:
+ LLFloaterAnimPreview(const char* filename);
+ virtual ~LLFloaterAnimPreview();
+
+ BOOL postBuild();
+
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
+ void refresh();
+
+ static void onMouseCaptureLost(LLMouseHandler*);
+ static void onBtnPlay(void*);
+ static void onBtnStop(void*);
+ static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+ static void onSliderMove(LLUICtrl*, void*);
+ static void onCommitBaseAnim(LLUICtrl*, void*);
+ static void onCommitLoop(LLUICtrl*, void*);
+ static void onCommitLoopIn(LLUICtrl*, void*);
+ static void onCommitLoopOut(LLUICtrl*, void*);
+ static BOOL validateLoopIn(LLUICtrl*, void*);
+ static BOOL validateLoopOut(LLUICtrl*, void*);
+ static void onCommitName(LLUICtrl*, void*);
+ static void onCommitHandPose(LLUICtrl*, void*);
+ static void onCommitEmote(LLUICtrl*, void*);
+ static void onCommitPriority(LLUICtrl*, void*);
+ static void onCommitEaseIn(LLUICtrl*, void*);
+ static void onCommitEaseOut(LLUICtrl*, void*);
+ static BOOL validateEaseIn(LLUICtrl*, void*);
+ static BOOL validateEaseOut(LLUICtrl*, void*);
+ static void onBtnOK(void*);
+ static void onSaveComplete(const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data,
+ S32 status);
+protected:
+ void draw();
+ void resetMotion();
+
+ LLPreviewAnimation* mAnimPreview;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ LLButton* mPlayButton;
+ LLButton* mStopButton;
+ F32 mLastSliderValue;
+ LLRect mPreviewRect;
+ LLRectf mPreviewImageRect;
+ LLAssetID mMotionID;
+ LLTransactionID mTransactionID;
+ BOOL mEnabled;
+ LLAnimPauseRequest mPauseRequest;
+
+ std::map<LLString, LLUUID> mIDList;
+
+ static S32 sUploadAmount;
+};
+
+#endif // LL_LLFLOATERANIMPREVIEW_H
diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp
new file mode 100644
index 0000000000..c86b9abfda
--- /dev/null
+++ b/indra/newview/llfloaterauction.cpp
@@ -0,0 +1,307 @@
+/**
+ * @file llfloaterauction.cpp
+ * @author James Cook, Ian Wilkes
+ * @brief Implementation of the auction floater.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterauction.h"
+
+#include "lldir.h"
+#include "llgl.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "llparcel.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+#include "llagent.h"
+#include "llcombobox.h"
+#include "llnotify.h"
+#include "llsavedsettingsglue.h"
+#include "llviewerimagelist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llui.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+void auction_j2c_upload_done(const LLUUID& asset_id,
+ void* user_data, S32 status);
+void auction_tga_upload_done(const LLUUID& asset_id,
+ void* user_data, S32 status);
+
+///----------------------------------------------------------------------------
+/// Class llfloaterauction
+///----------------------------------------------------------------------------
+
+LLFloaterAuction* LLFloaterAuction::sInstance = NULL;
+
+// Default constructor
+LLFloaterAuction::LLFloaterAuction() :
+ LLFloater("floater_auction"),
+ mParcelID(-1)
+{
+ gUICtrlFactory->buildFloater(this, "floater_auction.xml");
+
+ childSetValue("fence_check",
+ LLSD( gSavedSettings.getBOOL("AuctionShowFence") ) );
+ childSetCommitCallback("fence_check",
+ LLSavedSettingsGlue::setBOOL, (void*)"AuctionShowFence");
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "saletype_combo");
+ if (combo)
+ {
+ combo->selectFirstItem();
+ }
+
+ childSetAction("snapshot_btn", onClickSnapshot, this);
+ childSetAction("ok_btn", onClickOK, this);
+}
+
+// Destroys the object
+LLFloaterAuction::~LLFloaterAuction()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterAuction::show()
+{
+ if(!sInstance)
+ {
+ sInstance = new LLFloaterAuction();
+ sInstance->center();
+ sInstance->setFocus(TRUE);
+ }
+ sInstance->initialize();
+ sInstance->open();
+}
+
+void LLFloaterAuction::initialize()
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if(parcel && region && !parcel->getForSale())
+ {
+ mParcelHost = region->getHost();
+ mParcelID = parcel->getLocalID();
+
+ childSetText("parcel_text", parcel->getName());
+ childEnable("snapshot_btn");
+ childEnable("ok_btn");
+ }
+ else
+ {
+ mParcelHost.invalidate();
+ if(parcel && parcel->getForSale())
+ {
+ childSetText("parcel_text", childGetText("already for sale"));
+ }
+ else
+ {
+ childSetText("parcel_text", "");
+ }
+ mParcelID = -1;
+ childSetEnabled("snapshot_btn", false);
+ childSetEnabled("ok_btn", false);
+ }
+ mImageID.setNull();
+ mImage = NULL;
+}
+
+void LLFloaterAuction::draw()
+{
+ LLFloater::draw();
+
+ if(getVisible() && !isMinimized() && mImage.notNull())
+ {
+ LLRect rect;
+ if (childGetRect("snapshot_icon", rect))
+ {
+ {
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
+ rect.stretch(-1);
+ }
+ {
+ LLGLSUIDefault gls_ui;
+ glColor3f(1.f, 1.f, 1.f);
+ gl_draw_scaled_image(rect.mLeft,
+ rect.mBottom,
+ rect.getWidth(),
+ rect.getHeight(),
+ mImage);
+ }
+ }
+ }
+}
+
+
+// static
+void LLFloaterAuction::onClickSnapshot(void* data)
+{
+ LLFloaterAuction* self = (LLFloaterAuction*)(data);
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+
+ gForceRenderLandFence = self->childGetValue("fence_check").asBoolean();
+ BOOL success = gViewerWindow->rawSnapshot(raw,
+ gViewerWindow->getWindowWidth(),
+ gViewerWindow->getWindowHeight(),
+ TRUE,
+ FALSE, FALSE);
+ gForceRenderLandFence = FALSE;
+
+ if (success)
+ {
+ self->mTransactionID.generate();
+ self->mImageID = self->mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+
+ gViewerWindow->playSnapshotAnimAndSound();
+ llinfos << "Writing TGA..." << llendl;
+
+ LLPointer<LLImageTGA> tga = new LLImageTGA;
+ tga->encode(raw);
+ LLVFile::writeFile(tga->getData(), tga->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_IMAGE_TGA);
+
+ raw->biasedScaleToPowerOfTwo(LLViewerImage::MAX_IMAGE_SIZE_DEFAULT);
+
+ llinfos << "Writing J2C..." << llendl;
+
+ LLPointer<LLImageJ2C> j2c = new LLImageJ2C;
+ j2c->encode(raw);
+ LLVFile::writeFile(j2c->getData(), j2c->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_TEXTURE);
+
+ self->mImage = new LLImageGL((LLImageRaw*)raw, FALSE);
+ self->mImage->bind();
+ self->mImage->setClamp(TRUE, TRUE);
+ }
+ else
+ {
+ llwarns << "Unable to take snapshot" << llendl;
+ }
+}
+
+// static
+void LLFloaterAuction::onClickOK(void* data)
+{
+ LLFloaterAuction* self = (LLFloaterAuction*)(data);
+ bool is_auction = false;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(self, "saletype_combo");
+ if (combo
+ && combo->getCurrentIndex() == 0)
+ {
+ is_auction = true;
+ }
+ if(self->mImageID.notNull())
+ {
+ LLSD parcel_name = self->childGetValue("parcel_text");
+
+ // create the asset
+ if(is_auction)
+ {
+ // only need the tga if it is an auction.
+ LLString* name = new LLString(parcel_name.asString());
+ gAssetStorage->storeAssetData(self->mTransactionID, LLAssetType::AT_IMAGE_TGA,
+ &auction_tga_upload_done,
+ (void*)name,
+ FALSE);
+ self->getWindow()->incBusyCount();
+ }
+
+ LLString* j2c_name = new LLString(parcel_name.asString());
+ gAssetStorage->storeAssetData(self->mTransactionID, LLAssetType::AT_TEXTURE,
+ &auction_j2c_upload_done,
+ (void*)j2c_name,
+ FALSE);
+ self->getWindow()->incBusyCount();
+
+ if(is_auction)
+ {
+ LLNotifyBox::showXml("UploadingAuctionSnapshot");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UploadingSnapshot");
+ }
+ }
+ LLMessageSystem* msg = gMessageSystem;
+ if(is_auction)
+ {
+ msg->newMessage("ViewerStartAuction");
+ }
+ else
+ {
+ msg->newMessage("ParcelGodReserveForNewbie");
+ }
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addS32("LocalID", self->mParcelID);
+ msg->addUUID("SnapshotID", self->mImageID);
+ msg->sendReliable(self->mParcelHost);
+
+ // clean up floater, and get out
+ self->mImageID.setNull();
+ self->mImage = NULL;
+ self->mParcelID = -1;
+ self->mParcelHost.invalidate();
+ self->close();
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+void auction_tga_upload_done(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLString* name = (LLString*)(user_data);
+ llinfos << "Upload of asset '" << *name << "' " << asset_id
+ << " returned " << status << llendl;
+ delete name;
+
+ gViewerWindow->getWindow()->decBusyCount();
+
+ if (0 == status)
+ {
+ LLNotifyBox::showXml("UploadWebSnapshotDone");
+ }
+ else
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("UploadAuctionSnapshotFail", args);
+ }
+}
+
+void auction_j2c_upload_done(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLString* name = (LLString*)(user_data);
+ llinfos << "Upload of asset '" << *name << "' " << asset_id
+ << " returned " << status << llendl;
+ delete name;
+
+ gViewerWindow->getWindow()->decBusyCount();
+
+ if (0 == status)
+ {
+ LLNotifyBox::showXml("UploadSnapshotDone");
+ }
+ else
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("UploadAuctionSnapshotFail", args);
+ }
+}
diff --git a/indra/newview/llfloaterauction.h b/indra/newview/llfloaterauction.h
new file mode 100644
index 0000000000..fe5ce8f9d9
--- /dev/null
+++ b/indra/newview/llfloaterauction.h
@@ -0,0 +1,53 @@
+/**
+ * @file llfloaterauction.h
+ * @author James Cook, Ian Wilkes
+ * @brief llfloaterauction class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERAUCTION_H
+#define LL_LLFLOATERAUCTION_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterAuction
+//
+// Class which holds the functionality to start auctions.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterAuction : public LLFloater
+{
+public:
+ // LLFloater interface
+ /*virtual*/ void onClose(bool app_quitting) { setVisible(FALSE); }
+ /*virtual*/ void draw();
+
+ // LLFloaterAuction interface
+ static void show();
+
+private:
+ LLFloaterAuction();
+ ~LLFloaterAuction();
+ void initialize();
+
+ static void onClickSnapshot(void* data);
+ static void onClickOK(void* data);
+
+ static LLFloaterAuction* sInstance;
+
+private:
+ LLTransactionID mTransactionID;
+ LLAssetID mImageID;
+ LLPointer<LLImageGL> mImage;
+ S32 mParcelID;
+ LLHost mParcelHost;
+};
+
+
+#endif // LL_LLFLOATERAUCTION_H
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
new file mode 100644
index 0000000000..adcc3c49ab
--- /dev/null
+++ b/indra/newview/llfloateravatarpicker.cpp
@@ -0,0 +1,374 @@
+/**
+ * @file llfloateravatarpicker.cpp
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateravatarpicker.h"
+
+#include "message.h"
+
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llinventoryview.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h"
+
+const S32 MIN_WIDTH = 200;
+const S32 MIN_HEIGHT = 340;
+const LLRect FLOATER_RECT(0, 380, 240, 0);
+const char FLOATER_TITLE[] = "Choose Person";
+
+// static
+LLFloaterAvatarPicker* LLFloaterAvatarPicker::sInstance = NULL;
+
+
+LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(callback_t callback,
+ void* userdata,
+ BOOL allow_multiple,
+ BOOL closeOnSelect)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterAvatarPicker();
+ sInstance->mCallback = callback;
+ sInstance->mCallbackUserdata = userdata;
+ sInstance->mCloseOnSelect = FALSE;
+
+ sInstance->open();
+ sInstance->center();
+ sInstance->setAllowMultiple(allow_multiple);
+ }
+ else
+ {
+ sInstance->open();
+ sInstance->mCallback = callback;
+ sInstance->mCallbackUserdata = userdata;
+ sInstance->setAllowMultiple(allow_multiple);
+ }
+
+ sInstance->mCloseOnSelect = closeOnSelect;
+ return sInstance;
+}
+
+// Default constructor
+LLFloaterAvatarPicker::LLFloaterAvatarPicker() :
+ LLFloater("avatarpicker", FLOATER_RECT, FLOATER_TITLE, TRUE, MIN_WIDTH, MIN_HEIGHT),
+ mResultsReturned(FALSE),
+ mCallback(NULL),
+ mCallbackUserdata(NULL)
+{
+ gUICtrlFactory->buildFloater(this, "floater_avatar_picker.xml", NULL);
+}
+
+BOOL LLFloaterAvatarPicker::postBuild()
+{
+ childSetKeystrokeCallback("Edit", editKeystroke, this);
+
+ childSetAction("Find", onBtnFind, this);
+ childDisable("Find");
+
+ mListNames = LLViewerUICtrlFactory::getScrollListByName(this, "Names");
+ childSetDoubleClickCallback("Names",onBtnAdd);
+ childSetCommitCallback("Names", onList, this);
+ childDisable("Names");
+
+ childSetAction("Select", onBtnAdd, this);
+ childDisable("Select");
+
+ childSetAction("Close", onBtnClose, this);
+
+ childSetFocus("Edit");
+
+ if (mListNames)
+ {
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, LLUUID::null );
+ row->addColumn("No results", LLFontGL::sSansSerif);
+ mListNames->addItem(row);
+ }
+
+ mInventoryPanel = (LLInventoryPanel*)this->getCtrlByNameAndType("Inventory Panel", WIDGET_TYPE_INVENTORY_PANEL);
+ if(mInventoryPanel)
+ {
+ mInventoryPanel->setFilterTypes(0x1 << LLInventoryType::IT_CALLINGCARD);
+ mInventoryPanel->setFollowsAll();
+ mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mInventoryPanel->openDefaultFolderForType(LLAssetType::AT_CALLINGCARD);
+ mInventoryPanel->setSelectCallback(LLFloaterAvatarPicker::onSelectionChange, this);
+ }
+
+ setAllowMultiple(FALSE);
+
+ return TRUE;
+}
+
+// Destroys the object
+LLFloaterAvatarPicker::~LLFloaterAvatarPicker()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ sInstance = NULL;
+}
+
+void LLFloaterAvatarPicker::onBtnFind(void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+ if(self) self->find();
+}
+
+void LLFloaterAvatarPicker::onBtnAdd(void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+
+ if(self->mCallback)
+ {
+ self->mCallback(self->mAvatarNames, self->mAvatarIDs, self->mCallbackUserdata);
+ }
+ if (self->mInventoryPanel)
+ {
+ self->mInventoryPanel->setSelection(LLUUID::null, FALSE);
+ }
+ if (self->mListNames)
+ {
+ self->mListNames->deselectAllItems(TRUE);
+ }
+ if(self->mCloseOnSelect)
+ {
+ self->mCloseOnSelect = FALSE;
+ self->close();
+ }
+}
+
+void LLFloaterAvatarPicker::onBtnClose(void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+ if(self) self->close();
+}
+
+void LLFloaterAvatarPicker::onList(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+ if (!self)
+ {
+ return;
+ }
+
+ self->mAvatarIDs.clear();
+ self->mAvatarNames.clear();
+
+ if (!self->mListNames)
+ {
+ return;
+ }
+
+ std::vector<LLScrollListItem*> items = self->mListNames->getAllSelected();
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = items.begin(); itor != items.end(); ++itor)
+ {
+ self->mAvatarNames.push_back((*itor)->getColumn(0)->getText());
+ self->mAvatarIDs.push_back((*itor)->getUUID());
+ self->childSetEnabled("Select", TRUE);
+ }
+}
+
+void LLFloaterAvatarPicker::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)data;
+ if (!self)
+ {
+ return;
+ }
+
+ self->mAvatarIDs.clear();
+ self->mAvatarNames.clear();
+
+ self->childSetEnabled("Select", FALSE);
+
+ if (!self->mListNames)
+ {
+ return;
+ }
+
+ std::deque<LLFolderViewItem*>::const_iterator item_it;
+ for (item_it = items.begin(); item_it != items.end(); ++item_it)
+ {
+ LLFolderViewEventListener* listenerp = (*item_it)->getListener();
+ if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
+ {
+ LLInventoryItem* item = gInventory.getItem(listenerp->getUUID());
+
+ if (item)
+ {
+ self->mAvatarIDs.push_back(item->getCreatorUUID());
+ self->mAvatarNames.push_back(listenerp->getName());
+ self->childSetEnabled("Select", TRUE);
+ self->mListNames->deselectAllItems();
+ }
+ }
+ }
+}
+
+
+void LLFloaterAvatarPicker::find()
+{
+ const LLString& text = childGetValue("Edit").asString();
+
+ mQueryID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessage("AvatarPickerRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", agent_get_id());
+ msg->addUUID("SessionID", agent_get_session_id());
+ msg->addUUID("QueryID", mQueryID); // not used right now
+ msg->nextBlock("Data");
+ msg->addString("Name", text);
+
+ agent_send_reliable_message();
+
+ if (mListNames)
+ {
+ mListNames->deleteAllItems();
+
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, LLUUID::null );
+ row->addColumn("Searching...", LLFontGL::sSansSerif);
+ mListNames->addItem(row);
+ }
+
+ childSetEnabled("Select", FALSE);
+ mResultsReturned = FALSE;
+}
+
+void LLFloaterAvatarPicker::setAllowMultiple(BOOL allow_multiple)
+{
+ mAllowMultiple = allow_multiple;
+ if (mInventoryPanel)
+ {
+ mInventoryPanel->setAllowMultiSelect(mAllowMultiple);
+ }
+ if (mListNames)
+ {
+ mListNames->setAllowMultipleSelection(mAllowMultiple);
+ }
+}
+
+// static
+void LLFloaterAvatarPicker::processAvatarPickerReply(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ LLUUID query_id;
+ LLUUID avatar_id;
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_LAST_NAME_BUF_SIZE];
+
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ msg->getUUID("AgentData", "QueryID", query_id);
+
+ // Not for us
+ if (agent_id != agent_get_id()) return;
+
+ // Dialog already closed
+ LLFloaterAvatarPicker *self = sInstance;
+ if (!self) return;
+
+ // these are not results from our last request
+ if (query_id != self->mQueryID)
+ {
+ return;
+ }
+
+ if (!self->mResultsReturned)
+ {
+ // clear "Searching" label on first results
+ if (self->mListNames)
+ {
+ self->mListNames->deleteAllItems();
+ }
+ }
+ self->mResultsReturned = TRUE;
+
+ if (self->mListNames)
+ {
+ BOOL found_one = FALSE;
+ S32 num_new_rows = msg->getNumberOfBlocks("Data");
+ for (S32 i = 0; i < num_new_rows; i++)
+ {
+ msg->getUUIDFast( _PREHASH_Data,_PREHASH_AvatarID, avatar_id, i);
+ msg->getStringFast(_PREHASH_Data,_PREHASH_FirstName, DB_FIRST_NAME_BUF_SIZE, first_name, i);
+ msg->getStringFast(_PREHASH_Data,_PREHASH_LastName, DB_LAST_NAME_BUF_SIZE, last_name, i);
+
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, avatar_id );
+
+ if (avatar_id.isNull())
+ {
+ self->childSetTextArg("NotFound", "[TEXT]", self->childGetText("Edit"));
+ LLString msg = self->childGetValue("NotFound").asString();
+ row->addColumn(msg, LLFontGL::sSansSerif);
+ self->mListNames->setEnabled(FALSE);
+ }
+ else
+ {
+ LLString buffer = LLString(first_name) + " " + last_name;
+ row->addColumn(buffer, LLFontGL::sSansSerif);
+ self->mListNames->setEnabled(TRUE);
+ found_one = TRUE;
+ }
+ self->mListNames->addItem(row);
+ }
+
+ if (found_one)
+ {
+ self->mListNames->selectFirstItem();
+ self->onList(self->mListNames, self);
+ self->mListNames->setFocus(TRUE);
+ }
+ }
+}
+
+//static
+void LLFloaterAvatarPicker::editKeystroke(LLLineEditor* caller, void* user_data)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)user_data;
+ if (caller->getText().size() >= 3)
+ {
+ self->childSetEnabled("Find",TRUE);
+ }
+ else
+ {
+ self->childSetEnabled("Find",FALSE);
+ }
+}
+
+// virtual
+BOOL LLFloaterAvatarPicker::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (key == KEY_RETURN
+ && mask == MASK_NONE)
+ {
+ if (childHasFocus("Edit"))
+ {
+ onBtnFind(this);
+ return TRUE;
+ }
+ else
+ {
+ onBtnAdd(this);
+ return TRUE;
+ }
+ }
+ else if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ close();
+ return TRUE;
+ }
+
+ return LLFloater::handleKeyHere(key, mask, called_from_parent);
+}
diff --git a/indra/newview/llfloateravatarpicker.h b/indra/newview/llfloateravatarpicker.h
new file mode 100644
index 0000000000..694b760f96
--- /dev/null
+++ b/indra/newview/llfloateravatarpicker.h
@@ -0,0 +1,78 @@
+/**
+ * @file llfloateravatarpicker.h
+ * @brief was llavatarpicker.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLFLOATERAVATARPICKER_H
+#define LLFLOATERAVATARPICKER_H
+
+#include "llfloater.h"
+
+#include <vector>
+
+class LLUICtrl;
+class LLTextBox;
+class LLLineEditor;
+class LLButton;
+class LLScrollListCtrl;
+class LLMessageSystem;
+class LLInventoryPanel;
+class LLFolderViewItem;
+
+class LLFloaterAvatarPicker : public LLFloater
+{
+public:
+ // Call this to select an avatar.
+ // The callback function will be called with an avatar name and UUID.
+ typedef void(*callback_t)(const std::vector<std::string>&, const std::vector<LLUUID>&, void*);
+ static LLFloaterAvatarPicker* show(callback_t callback,
+ void* userdata,
+ BOOL allow_multiple = FALSE,
+ BOOL closeOnSelect = FALSE);
+ virtual BOOL postBuild();
+
+ static void processAvatarPickerReply(LLMessageSystem* msg, void**);
+ static void editKeystroke(LLLineEditor* caller, void* user_data);
+
+protected:
+ static void* createInventoryPanel(void* userdata);
+
+ static void onBtnFind(void* userdata);
+ static void onBtnAdd(void* userdata);
+ static void onBtnClose(void* userdata);
+ static void onList(LLUICtrl* ctrl, void* userdata);
+ static void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data);
+
+ void find();
+ void setAllowMultiple(BOOL allow_multiple);
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+protected:
+ LLScrollListCtrl* mListNames;
+ LLInventoryPanel* mInventoryPanel;
+
+ std::vector<LLUUID> mAvatarIDs;
+ std::vector<std::string> mAvatarNames;
+ BOOL mAllowMultiple;
+ LLUUID mQueryID;
+ BOOL mResultsReturned;
+ BOOL mCloseOnSelect;
+
+ void (*mCallback)(const std::vector<std::string>& name, const std::vector<LLUUID>& id, void* userdata);
+ void* mCallbackUserdata;
+
+protected:
+ static LLFloaterAvatarPicker* sInstance;
+
+protected:
+ // do not call these directly
+ LLFloaterAvatarPicker();
+ virtual ~LLFloaterAvatarPicker();
+};
+
+
+#endif
diff --git a/indra/newview/llfloateravatartextures.cpp b/indra/newview/llfloateravatartextures.cpp
new file mode 100644
index 0000000000..cb4d8dd8e0
--- /dev/null
+++ b/indra/newview/llfloateravatartextures.cpp
@@ -0,0 +1,180 @@
+/**
+ * @file llfloateravatartextures.cpp
+ * @brief Debugging view showing underlying avatar textures and baked textures.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateravatartextures.h"
+
+#include "lltexturectrl.h"
+
+#include "llvieweruictrlfactory.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+
+LLFloaterAvatarTextures::LLFloaterAvatarTextures(const LLUUID& id) :
+ LLFloater("avatar_texture_debug"),
+ mID(id)
+{
+}
+
+LLFloaterAvatarTextures::~LLFloaterAvatarTextures()
+{
+}
+
+LLFloaterAvatarTextures* LLFloaterAvatarTextures::show(const LLUUID &id)
+{
+
+ LLFloaterAvatarTextures* floaterp = new LLFloaterAvatarTextures(id);
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(floaterp, "floater_avatar_textures.xml");
+
+ gFloaterView->addChild(floaterp);
+ floaterp->open();
+
+ gFloaterView->adjustToFitScreen(floaterp, FALSE);
+
+ return floaterp;
+}
+
+BOOL LLFloaterAvatarTextures::postBuild()
+{
+ mBakedHead = (LLTextureCtrl*)getChildByName("baked_head");
+ mBakedEyes = (LLTextureCtrl*)getChildByName("baked_eyes");
+ mBakedUpper = (LLTextureCtrl*)getChildByName("baked_upper_body");
+ mBakedLower = (LLTextureCtrl*)getChildByName("baked_lower_body");
+ mBakedSkirt = (LLTextureCtrl*)getChildByName("baked_skirt");
+ mHair = (LLTextureCtrl*)getChildByName("hair");
+ mMakeup = (LLTextureCtrl*)getChildByName("head_bodypaint");
+ mEye = (LLTextureCtrl*)getChildByName("eye_texture");
+ mShirt = (LLTextureCtrl*)getChildByName("shirt");
+ mUpperTattoo = (LLTextureCtrl*)getChildByName("upper_bodypaint");
+ mUpperJacket = (LLTextureCtrl*)getChildByName("upper_jacket");
+ mGloves = (LLTextureCtrl*)getChildByName("gloves");
+ mUndershirt = (LLTextureCtrl*)getChildByName("undershirt");
+ mPants = (LLTextureCtrl*)getChildByName("pants");
+ mLowerTattoo = (LLTextureCtrl*)getChildByName("lower_bodypaint");
+ mShoes = (LLTextureCtrl*)getChildByName("shoes");
+ mSocks = (LLTextureCtrl*)getChildByName("socks");
+ mJacket = (LLTextureCtrl*)getChildByName("jacket");
+ mUnderpants = (LLTextureCtrl*)getChildByName("underpants");
+ mSkirt = (LLTextureCtrl*)getChildByName("skirt_texture");
+ mTitle = getTitle();
+
+ childSetAction("Dump", onClickDump, this);
+
+ refresh();
+ return TRUE;
+}
+
+void LLFloaterAvatarTextures::draw()
+{
+ refresh();
+ LLFloater::draw();
+}
+
+static void update_texture_ctrl(LLVOAvatar* avatarp,
+ LLTextureCtrl* ctrl,
+ LLVOAvatar::ETextureIndex te)
+{
+ LLUUID id = avatarp->getTE(te)->getID();
+ if (id == IMG_DEFAULT_AVATAR)
+ {
+ ctrl->setImageAssetID(LLUUID::null);
+ ctrl->setToolTip("IMG_DEFAULT_AVATAR");
+ }
+ else
+ {
+ ctrl->setImageAssetID(id);
+ ctrl->setToolTip(id.getString());
+ }
+}
+
+static LLVOAvatar* find_avatar(const LLUUID& id)
+{
+ LLViewerObject *obj = gObjectList.findObject(id);
+ while (obj && obj->isAttachment())
+ {
+ obj = (LLViewerObject *)obj->getParent();
+ }
+
+ if (obj && obj->isAvatar())
+ {
+ return (LLVOAvatar*)obj;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void LLFloaterAvatarTextures::refresh()
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+ LLVOAvatar *avatarp = find_avatar(mID);
+ if (avatarp)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ if (gCacheName->getName(avatarp->getID(), firstname, lastname))
+ {
+ LLString name;
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+
+ setTitle(mTitle + ": " + name);
+ }
+ update_texture_ctrl(avatarp, mBakedHead, LLVOAvatar::TEX_HEAD_BAKED);
+ update_texture_ctrl(avatarp, mBakedEyes, LLVOAvatar::TEX_EYES_BAKED);
+ update_texture_ctrl(avatarp, mBakedUpper, LLVOAvatar::TEX_UPPER_BAKED);
+ update_texture_ctrl(avatarp, mBakedLower, LLVOAvatar::TEX_LOWER_BAKED);
+ update_texture_ctrl(avatarp, mBakedSkirt, LLVOAvatar::TEX_SKIRT_BAKED);
+
+ update_texture_ctrl(avatarp, mMakeup, LLVOAvatar::TEX_HEAD_BODYPAINT);
+ update_texture_ctrl(avatarp, mHair, LLVOAvatar::TEX_HAIR);
+ update_texture_ctrl(avatarp, mEye, LLVOAvatar::TEX_EYES_IRIS);
+
+ update_texture_ctrl(avatarp, mShirt, LLVOAvatar::TEX_UPPER_SHIRT);
+ update_texture_ctrl(avatarp, mUpperTattoo, LLVOAvatar::TEX_UPPER_BODYPAINT);
+ update_texture_ctrl(avatarp, mUpperJacket, LLVOAvatar::TEX_UPPER_JACKET);
+ update_texture_ctrl(avatarp, mGloves, LLVOAvatar::TEX_UPPER_GLOVES);
+ update_texture_ctrl(avatarp, mUndershirt, LLVOAvatar::TEX_UPPER_UNDERSHIRT);
+
+ update_texture_ctrl(avatarp, mPants, LLVOAvatar::TEX_LOWER_PANTS);
+ update_texture_ctrl(avatarp, mLowerTattoo, LLVOAvatar::TEX_LOWER_BODYPAINT);
+ update_texture_ctrl(avatarp, mShoes, LLVOAvatar::TEX_LOWER_SHOES);
+ update_texture_ctrl(avatarp, mSocks, LLVOAvatar::TEX_LOWER_SOCKS);
+ update_texture_ctrl(avatarp, mJacket, LLVOAvatar::TEX_LOWER_JACKET);
+ update_texture_ctrl(avatarp, mUnderpants, LLVOAvatar::TEX_LOWER_UNDERPANTS);
+ update_texture_ctrl(avatarp, mSkirt, LLVOAvatar::TEX_SKIRT);
+ }
+ else
+ {
+ setTitle(mTitle + ": INVALID AVATAR (" + mID.getString() + ")");
+ }
+#endif
+}
+
+// static
+void LLFloaterAvatarTextures::onClickDump(void* data)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+ LLFloaterAvatarTextures* self = (LLFloaterAvatarTextures*)data;
+ LLVOAvatar* avatarp = find_avatar(self->mID);
+ if (!avatarp) return;
+
+ for (S32 i = 0; i < avatarp->getNumTEs(); i++)
+ {
+ const LLTextureEntry* te = avatarp->getTE(i);
+ if (!te) continue;
+
+ llinfos << "Avatar TE " << i << " id " << te->getID() << llendl;
+ }
+#endif
+}
diff --git a/indra/newview/llfloateravatartextures.h b/indra/newview/llfloateravatartextures.h
new file mode 100644
index 0000000000..1366b21534
--- /dev/null
+++ b/indra/newview/llfloateravatartextures.h
@@ -0,0 +1,59 @@
+/**
+ * @file llfloateravatartextures.h
+ * @brief Debugging view showing underlying avatar textures and baked textures.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERAVATARTEXTURES_H
+#define LL_LLFLOATERAVATARTEXTURES_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "llstring.h"
+
+class LLTextureCtrl;
+
+class LLFloaterAvatarTextures : public LLFloater
+{
+public:
+ LLFloaterAvatarTextures(const LLUUID& id);
+ virtual ~LLFloaterAvatarTextures();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ static LLFloaterAvatarTextures* show(const LLUUID& id);
+
+private:
+ static void onClickDump(void*);
+
+private:
+ LLUUID mID;
+ LLString mTitle;
+ LLTextureCtrl* mBakedHead;
+ LLTextureCtrl* mBakedEyes;
+ LLTextureCtrl* mBakedUpper;
+ LLTextureCtrl* mBakedLower;
+ LLTextureCtrl* mBakedSkirt;
+ LLTextureCtrl* mHair;
+ LLTextureCtrl* mMakeup;
+ LLTextureCtrl* mEye;
+ LLTextureCtrl* mShirt;
+ LLTextureCtrl* mUpperTattoo;
+ LLTextureCtrl* mUpperJacket;
+ LLTextureCtrl* mGloves;
+ LLTextureCtrl* mUndershirt;
+ LLTextureCtrl* mPants;
+ LLTextureCtrl* mLowerTattoo;
+ LLTextureCtrl* mShoes;
+ LLTextureCtrl* mSocks;
+ LLTextureCtrl* mJacket;
+ LLTextureCtrl* mUnderpants;
+ LLTextureCtrl* mSkirt;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuildoptions.cpp b/indra/newview/llfloaterbuildoptions.cpp
new file mode 100644
index 0000000000..de58497430
--- /dev/null
+++ b/indra/newview/llfloaterbuildoptions.cpp
@@ -0,0 +1,73 @@
+/**
+ * @file llfloaterbuildoptions.cpp
+ * @brief LLFloaterBuildOptions class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Panel for setting global object-editing options, specifically
+ * grid size and spacing.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuildoptions.h"
+#include "llvieweruictrlfactory.h"
+
+// library includes
+#include "llfontgl.h"
+#include "llcheckboxctrl.h"
+#include "llspinctrl.h"
+#include "llsliderctrl.h"
+
+// newview includes
+#include "llresmgr.h"
+#include "llviewercontrol.h"
+
+//
+// Globals
+//
+LLFloaterBuildOptions *LLFloaterBuildOptions::sInstance = NULL;
+
+//
+// Methods
+//
+LLFloaterBuildOptions::LLFloaterBuildOptions( )
+: LLFloater("build options floater")
+{
+ sInstance = this;
+}
+
+LLFloaterBuildOptions::~LLFloaterBuildOptions()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterBuildOptions::show(void*)
+{
+ if (sInstance)
+ {
+ sInstance->open();
+ }
+ else
+ {
+ LLFloaterBuildOptions* floater = new LLFloaterBuildOptions();
+
+ gUICtrlFactory->buildFloater(floater, "floater_build_options.xml");
+ floater->open();
+ }
+}
+
+LLFloaterBuildOptions* LLFloaterBuildOptions::getInstance()
+{
+ return sInstance;
+}
+
+// static
+BOOL LLFloaterBuildOptions::visible(void*)
+{
+ return (sInstance != NULL);
+}
diff --git a/indra/newview/llfloaterbuildoptions.h b/indra/newview/llfloaterbuildoptions.h
new file mode 100644
index 0000000000..d3483dd6d4
--- /dev/null
+++ b/indra/newview/llfloaterbuildoptions.h
@@ -0,0 +1,36 @@
+/**
+ * @file llfloaterbuildoptions.h
+ * @brief LLFloaterBuildOptions class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Panel for setting global object-editing options, specifically
+ * grid size and spacing.
+ */
+
+#ifndef LL_LLFLOATERBUILDOPTIONS_H
+#define LL_LLFLOATERBUILDOPTIONS_H
+
+#include "llfloater.h"
+
+
+class LLFloaterBuildOptions
+: public LLFloater
+{
+protected:
+ LLFloaterBuildOptions();
+ ~LLFloaterBuildOptions();
+
+public:
+ static void show(void*);
+ static LLFloaterBuildOptions* getInstance();
+ static BOOL visible(void*);
+
+protected:
+ static LLFloaterBuildOptions* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp
new file mode 100644
index 0000000000..8b81c661d8
--- /dev/null
+++ b/indra/newview/llfloaterbump.cpp
@@ -0,0 +1,138 @@
+/**
+ * @file llfloaterbump.cpp
+ * @brief Floater showing recent bumps, hits with objects, pushes, etc.
+ * @author Cory Ondrejka, James Cook
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbump.h"
+
+#include "llscrolllistctrl.h"
+
+#include "llvieweruictrlfactory.h"
+#include "viewer.h" // gPacificDaylightTime
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+extern LLLinkedList<LLMeanCollisionData> gMeanCollisionList;
+
+LLFloaterBump* LLFloaterBump::sInstance = NULL;
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterBump
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFloaterBump::LLFloaterBump()
+: LLFloater()
+{
+ sInstance = this;
+
+ gUICtrlFactory->buildFloater(this, "floater_bumps.xml");
+}
+
+
+// Destroys the object
+LLFloaterBump::~LLFloaterBump()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterBump::show(void *contents)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterBump();
+ }
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "bump_list");
+ if (!list) return;
+ list->deleteAllItems();
+
+ if (gMeanCollisionList.isEmpty())
+ {
+ LLString none_detected = sInstance->childGetText("none_detected");
+ LLScrollListItem *item = new LLScrollListItem();
+ item->addColumn(none_detected, LLFontGL::sSansSerifBold);
+ list->addItem(item);
+ }
+ else
+ {
+ for (LLMeanCollisionData* mcd = gMeanCollisionList.getFirstData();
+ mcd;
+ mcd = gMeanCollisionList.getNextData())
+ {
+ LLFloaterBump::add(list, mcd);
+ }
+ }
+
+ sInstance->open();
+}
+
+void LLFloaterBump::add(LLScrollListCtrl* list, LLMeanCollisionData* mcd)
+{
+ if (!sInstance)
+ {
+ new LLFloaterBump();
+ }
+
+ if (!mcd->mFirstName[0]
+ || list->getItemCount() >= 20)
+ {
+ return;
+ }
+
+ // There's only one internal tm buffer.
+ struct tm* timep;
+
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(mcd->mTime, gPacificDaylightTime);
+
+ LLString time = llformat("[%d:%02d]", timep->tm_hour, timep->tm_min);
+
+ LLString action;
+ switch(mcd->mType)
+ {
+ case MEAN_BUMP:
+ action = "bump";
+ break;
+ case MEAN_LLPUSHOBJECT:
+ action = "llpushobject";
+ break;
+ case MEAN_SELECTED_OBJECT_COLLIDE:
+ action = "selected_object_collide";
+ break;
+ case MEAN_SCRIPTED_OBJECT_COLLIDE:
+ action = "scripted_object_collide";
+ break;
+ case MEAN_PHYSICAL_OBJECT_COLLIDE:
+ action = "physical_object_collide";
+ break;
+ default:
+ llinfos << "LLFloaterBump::add unknown mean collision type "
+ << mcd->mType << llendl;
+ return;
+ }
+
+ // All above action strings are in XML file
+ LLUIString text = sInstance->childGetText(action);
+ text.setArg("[TIME]", time);
+ text.setArg("[FIRST]", mcd->mFirstName);
+ text.setArg("[LAST]", mcd->mLastName);
+
+ LLScrollListItem *item = new LLScrollListItem(TRUE, NULL, mcd->mPerp);
+ item->addColumn(text, LLFontGL::sSansSerifBold);
+ list->addItem(item);
+}
diff --git a/indra/newview/llfloaterbump.h b/indra/newview/llfloaterbump.h
new file mode 100644
index 0000000000..39f98dfb9f
--- /dev/null
+++ b/indra/newview/llfloaterbump.h
@@ -0,0 +1,33 @@
+/**
+ * @file llfloaterbump.h
+ * @brief Floater showing recent bumps, hits with objects, pushes, etc.
+ * @author Cory Ondrejka, James Cook
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERBUMP_H
+#define LL_LLFLOATERBUMP_H
+
+#include "llfloater.h"
+
+class LLMeanCollisionData;
+class LLScrollListCtrl;
+
+class LLFloaterBump
+: public LLFloater
+{
+public:
+ static void show(void *);
+
+private:
+ LLFloaterBump();
+ virtual ~LLFloaterBump();
+ static void add(LLScrollListCtrl* list, LLMeanCollisionData *mcd);
+
+private:
+ static LLFloaterBump* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuy.cpp b/indra/newview/llfloaterbuy.cpp
new file mode 100644
index 0000000000..221c58cff3
--- /dev/null
+++ b/indra/newview/llfloaterbuy.cpp
@@ -0,0 +1,288 @@
+/**
+ * @file llfloaterbuy.cpp
+ * @author James Cook
+ * @brief LLFloaterBuy class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Floater that appears when buying an object, giving a preview
+ * of its contents and their permissions.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuy.h"
+
+#include "llagent.h" // for agent id
+#include "llalertdialog.h"
+#include "llinventorymodel.h" // for gInventory
+#include "llinventoryview.h" // for get_item_icon
+#include "llselectmgr.h"
+#include "llscrolllistctrl.h"
+#include "llviewerobject.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+LLFloaterBuy* LLFloaterBuy::sInstance = NULL;
+
+LLFloaterBuy::LLFloaterBuy()
+: LLFloater("floater_buy_object", "FloaterBuyRect", "")
+{
+ gUICtrlFactory->buildFloater(this, "floater_buy_object.xml");
+
+ childDisable("object_list");
+ childDisable("item_list");
+
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("buy_btn", onClickBuy, this);
+
+ setDefaultBtn("buy_btn");
+}
+
+LLFloaterBuy::~LLFloaterBuy()
+{
+ gSelectMgr->deselectAll();
+
+ sInstance = NULL;
+}
+
+void LLFloaterBuy::reset()
+{
+ LLScrollListCtrl* object_list = LLUICtrlFactory::getScrollListByName(this, "object_list");
+ if (object_list) object_list->deleteAllItems();
+
+ LLScrollListCtrl* item_list = LLUICtrlFactory::getScrollListByName(this, "item_list");
+ if (item_list) item_list->deleteAllItems();
+}
+
+// static
+void LLFloaterBuy::show(const LLSaleInfo& sale_info)
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("BuyOneObjectOnly");
+ return;
+ }
+
+ // Create a new instance only if one doesn't exist
+ if (sInstance)
+ {
+ // Clean up the lists...
+ sInstance->reset();
+ }
+ else
+ {
+ sInstance = new LLFloaterBuy();
+ }
+
+ sInstance->open();
+ sInstance->setFocus(TRUE);
+ sInstance->mSaleInfo = sale_info;
+
+ // Always center the dialog. User can change the size,
+ // but purchases are important and should be center screen.
+ // This also avoids problems where the user resizes the application window
+ // mid-session and the saved rect is off-center.
+ sInstance->center();
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return;
+
+ // Set title based on sale type
+ std::ostringstream title;
+ switch (sale_info.getSaleType())
+ {
+ case LLSaleInfo::FS_ORIGINAL:
+ title << "Buy " << node->mName; // XUI:translate
+ break;
+ case LLSaleInfo::FS_COPY:
+ default:
+ title << "Buy Copy of " << node->mName; // XUI:translate
+ break;
+ }
+ sInstance->setTitle(title.str());
+
+ LLUUID owner_id;
+ LLString owner_name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+ if (!owners_identical)
+ {
+ gViewerWindow->alertXml("BuyObjectOneOwner");
+ return;
+ }
+
+ LLCtrlListInterface *object_list = sInstance->childGetListInterface("object_list");
+ if (!object_list)
+ {
+ return;
+ }
+
+ // Update the display
+ // Display next owner permissions
+ LLSD row;
+
+ // Compute icon for this item
+ LLUUID icon_id = get_item_icon_uuid(LLAssetType::AT_OBJECT,
+ LLInventoryType::IT_OBJECT,
+ 0x0);
+ row["columns"][0]["column"] = "icon";
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+
+ // Append the permissions that you will acquire (not the current
+ // permissions).
+ U32 next_owner_mask = node->mPermissions->getMaskNextOwner();
+ LLString text = node->mName;
+ if (!(next_owner_mask & PERM_COPY))
+ {
+ text.append(" (no copy)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_MODIFY))
+ {
+ text.append(" (no modify)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_TRANSFER))
+ {
+ text.append(" (no transfer)"); // XUI:translate
+ }
+
+ row["columns"][1]["column"] = "text";
+ row["columns"][1]["value"] = text;
+ row["columns"][1]["font"] = "SANSSERIF";
+
+ // Add after columns added so appropriate heights are correct.
+ object_list->addElement(row);
+
+ sInstance->childSetTextArg("buy_text", "[AMOUNT]", llformat("%d", sale_info.getSalePrice()));
+ sInstance->childSetTextArg("buy_text", "[NAME]", owner_name);
+
+ // Must do this after the floater is created, because
+ // sometimes the inventory is already there and
+ // the callback is called immediately.
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ sInstance->registerVOInventoryListener(obj,NULL);
+ sInstance->requestVOInventory();
+}
+
+
+void LLFloaterBuy::inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data)
+{
+ if (!obj)
+ {
+ llwarns << "No object in LLFloaterBuy::inventoryChanged" << llendl;
+ return;
+ }
+
+ if (!inv)
+ {
+ llwarns << "No inventory in LLFloaterBuy::inventoryChanged"
+ << llendl;
+ removeVOInventoryListener();
+ return;
+ }
+
+ LLCtrlListInterface *item_list = childGetListInterface("item_list");
+ if (!item_list)
+ {
+ removeVOInventoryListener();
+ return;
+ }
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it )
+ {
+ LLInventoryObject* obj = (LLInventoryObject*)(*it);
+
+ // Skip folders, so we know we have inventory items only
+ if (obj->getType() == LLAssetType::AT_CATEGORY)
+ continue;
+
+ // Skip root folders, so we know we have inventory items only
+ if (obj->getType() == LLAssetType::AT_ROOT_CATEGORY)
+ continue;
+
+ // Skip the mysterious blank InventoryObject
+ if (obj->getType() == LLAssetType::AT_NONE)
+ continue;
+
+
+ LLInventoryItem* inv_item = (LLInventoryItem*)(obj);
+
+ // Skip items we can't transfer
+ if (!inv_item->getPermissions().allowTransferTo(gAgent.getID()))
+ continue;
+
+ // Create the line in the list
+ LLSD row;
+
+ // Compute icon for this item
+ LLUUID icon_id = get_item_icon_uuid(inv_item->getType(),
+ inv_item->getInventoryType(),
+ inv_item->getFlags());
+ row["columns"][0]["column"] = "icon";
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+
+ // Append the permissions that you will acquire (not the current
+ // permissions).
+ U32 next_owner_mask = inv_item->getPermissions().getMaskNextOwner();
+ LLString text = obj->getName();
+ if (!(next_owner_mask & PERM_COPY))
+ {
+ text.append(" (no copy)");
+ }
+ if (!(next_owner_mask & PERM_MODIFY))
+ {
+ text.append(" (no modify)");
+ }
+ if (!(next_owner_mask & PERM_TRANSFER))
+ {
+ text.append(" (no transfer)");
+ }
+
+ row["columns"][1]["column"] = "text";
+ row["columns"][1]["value"] = text;
+ row["columns"][1]["font"] = "SANSSERIF";
+
+ item_list->addElement(row);
+ }
+ removeVOInventoryListener();
+}
+
+
+// static
+void LLFloaterBuy::onClickBuy(void*)
+{
+ if (!sInstance)
+ {
+ llinfos << "LLFloaterBuy::onClickBuy no sInstance!" << llendl;
+ return;
+ }
+
+ // Put the items where we put new folders.
+ LLUUID category_id;
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+
+ // FIXME: doesn't work for multiple object buy, which UI does not currently support
+ // sale info is used for verification only, if it doesn't match region info then sale is canceled
+ gSelectMgr->sendBuy(gAgent.getID(), category_id, sInstance->mSaleInfo );
+
+ sInstance->close();
+}
+
+
+// static
+void LLFloaterBuy::onClickCancel(void*)
+{
+ if (sInstance)
+ {
+ sInstance->close();
+ }
+}
diff --git a/indra/newview/llfloaterbuy.h b/indra/newview/llfloaterbuy.h
new file mode 100644
index 0000000000..e90a77b7c9
--- /dev/null
+++ b/indra/newview/llfloaterbuy.h
@@ -0,0 +1,53 @@
+/**
+ * @file llfloaterbuy.h
+ * @author James Cook
+ * @brief LLFloaterBuy class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Floater that appears when buying an object, giving a preview
+ * of its contents and their permissions.
+ */
+
+#ifndef LL_LLFLOATERBUY_H
+#define LL_LLFLOATERBUY_H
+
+#include "llfloater.h"
+#include "llvoinventorylistener.h"
+#include "llsaleinfo.h"
+#include "llinventory.h"
+
+class LLViewerObject;
+class LLSaleInfo;
+
+class LLFloaterBuy
+: public LLFloater, public LLVOInventoryListener
+{
+public:
+ static void show(const LLSaleInfo& sale_info);
+
+protected:
+ LLFloaterBuy();
+ ~LLFloaterBuy();
+
+ void reset();
+
+ void requestObjectInventories();
+ /*virtual*/ void inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data);
+
+ static void onClickBuy(void*);
+ static void onClickCancel(void*);
+
+private:
+ static LLFloaterBuy* sInstance;
+
+ LLSaleInfo mSaleInfo;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuycontents.cpp b/indra/newview/llfloaterbuycontents.cpp
new file mode 100644
index 0000000000..c0ca919344
--- /dev/null
+++ b/indra/newview/llfloaterbuycontents.cpp
@@ -0,0 +1,274 @@
+/**
+ * @file llfloaterbuycontents.cpp
+ * @author James Cook
+ * @brief LLFloaterBuyContents class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Shows the contents of an object and their permissions when you
+ * click "Buy..." on an object with "Sell Contents" checked.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuycontents.h"
+
+#include "llcachename.h"
+
+#include "llagent.h" // for agent id
+#include "llalertdialog.h"
+#include "llcheckboxctrl.h"
+#include "llinventorymodel.h" // for gInventory
+#include "llinventoryview.h" // for get_item_icon
+#include "llselectmgr.h"
+#include "llscrolllistctrl.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+LLFloaterBuyContents* LLFloaterBuyContents::sInstance = NULL;
+
+LLFloaterBuyContents::LLFloaterBuyContents()
+: LLFloater("floater_buy_contents", "FloaterBuyContentsRect", "")
+{
+ gUICtrlFactory->buildFloater(this, "floater_buy_contents.xml");
+
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("buy_btn", onClickBuy, this);
+
+ childDisable("item_list");
+ childDisable("buy_btn");
+ childDisable("wear_check");
+
+ setDefaultBtn("buy_btn");
+}
+
+LLFloaterBuyContents::~LLFloaterBuyContents()
+{
+ gSelectMgr->deselectAll();
+
+ sInstance = NULL;
+}
+
+
+// static
+void LLFloaterBuyContents::show(const LLSaleInfo& sale_info)
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("BuyContentsOneOnly");
+ return;
+ }
+
+ // Create a new instance only if needed
+ if (sInstance)
+ {
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "item_list");
+ if (list) list->deleteAllItems();
+ }
+ else
+ {
+ sInstance = new LLFloaterBuyContents();
+ }
+
+ sInstance->open();
+ sInstance->setFocus(TRUE);
+
+ // Always center the dialog. User can change the size,
+ // but purchases are important and should be center screen.
+ // This also avoids problems where the user resizes the application window
+ // mid-session and the saved rect is off-center.
+ sInstance->center();
+
+ LLUUID owner_id;
+ LLString owner_name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+ if (!owners_identical)
+ {
+ gViewerWindow->alertXml("BuyContentsOneOwner");
+ return;
+ }
+
+ sInstance->mSaleInfo = sale_info;
+
+ // Update the display
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return;
+ if(node->mPermissions->isGroupOwned())
+ {
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(owner_id, group_name);
+ owner_name.assign(group_name);
+ }
+
+ sInstance->childSetTextArg("contains_text", "[NAME]", node->mName);
+ sInstance->childSetTextArg("buy_text", "[AMOUNT]", llformat("%d", sale_info.getSalePrice()));
+ sInstance->childSetTextArg("buy_text", "[NAME]", owner_name);
+
+ // Must do this after the floater is created, because
+ // sometimes the inventory is already there and
+ // the callback is called immediately.
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ sInstance->registerVOInventoryListener(obj,NULL);
+ sInstance->requestVOInventory();
+}
+
+
+void LLFloaterBuyContents::inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data)
+{
+ if (!obj)
+ {
+ llwarns << "No object in LLFloaterBuyContents::inventoryChanged" << llendl;
+ return;
+ }
+
+ if (!inv)
+ {
+ llwarns << "No inventory in LLFloaterBuyContents::inventoryChanged"
+ << llendl;
+ removeVOInventoryListener();
+ return;
+ }
+
+ LLCtrlListInterface *item_list = childGetListInterface("item_list");
+ if (!item_list)
+ {
+ removeVOInventoryListener();
+ return;
+ }
+
+ // default to turning off the buy button.
+ childDisable("buy_btn");
+
+ LLUUID owner_id;
+ BOOL is_group_owned;
+ LLAssetType::EType asset_type;
+ LLInventoryType::EType inv_type;
+ S32 wearable_count = 0;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+
+ for ( ; it != end; ++it )
+ {
+ asset_type = (*it)->getType();
+
+ // Skip folders, so we know we have inventory items only
+ if (asset_type == LLAssetType::AT_CATEGORY)
+ continue;
+
+ // Skip root folders, so we know we have inventory items only
+ if (asset_type == LLAssetType::AT_ROOT_CATEGORY)
+ continue;
+
+ LLInventoryItem* inv_item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ inv_type = inv_item->getInventoryType();
+
+ // Count clothing items for later
+ if (LLInventoryType::IT_WEARABLE == inv_type)
+ {
+ wearable_count++;
+ }
+
+ // Skip items the object's owner can't copy (and hence can't sell)
+ if (!inv_item->getPermissions().getOwnership(owner_id, is_group_owned))
+ continue;
+
+ if (!inv_item->getPermissions().allowCopyBy(owner_id, owner_id))
+ continue;
+
+ // Skip items we can't transfer
+ if (!inv_item->getPermissions().allowTransferTo(gAgent.getID()))
+ continue;
+
+ // There will be at least one item shown in the display, so go
+ // ahead and enable the buy button.
+ childEnable("buy_btn");
+
+ // Create the line in the list
+ LLSD row;
+ LLUUID icon_id = get_item_icon_uuid(inv_item->getType(),
+ inv_item->getInventoryType(),
+ inv_item->getFlags());
+ row["columns"][0]["column"] = "icon";
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+
+ // Append the permissions that you will acquire (not the current
+ // permissions).
+ U32 next_owner_mask = inv_item->getPermissions().getMaskNextOwner();
+ LLString text = (*it)->getName();
+
+ // *TODO: Move into shared library function.
+ if (!(next_owner_mask & PERM_COPY))
+ {
+ text.append(" (no copy)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_MODIFY))
+ {
+ text.append(" (no modify)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_TRANSFER))
+ {
+ text.append(" (no transfer)"); // XUI:translate
+ }
+
+ row["columns"][1]["column"] = "text";
+ row["columns"][1]["value"] = text;
+ row["columns"][1]["font"] = "SANSSERIF";
+
+ item_list->addElement(row);
+ }
+
+ if (wearable_count > 0)
+ {
+ childEnable("wear_check");
+ childSetValue("wear_check", LLSD(false) );
+ }
+
+ removeVOInventoryListener();
+}
+
+
+// static
+void LLFloaterBuyContents::onClickBuy(void*)
+{
+ // Make sure this wasn't selected through other mechanisms
+ // (ie, being the default button and pressing enter.
+ if(!sInstance->childIsEnabled("buy_btn"))
+ {
+ // We shouldn't be enabled. Just close.
+ sInstance->close();
+ return;
+ }
+
+ // We may want to wear this item
+ if (sInstance->childGetValue("wear_check"))
+ {
+ LLInventoryView::sWearNewClothing = TRUE;
+ }
+
+ // Put the items where we put new folders.
+ LLUUID category_id;
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CATEGORY);
+
+ // FIXME: doesn't work for multiple object buy, which UI does not currently support
+ // sale info is used for verification only, if it doesn't match region info then sale is canceled
+ gSelectMgr->sendBuy(gAgent.getID(), category_id, sInstance->mSaleInfo);
+
+ sInstance->close();
+}
+
+
+// static
+void LLFloaterBuyContents::onClickCancel(void*)
+{
+ sInstance->close();
+}
diff --git a/indra/newview/llfloaterbuycontents.h b/indra/newview/llfloaterbuycontents.h
new file mode 100644
index 0000000000..1f834da668
--- /dev/null
+++ b/indra/newview/llfloaterbuycontents.h
@@ -0,0 +1,49 @@
+/**
+ * @file llfloaterbuycontents.h
+ * @author James Cook
+ * @brief LLFloaterBuyContents class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Shows the contents of an object and their permissions when you
+ * click "Buy..." on an object with "Sell Contents" checked.
+ */
+
+#ifndef LL_LLFLOATERBUYCONTENTS_H
+#define LL_LLFLOATERBUYCONTENTS_H
+
+#include "llfloater.h"
+#include "llvoinventorylistener.h"
+#include "llinventory.h"
+
+class LLViewerObject;
+
+class LLFloaterBuyContents
+: public LLFloater, public LLVOInventoryListener
+{
+public:
+ static void show(const LLSaleInfo& sale_info);
+
+protected:
+ LLFloaterBuyContents();
+ ~LLFloaterBuyContents();
+
+ void requestObjectInventories();
+ /*virtual*/ void inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data);
+
+ static void onClickBuy(void*);
+ static void onClickCancel(void*);
+
+protected:
+ static LLFloaterBuyContents* sInstance;
+
+ LLSaleInfo mSaleInfo;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp
new file mode 100644
index 0000000000..55667b55e1
--- /dev/null
+++ b/indra/newview/llfloaterbuycurrency.cpp
@@ -0,0 +1,356 @@
+/**
+ * @file llfloaterbuycurrency.cpp
+ * @brief LLFloaterBuyCurrency class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuycurrency.h"
+
+// viewer includes
+#include "llcurrencyuimanager.h"
+#include "llfloater.h"
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "llviewchildren.h"
+#include "llviewerwindow.h"
+#include "llvieweruictrlfactory.h"
+#include "llweb.h"
+#include "llwindow.h"
+#include "viewer.h"
+
+static const S32 STANDARD_BUY_AMOUNT = 1000;
+static const S32 MINIMUM_BALANCE_AMOUNT = 0;
+
+class LLFloaterBuyCurrencyUI
+: public LLFloater
+{
+public:
+ static LLFloaterBuyCurrencyUI* soleInstance(bool createIfNeeded);
+
+private:
+ static LLFloaterBuyCurrencyUI* sInstance;
+
+ LLFloaterBuyCurrencyUI();
+ virtual ~LLFloaterBuyCurrencyUI();
+
+
+public:
+ LLViewChildren mChildren;
+ LLCurrencyUIManager mManager;
+
+ bool mHasTarget;
+ std::string mTargetName;
+ S32 mTargetPrice;
+
+public:
+ void noTarget();
+ void target(const std::string& name, S32 price);
+
+ virtual BOOL postBuild();
+
+ void updateUI();
+
+ virtual void draw();
+ virtual BOOL canClose();
+ virtual void onClose(bool app_quitting);
+
+ static void onClickBuy(void* data);
+ static void onClickCancel(void* data);
+ static void onClickErrorWeb(void* data);
+};
+
+
+// static
+LLFloaterBuyCurrencyUI* LLFloaterBuyCurrencyUI::sInstance = NULL;
+
+// static
+LLFloaterBuyCurrencyUI* LLFloaterBuyCurrencyUI::soleInstance(bool createIfNeeded)
+{
+ if (!sInstance && createIfNeeded)
+ {
+ sInstance = new LLFloaterBuyCurrencyUI();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_buy_currency.xml");
+ sInstance->center();
+ }
+
+ return sInstance;
+}
+
+
+#if LL_WINDOWS
+// passing 'this' during construction generates a warning. The callee
+// only uses the pointer to hold a reference to 'this' which is
+// already valid, so this call does the correct thing. Disable the
+// warning so that we can compile without generating a warning.
+#pragma warning(disable : 4355)
+#endif
+LLFloaterBuyCurrencyUI::LLFloaterBuyCurrencyUI()
+: LLFloater("Buy Currency"),
+ mChildren(*this),
+ mManager(*this)
+{
+}
+
+LLFloaterBuyCurrencyUI::~LLFloaterBuyCurrencyUI()
+{
+ if (sInstance == this)
+ {
+ sInstance = NULL;
+ }
+}
+
+
+void LLFloaterBuyCurrencyUI::noTarget()
+{
+ mHasTarget = false;
+ mManager.setAmount(STANDARD_BUY_AMOUNT);
+}
+
+void LLFloaterBuyCurrencyUI::target(const std::string& name, S32 price)
+{
+ mHasTarget = true;
+ mTargetName = name;
+ mTargetPrice = price;
+
+ S32 balance = gStatusBar->getBalance();
+ S32 need = price - balance;
+ if (need < 0)
+ {
+ need = 0;
+ }
+
+ mManager.setAmount(need + MINIMUM_BALANCE_AMOUNT);
+}
+
+
+// virtual
+BOOL LLFloaterBuyCurrencyUI::postBuild()
+{
+ mManager.prepare();
+
+ childSetAction("buy_btn", onClickBuy, this);
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("error_web", onClickErrorWeb, this);
+
+ updateUI();
+
+ return TRUE;
+}
+
+void LLFloaterBuyCurrencyUI::draw()
+{
+ if (mManager.process())
+ {
+ if (mManager.bought())
+ {
+ close();
+ return;
+ }
+
+ updateUI();
+ }
+
+ LLFloater::draw();
+}
+
+BOOL LLFloaterBuyCurrencyUI::canClose()
+{
+ return mManager.canCancel();
+}
+
+void LLFloaterBuyCurrencyUI::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+ delete this;
+}
+
+void LLFloaterBuyCurrencyUI::updateUI()
+{
+ bool hasError = mManager.hasError();
+ mManager.updateUI(!hasError && !mManager.buying());
+
+ // section zero: title area
+ {
+ childSetVisible("info_buying", false);
+ childSetVisible("info_cannot_buy", false);
+ childSetVisible("info_need_more", false);
+ if (hasError)
+ {
+ childSetVisible("info_cannot_buy", true);
+ }
+ else if (mHasTarget)
+ {
+ childSetVisible("info_need_more", true);
+ }
+ else
+ {
+ childSetVisible("info_buying", true);
+ }
+ }
+
+ // error section
+ if (hasError)
+ {
+ mChildren.setBadge("step_error", LLViewChildren::BADGE_ERROR);
+
+ LLTextBox* message = LLUICtrlFactory::getTextBoxByName(this, "error_message");
+ if (message)
+ {
+ message->setVisible(true);
+ message->setWrappedText(mManager.errorMessage());
+ }
+
+ childSetVisible("error_web", !mManager.errorURI().empty());
+ }
+ else
+ {
+ childHide("step_error");
+ childHide("error_message");
+ childHide("error_web");
+ }
+
+
+ // currency
+ childSetVisible("contacting", false);
+ childSetVisible("buy_action", false);
+ childSetVisible("buy_action_unknown", false);
+
+ if (!hasError)
+ {
+ mChildren.setBadge("step_1", LLViewChildren::BADGE_NOTE);
+
+ if (mManager.buying())
+ {
+ childSetVisible("contacting", true);
+ }
+ else
+ {
+ if (mHasTarget)
+ {
+ childSetVisible("buy_action", true);
+ childSetTextArg("buy_action", "[NAME]", mTargetName);
+ childSetTextArg("buy_action", "[PRICE]", llformat("%d",mTargetPrice));
+ }
+ else
+ {
+ childSetVisible("buy_action_unknown", true);
+ }
+ }
+
+ S32 balance = gStatusBar->getBalance();
+ childShow("balance_label");
+ childShow("balance_amount");
+ childSetTextArg("balance_amount", "[AMT]", llformat("%d", balance));
+
+ S32 buying = mManager.getAmount();
+ childShow("buying_label");
+ childShow("buying_amount");
+ childSetTextArg("buying_amount", "[AMT]", llformat("%d", buying));
+
+ S32 total = balance + buying;
+ childShow("total_label");
+ childShow("total_amount");
+ childSetTextArg("total_amount", "[AMT]", llformat("%d", total));
+
+ childSetVisible("purchase_warning_repurchase", false);
+ childSetVisible("purchase_warning_notenough", false);
+ if (mHasTarget)
+ {
+ if (total >= mTargetPrice)
+ {
+ childSetVisible("purchase_warning_repurchase", true);
+ }
+ else
+ {
+ childSetVisible("purchase_warning_notenough", true);
+ }
+ }
+ }
+ else
+ {
+ childHide("step_1");
+ childHide("balance_label");
+ childHide("balance_amount");
+ childHide("buying_label");
+ childHide("buying_amount");
+ childHide("total_label");
+ childHide("total_amount");
+ childHide("purchase_warning_repurchase");
+ childHide("purchase_warning_notenough");
+ }
+
+ childSetEnabled("buy_btn", mManager.canBuy());
+}
+
+// static
+void LLFloaterBuyCurrencyUI::onClickBuy(void* data)
+{
+ LLFloaterBuyCurrencyUI* self = LLFloaterBuyCurrencyUI::soleInstance(false);
+ if (self)
+ {
+ self->mManager.buy();
+ self->updateUI();
+ // JC: updateUI() doesn't get called again until progress is made
+ // with transaction processing, so the "Purchase" button would be
+ // left enabled for some time. Pre-emptively disable.
+ self->childSetEnabled("buy_btn", false);
+ }
+}
+
+// static
+void LLFloaterBuyCurrencyUI::onClickCancel(void* data)
+{
+ LLFloaterBuyCurrencyUI* self = LLFloaterBuyCurrencyUI::soleInstance(false);
+ if (self)
+ {
+ self->close();
+ }
+}
+
+// static
+void LLFloaterBuyCurrencyUI::onClickErrorWeb(void* data)
+{
+ LLFloaterBuyCurrencyUI* self = LLFloaterBuyCurrencyUI::soleInstance(false);
+ if (self)
+ {
+ LLWeb::loadURLExternal(self->mManager.errorURI());
+ self->close();
+ }
+}
+
+// static
+void LLFloaterBuyCurrency::buyCurrency()
+{
+ if (gHideLinks)
+ {
+ return;
+ }
+
+ LLFloaterBuyCurrencyUI* ui = LLFloaterBuyCurrencyUI::soleInstance(true);
+ ui->noTarget();
+ ui->updateUI();
+ ui->open();
+}
+
+void LLFloaterBuyCurrency::buyCurrency(const std::string& name, S32 price)
+{
+ if (gHideLinks)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = name.c_str();
+ args["[PRICE]"] = price;
+ gViewerWindow->alertXml("NotEnoughCurrency", args);
+ return;
+ }
+
+ LLFloaterBuyCurrencyUI* ui = LLFloaterBuyCurrencyUI::soleInstance(true);
+ ui->target(name, price);
+ ui->updateUI();
+ ui->open();
+}
+
diff --git a/indra/newview/llfloaterbuycurrency.h b/indra/newview/llfloaterbuycurrency.h
new file mode 100644
index 0000000000..aaa9a5328f
--- /dev/null
+++ b/indra/newview/llfloaterbuycurrency.h
@@ -0,0 +1,28 @@
+/**
+ * @file llfloaterbuycurrency.h
+ * @brief LLFloaterBuyCurrency class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERBUYCURRENCY_H
+#define LL_LLFLOATERBUYCURRENCY_H
+
+#include "stdtypes.h"
+
+class LLFloaterBuyCurrency
+{
+public:
+ static void buyCurrency();
+ static void buyCurrency(const std::string& name, S32 price);
+ /* name should be a noun phrase of the object or service being bought:
+ "That object costs"
+ "Trying to give"
+ "Uploading costs"
+ a space and the price will be appended
+ */
+};
+
+
+#endif
diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp
new file mode 100644
index 0000000000..6c8b7368a0
--- /dev/null
+++ b/indra/newview/llfloaterbuyland.cpp
@@ -0,0 +1,1342 @@
+/**
+ * @file llfloaterbuyland.cpp
+ * @brief LLFloaterBuyLand class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuyland.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcachename.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llconfirmationmanager.h"
+#include "llcurrencyuimanager.h"
+#include "llfloater.h"
+#include "llfloatertools.h"
+#include "llframetimer.h"
+#include "lliconctrl.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llparcel.h"
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "llviewchildren.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llweb.h"
+#include "llwindow.h"
+#include "llworld.h"
+#include "llxmlrpctransaction.h"
+#include "viewer.h"
+#include "roles_constants.h"
+
+// NOTE: This is duplicated in lldatamoney.cpp ...
+const F32 GROUP_LAND_BONUS_FACTOR = 1.1f;
+const F64 CURRENCY_ESTIMATE_FREQUENCY = 0.5;
+ // how long of a pause in typing a currency buy amount before an
+ // esimate is fetched from the server
+
+class LLFloaterBuyLandUI
+: public LLFloater
+{
+private:
+ LLFloaterBuyLandUI();
+ virtual ~LLFloaterBuyLandUI();
+
+ LLViewerRegion* mRegion;
+ LLParcel* mParcel;
+ bool mIsClaim;
+ bool mIsForGroup;
+
+ bool mCanBuy;
+ bool mCannotBuyIsError;
+ std::string mCannotBuyReason;
+ std::string mCannotBuyURI;
+
+ bool mBought;
+
+ // information about the agent
+ S32 mAgentCommittedTier;
+ S32 mAgentCashBalance;
+ bool mAgentHasNeverOwnedLand;
+
+ // information about the parcel
+ bool mParcelValid;
+ bool mParcelIsForSale;
+ bool mParcelIsFirstLand;
+ bool mParcelIsGroupLand;
+ S32 mParcelGroupContribution;
+ S32 mParcelPrice;
+ S32 mParcelActualArea;
+ S32 mParcelBillableArea;
+ S32 mParcelSupportedObjects;
+ bool mParcelSoldWithObjects;
+ std::string mParcelLocation;
+ LLUUID mParcelSnapshot;
+ std::string mParcelSellerName;
+
+ // user's choices
+ S32 mUserPlanChoice;
+
+ // from website
+ bool mSiteValid;
+ bool mSiteMembershipUpgrade;
+ std::string mSiteMembershipAction;
+ std::vector<std::string>
+ mSiteMembershipPlanIDs;
+ std::vector<std::string>
+ mSiteMembershipPlanNames;
+ bool mSiteLandUseUpgrade;
+ std::string mSiteLandUseAction;
+ std::string mSiteConfirm;
+
+ // values in current Preflight transaction... used to avoid extra
+ // preflights when the parcel manager goes update crazy
+ S32 mPreflightAskBillableArea;
+ S32 mPreflightAskCurrencyBuy;
+
+ LLViewChildren mChildren;
+ LLCurrencyUIManager mCurrency;
+
+ enum TransactionType
+ {
+ TransactionPreflight,
+ TransactionCurrency,
+ TransactionBuy
+ };
+ LLXMLRPCTransaction* mTransaction;
+ TransactionType mTransactionType;
+
+ LLViewerParcelMgr::ParcelBuyInfo* mParcelBuyInfo;
+
+ static LLFloaterBuyLandUI* sInstance;
+
+public:
+ static LLFloaterBuyLandUI* soleInstance(bool createIfNeeded);
+
+ void setForGroup(bool is_for_group);
+ void setParcel(LLViewerRegion* region, LLParcel* parcel);
+
+ void updateAgentInfo();
+ void updateParcelInfo();
+ void updateCovenantInfo();
+ static void onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data);
+ void updateCovenantText(const std::string& string, const LLUUID &asset_id);
+ void updateEstateName(const std::string& name);
+ void updateLastModified(const std::string& text);
+ void updateEstateOwnerName(const std::string& name);
+ void updateWebSiteInfo();
+ void finishWebSiteInfo();
+
+ void runWebSitePrep(const std::string& password);
+ void finishWebSitePrep();
+ void sendBuyLand();
+
+ void updateNames();
+
+ void refreshUI();
+
+ void startTransaction(TransactionType type, LLXMLRPCValue params);
+ bool checkTransaction();
+
+ void tellUserError(const std::string& message, const std::string& uri);
+
+ virtual BOOL postBuild();
+
+ void startBuyPreConfirm();
+ void startBuyPostConfirm(const std::string& password);
+
+ static void onClickBuy(void* data);
+ static void onClickCancel(void* data);
+ static void onClickErrorWeb(void* data);
+
+ virtual void draw();
+ virtual BOOL canClose();
+ virtual void onClose(bool app_quitting);
+
+private:
+ class SelectionObserver : public LLParcelObserver
+ {
+ public:
+ virtual void changed();
+ };
+};
+
+static void cacheNameUpdateRefreshesBuyLand(const LLUUID&,
+ const char*, const char*, BOOL, void* data)
+{
+ LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(false);
+ if (ui)
+ {
+ ui->updateNames();
+ }
+}
+
+// static
+void LLFloaterBuyLand::buyLand(
+ LLViewerRegion* region, LLParcel* parcel, bool is_for_group)
+{
+ if(is_for_group && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
+ {
+ gViewerWindow->alertXml("OnlyOfficerCanBuyLand");
+ return;
+ }
+
+ LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(true);
+ ui->setForGroup(is_for_group);
+ ui->setParcel(region, parcel);
+ ui->open();
+}
+
+// static
+void LLFloaterBuyLand::updateCovenantText(const std::string& string, const LLUUID &asset_id)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateCovenantText(string, asset_id);
+ }
+}
+
+// static
+void LLFloaterBuyLand::updateEstateName(const std::string& name)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateEstateName(name);
+ }
+}
+
+// static
+void LLFloaterBuyLand::updateLastModified(const std::string& text)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateLastModified(text);
+ }
+}
+
+// static
+void LLFloaterBuyLand::updateEstateOwnerName(const std::string& name)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateEstateOwnerName(name);
+ }
+}
+
+// static
+LLFloaterBuyLandUI* LLFloaterBuyLandUI::sInstance = NULL;
+
+// static
+LLFloaterBuyLandUI* LLFloaterBuyLandUI::soleInstance(bool createIfNeeded)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+ if (createIfNeeded)
+ {
+ delete sInstance;
+ sInstance = NULL;
+ }
+#endif
+ if (!sInstance && createIfNeeded)
+ {
+ sInstance = new LLFloaterBuyLandUI();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_buy_land.xml");
+ sInstance->center();
+
+ static bool observingCacheName = false;
+ if (!observingCacheName)
+ {
+ gCacheName->addObserver(cacheNameUpdateRefreshesBuyLand);
+ observingCacheName = true;
+ }
+
+ static SelectionObserver* parcelSelectionObserver = NULL;
+ if (!parcelSelectionObserver)
+ {
+ parcelSelectionObserver = new SelectionObserver;
+ gParcelMgr->addObserver(parcelSelectionObserver);
+ }
+ }
+
+ return sInstance;
+}
+
+
+#if LL_WINDOWS
+// passing 'this' during construction generates a warning. The callee
+// only uses the pointer to hold a reference to 'this' which is
+// already valid, so this call does the correct thing. Disable the
+// warning so that we can compile without generating a warning.
+#pragma warning(disable : 4355)
+#endif
+LLFloaterBuyLandUI::LLFloaterBuyLandUI()
+: LLFloater("Buy Land"),
+ mParcel(0),
+ mBought(false),
+ mParcelValid(false), mSiteValid(false),
+ mChildren(*this), mCurrency(*this), mTransaction(0),
+ mParcelBuyInfo(0)
+{
+}
+
+LLFloaterBuyLandUI::~LLFloaterBuyLandUI()
+{
+ delete mTransaction;
+
+ gParcelMgr->deleteParcelBuy(mParcelBuyInfo);
+
+ if (sInstance == this)
+ {
+ sInstance = NULL;
+ }
+}
+
+void LLFloaterBuyLandUI::SelectionObserver::changed()
+{
+ LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(false);
+ if (ui)
+ {
+ if (gParcelMgr->selectionEmpty())
+ {
+ ui->close();
+ }
+ else {
+ ui->setParcel(
+ gParcelMgr->getSelectionRegion(),
+ gParcelMgr->getSelectedParcel());
+ }
+ }
+}
+
+
+void LLFloaterBuyLandUI::updateAgentInfo()
+{
+ mAgentCommittedTier = gStatusBar->getSquareMetersCommitted();
+ mAgentCashBalance = gStatusBar->getBalance();
+ mAgentHasNeverOwnedLand = mAgentCommittedTier == 0;
+ // FIXME: this is an approximation, see SL-10728
+}
+
+void LLFloaterBuyLandUI::updateParcelInfo()
+{
+ mParcelValid = mParcel && mRegion;
+ mParcelIsForSale = false;
+ mParcelIsFirstLand = false;
+ mParcelIsGroupLand = false;
+ mParcelGroupContribution = 0;
+ mParcelPrice = 0;
+ mParcelActualArea = 0;
+ mParcelBillableArea = 0;
+ mParcelSupportedObjects = 0;
+ mParcelSoldWithObjects = false;
+ mParcelLocation = "";
+ mParcelSnapshot.setNull();
+ mParcelSellerName = "";
+
+ mCanBuy = false;
+ mCannotBuyIsError = false;
+
+ if (!mParcelValid)
+ {
+ mCannotBuyReason= childGetText("no_land_selected");
+ return;
+ }
+
+ if (gParcelMgr->getMultipleOwners())
+ {
+ mCannotBuyReason = childGetText("multiple_parcels_selected");
+ return;
+ }
+
+
+ const LLUUID& parcelOwner = mParcel->getOwnerID();
+
+ mIsClaim = mParcel->isPublic();
+ if (!mIsClaim)
+ {
+ mParcelActualArea = mParcel->getArea();
+ mParcelIsForSale = mParcel->getForSale();
+ mParcelIsFirstLand = mParcel->getReservedForNewbie();
+ mParcelIsGroupLand = mParcel->getIsGroupOwned();
+ mParcelPrice = mParcelIsForSale ? mParcel->getSalePrice() : 0;
+
+ if (mParcelIsGroupLand)
+ {
+ LLUUID group_id = mParcel->getGroupID();
+ mParcelGroupContribution = gAgent.getGroupContribution(group_id);
+ }
+ }
+ else
+ {
+ mParcelActualArea = gParcelMgr->getClaimableArea();
+ mParcelIsForSale = true;
+ mParcelPrice = mParcelActualArea * mParcel->getClaimPricePerMeter();
+ }
+
+ mParcelBillableArea =
+ llround(mRegion->getBillableFactor() * mParcelActualArea);
+
+ mParcelSupportedObjects = mParcel->getMaxPrimCapacity();
+ mParcelSoldWithObjects = mParcel->getSellWithObjects();
+
+ LLVector3 center = mParcel->getCenterpoint();
+ mParcelLocation = llformat("%s %d,%d",
+ mRegion->getName().c_str(),
+ (int)center[VX], (int)center[VY]
+ );
+
+ mParcelSnapshot = mParcel->getSnapshotID();
+
+ updateNames();
+
+ bool haveEnoughCash = mParcelPrice <= mAgentCashBalance;
+ S32 cashBuy = haveEnoughCash ? 0 : (mParcelPrice - mAgentCashBalance);
+ mCurrency.setAmount(cashBuy, true);
+ mCurrency.setZeroMessage(haveEnoughCash ? childGetText("none_needed") : "");
+
+ // checks that we can buy the land
+
+ if(mIsForGroup && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
+ {
+ mCannotBuyReason = childGetText("cant_buy_for_group");
+ return;
+ }
+
+ if (!mIsClaim)
+ {
+ const LLUUID& authorizedBuyer = mParcel->getAuthorizedBuyerID();
+ const LLUUID buyer = gAgent.getID();
+ const LLUUID newOwner = mIsForGroup ? gAgent.getGroupID() : buyer;
+
+ if (!mParcelIsForSale
+ || (mParcelPrice == 0 && authorizedBuyer.isNull()))
+ {
+
+ mCannotBuyReason = childGetText("parcel_not_for_sale");
+ return;
+ }
+
+ if (parcelOwner == newOwner)
+ {
+ if (mIsForGroup)
+ {
+ mCannotBuyReason = childGetText("group_already_owns");
+ }
+ else
+ {
+ mCannotBuyReason = childGetText("you_already_own");
+ }
+ return;
+ }
+
+ if (!authorizedBuyer.isNull() && buyer != authorizedBuyer)
+ {
+ mCannotBuyReason = childGetText("set_to_sell_to_other");
+ return;
+ }
+ }
+ else
+ {
+ if (mParcelActualArea == 0)
+ {
+ mCannotBuyReason = childGetText("no_public_land");
+ return;
+ }
+
+ if (gParcelMgr->hasOthersSelected())
+ {
+ // Policy: Must not have someone else's land selected
+ mCannotBuyReason = childGetText("not_owned_by_you");
+ return;
+ }
+ }
+
+ /*
+ if ((mRegion->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ && !gAgent.isGodlike())
+ {
+ mCannotBuyReason = llformat(
+ "The region %s does not allow transfer of land.",
+ mRegion->getName().c_str() );
+ return;
+ }
+ */
+
+ if (mParcel->getReservedForNewbie())
+ {
+ if (mIsForGroup)
+ {
+ mCannotBuyReason = childGetText("first_time_group");
+ return;
+ }
+
+ if (gStatusBar->getSquareMetersCommitted() > 0)
+ {
+ mCannotBuyReason == childGetText("first_time");
+ return;
+ }
+
+ // FIXME: There should be a check based on the database value
+ // indra.user.ever_owned_land, only that value never makes it
+ // to the viewer, see SL-10728
+ }
+
+ mCanBuy = true;
+}
+
+void LLFloaterBuyLandUI::updateCovenantInfo()
+{
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if(!region) return;
+
+ LLTextBox* region_name = (LLTextBox*)getChildByName("region_name_text");
+ if (region_name)
+ {
+ region_name->setText(region->getName());
+ }
+
+ LLTextBox* resellable_clause = (LLTextBox*)getChildByName("resellable_clause");
+ if (resellable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ {
+ resellable_clause->setText(childGetText("can_not_resell"));
+ }
+ else
+ {
+ resellable_clause->setText(childGetText("can_resell"));
+ }
+ }
+
+ LLTextBox* changeable_clause = (LLTextBox*)getChildByName("changeable_clause");
+ if (changeable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ changeable_clause->setText(childGetText("can_change"));
+ }
+ else
+ {
+ changeable_clause->setText(childGetText("can_not_change"));
+ }
+ }
+
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant");
+ if(check)
+ {
+ check->set(false);
+ check->setEnabled(true);
+ check->setCallbackUserData(this);
+ check->setCommitCallback(onChangeAgreeCovenant);
+ }
+
+ LLTextBox* box = (LLTextBox*)getChildByName("covenant_text");
+ if(box)
+ {
+ box->setVisible(FALSE);
+ }
+
+ // send EstateCovenantInfo message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("EstateCovenantRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLFloaterBuyLandUI::onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)user_data;
+ if(self)
+ {
+ self->refreshUI();
+ }
+}
+
+void LLFloaterBuyLandUI::updateCovenantText(const std::string &string, const LLUUID& asset_id)
+{
+ LLViewerTextEditor* editor = (LLViewerTextEditor*)getChildByName("covenant_editor");
+ if (editor)
+ {
+ editor->setHandleEditKeysDirectly(FALSE);
+ editor->setText(string);
+
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant");
+ LLTextBox* box = (LLTextBox*)getChildByName("covenant_text");
+ if(check && box)
+ {
+ if (asset_id.isNull())
+ {
+ check->set(true);
+ check->setEnabled(false);
+ refreshUI();
+
+ // remove the line stating that you must agree
+ box->setVisible(FALSE);
+ }
+ else
+ {
+ check->setEnabled(true);
+
+ // remove the line stating that you must agree
+ box->setVisible(TRUE);
+ }
+ }
+ }
+}
+
+void LLFloaterBuyLandUI::updateEstateName(const std::string& name)
+{
+ LLTextBox* box = (LLTextBox*)getChildByName("estate_name_text");
+ if (box) box->setText(name);
+}
+
+void LLFloaterBuyLandUI::updateLastModified(const std::string& text)
+{
+ LLTextBox* editor = (LLTextBox*)getChildByName("covenant_timestamp_text");
+ if (editor) editor->setText(text);
+}
+
+void LLFloaterBuyLandUI::updateEstateOwnerName(const std::string& name)
+{
+ LLTextBox* box = (LLTextBox*)getChildByName("estate_owner_text");
+ if (box) box->setText(name);
+}
+
+void LLFloaterBuyLandUI::updateWebSiteInfo()
+{
+ S32 askBillableArea = mIsForGroup ? 0 : mParcelBillableArea;
+ S32 askCurrencyBuy = mCurrency.getAmount();
+
+ if (mTransaction && mTransactionType == TransactionPreflight
+ && mPreflightAskBillableArea == askBillableArea
+ && mPreflightAskCurrencyBuy == askCurrencyBuy)
+ {
+ return;
+ }
+
+ mPreflightAskBillableArea = askBillableArea;
+ mPreflightAskCurrencyBuy = askCurrencyBuy;
+
+#if 0
+ // enable this code if you want the details to blank while we're talking
+ // to the web site... it's kind of jarring
+ mSiteValid = false;
+ mSiteMembershipUpgrade = false;
+ mSiteMembershipAction = "(waiting)";
+ mSiteMembershipPlanIDs.clear();
+ mSiteMembershipPlanNames.clear();
+ mSiteLandUseUpgrade = false;
+ mSiteLandUseAction = "(waiting)";
+ mSiteCurrencyEstimated = false;
+ mSiteCurrencyEstimatedCost = 0;
+#endif
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId", gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendInt("billableArea", mPreflightAskBillableArea);
+ keywordArgs.appendInt("currencyBuy", mPreflightAskCurrencyBuy);
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionPreflight, params);
+}
+
+void LLFloaterBuyLandUI::finishWebSiteInfo()
+{
+
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ mSiteValid = result["success"].asBool();
+ if (!mSiteValid)
+ {
+ tellUserError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ return;
+ }
+
+ LLXMLRPCValue membership = result["membership"];
+ mSiteMembershipUpgrade = membership["upgrade"].asBool();
+ mSiteMembershipAction = membership["action"].asString();
+ mSiteMembershipPlanIDs.clear();
+ mSiteMembershipPlanNames.clear();
+ LLXMLRPCValue levels = membership["levels"];
+ for (LLXMLRPCValue level = levels.rewind();
+ level.isValid();
+ level = levels.next())
+ {
+ mSiteMembershipPlanIDs.push_back(level["id"].asString());
+ mSiteMembershipPlanNames.push_back(level["description"].asString());
+ }
+ mUserPlanChoice = 0;
+
+ LLXMLRPCValue landUse = result["landUse"];
+ mSiteLandUseUpgrade = landUse["upgrade"].asBool();
+ mSiteLandUseAction = landUse["action"].asString();
+
+ LLXMLRPCValue currency = result["currency"];
+ mCurrency.setEstimate(currency["estimatedCost"].asInt());
+
+ mSiteConfirm = result["confirm"].asString();
+}
+
+void LLFloaterBuyLandUI::runWebSitePrep(const std::string& password)
+{
+ if (!mCanBuy)
+ {
+ return;
+ }
+
+ BOOL remove_contribution = childGetValue("remove_contribution").asBoolean();
+ mParcelBuyInfo = gParcelMgr->setupParcelBuy(gAgent.getID(), gAgent.getSessionID(),
+ gAgent.getGroupID(), mIsForGroup, mIsClaim, remove_contribution);
+
+ if (!mSiteMembershipUpgrade
+ && !mSiteLandUseUpgrade
+ && mCurrency.getAmount() == 0
+ && mSiteConfirm != "password")
+ {
+ sendBuyLand();
+ return;
+ }
+
+
+ std::string newLevel = "noChange";
+
+ if (mSiteMembershipUpgrade)
+ {
+ LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level");
+ if (levels)
+ {
+ mUserPlanChoice = levels->getCurrentIndex();
+ newLevel = mSiteMembershipPlanIDs[mUserPlanChoice];
+ }
+ }
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId", gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendString("levelId", newLevel);
+ keywordArgs.appendInt("billableArea",
+ mIsForGroup ? 0 : mParcelBillableArea);
+ keywordArgs.appendInt("currencyBuy", mCurrency.getAmount());
+ keywordArgs.appendInt("estimatedCost", mCurrency.getEstimate());
+ keywordArgs.appendString("confirm", mSiteConfirm);
+ if (!password.empty())
+ {
+ keywordArgs.appendString("password", password);
+ }
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionBuy, params);
+}
+
+void LLFloaterBuyLandUI::finishWebSitePrep()
+{
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ bool success = result["success"].asBool();
+ if (!success)
+ {
+ tellUserError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ return;
+ }
+
+ sendBuyLand();
+}
+
+void LLFloaterBuyLandUI::sendBuyLand()
+{
+ gParcelMgr->sendParcelBuy(mParcelBuyInfo);
+ gParcelMgr->deleteParcelBuy(mParcelBuyInfo);
+
+ mBought = true;
+}
+
+void LLFloaterBuyLandUI::updateNames()
+{
+ if (!mParcelValid)
+ {
+ mParcelSellerName = "";
+ return;
+ }
+
+ if (mIsClaim)
+ {
+ mParcelSellerName = "Linden Lab";
+ }
+ else if (mParcel->getIsGroupOwned())
+ {
+ char groupName[DB_LAST_NAME_BUF_SIZE];
+
+ gCacheName->getGroupName(mParcel->getGroupID(), &groupName[0]);
+ mParcelSellerName = groupName;
+ }
+ else
+ {
+ char firstName[DB_LAST_NAME_BUF_SIZE];
+ char lastName[DB_LAST_NAME_BUF_SIZE];
+
+ gCacheName->getName(mParcel->getOwnerID(), firstName, lastName);
+ mParcelSellerName = llformat("%s %s", firstName, lastName);
+ }
+}
+
+
+void LLFloaterBuyLandUI::startTransaction(TransactionType type,
+ LLXMLRPCValue params)
+{
+ delete mTransaction;
+ mTransaction = NULL;
+
+ mTransactionType = type;
+
+ // Select a URI and method appropriate for the transaction type.
+ static std::string transaction_uri;
+ if (transaction_uri.empty())
+ {
+ transaction_uri = getHelperURI() + "landtool.php";
+ }
+
+ const char* method;
+ switch (mTransactionType)
+ {
+ case TransactionPreflight:
+ method = "preflightBuyLandPrep";
+ break;
+ case TransactionBuy:
+ method = "buyLandPrep";
+ break;
+ default:
+ llwarns << "LLFloaterBuyLandUI: Unknown transaction type!" << llendl;
+ return;
+ }
+
+ mTransaction = new LLXMLRPCTransaction(
+ transaction_uri,
+ method,
+ params,
+ false /* don't use gzip */
+ );
+}
+
+bool LLFloaterBuyLandUI::checkTransaction()
+{
+ if (!mTransaction)
+ {
+ return false;
+ }
+
+ if (!mTransaction->process())
+ {
+ return false;
+ }
+
+ if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete)
+ {
+ tellUserError(mTransaction->statusMessage(), mTransaction->statusURI());
+ }
+ else {
+ switch (mTransactionType)
+ {
+ case TransactionPreflight: finishWebSiteInfo(); break;
+ case TransactionBuy: finishWebSitePrep(); break;
+ default: ;
+ }
+ }
+
+ delete mTransaction;
+ mTransaction = NULL;
+
+ return true;
+}
+
+void LLFloaterBuyLandUI::tellUserError(
+ const std::string& message, const std::string& uri)
+{
+ mCanBuy = false;
+ mCannotBuyIsError = true;
+ mCannotBuyReason = childGetText("fetching_error");
+ mCannotBuyReason += message;
+ mCannotBuyURI = uri;
+}
+
+
+// virtual
+BOOL LLFloaterBuyLandUI::postBuild()
+{
+ mCurrency.prepare();
+
+ childSetAction("buy_btn", onClickBuy, this);
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("error_web", onClickErrorWeb, this);
+
+ return TRUE;
+}
+
+void LLFloaterBuyLandUI::setParcel(LLViewerRegion* region, LLParcel* parcel)
+{
+ if (mTransaction && mTransactionType == TransactionBuy)
+ {
+ // the user is buying, don't change the selection
+ return;
+ }
+
+ mRegion = region;
+ mParcel = parcel;
+
+ updateAgentInfo();
+ updateParcelInfo();
+ updateCovenantInfo();
+ if (mCanBuy)
+ {
+ updateWebSiteInfo();
+ }
+ refreshUI();
+}
+
+void LLFloaterBuyLandUI::setForGroup(bool forGroup)
+{
+ mIsForGroup = forGroup;
+}
+
+void LLFloaterBuyLandUI::draw()
+{
+ LLFloater::draw();
+
+ bool needsUpdate = false;
+ needsUpdate |= checkTransaction();
+ needsUpdate |= mCurrency.process();
+
+ if (mBought)
+ {
+ close();
+ }
+ else if (needsUpdate)
+ {
+ if (mCanBuy && mCurrency.hasError())
+ {
+ tellUserError(mCurrency.errorMessage(), mCurrency.errorURI());
+ }
+
+ refreshUI();
+ }
+}
+
+BOOL LLFloaterBuyLandUI::canClose()
+{
+ return (mTransaction ? FALSE : TRUE) && mCurrency.canCancel();
+}
+
+void LLFloaterBuyLandUI::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+ delete this;
+}
+
+
+void LLFloaterBuyLandUI::refreshUI()
+{
+ // section zero: title area
+ {
+ LLTextureCtrl* snapshot = LLViewerUICtrlFactory::getTexturePickerByName(this, "info_image");
+ if (snapshot)
+ {
+ snapshot->setImageAssetID(
+ mParcelValid ? mParcelSnapshot : LLUUID::null);
+ }
+
+
+
+ if (mParcelValid)
+ {
+ childSetText("info_parcel", mParcelLocation);
+
+
+ childSetTextArg("meters_supports_object", "[AMOUNT]", llformat("%d", mParcelActualArea));
+ childSetTextArg("meters_supports_object", "[AMOUNT2]", llformat("%d", mParcelSupportedObjects));
+
+ childSetText("info_size", childGetText("meters_supports_object"));
+
+
+ childSetText("info_price",
+ llformat(
+ "L$ %d%s",
+ mParcelPrice,
+ mParcelSoldWithObjects
+ ? "\nsold with objects"
+ : ""));
+ childSetVisible("info_price", mParcelIsForSale);
+ }
+ else
+ {
+ childSetText("info_parcel", "(no parcel selected)");
+ childSetText("info_size", "");
+ childSetText("info_price", "");
+ }
+
+ childSetText("info_action",
+ mCanBuy
+ ?
+ mIsForGroup
+ ? childGetText("buying_for_group")//"Buying land for group:"
+ : childGetText("buying_will")//"Buying this land will:"
+ :
+ mCannotBuyIsError
+ ? childGetText("cannot_buy_now")//"Cannot buy now:"
+ : childGetText("not_for_sale")//"Not for sale:"
+
+ );
+ }
+
+ bool showingError = !mCanBuy || !mSiteValid;
+
+ // error section
+ if (showingError)
+ {
+ mChildren.setBadge("step_error",
+ mCannotBuyIsError
+ ? LLViewChildren::BADGE_ERROR
+ : LLViewChildren::BADGE_WARN);
+
+ LLTextBox* message = LLUICtrlFactory::getTextBoxByName(this, "error_message");
+ if (message)
+ {
+ message->setVisible(true);
+ message->setWrappedText(
+ !mCanBuy ? mCannotBuyReason : "(waiting for data)"
+ );
+ }
+
+ childSetVisible("error_web",
+ mCannotBuyIsError && !mCannotBuyURI.empty());
+ }
+ else
+ {
+ childHide("step_error");
+ childHide("error_message");
+ childHide("error_web");
+ }
+
+
+ // section one: account
+ if (!showingError)
+ {
+ mChildren.setBadge("step_1",
+ mSiteMembershipUpgrade
+ ? LLViewChildren::BADGE_NOTE
+ : LLViewChildren::BADGE_OK);
+ childSetText("account_action", mSiteMembershipAction);
+ childSetText("account_reason",
+ mSiteMembershipUpgrade
+ ? childGetText("must_upgrade")
+ : childGetText("cant_own_land")
+ );
+
+ LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level");
+ if (levels)
+ {
+ levels->setVisible(mSiteMembershipUpgrade);
+
+ levels->removeall();
+ for(std::vector<std::string>::const_iterator i
+ = mSiteMembershipPlanNames.begin();
+ i != mSiteMembershipPlanNames.end();
+ ++i)
+ {
+ levels->add(*i);
+ }
+
+ levels->setCurrentByIndex(mUserPlanChoice);
+ }
+
+ childShow("step_1");
+ childShow("account_action");
+ childShow("account_reason");
+ }
+ else
+ {
+ childHide("step_1");
+ childHide("account_action");
+ childHide("account_reason");
+ childHide("account_level");
+ }
+
+ // section two: land use fees
+ if (!showingError)
+ {
+ mChildren.setBadge("step_2",
+ mSiteLandUseUpgrade
+ ? LLViewChildren::BADGE_NOTE
+ : LLViewChildren::BADGE_OK);
+ childSetText("land_use_action", mSiteLandUseAction);
+
+ std::string message;
+
+ if (mIsForGroup)
+ {
+ childSetTextArg("insufficient_land_credits", "[GROUP]", gAgent.mGroupName);
+
+
+ message += childGetText("insufficient_land_credits");
+
+ }
+ else if (mAgentHasNeverOwnedLand)
+ {
+ if (mParcelIsFirstLand)
+ {
+ message += childGetText("first_purchase");
+ }
+ else
+ {
+ message += childGetText("first_time_but_not_first_land");
+ }
+ }
+ else
+ {
+
+ childSetTextArg("land_holdings", "[BUYER]", llformat("%d", mAgentCommittedTier));
+ message += childGetText("land_holdings");
+
+ }
+
+ if (!mParcelValid)
+ {
+ message += "(no parcel selected)";
+ }
+ else if (mParcelBillableArea == mParcelActualArea)
+ {
+ childSetTextArg("parcel_meters", "[AMOUNT]", llformat("%d", mParcelActualArea));
+ message += childGetText("parcel_meters");
+ }
+ else
+ {
+
+ if (mParcelBillableArea > mParcelActualArea)
+ {
+ childSetTextArg("premium_land", "[AMOUNT]", llformat("%d", mParcelBillableArea) );
+ message += childGetText("premium_land");
+ }
+ else
+ {
+ childSetTextArg("discounted_land", "[AMOUNT]", llformat("%d", mParcelBillableArea) );
+ message += childGetText("discounted_land");
+ }
+ }
+
+ childSetWrappedText("land_use_reason", message);
+
+ childShow("step_2");
+ childShow("land_use_action");
+ childShow("land_use_reason");
+ }
+ else
+ {
+ childHide("step_2");
+ childHide("land_use_action");
+ childHide("land_use_reason");
+ }
+
+ // section three: purchase & currency
+ S32 finalBalance = mAgentCashBalance + mCurrency.getAmount() - mParcelPrice;
+ bool willHaveEnough = finalBalance >= 0;
+ bool haveEnough = mAgentCashBalance >= mParcelPrice;
+ S32 minContribution = llceil((F32)mParcelBillableArea / GROUP_LAND_BONUS_FACTOR);
+ bool groupContributionEnough = mParcelGroupContribution >= minContribution;
+
+ mCurrency.updateUI(!showingError && !haveEnough);
+
+ if (!showingError)
+ {
+ mChildren.setBadge("step_3",
+ !willHaveEnough
+ ? LLViewChildren::BADGE_WARN
+ : mCurrency.getAmount() > 0
+ ? LLViewChildren::BADGE_NOTE
+ : LLViewChildren::BADGE_OK);
+
+ childSetText("purchase_action",
+ llformat(
+ "Pay L$ %d to %s for this land",
+ mParcelPrice,
+ mParcelSellerName.c_str()
+ ));
+ childSetVisible("purchase_action", mParcelValid);
+
+ std::string reasonString;
+
+ if (haveEnough)
+ {
+
+ childSetTextArg("have_enough_lindens", "[AMOUNT]", llformat("%d", mAgentCashBalance));
+ childSetText("currency_reason", childGetText("have_enough_lindens"));
+ }
+ else
+ {
+ childSetTextArg("not_enough_lindens", "[AMOUNT]", llformat("%d", mAgentCashBalance));
+ childSetTextArg("not_enough_lindens", "[AMOUNT2]", llformat("%d", mParcelPrice - mAgentCashBalance));
+
+ childSetText("currency_reason", childGetText("not_enough_lindens"));
+
+ childSetTextArg("currency_est", "[AMOUNT2]", llformat("%#.2f", mCurrency.getEstimate() / 100.0));
+
+ }
+
+ if (willHaveEnough)
+ {
+
+ childSetTextArg("balance_left", "[AMOUNT]", llformat("%d", finalBalance));
+
+ childSetText("currency_balance", childGetText("balance_left"));
+
+ }
+ else
+ {
+
+ childSetTextArg("balance_needed", "[AMOUNT]", llformat("%d", mParcelPrice - mAgentCashBalance));
+
+ childSetText("currency_balance", childGetText("balance_needed"));
+
+ }
+
+ //remove_contribution not in XML - ?!
+ childSetValue("remove_contribution", LLSD(groupContributionEnough));
+ childSetEnabled("remove_contribution", groupContributionEnough);
+ bool showRemoveContribution = mParcelIsGroupLand
+ && (mParcelGroupContribution > 0);
+ childSetText("remove_contribution",
+ llformat("Remove %d square meters of contribution from group",
+ minContribution));
+ childSetVisible("remove_contribution", showRemoveContribution);
+
+ childShow("step_3");
+ childShow("purchase_action");
+ childShow("currency_reason");
+ childShow("currency_balance");
+ }
+ else
+ {
+ childHide("step_3");
+ childHide("purchase_action");
+ childHide("currency_reason");
+ childHide("currency_balance");
+ childHide("remove_group_donation");
+ }
+
+
+ bool agrees_to_covenant = false;
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant");
+ if (check)
+ {
+ agrees_to_covenant = check->get();
+ }
+
+ childSetEnabled("buy_btn",
+ mCanBuy && mSiteValid && willHaveEnough && !mTransaction && agrees_to_covenant);
+}
+
+void LLFloaterBuyLandUI::startBuyPreConfirm()
+{
+ std::string action;
+
+ if (mSiteMembershipUpgrade)
+ {
+ action += mSiteMembershipAction;
+ action += "\n";
+
+ LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level");
+ if (levels)
+ {
+ action += " * ";
+ action += mSiteMembershipPlanNames[levels->getCurrentIndex()];
+ action += "\n";
+ }
+ }
+ if (mSiteLandUseUpgrade)
+ {
+ action += mSiteLandUseAction;
+ action += "\n";
+ }
+ if (mCurrency.getAmount() > 0)
+ {
+
+ childSetTextArg("buy_for_US", "[AMOUNT]", llformat("%d", mCurrency.getAmount()));
+ childSetTextArg("buy_for_US", "[AMOUNT2]", llformat("%#.2f", mCurrency.getEstimate() / 100.0));
+
+ action += childGetText("buy_for_US");
+ }
+
+ childSetTextArg("pay_to_for_land", "[AMOUNT]", llformat("%d", mParcelPrice));
+ childSetTextArg("pay_to_for_land", "[SELLER]", mParcelSellerName.c_str());
+
+ action += childGetText("pay_to_for_land");
+
+
+ LLConfirmationManager::confirm(mSiteConfirm,
+ action,
+ *this,
+ &LLFloaterBuyLandUI::startBuyPostConfirm);
+}
+
+void LLFloaterBuyLandUI::startBuyPostConfirm(const std::string& password)
+{
+ runWebSitePrep(password);
+
+ mCanBuy = false;
+ mCannotBuyReason = childGetText("processing");
+ refreshUI();
+}
+
+
+// static
+void LLFloaterBuyLandUI::onClickBuy(void* data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data;
+ self->startBuyPreConfirm();
+}
+
+// static
+void LLFloaterBuyLandUI::onClickCancel(void* data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data;
+ self->close();
+}
+
+// static
+void LLFloaterBuyLandUI::onClickErrorWeb(void* data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data;
+ LLWeb::loadURLExternal(self->mCannotBuyURI);
+ self->close();
+}
+
diff --git a/indra/newview/llfloaterbuyland.h b/indra/newview/llfloaterbuyland.h
new file mode 100644
index 0000000000..092c95802a
--- /dev/null
+++ b/indra/newview/llfloaterbuyland.h
@@ -0,0 +1,28 @@
+/**
+ * @file llfloaterbuyland.h
+ * @brief LLFloaterBuyLand class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERBUYLAND_H
+#define LL_LLFLOATERBUYLAND_H
+
+class LLParcel;
+class LLViewerRegion;
+class LLViewerTextEditor;
+
+class LLFloaterBuyLand
+{
+public:
+ static void buyLand(LLViewerRegion* region,
+ LLParcel* parcel,
+ bool is_for_group);
+ static void updateCovenantText(const std::string& string, const LLUUID& asset_id);
+ static void updateEstateName(const std::string& name);
+ static void updateLastModified(const std::string& text);
+ static void updateEstateOwnerName(const std::string& name);
+};
+
+#endif
diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp
new file mode 100644
index 0000000000..3d8912d001
--- /dev/null
+++ b/indra/newview/llfloaterchat.cpp
@@ -0,0 +1,420 @@
+/**
+ * @file llfloaterchat.cpp
+ * @brief LLFloaterChat class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Actually the "Chat History" floater.
+ * Should be llfloaterchathistory, not llfloaterchat.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterchat.h"
+#include "llfloaterscriptdebug.h"
+
+#include "llchat.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+
+// project include
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llconsole.h"
+#include "llfloatermute.h"
+#include "llkeyboard.h"
+//#include "lllineeditor.h"
+#include "llmutelist.h"
+//#include "llresizehandle.h"
+#include "llstatusbar.h"
+#include "llviewertexteditor.h"
+#include "llviewergesture.h" // for triggering gestures
+#include "llviewermessage.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "llchatbar.h"
+#include "lllogchat.h"
+#include "lltexteditor.h"
+#include "llfloaterhtml.h"
+#include "llweb.h"
+
+//
+// Constants
+//
+const char FLOATER_TITLE[] = "Chat History";
+const F32 INSTANT_MSG_SIZE = 8.0f;
+const F32 CHAT_MSG_SIZE = 8.0f;
+const LLColor4 INSTANT_MSG_COLOR(1, 1, 1, 1);
+const LLColor4 MUTED_MSG_COLOR(0.5f, 0.5f, 0.5f, 1.f);
+const S32 MAX_CHATTER_COUNT = 16;
+
+//
+// Global statics
+//
+LLFloaterChat* gFloaterChat = NULL;
+
+LLColor4 get_text_color(const LLChat& chat);
+
+//
+// Member Functions
+//
+LLFloaterChat::LLFloaterChat()
+: LLFloater("chat floater", "FloaterChatRect", FLOATER_TITLE,
+ RESIZE_YES, 440, 100, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_chat_history.xml");
+
+ childSetAction("Mute resident",onClickMute,this);
+ childSetAction("Chat", onClickChat, this);
+ childSetCommitCallback("chatter combobox",onCommitUserSelect,this);
+ childSetCommitCallback("show mutes",onClickToggleShowMute,this); //show mutes
+ childSetVisible("Chat History Editor with mute",FALSE);
+ setDefaultBtn("Chat");
+}
+
+LLFloaterChat::~LLFloaterChat()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+void LLFloaterChat::setVisible(BOOL visible)
+{
+ LLFloater::setVisible( visible );
+
+ gSavedSettings.setBOOL("ShowChatHistory", visible);
+
+ // Hide the chat overlay when our history is visible.
+ gConsole->setVisible( !visible );
+}
+
+
+// public virtual
+void LLFloaterChat::onClose(bool app_quitting)
+{
+ LLFloater::setVisible( FALSE );
+
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowChatHistory", FALSE);
+ }
+
+ // Hide the chat overlay when our history is visible.
+ gConsole->setVisible( TRUE );
+}
+
+
+// public
+void LLFloaterChat::show()
+{
+ open();
+}
+
+void add_timestamped_line(LLViewerTextEditor* edit, const LLString& line, const LLColor4& color)
+{
+ bool prepend_newline = true;
+ if (gSavedSettings.getBOOL("ChatShowTimestamps"))
+ {
+ edit->appendTime(prepend_newline);
+ prepend_newline = false;
+ }
+ edit->appendColoredText(line, false, prepend_newline, color);
+}
+
+// static
+void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
+{
+ if ( gSavedPerAccountSettings.getBOOL("LogChat") && log_to_file)
+ {
+ LLLogChat::saveHistory("chat",chat.mText);
+ }
+
+ LLColor4 color = get_text_color(chat);
+
+ if (!log_to_file) color = LLColor4::grey; //Recap from log file.
+
+ if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
+ {
+ LLFloaterScriptDebug::addScriptLine(chat.mText,
+ chat.mFromName,
+ color,
+ chat.mFromID);
+ if (!gSavedSettings.getBOOL("ScriptErrorsAsChat"))
+ {
+ return;
+ }
+ }
+
+ // could flash the chat button in the status bar here. JC
+ if (!gFloaterChat) return;
+
+ LLViewerTextEditor* HistoryEditor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
+ LLViewerTextEditor* HistoryEditorWithMute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
+
+ HistoryEditor->setParseHTML(TRUE);
+ HistoryEditorWithMute->setParseHTML(TRUE);
+
+ if (!chat.mMuted)
+ {
+ add_timestamped_line(HistoryEditor, chat.mText, color);
+ add_timestamped_line(HistoryEditorWithMute, chat.mText, color);
+ }
+ else
+ {
+ // desaturate muted chat
+ LLColor4 muted_color = lerp(color, LLColor4::grey, 0.5f);
+ add_timestamped_line(HistoryEditorWithMute, chat.mText, color);
+ }
+
+ if (!chat.mMuted
+ && chat.mSourceType != CHAT_SOURCE_SYSTEM
+ && chat.mFromID.notNull()
+ && chat.mFromID != gAgent.getID())
+ {
+
+ LLComboBox* ChatterCombo = LLUICtrlFactory::getComboBoxByName(gFloaterChat,"chatter combobox");
+
+ if(!ChatterCombo)
+ return;
+
+ if (!ChatterCombo->setCurrentByID(chat.mFromID))
+ {
+ // if we have too many items...
+ if (ChatterCombo->getItemCount() >= MAX_CHATTER_COUNT)
+ {
+ ChatterCombo->remove(0);
+ }
+
+ LLMute mute(chat.mFromID, chat.mFromName);
+ if (chat.mSourceType == CHAT_SOURCE_OBJECT)
+ {
+ mute.mType = LLMute::OBJECT;
+ }
+ else if (chat.mSourceType == CHAT_SOURCE_AGENT)
+ {
+ mute.mType = LLMute::AGENT;
+ }
+ LLString item = mute.getDisplayName();
+ ChatterCombo->add(item, chat.mFromID);
+ ChatterCombo->setCurrentByIndex(ChatterCombo->getItemCount() - 1);
+ gFloaterChat->childSetEnabled("Mute resident",TRUE);
+ }
+ }
+}
+
+// static
+void LLFloaterChat::setHistoryCursorAndScrollToEnd()
+{
+ if (gFloaterChat)
+ {
+ LLViewerTextEditor* HistoryEditor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
+ LLViewerTextEditor* HistoryEditorWithMute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
+
+ HistoryEditor->setCursorAndScrollToEnd();
+ HistoryEditorWithMute->setCursorAndScrollToEnd();
+ }
+}
+
+
+// static
+void LLFloaterChat::toggle(void*)
+{
+ if (gFloaterChat->getVisible())
+ {
+ gFloaterChat->close();
+ }
+ else
+ {
+ gFloaterChat->show();
+ }
+}
+
+// static
+BOOL LLFloaterChat::visible(void*)
+{
+ return (gFloaterChat && gFloaterChat->getVisible());
+}
+
+//static
+void LLFloaterChat::onClickMute(void *data)
+{
+ LLFloaterChat* self = (LLFloaterChat*)data;
+
+ LLComboBox* ChatterCombo = LLUICtrlFactory::getComboBoxByName(self,"chatter combobox");
+
+ const LLString& name = ChatterCombo->getSimple();
+ LLUUID id = ChatterCombo->getCurrentID();
+
+ if (name.empty()) return;
+
+ LLMute mute(id);
+ mute.setFromDisplayName(name);
+ gMuteListp->add(mute);
+
+ if (gFloaterMute)
+ {
+ gFloaterMute->show();
+ }
+}
+
+//static
+void LLFloaterChat::onClickChat(void*)
+{
+ // we need this function as a level of indirection because otherwise startChat would
+ // cast the data pointer to a character string, and dump garbage in the chat
+ LLChatBar::startChat(NULL);
+}
+
+//static
+void LLFloaterChat::onCommitUserSelect(LLUICtrl* caller, void* data)
+{
+ LLFloaterChat* floater = (LLFloaterChat*)data;
+ LLComboBox* combo = (LLComboBox*)caller;
+
+ if (combo->getCurrentIndex() == -1)
+ {
+ floater->childSetEnabled("Mute resident",FALSE);
+ }
+ else
+ {
+ floater->childSetEnabled("Mute resident",TRUE);
+ }
+}
+
+//static
+void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
+{
+ LLFloaterChat* floater = (LLFloaterChat*)data;
+
+
+ //LLCheckBoxCtrl*
+ BOOL show_mute = LLUICtrlFactory::getCheckBoxByName(floater,"show mutes")->get();
+ LLViewerTextEditor* HistoryEditor = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor");
+ LLViewerTextEditor* HistoryEditorWithMute = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor with mute");
+
+ if (!HistoryEditor || !HistoryEditorWithMute)
+ return;
+
+ //BOOL show_mute = floater->mShowMuteCheckBox->get();
+ if (show_mute)
+ {
+ HistoryEditor->setVisible(FALSE);
+ HistoryEditorWithMute->setVisible(TRUE);
+ HistoryEditorWithMute->setCursorAndScrollToEnd();
+ }
+ else
+ {
+ HistoryEditor->setVisible(TRUE);
+ HistoryEditorWithMute->setVisible(FALSE);
+ HistoryEditor->setCursorAndScrollToEnd();
+ }
+}
+
+// Put a line of chat in all the right places
+void LLFloaterChat::addChat(const LLChat& chat,
+ BOOL from_instant_message,
+ BOOL local_agent)
+{
+ LLColor4 text_color = get_text_color(chat);
+
+ BOOL invisible_script_debug_chat =
+ chat.mChatType == CHAT_TYPE_DEBUG_MSG
+ && !gSavedSettings.getBOOL("ScriptErrorsAsChat");
+
+ if (!invisible_script_debug_chat
+ && !chat.mMuted
+ && gConsole
+ && !local_agent)
+ {
+ F32 size = CHAT_MSG_SIZE;
+ if(from_instant_message)
+ {
+ text_color = INSTANT_MSG_COLOR;
+ size = INSTANT_MSG_SIZE;
+ }
+ gConsole->addLine(chat.mText, size, text_color);
+ }
+
+ if( !from_instant_message || gSavedSettings.getBOOL("IMInChatHistory") )
+ {
+ addChatHistory(chat);
+ }
+}
+
+LLColor4 get_text_color(const LLChat& chat)
+{
+ LLColor4 text_color;
+
+ if(chat.mMuted)
+ {
+ text_color.setVec(0.8f, 0.8f, 0.8f, 1.f);
+ }
+ else
+ {
+ switch(chat.mSourceType)
+ {
+ case CHAT_SOURCE_SYSTEM:
+ text_color = gSavedSettings.getColor4("SystemChatColor");
+ break;
+ case CHAT_SOURCE_AGENT:
+ if (chat.mFromID.isNull())
+ {
+ text_color = gSavedSettings.getColor4("SystemChatColor");
+ }
+ else
+ {
+ text_color = gSavedSettings.getColor4("AgentChatColor");
+ }
+ break;
+ case CHAT_SOURCE_OBJECT:
+ if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
+ {
+ text_color = gSavedSettings.getColor4("ScriptErrorColor");
+ }
+ else
+ {
+ text_color = gSavedSettings.getColor4("ObjectChatColor");
+ }
+ break;
+ default:
+ text_color.setToWhite();
+ }
+
+ if (!chat.mPosAgent.isExactlyZero())
+ {
+ LLVector3 pos_agent = gAgent.getPositionAgent();
+ F32 distance = dist_vec(pos_agent, chat.mPosAgent);
+ if (distance > gAgent.getNearChatRadius())
+ {
+ // diminish far-off chat
+ text_color.mV[VALPHA] = 0.8f;
+ }
+ }
+ }
+
+ return text_color;
+}
+
+//static
+void LLFloaterChat::loadHistory()
+{
+ LLLogChat::loadHistory("chat", &chatFromLogFile, (void *)gFloaterChat);
+}
+
+//static
+void LLFloaterChat::chatFromLogFile(LLString line, void* userdata)
+{
+ LLChat chat;
+
+ chat.mText = line;
+ addChatHistory(chat, FALSE);
+}
diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h
new file mode 100644
index 0000000000..bf93afb455
--- /dev/null
+++ b/indra/newview/llfloaterchat.h
@@ -0,0 +1,61 @@
+/**
+ * @file llfloaterchat.h
+ * @brief LLFloaterChat class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Actually the "Chat History" floater.
+ * Should be llfloaterchathistory, not llfloaterchat.
+ */
+
+#ifndef LL_LLFLOATERCHAT_H
+#define LL_LLFLOATERCHAT_H
+
+#include "llfloater.h"
+
+class LLButton;
+class LLChat;
+class LLComboBox;
+class LLLineEditor;
+class LLViewerTextEditor;
+class LLMessageSystem;
+class LLUUID;
+class LLCheckBoxCtrl;
+
+class LLFloaterChat
+: public LLFloater
+{
+public:
+ LLFloaterChat();
+ ~LLFloaterChat();
+
+ void show();
+ virtual void onClose(bool app_quitting);
+ virtual void setVisible( BOOL b );
+
+ static void setHistoryCursorAndScrollToEnd();
+
+ // Add chat to console and history list.
+ // Color based on source, type, distance.
+ static void addChat(const LLChat& chat, BOOL from_im = FALSE, BOOL local_agent = FALSE);
+
+ // Add chat to history alone.
+ static void addChatHistory(const LLChat& chat, bool log_to_file = true);
+
+ static void toggle(void*);
+ static BOOL visible(void*);
+
+ static void onClickMute(void *data);
+ static void onClickChat(void *);
+ static void onCommitUserSelect(LLUICtrl* caller, void* data);
+ static void onClickToggleShowMute(LLUICtrl* caller, void *data);
+ static void chatFromLogFile(LLString line, void* userdata);
+ static void loadHistory();
+};
+
+extern LLFloaterChat* gFloaterChat;
+
+#endif
diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp
new file mode 100644
index 0000000000..181a66c2ef
--- /dev/null
+++ b/indra/newview/llfloatercolorpicker.cpp
@@ -0,0 +1,1259 @@
+/**
+ * @file llfloatercolorpicker.cpp
+ * @brief Generic system color picker
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <sstream>
+#include <iomanip>
+
+#include "llfloatercolorpicker.h"
+
+#include "llfontgl.h"
+#include "llsys.h"
+#include "llgl.h"
+#include "v3dmath.h"
+#include "lldir.h"
+#include "llui.h"
+#include "lllineeditor.h"
+#include "v4coloru.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llgl.h"
+#include "llmemory.h"
+#include "llimage.h"
+#include "llmousehandler.h"
+#include "llimagegl.h"
+#include "llglheaders.h"
+#include "llcheckboxctrl.h"
+#include "llworld.h"
+#include "lltextbox.h"
+#include "lluiconstants.h"
+#include "llfocusmgr.h"
+#include "lltoolmgr.h"
+#include "lltoolpipette.h"
+#include "lldraghandle.h"
+
+const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
+const F32 CONTEXT_CONE_OUT_ALPHA = 0.35f;
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Class LLFloaterColorPicker
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+// default ctor
+LLFloaterColorPicker::
+LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show_apply_immediate )
+ : LLFloater ("Color Picker Floater"),
+ mComponents ( 3 ),
+ mMouseDownInLumRegion ( FALSE ),
+ mMouseDownInHueRegion ( FALSE ),
+ mMouseDownInSwatch ( FALSE ),
+ mRGBViewerImageLeft ( 140 ),
+ mRGBViewerImageTop ( 356 ),
+ mRGBViewerImageWidth ( 256 ),
+ mRGBViewerImageHeight ( 256 ),
+ mLumRegionLeft ( mRGBViewerImageLeft + mRGBViewerImageWidth + 16 ),
+ mLumRegionTop ( mRGBViewerImageTop ),
+ mLumRegionWidth ( 16 ),
+ mLumRegionHeight ( mRGBViewerImageHeight ),
+ mLumMarkerSize ( 6 ),
+ mSwatchRegionLeft ( 12 ),
+ mSwatchRegionTop ( 160 + 16 - 4 ), // get help text baseline to line up with bottom of RGB viewer
+ mSwatchRegionWidth ( 110 ),
+ mSwatchRegionHeight ( 60 ),
+ mSwatchView ( NULL ),
+ numPaletteColumns ( 16 ),
+ numPaletteRows ( 2 ),
+ highlightEntry ( -1 ),
+ mPaletteRegionLeft ( mSwatchRegionLeft - 1 ),
+ mPaletteRegionTop ( 100 - 8 ),
+ mPaletteRegionWidth ( mLumRegionLeft + mLumRegionWidth - mSwatchRegionLeft + 2 ),
+ mPaletteRegionHeight ( 40 ),
+ mSwatch ( swatch ),
+ mActive ( TRUE ),
+ mCanApplyImmediately ( show_apply_immediate ),
+ mContextConeOpacity ( 0.f )
+{
+ // create user interface for this picker
+ createUI ();
+
+ if (!mCanApplyImmediately)
+ {
+ mApplyImmediateCheck->setEnabled(FALSE);
+ mApplyImmediateCheck->set(FALSE);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// dtor
+LLFloaterColorPicker::
+~LLFloaterColorPicker()
+{
+ // destroy the UI we created
+ destroyUI ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+createUI ()
+{
+ // build the majority of the gui using the factory builder
+ gUICtrlFactory->buildFloater ( this, "floater_color_picker.xml" );
+ setVisible ( FALSE );
+
+ // create RGB type area (not really RGB but it's got R,G & B in it.,..
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw ( mRGBViewerImageWidth, mRGBViewerImageHeight, mComponents );
+ U8* bits = raw->getData();
+ S32 linesize = mRGBViewerImageWidth * mComponents;
+ for ( S32 y = 0; y < mRGBViewerImageHeight; ++y )
+ {
+ for ( S32 x = 0; x < linesize; x += mComponents )
+ {
+ F32 rVal, gVal, bVal;
+
+ hslToRgb ( (F32)x / (F32) ( linesize - 1 ),
+ (F32)y / (F32) ( mRGBViewerImageHeight - 1 ),
+ 0.5f,
+ rVal,
+ gVal,
+ bVal );
+
+ * ( bits + x + y * linesize + 0 ) = ( U8 )( rVal * 255.0f );
+ * ( bits + x + y * linesize + 1 ) = ( U8 )( gVal * 255.0f );
+ * ( bits + x + y * linesize + 2 ) = ( U8 )( bVal * 255.0f );
+ }
+ }
+ mRGBImage = new LLImageGL ( (LLImageRaw*)raw, FALSE );
+ mRGBImage->bind();
+ mRGBImage->setClamp(TRUE, TRUE);
+
+ // create palette
+ for ( S32 each = 0; each < numPaletteColumns * numPaletteRows; ++each )
+ {
+ std::ostringstream codec;
+ codec << "ColorPaletteEntry" << std::setfill ( '0' ) << std::setw ( 2 ) << each + 1;
+
+ // argh!
+ const std::string s ( codec.str () );
+ mPalette.push_back ( new LLColor4 ( gSavedSettings.getColor4 ( s ) ) );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+showUI ()
+{
+ setVisible ( TRUE );
+ setFocus ( TRUE );
+ open();
+
+ // HACK: if system color picker is required - close the SL one we made and use default system dialog
+ if ( gSavedSettings.getBOOL ( "UseDefaultColorPicker" ) )
+ {
+ LLColorSwatchCtrl* swatch = getSwatch ();
+
+ setVisible ( FALSE );
+
+ // code that will get switched in for default system color picker
+ if ( swatch )
+ {
+ LLColor4 curCol = swatch->get ();
+ send_agent_pause();
+ gViewerWindow->getWindow ()->dialog_color_picker ( &curCol [ 0 ], &curCol [ 1 ], &curCol [ 2 ] );
+ send_agent_resume();
+
+ setOrigRgb ( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] );
+ setCurRgb( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] );
+
+ LLColorSwatchCtrl::onColorChanged ( swatch, LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+
+ close();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called after the dialog is rendered
+BOOL
+LLFloaterColorPicker::
+postBuild()
+{
+ mCancelBtn = LLViewerUICtrlFactory::getButtonByName( this, "cancel_btn" );
+ mCancelBtn->setClickedCallback ( onClickCancel );
+ mCancelBtn->setCallbackUserData ( this );
+
+ mSelectBtn = LLViewerUICtrlFactory::getButtonByName( this, "select_btn");
+ mSelectBtn->setClickedCallback ( onClickSelect );
+ mSelectBtn->setCallbackUserData ( this );
+ mSelectBtn->setFocus ( TRUE );
+
+ mPipetteBtn = LLViewerUICtrlFactory::getButtonByName ( this, "color_pipette" );
+
+ mPipetteBtn->setImages("eye_button_inactive.tga", "eye_button_active.tga");
+
+ mPipetteBtn->setClickedCallback( onClickPipette );
+ mPipetteBtn->setCallbackUserData ( this );
+
+ mApplyImmediateCheck = LLViewerUICtrlFactory::getCheckBoxByName( this, "apply_immediate");
+ mApplyImmediateCheck->set(gSavedSettings.getBOOL("ApplyColorImmediately"));
+ mApplyImmediateCheck->setCommitCallback(onImmediateCheck);
+ mApplyImmediateCheck->setCallbackUserData(this);
+
+ childSetCommitCallback("rspin", onTextCommit, (void*)this );
+ childSetCommitCallback("gspin", onTextCommit, (void*)this );
+ childSetCommitCallback("bspin", onTextCommit, (void*)this );
+ childSetCommitCallback("hspin", onTextCommit, (void*)this );
+ childSetCommitCallback("sspin", onTextCommit, (void*)this );
+ childSetCommitCallback("lspin", onTextCommit, (void*)this );
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+initUI ( F32 rValIn, F32 gValIn, F32 bValIn )
+{
+ // start catching lose-focus events from entry widgets
+ enableTextCallbacks ( TRUE );
+
+ // under some circumstances, we get rogue values that can be calmed by clamping...
+ rValIn = llclamp ( rValIn, 0.0f, 1.0f );
+ gValIn = llclamp ( gValIn, 0.0f, 1.0f );
+ bValIn = llclamp ( bValIn, 0.0f, 1.0f );
+
+ // store initial value in case cancel or revert is selected
+ setOrigRgb ( rValIn, gValIn, bValIn );
+
+ // starting point for current value to
+ setCurRgb ( rValIn, gValIn, bValIn );
+
+ // unpdate text entry fields
+ updateTextEntry ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+destroyUI ()
+{
+ // shut down pipette tool if active
+ stopUsingPipette();
+
+ // delete palette we created
+ std::vector < LLColor4* >::iterator iter = mPalette.begin ();
+ while ( iter != mPalette.end () )
+ {
+ delete ( *iter );
+ ++iter;
+ }
+
+ if ( mSwatchView )
+ {
+ this->removeChild ( mSwatchView );
+ delete mSwatchView;
+ mSwatchView = NULL;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+rgbToHsl ( F32 rValIn, F32 gValIn, F32 bValIn, F32& hValOut, F32& sValOut, F32& lValOut )
+{
+ F32 var_R = ( rValIn );
+ F32 var_G = ( gValIn );
+ F32 var_B = ( bValIn );
+
+ F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) );
+ F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) );
+
+ F32 del_Max = var_Max - var_Min;
+
+ F32 L = ( var_Max + var_Min ) / 2.0f;
+ F32 H = 0.0f;
+ F32 S = 0.0f;
+
+ if ( del_Max == 0.0f )
+ {
+ H = 0.0f;
+ S = 0.0f;
+ }
+ else
+ {
+ if ( L < 0.5 )
+ S = del_Max / ( var_Max + var_Min );
+ else
+ S = del_Max / ( 2.0f - var_Max - var_Min );
+
+ F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
+ F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
+ F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
+
+ if ( var_R >= var_Max )
+ H = del_B - del_G;
+ else
+ if ( var_G >= var_Max )
+ H = ( 1.0f / 3.0f ) + del_R - del_B;
+ else
+ if ( var_B >= var_Max )
+ H = ( 2.0f / 3.0f ) + del_G - del_R;
+
+ if ( H < 0.0f ) H += 1.0f;
+ if ( H > 1.0f ) H -= 1.0f;
+ }
+
+ // scale to meet calculation requirements
+ hValOut = H * 360.0f / 360.0f;
+ sValOut = S * 100.0f / 100.0f;
+ lValOut = L * 100.0f / 100.0f;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+F32
+LLFloaterColorPicker::
+hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn )
+{
+ if ( valHUeIn < 0.0f ) valHUeIn += 1.0f;
+ if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f;
+ if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn );
+ if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In );
+ if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f );
+ return ( val1In );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+hslToRgb ( F32 hValIn, F32 sValIn, F32 lValIn, F32& rValOut, F32& gValOut, F32& bValOut )
+{
+ if ( sValIn < 0.00001f )
+ {
+ rValOut = lValIn;
+ gValOut = lValIn;
+ bValOut = lValIn;
+ }
+ else
+ {
+ F32 interVal1;
+ F32 interVal2;
+
+ if ( lValIn < 0.5f )
+ interVal2 = lValIn * ( 1.0f + sValIn );
+ else
+ interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn );
+
+ interVal1 = 2.0f * lValIn - interVal2;
+
+ rValOut = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) );
+ gValOut = hueToRgb ( interVal1, interVal2, hValIn );
+ bValOut = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// mutator for original RGB value
+void
+LLFloaterColorPicker::
+setOrigRgb ( F32 origRIn, F32 origGIn, F32 origBIn )
+{
+ origR = origRIn;
+ origG = origGIn;
+ origB = origBIn;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// accessor for original RGB value
+void
+LLFloaterColorPicker::
+getOrigRgb ( F32& origROut, F32& origGOut, F32& origBOut )
+{
+ origROut = origR;
+ origGOut = origG;
+ origBOut = origB;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// mutator for current RGB value
+void
+LLFloaterColorPicker::
+setCurRgb ( F32 curRIn, F32 curGIn, F32 curBIn )
+{
+ // save current RGB
+ curR = curRIn;
+ curG = curGIn;
+ curB = curBIn;
+
+ // update corresponding HSL values and
+ rgbToHsl ( curR, curG, curB, curH, curS, curL );
+
+ // color changed so update text fields (fixes SL-16968)
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ // CP: this was required when I first wrote the code but this may not be necessary anymore - leaving it there just in case
+ enableTextCallbacks( FALSE );
+ updateTextEntry();
+ enableTextCallbacks( TRUE );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// accessor for current RGB value
+void
+LLFloaterColorPicker::
+getCurRgb ( F32& curROut, F32& curGOut, F32& curBOut )
+{
+ curROut = curR;
+ curGOut = curG;
+ curBOut = curB;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// mutator for current HSL value
+void
+LLFloaterColorPicker::
+setCurHsl ( F32 curHIn, F32 curSIn, F32 curLIn )
+{
+ // save current HSL
+ curH = curHIn;
+ curS = curSIn;
+ curL = curLIn;
+
+ // update corresponding RGB values and
+ hslToRgb ( curH, curS, curL, curR, curG, curB );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// accessor for current HSL value
+void
+LLFloaterColorPicker::
+getCurHsl ( F32& curHOut, F32& curSOut, F32& curLOut )
+{
+ curHOut = curH;
+ curSOut = curS;
+ curLOut = curL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called when 'cancel' clicked
+void
+LLFloaterColorPicker::
+onClickCancel ( void* data )
+{
+ if (data)
+ {
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+
+ if ( self )
+ {
+ self->cancelSelection ();
+ self->close();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called when 'select' clicked
+void
+LLFloaterColorPicker::
+onClickSelect ( void* data )
+{
+ if (data)
+ {
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+
+ if ( self )
+ {
+ // apply to selection
+ LLColorSwatchCtrl::onColorChanged ( self->getSwatch (), LLColorSwatchCtrl::COLOR_SELECT );
+ self->close();
+ }
+ }
+}
+
+void LLFloaterColorPicker::onClickPipette( void* data )
+{
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+
+ if ( self && gToolMgr)
+ {
+ BOOL pipette_active = self->mPipetteBtn->getToggleState();
+ pipette_active = !pipette_active;
+ if (pipette_active)
+ {
+ gToolPipette->setSelectCallback(onColorSelect, self);
+ gToolMgr->setTransientTool(gToolPipette);
+ }
+ else
+ {
+ gToolMgr->clearTransientTool();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called when 'text is committed' - i,e. focus moves from a text field
+void
+LLFloaterColorPicker::
+onTextCommit ( LLUICtrl* ctrl, void* data )
+{
+ if ( data )
+ {
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+ if ( self )
+ {
+ self->onTextEntryChanged ( ctrl );
+ }
+ }
+}
+
+void LLFloaterColorPicker::onImmediateCheck( LLUICtrl* ctrl, void* data)
+{
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+ if (self)
+ {
+ gSavedSettings.setBOOL("ApplyColorImmediately", self->mApplyImmediateCheck->get());
+
+ if (self->mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( self->getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+ }
+}
+
+void LLFloaterColorPicker::onColorSelect( const LLTextureEntry& te, void *data )
+{
+ LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
+ if (self)
+ {
+ self->setCurRgb(te.getColor().mV[VRED], te.getColor().mV[VGREEN], te.getColor().mV[VBLUE]);
+ if (self->mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( self->getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+ }
+}
+
+void LLFloaterColorPicker::onMouseCaptureLost(LLMouseHandler *old_captor)
+{
+ LLFloaterColorPicker* self = (LLFloaterColorPicker*)old_captor;
+ self->setMouseDownInHueRegion(FALSE);
+ self->setMouseDownInLumRegion(FALSE);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void LLFloaterColorPicker::draw()
+{
+ LLRect swatch_rect;
+ mSwatch->localRectToOtherView(mSwatch->getLocalRect(), &swatch_rect, this);
+ // draw context cone connecting color picker with color swatch in parent floater
+ LLRect local_rect = getLocalRect();
+ if (gFocusMgr.childHasKeyboardFocus(this) && mSwatch->isInVisibleChain() && mContextConeOpacity > 0.001f)
+ {
+ LLGLSNoTexture no_texture;
+ LLGLEnable(GL_CULL_FACE);
+ glBegin(GL_QUADS);
+ {
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mTop);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mBottom);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mTop);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mBottom);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mBottom);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mBottom);
+ }
+ glEnd();
+ }
+
+ if (gFocusMgr.childHasMouseCapture(mDragHandle))
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 1.f, LLCriticalDamp::getInterpolant(0.1f));
+ }
+ else
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(0.2f));
+ }
+
+ mPipetteBtn->setEnabled(gToolMgr != NULL);
+ mPipetteBtn->setToggleState(gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette);
+ mApplyImmediateCheck->setEnabled(mActive && mCanApplyImmediately);
+ mSelectBtn->setEnabled(mActive);
+
+ // base floater stuff
+ LLFloater::draw ();
+
+ // draw image for RGB area (not really RGB but you'll see what I mean...
+ gl_draw_image ( mRGBViewerImageLeft, mRGBViewerImageTop - mRGBViewerImageHeight, mRGBImage, LLColor4::white );
+
+ // update 'cursor' into RGB Section
+ S32 xPos = ( S32 ) ( ( F32 )mRGBViewerImageWidth * getCurH () ) - 8;
+ S32 yPos = ( S32 ) ( ( F32 )mRGBViewerImageHeight * getCurS () ) - 8;
+ gl_line_2d ( mRGBViewerImageLeft + xPos,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos + 8,
+ mRGBViewerImageLeft + xPos + 16,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos + 8,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ) );
+
+ gl_line_2d ( mRGBViewerImageLeft + xPos + 8,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos,
+ mRGBViewerImageLeft + xPos + 8,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos + 16,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ) );
+
+ // create rgb area outline
+ gl_rect_2d ( mRGBViewerImageLeft,
+ mRGBViewerImageTop - mRGBViewerImageHeight,
+ mRGBViewerImageLeft + mRGBViewerImageWidth,
+ mRGBViewerImageTop,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ),
+ FALSE );
+
+ // draw luminance slider
+ for ( S32 y = 0; y < mLumRegionHeight; ++y )
+ {
+ F32 rValSlider, gValSlider, bValSlider;
+ hslToRgb ( getCurH (), getCurS (), ( F32 )y / ( F32 )mLumRegionHeight, rValSlider, gValSlider, bValSlider );
+
+ gl_rect_2d( mLumRegionLeft,
+ mLumRegionTop - mLumRegionHeight + y,
+ mLumRegionLeft + mLumRegionWidth,
+ mLumRegionTop - mLumRegionHeight + y - 1,
+ LLColor4 ( rValSlider, gValSlider, bValSlider, 1.0f ) );
+ }
+
+
+ // draw luninance marker
+ S32 startX = mLumRegionLeft + mLumRegionWidth;
+ S32 startY = mLumRegionTop - mLumRegionHeight + ( S32 ) ( mLumRegionHeight * getCurL () );
+ gl_triangle_2d ( startX, startY,
+ startX + mLumMarkerSize, startY - mLumMarkerSize,
+ startX + mLumMarkerSize, startY + mLumMarkerSize,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ), TRUE );
+
+ // draw luminance slider outline
+ gl_rect_2d ( mLumRegionLeft,
+ mLumRegionTop - mLumRegionHeight,
+ mLumRegionLeft + mLumRegionWidth,
+ mLumRegionTop,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ),
+ FALSE );
+
+ // draw selected color swatch
+ gl_rect_2d ( mSwatchRegionLeft,
+ mSwatchRegionTop - mSwatchRegionHeight,
+ mSwatchRegionLeft + mSwatchRegionWidth,
+ mSwatchRegionTop,
+ LLColor4 ( getCurR (), getCurG (), getCurB (), 1.0f ),
+ TRUE );
+
+ // draw selected color swatch outline
+ gl_rect_2d ( mSwatchRegionLeft,
+ mSwatchRegionTop - mSwatchRegionHeight,
+ mSwatchRegionLeft + mSwatchRegionWidth,
+ mSwatchRegionTop,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ),
+ FALSE );
+
+ // color palette code is a little more involved so break it out into its' own method
+ drawPalette ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// find a complimentary color to the one passed in that can be used to highlight
+LLColor4&
+LLFloaterColorPicker::
+getComplimentaryColor ( LLColor4& backgroundColor )
+{
+ // going to base calculation on luminance
+ F32 hVal, sVal, lVal;
+ rgbToHsl ( backgroundColor [ 0 ], backgroundColor [ 1 ], backgroundColor [ 2 ], hVal, sVal, lVal );
+
+ // fairly simple heuristic for now...!
+ if ( lVal < 0.5f )
+ {
+ return LLColor4::white;
+ }
+
+ return LLColor4::black;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// draw color palette
+void
+LLFloaterColorPicker::
+drawPalette ()
+{
+ S32 curEntry = 0;
+
+ for ( S32 y = 0; y < numPaletteRows; ++y )
+ {
+ for ( S32 x = 0; x < numPaletteColumns; ++x )
+ {
+ // calculate position
+ S32 x1 = mPaletteRegionLeft + ( mPaletteRegionWidth * x ) / numPaletteColumns;
+ S32 y1 = mPaletteRegionTop - ( mPaletteRegionHeight * y ) / numPaletteRows;
+ S32 x2 = ( mPaletteRegionLeft + ( mPaletteRegionWidth * ( x + 1 ) ) / numPaletteColumns );
+ S32 y2 = ( mPaletteRegionTop - ( mPaletteRegionHeight * ( y + 1 ) ) / numPaletteRows );
+
+ // draw palette entry color
+ if ( mPalette [ curEntry ] )
+ {
+ gl_rect_2d ( x1 + 2, y1 - 2, x2 - 2, y2 + 2, *mPalette [ curEntry++ ], TRUE );
+ gl_rect_2d ( x1 + 1, y1 - 1, x2 - 1, y2 + 1, LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ), FALSE );
+ }
+ }
+ }
+
+ // if there is something to highlight (mouse down in swatch & hovering over palette)
+ if ( highlightEntry >= 0 )
+ {
+ // extract row/column from palette index
+ S32 entryColumn = highlightEntry % numPaletteColumns;
+ S32 entryRow = highlightEntry / numPaletteColumns;
+
+ // calculate position of this entry
+ S32 x1 = mPaletteRegionLeft + ( mPaletteRegionWidth * entryColumn ) / numPaletteColumns;
+ S32 y1 = mPaletteRegionTop - ( mPaletteRegionHeight * entryRow ) / numPaletteRows;
+ S32 x2 = ( mPaletteRegionLeft + ( mPaletteRegionWidth * ( entryColumn + 1 ) ) / numPaletteColumns );
+ S32 y2 = ( mPaletteRegionTop - ( mPaletteRegionHeight * ( entryRow + 1 ) ) / numPaletteRows );
+
+ // center position of entry
+ S32 xCenter = x1 + ( x2 - x1 ) / 2;
+ S32 yCenter = y1 - ( y1 - y2 ) / 2;
+
+ // find a color that works well as a highlight color
+ LLColor4 hlColor ( getComplimentaryColor ( *mPalette [ highlightEntry ] ) );
+
+ // mark a cross for entry that is being hovered
+ gl_line_2d ( xCenter - 4, yCenter - 4, xCenter + 4, yCenter + 4, hlColor );
+ gl_line_2d ( xCenter + 4, yCenter - 4, xCenter - 4, yCenter + 4, hlColor );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// update text entry values for RGB/HSL (can't be done in ::draw () since this overwrites input
+void
+LLFloaterColorPicker::
+updateTextEntry ()
+{
+ // set values in spinners
+ childSetValue("rspin", ( getCurR () * 255.0f ) );
+ childSetValue("gspin", ( getCurG () * 255.0f ) );
+ childSetValue("bspin", ( getCurB () * 255.0f ) );
+ childSetValue("hspin", ( getCurH () * 360.0f ) );
+ childSetValue("sspin", ( getCurS () * 100.0f ) );
+ childSetValue("lspin", ( getCurL () * 100.0f ) );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// turns on or off text entry commit call backs
+void
+LLFloaterColorPicker::
+enableTextCallbacks ( BOOL stateIn )
+{
+ if ( stateIn )
+ {
+ childSetCommitCallback("rspin", onTextCommit, (void*)this );
+ childSetCommitCallback("gspin", onTextCommit, (void*)this );
+ childSetCommitCallback("bspin", onTextCommit, (void*)this );
+ childSetCommitCallback("hspin", onTextCommit, (void*)this );
+ childSetCommitCallback("sspin", onTextCommit, (void*)this );
+ childSetCommitCallback("lspin", onTextCommit, (void*)this );
+ }
+ else
+ {
+ childSetCommitCallback("rspin", 0, (void*)this );
+ childSetCommitCallback("gspin", 0, (void*)this );
+ childSetCommitCallback("bspin", 0, (void*)this );
+ childSetCommitCallback("hspin", 0, (void*)this );
+ childSetCommitCallback("sspin", 0, (void*)this );
+ childSetCommitCallback("lspin", 0, (void*)this );
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+onTextEntryChanged ( LLUICtrl* ctrl )
+{
+ // value in RGB boxes changed
+ LLString name = ctrl->getName();
+ if ( ( name == "rspin" ) || ( name == "gspin" ) || ( name == "bspin" ) )
+ {
+ // get current RGB
+ F32 rVal, gVal, bVal;
+ getCurRgb ( rVal, gVal, bVal );
+
+ // update component value with new value from text
+ if ( name == "rspin" )
+ {
+ rVal = (F32)ctrl->getValue().asReal() / 255.0f;
+ }
+ else
+ if ( name == "gspin" )
+ {
+ gVal = (F32)ctrl->getValue().asReal() / 255.0f;
+ }
+ else
+ if ( name == "bspin" )
+ {
+ bVal = (F32)ctrl->getValue().asReal() / 255.0f;
+ }
+
+ // update current RGB (and implicitly HSL)
+ setCurRgb ( rVal, gVal, bVal );
+
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ enableTextCallbacks ( FALSE );
+ updateTextEntry ();
+ enableTextCallbacks ( TRUE );
+ }
+ else
+ // value in HSL boxes changed
+ if ( ( name == "hspin" ) || ( name == "sspin" ) || ( name == "lspin" ) )
+ {
+ // get current HSL
+ F32 hVal, sVal, lVal;
+ getCurHsl ( hVal, sVal, lVal );
+
+ // update component value with new value from text
+ if ( name == "hspin" )
+ hVal = (F32)ctrl->getValue().asReal() / 360.0f;
+ else
+ if ( name == "sspin" )
+ sVal = (F32)ctrl->getValue().asReal() / 100.0f;
+ else
+ if ( name == "lspin" )
+ lVal = (F32)ctrl->getValue().asReal() / 100.0f;
+
+ // update current HSL (and implicitly RGB)
+ setCurHsl ( hVal, sVal, lVal );
+
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ enableTextCallbacks ( FALSE );
+ updateTextEntry ();
+ enableTextCallbacks ( TRUE );
+ }
+
+ if (mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL
+LLFloaterColorPicker::
+updateRgbHslFromPoint ( S32 xPosIn, S32 yPosIn )
+{
+ if ( xPosIn >= mRGBViewerImageLeft &&
+ xPosIn <= mRGBViewerImageLeft + mRGBViewerImageWidth &&
+ yPosIn <= mRGBViewerImageTop &&
+ yPosIn >= mRGBViewerImageTop - mRGBViewerImageHeight )
+ {
+ // update HSL (and therefore RGB) based on new H & S and current L
+ setCurHsl ( ( ( F32 )xPosIn - ( F32 )mRGBViewerImageLeft ) / ( F32 )mRGBViewerImageWidth,
+ ( ( F32 )yPosIn - ( ( F32 )mRGBViewerImageTop - ( F32 )mRGBViewerImageHeight ) ) / ( F32 )mRGBViewerImageHeight,
+ getCurL () );
+
+ // indicate a value changed
+ return TRUE;
+ }
+ else
+ if ( xPosIn >= mLumRegionLeft &&
+ xPosIn <= mLumRegionLeft + mLumRegionWidth &&
+ yPosIn <= mLumRegionTop &&
+ yPosIn >= mLumRegionTop - mLumRegionHeight )
+ {
+
+ // update HSL (and therefore RGB) based on current HS and new L
+ setCurHsl ( getCurH (),
+ getCurS (),
+ ( ( F32 )yPosIn - ( ( F32 )mRGBViewerImageTop - ( F32 )mRGBViewerImageHeight ) ) / ( F32 )mRGBViewerImageHeight );
+
+ // indicate a value changed
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL
+LLFloaterColorPicker::
+handleMouseDown ( S32 x, S32 y, MASK mask )
+{
+ // if this window is in the foreground
+ if ( mForeground )
+ {
+ // make it the frontmost
+ gFloaterView->bringToFront(this);
+
+ // rect containing RGB area
+ LLRect rgbAreaRect ( mRGBViewerImageLeft,
+ mRGBViewerImageTop,
+ mRGBViewerImageLeft + mRGBViewerImageWidth,
+ mRGBViewerImageTop - mRGBViewerImageHeight );
+
+ if ( rgbAreaRect.pointInRect ( x, y ) )
+ {
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ // mouse button down
+ setMouseDownInHueRegion ( TRUE );
+
+ // update all values based on initial click
+ updateRgbHslFromPoint ( x, y );
+
+ // required by base class
+ return TRUE;
+ }
+
+ // rect containing RGB area
+ LLRect lumAreaRect ( mLumRegionLeft,
+ mLumRegionTop,
+ mLumRegionLeft + mLumRegionWidth + mLumMarkerSize,
+ mLumRegionTop - mLumRegionHeight );
+
+ if ( lumAreaRect.pointInRect ( x, y ) )
+ {
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ // mouse button down
+ setMouseDownInLumRegion ( TRUE );
+
+ // required by base class
+ return TRUE;
+ }
+
+ // rect containing swatch area
+ LLRect swatchRect ( mSwatchRegionLeft,
+ mSwatchRegionTop,
+ mSwatchRegionLeft + mSwatchRegionWidth,
+ mSwatchRegionTop - mSwatchRegionHeight );
+
+ setMouseDownInSwatch( FALSE );
+ if ( swatchRect.pointInRect ( x, y ) )
+ {
+ setMouseDownInSwatch( TRUE );
+
+ // required - dont drag windows here.
+ return TRUE;
+ }
+
+ // rect containing palette area
+ LLRect paletteRect ( mPaletteRegionLeft,
+ mPaletteRegionTop,
+ mPaletteRegionLeft + mPaletteRegionWidth,
+ mPaletteRegionTop - mPaletteRegionHeight );
+
+ if ( paletteRect.pointInRect ( x, y ) )
+ {
+ // release keyboard focus so we can change text values
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ mSelectBtn->setFocus(TRUE);
+ }
+
+ // calculate which palette index we selected
+ S32 c = ( ( x - mPaletteRegionLeft ) * numPaletteColumns ) / mPaletteRegionWidth;
+ S32 r = ( ( y - ( mPaletteRegionTop - mPaletteRegionHeight ) ) * numPaletteRows ) / mPaletteRegionHeight;
+
+ U32 index = ( numPaletteRows - r - 1 ) * numPaletteColumns + c;
+
+ if ( index <= mPalette.size () )
+ {
+ LLColor4 selected = *mPalette [ index ];
+
+ setCurRgb ( selected [ 0 ], selected [ 1 ], selected [ 2 ] );
+
+ if (mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ enableTextCallbacks ( FALSE );
+ updateTextEntry ();
+ enableTextCallbacks ( TRUE );
+ }
+
+ return TRUE;
+ }
+ }
+ // dispatch to base class for the rest of things
+ return LLFloater::handleMouseDown ( x, y, mask );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL
+LLFloaterColorPicker::
+handleHover ( S32 x, S32 y, MASK mask )
+{
+ // if we're the front most window
+ if ( isFrontmost () )
+ {
+ // mouse was pressed within region
+ if ( getMouseDownInHueRegion() || getMouseDownInLumRegion())
+ {
+ S32 clamped_x, clamped_y;
+ if (getMouseDownInHueRegion())
+ {
+ clamped_x = llclamp(x, mRGBViewerImageLeft, mRGBViewerImageLeft + mRGBViewerImageWidth);
+ clamped_y = llclamp(y, mRGBViewerImageTop - mRGBViewerImageHeight, mRGBViewerImageTop);
+ }
+ else
+ {
+ clamped_x = llclamp(x, mLumRegionLeft, mLumRegionLeft + mLumRegionWidth);
+ clamped_y = llclamp(y, mLumRegionTop - mLumRegionHeight, mLumRegionTop);
+ }
+
+ // update the stored RGB/HSL values using the mouse position - returns TRUE if RGB was updated
+ if ( updateRgbHslFromPoint ( clamped_x, clamped_y ) )
+ {
+ // update text entry fields
+ updateTextEntry ();
+
+ // RN: apparently changing color when dragging generates too much traffic and results in sporadic updates
+ //// commit changed color to swatch subject
+ //// REVIEW: this gets sent each time a color changes - is this okay ?
+ //if (mApplyImmediateCheck->get())
+ //{
+ // LLColorSwatchCtrl::onColorChanged ( getSwatch () );
+ //}
+ }
+ }
+
+ highlightEntry = -1;
+
+ if ( mMouseDownInSwatch )
+ {
+ getWindow()->setCursor ( UI_CURSOR_ARROWDRAG );
+
+ // if cursor if over a palette entry
+ LLRect paletteRect ( mPaletteRegionLeft,
+ mPaletteRegionTop,
+ mPaletteRegionLeft + mPaletteRegionWidth,
+ mPaletteRegionTop - mPaletteRegionHeight );
+
+ if ( paletteRect.pointInRect ( x, y ) )
+ {
+ // find row/column in palette
+ S32 xOffset = ( ( x - mPaletteRegionLeft ) * numPaletteColumns ) / mPaletteRegionWidth;
+ S32 yOffset = ( ( mPaletteRegionTop - y - 1 ) * numPaletteRows ) / mPaletteRegionHeight;
+
+ // calculate the entry 0..n-1 to highlight and set variable to next draw() picks it up
+ highlightEntry = xOffset + yOffset * numPaletteColumns;
+ }
+
+ return TRUE;
+ }
+ }
+
+ // dispatch to base class for the rest of things
+ return LLFloater::handleHover ( x, y, mask );
+}
+
+void LLFloaterColorPicker::onClose(bool app_quitting)
+{
+ //RN: this is consistent with texture picker in that closing the window leaves the current selection
+ // to change this to "close to cancel", uncomment the following line
+ //cancelSelection();
+ LLFloater::onClose(app_quitting);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// reverts state once mouse button is released
+BOOL
+LLFloaterColorPicker::
+handleMouseUp ( S32 x, S32 y, MASK mask )
+{
+ getWindow()->setCursor ( UI_CURSOR_ARROW );
+
+ if (getMouseDownInHueRegion() || getMouseDownInLumRegion())
+ {
+ if (mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+ }
+
+ // rect containing palette area
+ LLRect paletteRect ( mPaletteRegionLeft,
+ mPaletteRegionTop,
+ mPaletteRegionLeft + mPaletteRegionWidth,
+ mPaletteRegionTop - mPaletteRegionHeight );
+
+ if ( paletteRect.pointInRect ( x, y ) )
+ {
+ if ( mMouseDownInSwatch )
+ {
+ S32 curEntry = 0;
+ for ( S32 row = 0; row < numPaletteRows; ++row )
+ {
+ for ( S32 column = 0; column < numPaletteColumns; ++column )
+ {
+ S32 left = mPaletteRegionLeft + ( mPaletteRegionWidth * column ) / numPaletteColumns;
+ S32 top = mPaletteRegionTop - ( mPaletteRegionHeight * row ) / numPaletteRows;
+ S32 right = ( mPaletteRegionLeft + ( mPaletteRegionWidth * ( column + 1 ) ) / numPaletteColumns );
+ S32 bottom = ( mPaletteRegionTop - ( mPaletteRegionHeight * ( row + 1 ) ) / numPaletteRows );
+
+ // rect is flipped vertically when testing here
+ LLRect dropRect ( left, top, right, bottom );
+
+ if ( dropRect.pointInRect ( x, y ) )
+ {
+ if ( mPalette [ curEntry ] )
+ {
+ delete mPalette [ curEntry ];
+
+ mPalette [ curEntry ] = new LLColor4 ( getCurR (), getCurG (), getCurB (), 1.0f );
+
+ // save off color
+ std::ostringstream codec;
+ codec << "ColorPaletteEntry" << std::setfill ( '0' ) << std::setw ( 2 ) << curEntry + 1;
+ const std::string s ( codec.str () );
+ gSavedSettings.setColor4( s, *mPalette [ curEntry ] );
+ }
+ }
+
+ ++curEntry;
+ }
+ }
+ }
+ }
+
+ // mouse button not down anymore
+ setMouseDownInHueRegion ( FALSE );
+ setMouseDownInLumRegion ( FALSE );
+
+ // mouse button not down in color swatch anymore
+ mMouseDownInSwatch = false;
+
+ if (gViewerWindow->hasMouseCapture(this))
+ {
+ gViewerWindow->setMouseCapture(NULL, NULL);
+ }
+
+ // dispatch to base class for the rest of things
+ return LLFloater::handleMouseUp ( x, y, mask );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cancel current color selection, revert to original and close picker
+void
+LLFloaterColorPicker::
+cancelSelection ()
+{
+ // restore the previous colour selection
+ setCurRgb ( getOrigR (), getOrigG (), getOrigB () );
+
+ // we're going away and when we do and the entry widgets lose focus, they do bad things so turn them off
+ enableTextCallbacks ( FALSE );
+
+ // update in world item with original color via current swatch
+ LLColorSwatchCtrl::onColorChanged( getSwatch(), LLColorSwatchCtrl::COLOR_CANCEL );
+
+ // hide picker dialog
+ this->setVisible ( FALSE );
+}
+
+void LLFloaterColorPicker::setMouseDownInHueRegion ( BOOL mouse_down_in_region )
+{
+ mMouseDownInHueRegion = mouse_down_in_region;
+ if (mouse_down_in_region)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // get focus out of spinners so that they can update freely
+ mSelectBtn->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloaterColorPicker::setMouseDownInLumRegion ( BOOL mouse_down_in_region )
+{
+ mMouseDownInLumRegion = mouse_down_in_region;
+ if (mouse_down_in_region)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // get focus out of spinners so that they can update freely
+ mSelectBtn->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloaterColorPicker::setMouseDownInSwatch (BOOL mouse_down_in_swatch)
+{
+ mMouseDownInSwatch = mouse_down_in_swatch;
+ if (mouse_down_in_swatch)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // get focus out of spinners so that they can update freely
+ mSelectBtn->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloaterColorPicker::setActive(BOOL active)
+{
+ // shut down pipette tool if active
+ if (!active && mPipetteBtn->getToggleState())
+ {
+ stopUsingPipette();
+ }
+ mActive = active;
+}
+
+void LLFloaterColorPicker::stopUsingPipette()
+{
+ if (gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette)
+ {
+ gToolMgr->clearTransientTool();
+ }
+}
diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h
new file mode 100644
index 0000000000..aa406c0dec
--- /dev/null
+++ b/indra/newview/llfloatercolorpicker.h
@@ -0,0 +1,179 @@
+/**
+ * @file llfloatercolorpicker.h
+ * @brief Generic system color picker
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERCOLORPICKER_H
+#define LL_LLFLOATERCOLORPICKER_H
+
+#include <vector>
+
+#include "llfloater.h"
+#include "llmemory.h"
+#include "llcolorswatch.h"
+#include "llspinctrl.h"
+#include "lltextureentry.h"
+
+class LLButton;
+class LLLineEditor;
+class LLCheckBoxCtrl;
+
+//////////////////////////////////////////////////////////////////////////////
+// floater class
+class LLFloaterColorPicker
+ : public LLFloater
+{
+ public:
+ LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show_apply_immediate = FALSE);
+ virtual ~LLFloaterColorPicker ();
+
+ // overrides
+ virtual BOOL postBuild ();
+ virtual void draw ();
+ virtual BOOL handleMouseDown ( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp ( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover ( S32 x, S32 y, MASK mask );
+ virtual void onClose(bool app_quitting);
+
+ // implicit methods
+ void createUI ();
+ void initUI ( F32 rValIn, F32 gValIn, F32 bValIn );
+ void showUI ();
+ void destroyUI ();
+ void cancelSelection ();
+ LLColorSwatchCtrl* getSwatch () { return mSwatch; };
+
+ // mutator / accessor for original RGB value
+ void setOrigRgb ( F32 origRIn, F32 origGIn, F32 origBIn );
+ void getOrigRgb ( F32& origROut, F32& origGOut, F32& origBOut );
+ F32 getOrigR () { return origR; };
+ F32 getOrigG () { return origG; };
+ F32 getOrigB () { return origB; };
+
+ // mutator / accessors for currernt RGB value
+ void setCurRgb ( F32 curRIn, F32 curGIn, F32 curBIn );
+ void getCurRgb ( F32& curROut, F32& curGOut, F32& curBOut );
+ F32 getCurR () { return curR; };
+ F32 getCurG () { return curG; };
+ F32 getCurB () { return curB; };
+
+ // mutator / accessors for currernt HSL value
+ void setCurHsl ( F32 curHIn, F32 curSIn, F32 curLIn );
+ void getCurHsl ( F32& curHOut, F32& curSOut, F32& curLOut );
+ F32 getCurH () { return curH; };
+ F32 getCurS () { return curS; };
+ F32 getCurL () { return curL; };
+
+ // updates current RGB/HSL values based on point in picker
+ BOOL updateRgbHslFromPoint ( S32 xPosIn, S32 yPosIn );
+
+ // updates text entry fields with current RGB/HSL
+ void updateTextEntry ();
+
+ void stopUsingPipette();
+
+ // mutator / accessor for mouse button pressed in region
+ void setMouseDownInHueRegion ( BOOL mouse_down_in_region );
+ BOOL getMouseDownInHueRegion () { return mMouseDownInHueRegion; };
+
+ void setMouseDownInLumRegion ( BOOL mouse_down_in_region );
+ BOOL getMouseDownInLumRegion () { return mMouseDownInLumRegion; };
+
+ void setMouseDownInSwatch (BOOL mouse_down_in_swatch);
+ BOOL getMouseDownInSwatch () { return mMouseDownInSwatch; }
+
+ // called when text entries (RGB/HSL etc.) are changed by user
+ void onTextEntryChanged ( LLUICtrl* ctrl );
+
+ // convert RGB to HSL and vice-versa
+ void hslToRgb ( F32 hValIn, F32 sValIn, F32 lValIn, F32& rValOut, F32& gValOut, F32& bValOut );
+ void rgbToHsl ( F32 rValIn, F32 gValIn, F32 bValIn, F32& hValOut, F32& sValOut, F32& lValOut );
+ F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn );
+
+ void setActive(BOOL active);
+ static void onMouseCaptureLost(LLMouseHandler* old_captor);
+
+ protected:
+ // callbacks
+ static void onClickCancel ( void* data );
+ static void onClickSelect ( void* data );
+ static void onClickPipette ( void* data );
+ static void onTextCommit ( LLUICtrl* ctrl, void* data );
+ static void onImmediateCheck ( LLUICtrl* ctrl, void* data );
+ static void onColorSelect( const LLTextureEntry& te, void *data );
+ private:
+ // turns on or off text entry commit call backs
+ void enableTextCallbacks ( BOOL stateIn );
+
+ // draws color selection palette
+ void drawPalette ();
+
+ // find a complimentary color to the one passed in that can be used to highlight
+ LLColor4& getComplimentaryColor ( LLColor4& backgroundColor );
+
+ // original RGB values
+ F32 origR, origG, origB;
+
+ // current RGB/HSL values
+ F32 curR, curG, curB;
+ F32 curH, curS, curL;
+
+ const S32 mComponents;
+
+ BOOL mMouseDownInLumRegion;
+ BOOL mMouseDownInHueRegion;
+ BOOL mMouseDownInSwatch;
+
+ const S32 mRGBViewerImageLeft;
+ const S32 mRGBViewerImageTop;
+ const S32 mRGBViewerImageWidth;
+ const S32 mRGBViewerImageHeight;
+
+ const S32 mLumRegionLeft;
+ const S32 mLumRegionTop;
+ const S32 mLumRegionWidth;
+ const S32 mLumRegionHeight;
+ const S32 mLumMarkerSize;
+
+ // Preview of the current color.
+ const S32 mSwatchRegionLeft;
+ const S32 mSwatchRegionTop;
+ const S32 mSwatchRegionWidth;
+ const S32 mSwatchRegionHeight;
+
+ LLView* mSwatchView;
+
+ const S32 numPaletteColumns;
+ const S32 numPaletteRows;
+ std::vector < LLColor4* > mPalette;
+ S32 highlightEntry;
+ const S32 mPaletteRegionLeft;
+ const S32 mPaletteRegionTop;
+ const S32 mPaletteRegionWidth;
+ const S32 mPaletteRegionHeight;
+
+ // image used to compose color grid
+ LLPointer<LLImageGL> mRGBImage;
+
+ // current swatch in use
+ LLColorSwatchCtrl* mSwatch;
+
+ // are we actively tied to some output?
+ BOOL mActive;
+
+ // enable/disable immediate updates
+ LLCheckBoxCtrl* mApplyImmediateCheck;
+ BOOL mCanApplyImmediately;
+
+ LLButton* mSelectBtn;
+ LLButton* mCancelBtn;
+
+ LLButton* mPipetteBtn;
+
+ F32 mContextConeOpacity;
+};
+
+#endif // LL_LLFLOATERCOLORPICKER_H
diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp
new file mode 100644
index 0000000000..e5aac8b252
--- /dev/null
+++ b/indra/newview/llfloaterfriends.cpp
@@ -0,0 +1,736 @@
+/**
+ * @file llfloaterfriends.cpp
+ * @author Phoenix
+ * @date 2005-01-13
+ * @brief Implementation of the friends floater
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterfriends.h"
+
+#include <sstream>
+
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llfloateravatarpicker.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llfloateravatarinfo.h"
+#include "llinventorymodel.h"
+#include "llnamelistctrl.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "llimview.h"
+#include "llvieweruictrlfactory.h"
+#include "llmenucommands.h"
+#include "llviewercontrol.h"
+#include "llviewermessage.h"
+#include "lltimer.h"
+#include "lltextbox.h"
+
+//Maximum number of people you can select to do an operation on at once.
+#define MAX_FRIEND_SELECT 20
+#define RIGHTS_CHANGE_TIMEOUT 5.0
+
+// simple class to observe the calling cards.
+class LLLocalFriendsObserver : public LLFriendObserver
+{
+public:
+ LLLocalFriendsObserver(LLFloaterFriends* floater) : mFloater(floater) {}
+ virtual ~LLLocalFriendsObserver() { mFloater = NULL; }
+ virtual void changed(U32 mask)
+ {
+ mFloater->updateFriends(mask);
+ }
+protected:
+ LLFloaterFriends* mFloater;
+};
+
+LLFloaterFriends* LLFloaterFriends::sInstance = NULL;
+
+LLFloaterFriends::LLFloaterFriends() :
+ LLFloater("Friends"),
+ LLEventTimer(1000000),
+ mObserver(NULL),
+ mMenuState(0),
+ mShowMaxSelectWarning(TRUE),
+ mAllowRightsChange(TRUE),
+ mNumRightsChanged(0)
+{
+ mTimer.stop();
+ sInstance = this;
+ mObserver = new LLLocalFriendsObserver(this);
+ LLAvatarTracker::instance().addObserver(mObserver);
+ gSavedSettings.setBOOL("ShowFriends", TRUE);
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(this, "floater_friends.xml");
+}
+
+LLFloaterFriends::~LLFloaterFriends()
+{
+ LLAvatarTracker::instance().removeObserver(mObserver);
+ delete mObserver;
+ sInstance = NULL;
+ gSavedSettings.setBOOL("ShowFriends", FALSE);
+}
+
+void LLFloaterFriends::tick()
+{
+ mTimer.stop();
+ mPeriod = 1000000;
+ mAllowRightsChange = TRUE;
+ updateFriends(LLFriendObserver::ADD);
+}
+
+// static
+void LLFloaterFriends::show(void*)
+{
+ if(sInstance)
+ {
+ sInstance->open();
+ }
+ else
+ {
+ LLFloaterFriends* self = new LLFloaterFriends;
+ self->open();
+ }
+}
+
+
+// static
+BOOL LLFloaterFriends::visible(void*)
+{
+ return sInstance && sInstance->getVisible();
+}
+
+
+// static
+void LLFloaterFriends::toggle(void*)
+{
+ if (sInstance)
+ {
+ sInstance->close();
+ }
+ else
+ {
+ show();
+ }
+}
+
+
+void LLFloaterFriends::updateFriends(U32 changed_mask)
+{
+ LLUUID selected_id;
+ LLCtrlListInterface *friends_list = sInstance->childGetListInterface("friend_list");
+ if (!friends_list) return;
+ LLCtrlScrollInterface *friends_scroll = sInstance->childGetScrollInterface("friend_list");
+ if (!friends_scroll) return;
+
+ // We kill the selection warning, otherwise we'll spam with warning popups
+ // if the maximum amount of friends are selected
+ mShowMaxSelectWarning = false;
+
+ LLDynamicArray<LLUUID> selected_friends = sInstance->getSelectedIDs();
+ if(changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
+ {
+ refreshNames();
+ }
+ else if(LLFriendObserver::POWERS)
+ {
+ --mNumRightsChanged;
+ if(mNumRightsChanged > 0)
+ {
+ mPeriod = RIGHTS_CHANGE_TIMEOUT;
+ mTimer.start();
+ mTimer.reset();
+ mAllowRightsChange = FALSE;
+ }
+ else
+ {
+ tick();
+ }
+ }
+ if(selected_friends.size() > 0)
+ {
+ // only non-null if friends was already found. This may fail,
+ // but we don't really care here, because refreshUI() will
+ // clean up the interface.
+ friends_list->setCurrentByID(selected_id);
+ for(LLDynamicArray<LLUUID>::iterator itr = selected_friends.begin(); itr != selected_friends.end(); ++itr)
+ {
+ friends_list->setSelectedByValue(*itr, true);
+ }
+ }
+
+ refreshUI();
+ mShowMaxSelectWarning = true;
+}
+
+// virtual
+BOOL LLFloaterFriends::postBuild()
+{
+
+ mFriendsList = LLUICtrlFactory::getScrollListByName(this, "friend_list");
+ mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT);
+ mFriendsList->setMaxiumumSelectCallback(onMaximumSelect);
+ childSetCommitCallback("friend_list", onSelectName, this);
+ childSetDoubleClickCallback("friend_list", onClickIM);
+
+ refreshNames();
+
+ childSetCommitCallback("online_status_cb", onClickOnlineStatus, this);
+ childSetCommitCallback("map_status_cb", onClickMapStatus, this);
+ childSetCommitCallback("modify_status_cb", onClickModifyStatus, this);
+ childSetAction("im_btn", onClickIM, this);
+ childSetAction("profile_btn", onClickProfile, this);
+ childSetAction("offer_teleport_btn", onClickOfferTeleport, this);
+ childSetAction("pay_btn", onClickPay, this);
+ childSetAction("add_btn", onClickAddFriend, this);
+ childSetAction("remove_btn", onClickRemove, this);
+ childSetAction("close_btn", onClickClose, this);
+
+ refreshUI();
+ return TRUE;
+}
+
+
+void LLFloaterFriends::addFriend(const std::string& name, const LLUUID& agent_id)
+{
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ const LLRelationship* relationInfo = at.getBuddyInfo(agent_id);
+ if(!relationInfo) return;
+ BOOL online = relationInfo->isOnline();
+
+ LLSD element;
+ element["id"] = agent_id;
+ element["columns"][LIST_FRIEND_NAME]["column"] = "friend_name";
+ element["columns"][LIST_FRIEND_NAME]["value"] = name.c_str();
+ element["columns"][LIST_FRIEND_NAME]["font"] = "SANSSERIF";
+ element["columns"][LIST_FRIEND_NAME]["font-style"] = "NORMAL";
+ element["columns"][LIST_ONLINE_STATUS]["column"] = "icon_online_status";
+ element["columns"][LIST_ONLINE_STATUS]["type"] = "text";
+ if (online)
+ {
+ element["columns"][LIST_FRIEND_NAME]["font-style"] = "BOLD";
+ element["columns"][LIST_ONLINE_STATUS]["type"] = "icon";
+ element["columns"][LIST_ONLINE_STATUS]["value"] = gViewerArt.getString("icon_avatar_online.tga");
+ }
+
+
+ element["columns"][LIST_VISIBLE_ONLINE]["column"] = "icon_visible_online";
+ element["columns"][LIST_VISIBLE_ONLINE]["type"] = "text";
+ if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
+ {
+ element["columns"][LIST_VISIBLE_ONLINE]["type"] = "icon";
+ element["columns"][LIST_VISIBLE_ONLINE]["value"] = gViewerArt.getString("ff_visible_online.tga");
+ }
+ element["columns"][LIST_VISIBLE_MAP]["column"] = "icon_visible_map";
+ element["columns"][LIST_VISIBLE_MAP]["type"] = "text";
+ if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))
+ {
+ element["columns"][LIST_VISIBLE_MAP]["type"] = "icon";
+ element["columns"][LIST_VISIBLE_MAP]["value"] = gViewerArt.getString("ff_visible_map.tga");
+ }
+ element["columns"][LIST_EDIT_MINE]["column"] = "icon_edit_mine";
+ element["columns"][LIST_EDIT_MINE]["type"] = "text";
+ if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ element["columns"][LIST_EDIT_MINE]["type"] = "icon";
+ element["columns"][LIST_EDIT_MINE]["value"] = gViewerArt.getString("ff_edit_mine.tga");
+ }
+ element["columns"][LIST_EDIT_THEIRS]["column"] = "icon_edit_theirs";
+ element["columns"][LIST_EDIT_THEIRS]["type"] = "text";
+ if(relationInfo->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ element["columns"][LIST_EDIT_THEIRS]["type"] = "icon";
+ element["columns"][LIST_EDIT_THEIRS]["value"] = gViewerArt.getString("ff_edit_theirs.tga");
+ }
+ mFriendsList->addElement(element, ADD_BOTTOM);
+}
+
+void LLFloaterFriends::reloadNames()
+{
+}
+
+void LLFloaterFriends::refreshRightsChangeList(U8 state)
+{
+ LLDynamicArray<LLUUID> friends = getSelectedIDs();
+ const LLRelationship* friend_status = NULL;
+ if(friends.size() > 0) friend_status = LLAvatarTracker::instance().getBuddyInfo(friends[0]);
+
+ LLSD row;
+ bool can_change_visibility = false;
+ bool can_change_modify = false;
+ bool can_change_online_multiple = true;
+ bool can_change_map_multiple = true;
+ LLTextBox* processing_label = LLUICtrlFactory::getTextBoxByName(this, "process_rights_label");
+ if(!mAllowRightsChange)
+ {
+ if(processing_label)
+ {
+ processing_label->setVisible(true);
+ state = 0;
+ }
+ }
+ else
+ {
+ if(processing_label)
+ {
+ processing_label->setVisible(false);
+ }
+ }
+ if(state == 1)
+ {
+ if(!friend_status->isOnline())
+ {
+ childSetEnabled("offer_teleport_btn", false);
+ }
+ can_change_visibility = true;
+ can_change_modify = true;
+ }
+ else if (state == 2)
+ {
+ can_change_visibility = true;
+ can_change_modify = false;
+ for(LLDynamicArray<LLUUID>::iterator itr = friends.begin(); itr != friends.end(); ++itr)
+ {
+ friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
+ if(!friend_status->isOnline())
+ {
+ childSetEnabled("offer_teleport_btn", false);
+ }
+ if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
+ {
+ can_change_online_multiple = false;
+ can_change_map_multiple = false;
+ }
+ else if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))
+ {
+ can_change_map_multiple = false;
+ }
+ }
+
+ }
+
+
+ LLCheckboxCtrl* check;
+ mMenuState = 0;
+
+ check = LLUICtrlFactory::getCheckBoxByName(this, "online_status_cb");
+ check->setEnabled(can_change_visibility);
+ check->set(FALSE);
+ if(!mAllowRightsChange) check->setVisible(FALSE);
+ else check->setVisible(TRUE);
+ if(friend_status)
+ {
+ check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_online_multiple);
+ if(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) mMenuState |= LLRelationship::GRANT_ONLINE_STATUS;
+ }
+
+ check = LLUICtrlFactory::getCheckBoxByName(this, "map_status_cb");
+ check->setEnabled(false);
+ check->set(FALSE);
+ if(!mAllowRightsChange) check->setVisible(FALSE);
+ else check->setVisible(TRUE);
+ if(friend_status)
+ {
+ check->setEnabled(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_visibility && can_change_online_multiple);
+ check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) && can_change_map_multiple);
+ if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION)) mMenuState |= LLRelationship::GRANT_MAP_LOCATION;
+ }
+
+ check = LLUICtrlFactory::getCheckBoxByName(this, "modify_status_cb");
+ check->setEnabled(can_change_modify);
+ check->set(FALSE);
+ if(!mAllowRightsChange) check->setVisible(FALSE);
+ else check->setVisible(TRUE);
+ if(can_change_modify)
+ {
+ if(friend_status)
+ {
+ check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
+ if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)) mMenuState |= LLRelationship::GRANT_MODIFY_OBJECTS;
+ }
+ }
+}
+
+void LLFloaterFriends::refreshNames()
+{
+ S32 pos = mFriendsList->getScrollPos();
+ mFriendsList->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ LLCollectAllBuddies collect;
+ LLAvatarTracker::instance().applyFunctor(collect);
+ LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
+ LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
+ for ( ; it != end; ++it)
+ {
+ const std::string& name = it->first;
+ const LLUUID& agent_id = it->second;
+ addFriend(name, agent_id);
+ }
+ it = collect.mOffline.begin();
+ end = collect.mOffline.end();
+ for ( ; it != end; ++it)
+ {
+ const std::string& name = it->first;
+ const LLUUID& agent_id = it->second;
+ addFriend(name, agent_id);
+ }
+ mFriendsList->setScrollPos(pos);
+}
+
+
+void LLFloaterFriends::refreshUI()
+{
+ BOOL single_selected = FALSE;
+ BOOL multiple_selected = FALSE;
+ int num_selected = mFriendsList->getAllSelected().size();
+ if(num_selected > 0)
+ {
+ single_selected = TRUE;
+ if(num_selected > 1) multiple_selected = TRUE;
+ }
+ //Options that can only be performed with one friend selected
+ childSetEnabled("profile_btn", single_selected && !multiple_selected);
+ childSetEnabled("pay_btn", single_selected && !multiple_selected);
+
+ //Options that can be performed with up to MAX_FRIEND_SELECT friends selected
+ //(single_selected will always be true in this situations)
+ childSetEnabled("remove_btn", single_selected);
+ childSetEnabled("im_btn", single_selected);
+ childSetEnabled("friend_rights", single_selected);
+
+ //Note: We reset this in refreshRightsChangeList since we already have to iterate
+ //through all selected friends there
+ childSetEnabled("offer_teleport_btn", single_selected);
+
+ refreshRightsChangeList((single_selected + multiple_selected));
+}
+
+// static
+LLDynamicArray<LLUUID> LLFloaterFriends::getSelectedIDs()
+{
+ LLUUID selected_id;
+ LLDynamicArray<LLUUID> friend_ids;
+ if(sInstance)
+ {
+ std::vector<LLScrollListItem*> selected = sInstance->mFriendsList->getAllSelected();
+ for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
+ {
+ friend_ids.push_back((*itr)->getUUID());
+ }
+ }
+ return friend_ids;
+}
+
+// static
+void LLFloaterFriends::onSelectName(LLUICtrl* ctrl, void* user_data)
+{
+ if(sInstance)
+ {
+ sInstance->refreshUI();
+ }
+}
+
+//static
+void LLFloaterFriends::onMaximumSelect(void* user_data)
+{
+ LLString::format_map_t args;
+ args["[MAX_SELECT]"] = llformat("%d", MAX_FRIEND_SELECT);
+ LLNotifyBox::showXml("MaxListSelectMessage", args);
+};
+
+// static
+void LLFloaterFriends::onClickProfile(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickProfile()" << llendl;
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() > 0)
+ {
+ LLUUID agent_id = ids[0];
+ BOOL online;
+ online = LLAvatarTracker::instance().isBuddyOnline(agent_id);
+ LLFloaterAvatarInfo::showFromFriend(agent_id, online);
+ }
+}
+
+// static
+void LLFloaterFriends::onClickIM(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickIM()" << llendl;
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() > 0)
+ {
+ if(ids.size() == 1)
+ {
+ LLUUID agent_id = ids[0];
+ const LLRelationship* info = LLAvatarTracker::instance().getBuddyInfo(agent_id);
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(info && gCacheName->getName(agent_id, first, last))
+ {
+ char buffer[MAX_STRING];
+ snprintf(buffer, MAX_STRING, "%s %s", first, last);
+ gIMView->setFloaterOpen(TRUE);
+ gIMView->addSession(
+ buffer,
+ IM_NOTHING_SPECIAL,
+ agent_id);
+ }
+ }
+ else
+ {
+ LLUUID session_id;
+ session_id.generate();
+ gIMView->setFloaterOpen(TRUE);
+ gIMView->addSession(
+ "Friends Conference",
+ IM_SESSION_ADD,
+ session_id,
+ ids);
+ }
+ }
+}
+
+// static
+void LLFloaterFriends::requestFriendship(const LLUUID& target_id, const LLString& target_name)
+{
+ // HACK: folder id stored as "message"
+ LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+ std::string message = calling_card_folder_id.getString();
+
+ send_improved_im(target_id,
+ target_name.c_str(),
+ message.c_str(),
+ IM_ONLINE,
+ IM_FRIENDSHIP_OFFERED);
+}
+
+// static
+void LLFloaterFriends::callbackAddFriend(S32 option, void* user_data)
+{
+ if (option == 0)
+ {
+ LLFloaterFriends* self = (LLFloaterFriends*)user_data;
+ requestFriendship(self->mAddFriendID, self->mAddFriendName);
+ }
+}
+
+// static
+void LLFloaterFriends::onPickAvatar(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& ids,
+ void* user_data)
+{
+ if (names.empty()) return;
+ if (ids.empty()) return;
+
+ LLFloaterFriends* self = (LLFloaterFriends*)user_data;
+ self->mAddFriendID = ids[0];
+ self->mAddFriendName = names[0];
+
+ if(ids[0] == gAgentID)
+ {
+ LLNotifyBox::showXml("AddSelfFriend");
+ return;
+ }
+
+ // TODO: accept a line of text with this dialog
+ LLString::format_map_t args;
+ args["[NAME]"] = names[0];
+ gViewerWindow->alertXml("AddFriend", args, callbackAddFriend, user_data);
+}
+
+// static
+void LLFloaterFriends::onClickAddFriend(void* user_data)
+{
+ LLFloaterAvatarPicker::show(onPickAvatar, user_data, FALSE, TRUE);
+}
+
+// static
+void LLFloaterFriends::onClickRemove(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickRemove()" << llendl;
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLStringBase<char>::format_map_t args;
+ if(ids.size() > 0)
+ {
+ LLString msgType = "RemoveFromFriends";
+ if(ids.size() == 1)
+ {
+ LLUUID agent_id = ids[0];
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ args["[FIRST_NAME]"] = first;
+ args["[LAST_NAME]"] = last;
+ }
+ }
+ else
+ {
+ msgType = "RemoveMultipleFromFriends";
+ }
+ gViewerWindow->alertXml(msgType,
+ args,
+ &handleRemove,
+ (void*)new LLDynamicArray<LLUUID>(ids));
+ }
+}
+
+// static
+void LLFloaterFriends::onClickOfferTeleport(void*)
+{
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() > 0)
+ {
+ handle_lure(ids);
+ }
+}
+
+// static
+void LLFloaterFriends::onClickPay(void*)
+{
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() == 1)
+ {
+ handle_pay_by_id(ids[0]);
+ }
+}
+
+// static
+void LLFloaterFriends::onClickClose(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickClose()" << llendl;
+ if(sInstance)
+ {
+ sInstance->onClose(false);
+ }
+}
+
+void LLFloaterFriends::onClickOnlineStatus(LLUICtrl* ctrl, void* user_data)
+{
+ bool checked = ctrl->getValue();
+ sInstance->updateMenuState(LLRelationship::GRANT_ONLINE_STATUS, checked);
+ sInstance->applyRightsToFriends(LLRelationship::GRANT_ONLINE_STATUS, checked);
+}
+
+void LLFloaterFriends::onClickMapStatus(LLUICtrl* ctrl, void* user_data)
+{
+ bool checked = ctrl->getValue();
+ sInstance->updateMenuState(LLRelationship::GRANT_MAP_LOCATION, checked);
+ sInstance->applyRightsToFriends(LLRelationship::GRANT_MAP_LOCATION, checked);
+}
+
+void LLFloaterFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
+{
+ bool checked = ctrl->getValue();
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLStringBase<char>::format_map_t args;
+ if(ids.size() > 0)
+ {
+ if(ids.size() == 1)
+ {
+ LLUUID agent_id = ids[0];
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ args["[FIRST_NAME]"] = first;
+ args["[LAST_NAME]"] = last;
+ }
+ if(checked) gViewerWindow->alertXml("GrantModifyRights", args, handleModifyRights, NULL);
+ else gViewerWindow->alertXml("RevokeModifyRights", args, handleModifyRights, NULL);
+ }
+ else return;
+ }
+}
+
+void LLFloaterFriends::handleModifyRights(S32 option, void* user_data)
+{
+ if(sInstance)
+ {
+ if(!option)
+ {
+ sInstance->updateMenuState(LLRelationship::GRANT_MODIFY_OBJECTS, !((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ sInstance->applyRightsToFriends(LLRelationship::GRANT_MODIFY_OBJECTS, ((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ }
+ sInstance->refreshUI();
+ }
+}
+
+void LLFloaterFriends::updateMenuState(S32 flag, BOOL value)
+{
+ if(value) mMenuState |= flag;
+ else mMenuState &= ~flag;
+}
+
+void LLFloaterFriends::applyRightsToFriends(S32 flag, BOOL value)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_GrantUserRights);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUID(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
+
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ S32 rights;
+ for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
+ {
+ rights = LLAvatarTracker::instance().getBuddyInfo(*itr)->getRightsGrantedTo();
+ if(LLAvatarTracker::instance().getBuddyInfo(*itr)->isRightGrantedTo(flag) != (bool)value)
+ {
+ if(value) rights |= flag;
+ else rights &= ~flag;
+ msg->nextBlockFast(_PREHASH_Rights);
+ msg->addUUID(_PREHASH_AgentRelated, *itr);
+ msg->addS32(_PREHASH_RelatedRights, rights);
+ }
+ }
+ mNumRightsChanged = ids.size();
+ gAgent.sendReliableMessage();
+}
+
+
+
+// static
+void LLFloaterFriends::handleRemove(S32 option, void* user_data)
+{
+ LLDynamicArray<LLUUID>* ids = static_cast<LLDynamicArray<LLUUID>*>(user_data);
+ for(LLDynamicArray<LLUUID>::iterator itr = ids->begin(); itr != ids->end(); ++itr)
+ {
+ LLUUID id = (*itr);
+ const LLRelationship* ip = LLAvatarTracker::instance().getBuddyInfo(id);
+ if(ip)
+ {
+ switch(option)
+ {
+ case 0: // YES
+ if( ip->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ LLAvatarTracker::instance().empower(id, FALSE);
+ LLAvatarTracker::instance().notifyObservers();
+ }
+ LLAvatarTracker::instance().terminateBuddy(id);
+ LLAvatarTracker::instance().notifyObservers();
+ gInventory.addChangedMask(LLInventoryObserver::LABEL | LLInventoryObserver::CALLING_CARD, LLUUID::null);
+ gInventory.notifyObservers();
+ break;
+
+ case 1: // NO
+ default:
+ llinfos << "No removal performed." << llendl;
+ break;
+ }
+ }
+
+ }
+ delete ids;
+}
diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h
new file mode 100644
index 0000000000..a71b53f206
--- /dev/null
+++ b/indra/newview/llfloaterfriends.h
@@ -0,0 +1,124 @@
+/**
+ * @file llfloaterfriends.h
+ * @author Phoenix
+ * @date 2005-01-13
+ * @brief Declaration of class for displaying the local agent's friends.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERFRIENDS_H
+#define LL_LLFLOATERFRIENDS_H
+
+#include "llfloater.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "lltimer.h"
+
+class LLFriendObserver;
+
+
+/**
+ * @class LLFloaterFriends
+ * @brief An instance of this class is used for displaying your friends
+ * and gives you quick access to all agents which a user relationship.
+ *
+ * @sa LLFloater
+ */
+class LLFloaterFriends : public LLFloater, public LLEventTimer
+{
+public:
+ virtual ~LLFloaterFriends();
+
+ /**
+ * @brief This method either creates or brings to the front the
+ * current instantiation of this floater. There is only once since
+ * you can currently only look at your local friends.
+ */
+ static void show(void* ignored = NULL);
+
+ virtual void tick();
+
+ /**
+ * @brief This method is called in response to the LLAvatarTracker
+ * sending out a changed() message.
+ */
+ void updateFriends(U32 changed_mask);
+
+ virtual BOOL postBuild();
+
+ static BOOL visible(void* unused = NULL);
+
+ // Toggles visibility of floater
+ static void toggle(void* unused = NULL);
+
+ static void requestFriendship(const LLUUID& target_id, const LLString& target_name);
+
+private:
+
+ enum FRIENDS_COLUMN_ORDER
+ {
+ LIST_ONLINE_STATUS,
+ LIST_FRIEND_NAME,
+ LIST_VISIBLE_ONLINE,
+ LIST_VISIBLE_MAP,
+ LIST_EDIT_MINE,
+ LIST_EDIT_THEIRS
+ };
+
+ // protected members
+ LLFloaterFriends();
+
+ void reloadNames();
+ void refreshNames();
+ void refreshUI();
+ void refreshRightsChangeList(U8 state);
+ void applyRightsToFriends(S32 flag, BOOL value);
+ void updateMenuState(S32 flag, BOOL value);
+ S32 getMenuState() { return mMenuState; }
+ void addFriend(const std::string& name, const LLUUID& agent_id);
+
+ // return LLUUID::null if nothing is selected
+ static LLDynamicArray<LLUUID> getSelectedIDs();
+
+ // callback methods
+ static void onSelectName(LLUICtrl* ctrl, void* user_data);
+ static void callbackAddFriend(S32 option, void* user_data);
+ static void onPickAvatar(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* user_data);
+ static void onMaximumSelect(void* user_data);
+
+ static void onClickIM(void* user_data);
+ static void onClickProfile(void* user_data);
+ static void onClickAddFriend(void* user_data);
+ static void onClickRemove(void* user_data);
+
+ static void onClickOfferTeleport(void* user_data);
+ static void onClickPay(void* user_data);
+
+ static void onClickClose(void* user_data);
+
+ static void onClickOnlineStatus(LLUICtrl* ctrl, void* user_data);
+ static void onClickMapStatus(LLUICtrl* ctrl, void* user_data);
+ static void onClickModifyStatus(LLUICtrl* ctrl, void* user_data);
+
+ static void handleRemove(S32 option, void* user_data);
+ static void handleModifyRights(S32 option, void* user_data);
+
+private:
+ // static data
+ static LLFloaterFriends* sInstance;
+
+ // member data
+ LLFriendObserver* mObserver;
+ LLUUID mAddFriendID;
+ LLString mAddFriendName;
+ LLScrollListCtrl* mFriendsList;
+ S32 mMenuState;
+ BOOL mShowMaxSelectWarning;
+ BOOL mAllowRightsChange;
+ S32 mNumRightsChanged;
+};
+
+
+#endif // LL_LLFLOATERFRIENDS_H
diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp
new file mode 100644
index 0000000000..a14c9f11cd
--- /dev/null
+++ b/indra/newview/llfloatergesture.cpp
@@ -0,0 +1,396 @@
+/**
+ * @file llfloatergesture.cpp
+ * @brief Read-only list of gestures from your inventory.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergesture.h"
+
+#include "lldir.h"
+#include "llinventory.h"
+#include "llmultigesture.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llgesturemgr.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llpreviewgesture.h"
+#include "llresizehandle.h"
+#include "llscrollbar.h"
+#include "llscrollcontainer.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llvoavatar.h"
+#include "llviewercontrol.h"
+
+// static
+LLFloaterGesture* LLFloaterGesture::sInstance = NULL;
+LLFloaterGestureObserver* LLFloaterGesture::sObserver = NULL;
+
+BOOL item_name_precedes( LLInventoryItem* a, LLInventoryItem* b )
+{
+ return LLString::precedesDict( a->getName(), b->getName() );
+}
+
+class LLFloaterGestureObserver : public LLGestureManagerObserver
+{
+public:
+ LLFloaterGestureObserver() {}
+ virtual ~LLFloaterGestureObserver() {}
+ virtual void changed() { LLFloaterGesture::refreshAll(); }
+};
+
+//---------------------------------------------------------------------------
+// LLFloaterGesture
+//---------------------------------------------------------------------------
+LLFloaterGesture::LLFloaterGesture()
+: LLFloater("Gesture Floater")
+{
+ sInstance = this;
+
+ sObserver = new LLFloaterGestureObserver;
+ gGestureManager.addObserver(sObserver);
+}
+
+// virtual
+LLFloaterGesture::~LLFloaterGesture()
+{
+ gGestureManager.removeObserver(sObserver);
+ delete sObserver;
+ sObserver = NULL;
+
+ sInstance = NULL;
+
+ // Custom saving rectangle, since load must be done
+ // after postBuild.
+ gSavedSettings.setRect("FloaterGestureRect", mRect);
+}
+
+// virtual
+BOOL LLFloaterGesture::postBuild()
+{
+ LLString label;
+
+ // Translate title
+ label = getTitle();
+
+ setTitle(label);
+
+ childSetCommitCallback("gesture_list", onCommitList, this);
+ childSetDoubleClickCallback("gesture_list", onClickPlay);
+
+ childSetAction("inventory_btn", onClickInventory, this);
+
+ childSetAction("edit_btn", onClickEdit, this);
+
+ childSetAction("play_btn", onClickPlay, this);
+ childSetAction("stop_btn", onClickPlay, this);
+
+ childSetAction("new_gesture_btn", onClickNew, this);
+
+ childSetVisible("play_btn", true);
+ childSetVisible("stop_btn", false);
+ setDefaultBtn("play_btn");
+
+ return TRUE;
+}
+
+
+// static
+void LLFloaterGesture::show()
+{
+ if (sInstance)
+ {
+ sInstance->open();
+ return;
+ }
+
+ LLFloaterGesture *self = new LLFloaterGesture();
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(self, "floater_gesture.xml");
+
+ // Fix up rectangle
+ LLRect rect = gSavedSettings.getRect("FloaterGestureRect");
+ self->reshape(rect.getWidth(), rect.getHeight());
+ self->setRect(rect);
+
+ self->buildGestureList();
+
+ self->childSetFocus("gesture_list");
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (list) list->selectFirstItem();
+
+ self->mSelectedID = LLUUID::null;
+
+ // Update button labels
+ onCommitList(NULL, self);
+ self->open();
+}
+
+// static
+void LLFloaterGesture::toggleVisibility()
+{
+ if(sInstance && sInstance->getVisible())
+ {
+ sInstance->close();
+ }
+ else
+ {
+ show();
+ }
+}
+
+// static
+void LLFloaterGesture::refreshAll()
+{
+ if (sInstance)
+ {
+ sInstance->buildGestureList();
+
+ LLCtrlListInterface *list = sInstance->childGetListInterface("gesture_list");
+ if (!list) return;
+
+ if (sInstance->mSelectedID.isNull())
+ {
+ list->selectFirstItem();
+ }
+ else
+ {
+ if (list->setCurrentByID(sInstance->mSelectedID))
+ {
+ LLCtrlScrollInterface *scroll = sInstance->childGetScrollInterface("gesture_list");
+ if (scroll) scroll->scrollToShowSelected();
+ }
+ else
+ {
+ list->selectFirstItem();
+ }
+ }
+
+ // Update button labels
+ onCommitList(NULL, sInstance);
+ }
+}
+
+void LLFloaterGesture::buildGestureList()
+{
+ LLCtrlListInterface *list = childGetListInterface("gesture_list");
+ if (!list) return;
+
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ LLGestureManager::item_map_t::iterator it;
+ for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
+ {
+ const LLUUID& item_id = (*it).first;
+ LLMultiGesture* gesture = (*it).second;
+
+ // Note: Can have NULL item if inventory hasn't arrived yet.
+ std::string item_name = "Loading...";
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if (item)
+ {
+ item_name = item->getName();
+ }
+
+ LLString font_style = "NORMAL";
+ // If gesture is playing, bold it
+
+ LLSD element;
+ element["id"] = item_id;
+
+ if (gesture)
+ {
+ if (gesture->mPlaying)
+ {
+ font_style = "BOLD";
+ }
+
+ element["columns"][0]["column"] = "trigger";
+ element["columns"][0]["value"] = gesture->mTrigger;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = font_style;
+
+ LLString key_string = LLKeyboard::stringFromKey(gesture->mKey);
+ LLString buffer;
+
+ {
+ if (gesture->mKey == KEY_NONE)
+ {
+ buffer = "---";
+ key_string = "~~~"; // alphabetize to end
+ }
+ else
+ {
+ if (gesture->mMask & MASK_CONTROL) buffer.append("Ctrl-");
+ if (gesture->mMask & MASK_ALT) buffer.append("Alt-");
+ if (gesture->mMask & MASK_SHIFT) buffer.append("Shift-");
+ if ((gesture->mMask & (MASK_CONTROL|MASK_ALT|MASK_SHIFT)) &&
+ (key_string[0] == '-' || key_string[0] == '='))
+ {
+ buffer.append(" ");
+ }
+ buffer.append(key_string);
+ }
+ }
+ element["columns"][1]["column"] = "shortcut";
+ element["columns"][1]["value"] = buffer;
+ element["columns"][1]["font"] = "SANSSERIF";
+ element["columns"][1]["font-style"] = font_style;
+
+ // hidden column for sorting
+ element["columns"][2]["column"] = "key";
+ element["columns"][2]["value"] = key_string;
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][2]["font-style"] = font_style;
+
+ // Only add "playing" if we've got the name, less confusing. JC
+ if (item && gesture->mPlaying)
+ {
+ item_name += " (Playing)";
+ }
+ element["columns"][3]["column"] = "name";
+ element["columns"][3]["value"] = item_name;
+ element["columns"][3]["font"] = "SANSSERIF";
+ element["columns"][3]["font-style"] = font_style;
+ }
+ else
+ {
+ element["columns"][0]["column"] = "trigger";
+ element["columns"][0]["value"] = "";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = font_style;
+ element["columns"][0]["column"] = "trigger";
+ element["columns"][0]["value"] = "---";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = font_style;
+ element["columns"][2]["column"] = "key";
+ element["columns"][2]["value"] = "~~~";
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][2]["font-style"] = font_style;
+ element["columns"][3]["column"] = "name";
+ element["columns"][3]["value"] = item_name;
+ element["columns"][3]["font"] = "SANSSERIF";
+ element["columns"][3]["font-style"] = font_style;
+ }
+ list->addElement(element, ADD_BOTTOM);
+ }
+}
+
+// static
+void LLFloaterGesture::onClickInventory(void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (!list) return;
+ const LLUUID& item_id = list->getCurrentID();
+
+ LLInventoryView* inv = LLInventoryView::showAgentInventory();
+ if (!inv) return;
+ inv->getPanel()->setSelection(item_id, TRUE);
+}
+
+// static
+void LLFloaterGesture::onClickPlay(void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (!list) return;
+ const LLUUID& item_id = list->getCurrentID();
+
+ if (gGestureManager.isGesturePlaying(item_id))
+ {
+ gGestureManager.stopGesture(item_id);
+ }
+ else
+ {
+ gGestureManager.playGesture(item_id);
+ }
+}
+
+class GestureShowCallback : public LLInventoryCallback
+{
+public:
+ GestureShowCallback(LLString &title)
+ {
+ mTitle = title;
+ }
+ ~GestureShowCallback() {}
+ void fire(const LLUUID &inv_item)
+ {
+ LLPreviewGesture::show(mTitle.c_str(), inv_item, LLUUID::null);
+ }
+private:
+ LLString mTitle;
+};
+
+// static
+void LLFloaterGesture::onClickNew(void* data)
+{
+ LLString title("Gesture: ");
+ title.append("New Gesture");
+ LLPointer<LLInventoryCallback> cb = new GestureShowCallback(title);
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ LLUUID::null, LLTransactionID::tnull, "New Gesture", "", LLAssetType::AT_GESTURE,
+ LLInventoryType::IT_GESTURE, NOT_WEARABLE, PERM_MOVE | PERM_TRANSFER, cb);
+}
+
+
+// static
+void LLFloaterGesture::onClickEdit(void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (!list) return;
+ const LLUUID& item_id = list->getCurrentID();
+
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if (!item) return;
+
+ LLString title("Gesture: ");
+ title.append(item->getName());
+
+ LLPreviewGesture* previewp = LLPreviewGesture::show(title, item_id, LLUUID::null);
+ if (!previewp->getHost())
+ {
+ previewp->setRect(gFloaterView->findNeighboringPosition(self, previewp));
+ }
+}
+
+// static
+void LLFloaterGesture::onCommitList(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ const LLUUID& item_id = self->childGetValue("gesture_list").asUUID();
+
+ self->mSelectedID = item_id;
+ if (gGestureManager.isGesturePlaying(item_id))
+ {
+ self->childSetVisible("play_btn", false);
+ self->childSetVisible("stop_btn", true);
+ }
+ else
+ {
+ self->childSetVisible("play_btn", true);
+ self->childSetVisible("stop_btn", false);
+ }
+}
diff --git a/indra/newview/llfloatergesture.h b/indra/newview/llfloatergesture.h
new file mode 100644
index 0000000000..7f78754577
--- /dev/null
+++ b/indra/newview/llfloatergesture.h
@@ -0,0 +1,64 @@
+/**
+ * @file llfloatergesture.h
+ * @brief Read-only list of gestures from your inventory.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * (Also has legacy gesture editor for testing.)
+ */
+
+#ifndef LL_LLFLOATERGESTURE_H
+#define LL_LLFLOATERGESTURE_H
+
+#include "llfloater.h"
+
+#include "lldarray.h"
+
+class LLScrollableContainerView;
+class LLView;
+class LLButton;
+class LLLineEditor;
+class LLComboBox;
+class LLViewerGesture;
+class LLGestureOptions;
+class LLScrollListCtrl;
+class LLFloaterGestureObserver;
+class LLFloaterGestureInventoryObserver;
+
+class LLFloaterGesture
+: public LLFloater
+{
+public:
+ LLFloaterGesture();
+ virtual ~LLFloaterGesture();
+
+ virtual BOOL postBuild();
+
+ static void show();
+ static void toggleVisibility();
+ static void refreshAll();
+
+protected:
+ // Reads from the gesture manager's list of active gestures
+ // and puts them in this list.
+ void buildGestureList();
+
+ static void onClickInventory(void* data);
+ static void onClickEdit(void* data);
+ static void onClickPlay(void* data);
+ static void onClickNew(void* data);
+ static void onCommitList(LLUICtrl* ctrl, void* data);
+
+protected:
+ LLUUID mSelectedID;
+
+ static LLFloaterGesture* sInstance;
+ static LLFloaterGestureObserver* sObserver;
+ static LLFloaterGestureInventoryObserver* sInventoryObserver;
+};
+
+
+#endif
diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp
new file mode 100644
index 0000000000..7c29460f0e
--- /dev/null
+++ b/indra/newview/llfloatergodtools.cpp
@@ -0,0 +1,1446 @@
+/**
+ * @file llfloatergodtools.cpp
+ * @brief The on-screen rectangle with tool options.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergodtools.h"
+
+#include "llcoord.h"
+#include "llfontgl.h"
+#include "llframetimer.h"
+#include "llgl.h"
+#include "llhost.h"
+#include "llregionflags.h"
+#include "llstring.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldraghandle.h"
+#include "llfloater.h"
+#include "llfocusmgr.h"
+#include "llfloatertopobjects.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llspinctrl.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lluictrl.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llfloateravatarpicker.h"
+#include "llnotify.h"
+#include "llxfermanager.h"
+#include "llvlcomposition.h"
+#include "llsurface.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+
+#include "lltransfertargetfile.h"
+#include "lltransfersourcefile.h"
+
+const F32 SECONDS_BETWEEN_UPDATE_REQUESTS = 5.0f;
+
+static LLFloaterGodTools* sGodTools = NULL;
+
+//*****************************************************************************
+// LLFloaterGodTools
+//*****************************************************************************
+
+// static
+LLFloaterGodTools* LLFloaterGodTools::instance()
+{
+ if (!sGodTools)
+ {
+ sGodTools = new LLFloaterGodTools();
+ sGodTools->open();
+ sGodTools->center();
+ sGodTools->setFocus(TRUE);
+ }
+ return sGodTools;
+}
+
+
+// static
+void LLFloaterGodTools::refreshAll()
+{
+ LLFloaterGodTools* god_tools = instance();
+ if (god_tools)
+ {
+ if (gAgent.getRegionHost() != god_tools->mCurrentHost)
+ {
+ // we're in a new region
+ god_tools->sendRegionInfoRequest();
+ }
+ }
+}
+
+
+
+LLFloaterGodTools::LLFloaterGodTools()
+: LLFloater("godtools floater"),
+ mCurrentHost(LLHost::invalid),
+ mUpdateTimer()
+{
+ LLCallbackMap::map_t factory_map;
+ factory_map["grid"] = LLCallbackMap(createPanelGrid, this);
+ factory_map["region"] = LLCallbackMap(createPanelRegion, this);
+ factory_map["objects"] = LLCallbackMap(createPanelObjects, this);
+ factory_map["request"] = LLCallbackMap(createPanelRequest, this);
+ gUICtrlFactory->buildFloater(this, "floater_god_tools.xml", &factory_map);
+
+ childSetTabChangeCallback("GodTools Tabs", "grid", onTabChanged, this);
+ childSetTabChangeCallback("GodTools Tabs", "region", onTabChanged, this);
+ childSetTabChangeCallback("GodTools Tabs", "objects", onTabChanged, this);
+ childSetTabChangeCallback("GodTools Tabs", "request", onTabChanged, this);
+
+ sendRegionInfoRequest();
+
+ childShowTab("GodTools Tabs", "region");
+}
+
+// static
+void* LLFloaterGodTools::createPanelGrid(void *userdata)
+{
+ return new LLPanelGridTools("grid");
+}
+
+// static
+void* LLFloaterGodTools::createPanelRegion(void *userdata)
+{
+ LLFloaterGodTools* self = (LLFloaterGodTools*)userdata;
+ self->mPanelRegionTools = new LLPanelRegionTools("region");
+ return self->mPanelRegionTools;
+}
+
+// static
+void* LLFloaterGodTools::createPanelObjects(void *userdata)
+{
+ LLFloaterGodTools* self = (LLFloaterGodTools*)userdata;
+ self->mPanelObjectTools = new LLPanelObjectTools("objects");
+ return self->mPanelObjectTools;
+}
+
+// static
+void* LLFloaterGodTools::createPanelRequest(void *userdata)
+{
+ return new LLPanelRequestTools("region");
+}
+
+LLFloaterGodTools::~LLFloaterGodTools()
+{
+ // children automatically deleted
+}
+
+U32 LLFloaterGodTools::computeRegionFlags() const
+{
+ U32 flags = gAgent.getRegion()->getRegionFlags();
+ if (mPanelRegionTools) flags = mPanelRegionTools->computeRegionFlags(flags);
+ if (mPanelObjectTools) flags = mPanelObjectTools->computeRegionFlags(flags);
+ return flags;
+}
+
+
+void LLFloaterGodTools::updatePopup(LLCoordGL center, MASK mask)
+{
+}
+
+// virtual
+void LLFloaterGodTools::onClose(bool app_quitting)
+{
+ if (sGodTools)
+ {
+ sGodTools->setVisible(FALSE);
+ }
+}
+
+// virtual
+void LLFloaterGodTools::draw()
+{
+ if (mCurrentHost == LLHost::invalid)
+ {
+ if (mUpdateTimer.getElapsedTimeF32() > SECONDS_BETWEEN_UPDATE_REQUESTS)
+ {
+ sendRegionInfoRequest();
+ }
+ }
+ else if (gAgent.getRegionHost() != mCurrentHost)
+ {
+ sendRegionInfoRequest();
+ }
+ LLFloater::draw();
+}
+
+// static
+void LLFloaterGodTools::show(void *)
+{
+ LLFloaterGodTools* god_tools = instance();
+ god_tools->open();
+ LLPanel *panel = god_tools->childGetVisibleTab("GodTools Tabs");
+ if (panel) panel->setFocus(TRUE);
+ if (god_tools->mPanelObjectTools) god_tools->mPanelObjectTools->setTargetAvatar(LLUUID::null);
+
+ if (gAgent.getRegionHost() != god_tools->mCurrentHost)
+ {
+ // we're in a new region
+ god_tools->sendRegionInfoRequest();
+ }
+}
+
+void LLFloaterGodTools::showPanel(const LLString& panel_name)
+{
+ childShowTab("GodTools Tabs", panel_name);
+ open();
+ LLPanel *panel = childGetVisibleTab("GodTools Tabs");
+ if (panel) panel->setFocus(TRUE);
+}
+
+
+// static
+void LLFloaterGodTools::onTabChanged(void* data, bool from_click)
+{
+ LLPanel* panel = (LLPanel*)data;
+ if (panel)
+ {
+ panel->setFocus(TRUE);
+ }
+}
+
+
+// static
+void LLFloaterGodTools::processRegionInfo(LLMessageSystem* msg)
+{
+ LLHost host = msg->getSender();
+ if (host != gAgent.getRegionHost())
+ {
+ // update is for a different region than the one we're in
+ return;
+ }
+
+ //const S32 SIM_NAME_BUF = 256;
+ U32 region_flags;
+ U8 sim_access;
+ U8 agent_limit;
+ char sim_name[MAX_STRING];
+ U32 estate_id;
+ U32 parent_estate_id;
+ F32 water_height;
+ F32 billable_factor;
+ F32 object_bonus_factor;
+ F32 terrain_raise_limit;
+ F32 terrain_lower_limit;
+ S32 price_per_meter;
+ S32 redirect_grid_x;
+ S32 redirect_grid_y;
+ LLUUID cache_id;
+
+ msg->getStringFast(_PREHASH_RegionInfo, _PREHASH_SimName, MAX_STRING, sim_name);
+ msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_EstateID, estate_id);
+ msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_ParentEstateID, parent_estate_id);
+ msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_RegionFlags, region_flags);
+ msg->getU8Fast(_PREHASH_RegionInfo, _PREHASH_SimAccess, sim_access);
+ msg->getU8Fast(_PREHASH_RegionInfo, _PREHASH_MaxAgents, agent_limit);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_ObjectBonusFactor, object_bonus_factor);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_BillableFactor, billable_factor);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_WaterHeight, water_height);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainRaiseLimit, terrain_raise_limit);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainLowerLimit, terrain_lower_limit);
+ msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_PricePerMeter, price_per_meter);
+ msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_RedirectGridX, redirect_grid_x);
+ msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_RedirectGridY, redirect_grid_y);
+
+ // push values to the current LLViewerRegion
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ regionp->setRegionNameAndZone(sim_name);
+ regionp->setRegionFlags(region_flags);
+ regionp->setSimAccess(sim_access);
+ regionp->setWaterHeight(water_height);
+ regionp->setBillableFactor(billable_factor);
+ }
+
+ // push values to god tools, if available
+ if (sGodTools
+ && sGodTools->mPanelRegionTools
+ && sGodTools->mPanelObjectTools
+ && msg
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* rtool = sGodTools->mPanelRegionTools;
+ sGodTools->mCurrentHost = host;
+
+ // store locally
+ rtool->setSimName(sim_name);
+ rtool->setEstateID(estate_id);
+ rtool->setParentEstateID(parent_estate_id);
+ rtool->setCheckFlags(region_flags);
+ rtool->setBillableFactor(billable_factor);
+ rtool->setPricePerMeter(price_per_meter);
+ rtool->setRedirectGridX(redirect_grid_x);
+ rtool->setRedirectGridY(redirect_grid_y);
+ rtool->enableAllWidgets();
+
+ LLPanelObjectTools *otool = sGodTools->mPanelObjectTools;
+ otool->setCheckFlags(region_flags);
+ otool->enableAllWidgets();
+
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if ( !regionp )
+ {
+ // -1 implies non-existent
+ rtool->setGridPosX(-1);
+ rtool->setGridPosY(-1);
+ }
+ else
+ {
+ //compute the grid position of the region
+ LLVector3d global_pos = regionp->getPosGlobalFromRegion(LLVector3::zero);
+ S32 grid_pos_x = (S32) (global_pos.mdV[VX] / 256.0f);
+ S32 grid_pos_y = (S32) (global_pos.mdV[VY] / 256.0f);
+
+ rtool->setGridPosX(grid_pos_x);
+ rtool->setGridPosY(grid_pos_y);
+ }
+ }
+}
+
+
+void LLFloaterGodTools::sendRegionInfoRequest()
+{
+ if (mPanelRegionTools) mPanelRegionTools->clearAllWidgets();
+ if (mPanelObjectTools) mPanelObjectTools->clearAllWidgets();
+ mCurrentHost = LLHost::invalid;
+ mUpdateTimer.reset();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("RequestRegionInfo");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+}
+
+
+void LLFloaterGodTools::sendGodUpdateRegionInfo()
+{
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (gAgent.isGodlike()
+ && sGodTools->mPanelRegionTools
+ && regionp
+ && gAgent.getRegionHost() == mCurrentHost)
+ {
+ LLMessageSystem *msg = gMessageSystem;
+ LLPanelRegionTools *rtool = sGodTools->mPanelRegionTools;
+
+ msg->newMessage("GodUpdateRegionInfo");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_RegionInfo);
+ msg->addStringFast(_PREHASH_SimName, rtool->getSimName().c_str());
+ msg->addU32Fast(_PREHASH_EstateID, rtool->getEstateID());
+ msg->addU32Fast(_PREHASH_ParentEstateID, rtool->getParentEstateID());
+ msg->addU32Fast(_PREHASH_RegionFlags, computeRegionFlags());
+ msg->addF32Fast(_PREHASH_BillableFactor, rtool->getBillableFactor());
+ msg->addS32Fast(_PREHASH_PricePerMeter, rtool->getPricePerMeter());
+ msg->addS32Fast(_PREHASH_RedirectGridX, rtool->getRedirectGridX());
+ msg->addS32Fast(_PREHASH_RedirectGridY, rtool->getRedirectGridY());
+
+ gAgent.sendReliableMessage();
+ }
+}
+
+//*****************************************************************************
+// LLPanelRegionTools
+//*****************************************************************************
+
+
+// || Region |______________________________________
+// | |
+// | Sim Name: [________________________________] |
+// | ^ ^ |
+// | LEFT R1 Estate id: [----] |
+// | Parent id: [----] |
+// | [X] Prelude Grid Pos: [--] [--] |
+// | [X] Visible Redirect Pos: [--] [--] |
+// | [X] Damage Bill Factor [8_______] |
+// | [X] Block Terraform PricePerMeter[8_______] |
+// | [Apply] |
+// | |
+// | [Bake Terrain] [Select Region] |
+// | [Revert Terrain] [Autosave Now] |
+// | [Swap Terrain] |
+// | |
+// |________________________________________________|
+// ^ ^ ^
+// LEFT R2 RIGHT
+
+
+// Floats because spinners only support floats. JC
+const F32 BILLABLE_FACTOR_DEFAULT = 1;
+const F32 BILLABLE_FACTOR_MIN = 0.0f;
+const F32 BILLABLE_FACTOR_MAX = 4.f;
+
+// floats because spinners only understand floats. JC
+const F32 PRICE_PER_METER_DEFAULT = 1.f;
+const F32 PRICE_PER_METER_MIN = 0.f;
+const F32 PRICE_PER_METER_MAX = 100.f;
+
+
+LLPanelRegionTools::LLPanelRegionTools(const std::string& title)
+: LLPanel(title)
+{
+}
+
+BOOL LLPanelRegionTools::postBuild()
+{
+ childSetCommitCallback("region name", onChangeAnything, this);
+ childSetKeystrokeCallback("region name", onChangeSimName, this);
+ childSetPrevalidate("region name", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ childSetCommitCallback("check prelude", onChangePrelude, this);
+ childSetCommitCallback("check fixed sun", onChangeAnything, this);
+ childSetCommitCallback("check reset home", onChangeAnything, this);
+ childSetCommitCallback("check visible", onChangeAnything, this);
+ childSetCommitCallback("check damage", onChangeAnything, this);
+ childSetCommitCallback("block dwell", onChangeAnything, this);
+ childSetCommitCallback("block terraform", onChangeAnything, this);
+ childSetCommitCallback("allow transfer", onChangeAnything, this);
+ childSetCommitCallback("is sandbox", onChangeAnything, this);
+
+ childSetAction("Bake Terrain", onBakeTerrain, this);
+ childSetAction("Revert Terrain", onRevertTerrain, this);
+ childSetAction("Swap Terrain", onSwapTerrain, this);
+
+ childSetCommitCallback("estate", onChangeAnything, this);
+ childSetPrevalidate("estate", &LLLineEditor::prevalidatePositiveS32);
+
+ childSetCommitCallback("parentestate", onChangeAnything, this);
+ childSetPrevalidate("parentestate", &LLLineEditor::prevalidatePositiveS32);
+ childDisable("parentestate");
+
+ childSetCommitCallback("gridposx", onChangeAnything, this);
+ childSetPrevalidate("gridposx", &LLLineEditor::prevalidatePositiveS32);
+ childDisable("gridposx");
+
+ childSetCommitCallback("gridposy", onChangeAnything, this);
+ childSetPrevalidate("gridposy", &LLLineEditor::prevalidatePositiveS32);
+ childDisable("gridposy");
+
+ childSetCommitCallback("redirectx", onChangeAnything, this);
+ childSetPrevalidate("redirectx", &LLLineEditor::prevalidatePositiveS32);
+
+ childSetCommitCallback("redirecty", onChangeAnything, this);
+ childSetPrevalidate("redirecty", &LLLineEditor::prevalidatePositiveS32);
+
+ childSetCommitCallback("billable factor", onChangeAnything, this);
+
+ childSetCommitCallback("land cost", onChangeAnything, this);
+
+ childSetAction("Refresh", onRefresh, this);
+ childSetAction("Apply", onApplyChanges, this);
+
+ childSetAction("Select Region", onSelectRegion, this);
+ childSetAction("Autosave now", onSaveState, this);
+
+ return TRUE;
+}
+
+// Destroys the object
+LLPanelRegionTools::~LLPanelRegionTools()
+{
+ // base class will take care of everything
+}
+
+U32 LLPanelRegionTools::computeRegionFlags(U32 flags) const
+{
+ flags &= getRegionFlagsMask();
+ flags |= getRegionFlags();
+ return flags;
+}
+
+
+void LLPanelRegionTools::refresh()
+{
+}
+
+
+void LLPanelRegionTools::clearAllWidgets()
+{
+ // clear all widgets
+ childSetValue("region name", "unknown");
+ childSetFocus("region name", FALSE);
+
+ childSetValue("check prelude", FALSE);
+ childDisable("check prelude");
+
+ childSetValue("check fixed sun", FALSE);
+ childDisable("check fixed sun");
+
+ childSetValue("check reset home", FALSE);
+ childDisable("check reset home");
+
+ childSetValue("check damage", FALSE);
+ childDisable("check damage");
+
+ childSetValue("check visible", FALSE);
+ childDisable("check visible");
+
+ childSetValue("block terraform", FALSE);
+ childDisable("block terraform");
+
+ childSetValue("block dwell", FALSE);
+ childDisable("block dwell");
+
+ childSetValue("is sandbox", FALSE);
+ childDisable("is sandbox");
+
+ childSetValue("billable factor", BILLABLE_FACTOR_DEFAULT);
+ childDisable("billable factor");
+
+ childSetValue("land cost", PRICE_PER_METER_DEFAULT);
+ childDisable("land cost");
+
+ childDisable("Apply");
+ childDisable("Bake Terrain");
+ childDisable("Autosave now");
+}
+
+
+void LLPanelRegionTools::enableAllWidgets()
+{
+ // enable all of the widgets
+
+ childEnable("check prelude");
+ childEnable("check fixed sun");
+ childEnable("check reset home");
+ childEnable("check damage");
+ childDisable("check visible"); // use estates to update...
+ childEnable("block terraform");
+ childEnable("block dwell");
+ childEnable("is sandbox");
+
+ childEnable("billable factor");
+ childEnable("land cost");
+
+ childDisable("Apply"); // don't enable this one
+ childEnable("Bake Terrain");
+ childEnable("Autosave now");
+}
+
+
+// static
+void LLPanelRegionTools::onSaveState(void* userdata)
+{
+ if (gAgent.isGodlike())
+ {
+ // Send message to save world state
+ gMessageSystem->newMessageFast(_PREHASH_StateSave);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_DataBlock);
+ gMessageSystem->addStringFast(_PREHASH_Filename, NULL);
+ gAgent.sendReliableMessage();
+ }
+}
+
+const std::string LLPanelRegionTools::getSimName() const
+{
+ return childGetValue("region name");
+}
+
+U32 LLPanelRegionTools::getEstateID() const
+{
+ U32 id = (U32)childGetValue("estate").asInteger();
+ return id;
+}
+
+U32 LLPanelRegionTools::getParentEstateID() const
+{
+ U32 id = (U32)childGetValue("parentestate").asInteger();
+ return id;
+}
+
+S32 LLPanelRegionTools::getRedirectGridX() const
+{
+ return childGetValue("redirectx").asInteger();
+}
+
+S32 LLPanelRegionTools::getRedirectGridY() const
+{
+ return childGetValue("redirecty").asInteger();
+}
+
+S32 LLPanelRegionTools::getGridPosX() const
+{
+ return childGetValue("gridposx").asInteger();
+}
+
+S32 LLPanelRegionTools::getGridPosY() const
+{
+ return childGetValue("gridposy").asInteger();
+}
+
+U32 LLPanelRegionTools::getRegionFlags() const
+{
+ U32 flags = 0x0;
+ flags = childGetValue("check prelude").asBoolean()
+ ? set_prelude_flags(flags)
+ : unset_prelude_flags(flags);
+
+ // override prelude
+ if (childGetValue("check fixed sun").asBoolean())
+ {
+ flags |= REGION_FLAGS_SUN_FIXED;
+ }
+ if (childGetValue("check reset home").asBoolean())
+ {
+ flags |= REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+ if (childGetValue("check visible").asBoolean())
+ {
+ flags |= REGION_FLAGS_EXTERNALLY_VISIBLE;
+ }
+ if (childGetValue("check damage").asBoolean())
+ {
+ flags |= REGION_FLAGS_ALLOW_DAMAGE;
+ }
+ if (childGetValue("block terraform").asBoolean())
+ {
+ flags |= REGION_FLAGS_BLOCK_TERRAFORM;
+ }
+ if (childGetValue("block dwell").asBoolean())
+ {
+ flags |= REGION_FLAGS_BLOCK_DWELL;
+ }
+ if (childGetValue("is sandbox").asBoolean())
+ {
+ flags |= REGION_FLAGS_SANDBOX;
+ }
+ return flags;
+}
+
+U32 LLPanelRegionTools::getRegionFlagsMask() const
+{
+ U32 flags = 0xffffffff;
+ flags = childGetValue("check prelude").asBoolean()
+ ? set_prelude_flags(flags)
+ : unset_prelude_flags(flags);
+
+ if (!childGetValue("check fixed sun").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_SUN_FIXED;
+ }
+ if (!childGetValue("check reset home").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+ if (!childGetValue("check visible").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_EXTERNALLY_VISIBLE;
+ }
+ if (!childGetValue("check damage").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_ALLOW_DAMAGE;
+ }
+ if (!childGetValue("block terraform").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_BLOCK_TERRAFORM;
+ }
+ if (!childGetValue("block dwell").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_BLOCK_DWELL;
+ }
+ if (!childGetValue("is sandbox").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_SANDBOX;
+ }
+ return flags;
+}
+
+F32 LLPanelRegionTools::getBillableFactor() const
+{
+ return (F32)childGetValue("billable factor").asReal();
+}
+
+S32 LLPanelRegionTools::getPricePerMeter() const
+{
+ return childGetValue("land cost");
+}
+
+void LLPanelRegionTools::setSimName(char *name)
+{
+ childSetValue("region name", name);
+}
+
+void LLPanelRegionTools::setEstateID(U32 id)
+{
+ childSetValue("estate", (S32)id);
+}
+
+void LLPanelRegionTools::setGridPosX(S32 pos)
+{
+ childSetValue("gridposx", pos);
+}
+
+void LLPanelRegionTools::setGridPosY(S32 pos)
+{
+ childSetValue("gridposy", pos);
+}
+
+void LLPanelRegionTools::setRedirectGridX(S32 pos)
+{
+ childSetValue("redirectx", pos);
+}
+
+void LLPanelRegionTools::setRedirectGridY(S32 pos)
+{
+ childSetValue("redirecty", pos);
+}
+
+void LLPanelRegionTools::setParentEstateID(U32 id)
+{
+ childSetValue("parentestate", (S32)id);
+}
+
+void LLPanelRegionTools::setCheckFlags(U32 flags)
+{
+ childSetValue("check prelude", is_prelude(flags) ? TRUE : FALSE);
+ childSetValue("check fixed sun", flags & REGION_FLAGS_SUN_FIXED ? TRUE : FALSE);
+ childSetValue("check reset home", flags & REGION_FLAGS_RESET_HOME_ON_TELEPORT ? TRUE : FALSE);
+ childSetValue("check damage", flags & REGION_FLAGS_ALLOW_DAMAGE ? TRUE : FALSE);
+ childSetValue("check visible", flags & REGION_FLAGS_EXTERNALLY_VISIBLE ? TRUE : FALSE);
+ childSetValue("block terraform", flags & REGION_FLAGS_BLOCK_TERRAFORM ? TRUE : FALSE);
+ childSetValue("block dwell", flags & REGION_FLAGS_BLOCK_DWELL ? TRUE : FALSE);
+ childSetValue("is sandbox", flags & REGION_FLAGS_SANDBOX ? TRUE : FALSE );
+}
+
+void LLPanelRegionTools::setBillableFactor(F32 billable_factor)
+{
+ childSetValue("billable factor", billable_factor);
+}
+
+void LLPanelRegionTools::setPricePerMeter(S32 price)
+{
+ childSetValue("land cost", price);
+}
+
+// static
+void LLPanelRegionTools::onChangeAnything(LLUICtrl* ctrl, void* userdata)
+{
+ if (sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata;
+ region_tools->childEnable("Apply");
+ }
+}
+
+// static
+void LLPanelRegionTools::onChangePrelude(LLUICtrl* ctrl, void* data)
+{
+ // checking prelude auto-checks fixed sun
+ LLPanelRegionTools* self = (LLPanelRegionTools*)data;
+ if (self->childGetValue("check prelude").asBoolean())
+ {
+ self->childSetValue("check fixed sun", TRUE);
+ self->childSetValue("check reset home", TRUE);
+ }
+ // pass on to default onChange handler
+ onChangeAnything(ctrl, data);
+}
+
+// static
+void LLPanelRegionTools::onChangeSimName(LLLineEditor* caller, void* userdata )
+{
+ if (sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata;
+ region_tools->childEnable("Apply");
+ }
+}
+
+//static
+void LLPanelRegionTools::onRefresh(void* userdata)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region
+ && sGodTools
+ && gAgent.isGodlike())
+ {
+ sGodTools->sendRegionInfoRequest();
+ }
+}
+
+// static
+void LLPanelRegionTools::onApplyChanges(void* userdata)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region
+ && sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata;
+
+ region_tools->childDisable("Apply");
+ sGodTools->sendGodUpdateRegionInfo();
+ }
+}
+
+// static
+void LLPanelRegionTools::onBakeTerrain(void *userdata)
+{
+ LLPanelRequestTools::sendRequest("terrain", "bake", gAgent.getRegionHost());
+}
+
+// static
+void LLPanelRegionTools::onRevertTerrain(void *userdata)
+{
+ LLPanelRequestTools::sendRequest("terrain", "revert", gAgent.getRegionHost());
+}
+
+// static
+void LLPanelRegionTools::onSwapTerrain(void *userdata)
+{
+ LLPanelRequestTools::sendRequest("terrain", "swap", gAgent.getRegionHost());
+}
+
+// static
+void LLPanelRegionTools::onSelectRegion(void* userdata)
+{
+ llinfos << "LLPanelRegionTools::onSelectRegion" << llendl;
+
+ if (!gWorldp)
+ {
+ return;
+ }
+ LLViewerRegion *regionp = gWorldp->getRegionFromPosGlobal(gAgent.getPositionGlobal());
+ if (!regionp)
+ {
+ return;
+ }
+
+ LLVector3d north_east(REGION_WIDTH_METERS, REGION_WIDTH_METERS, 0);
+ gParcelMgr->selectLand(regionp->getOriginGlobal(),
+ regionp->getOriginGlobal() + north_east, FALSE);
+
+}
+
+
+//*****************************************************************************
+// Class LLPanelGridTools
+//*****************************************************************************
+
+// || Grid |_____________________________________
+// | |
+// | |
+// | Sun Phase: >--------[]---------< [________] |
+// | |
+// | ^ ^ |
+// | LEFT R1 |
+// | |
+// | [Kick all users] |
+// | |
+// | |
+// | |
+// | |
+// | |
+// |_______________________________________________|
+// ^ ^ ^
+// LEFT R2 RIGHT
+
+const F32 HOURS_TO_RADIANS = (2.f*F_PI)/24.f;
+const char FLOATER_GRID_ADMIN_TITLE[] = "Grid Administration";
+
+
+LLPanelGridTools::LLPanelGridTools(const std::string& name) :
+ LLPanel(name)
+{
+}
+
+// Destroys the object
+LLPanelGridTools::~LLPanelGridTools()
+{
+}
+
+BOOL LLPanelGridTools::postBuild()
+{
+ childSetAction("Kick all users", onClickKickAll, this);
+ childSetAction("Flush This Region's Map Visibility Caches", onClickFlushMapVisibilityCaches, this);
+
+ return TRUE;
+}
+
+void LLPanelGridTools::refresh()
+{
+}
+
+
+// static
+void LLPanelGridTools::onClickKickAll(void* userdata)
+{
+ LLPanelGridTools* self = (LLPanelGridTools*) userdata;
+
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect(left, top, left+400, top-300);
+
+ gViewerWindow->alertXmlEditText("KickAllUsers", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelGridTools::confirmKick, self);
+}
+
+
+void LLPanelGridTools::confirmKick(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelGridTools* self = (LLPanelGridTools*) userdata;
+
+ if (option == 0)
+ {
+ self->mKickMessage = text;
+ gViewerWindow->alertXml("ConfirmKick",LLPanelGridTools::finishKick, self);
+ }
+}
+
+
+// static
+void LLPanelGridTools::finishKick(S32 option, void* userdata)
+{
+ LLPanelGridTools* self = (LLPanelGridTools*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, LL_UUID_ALL_AGENTS );
+ msg->addU32("KickFlags", KICK_FLAGS_DEFAULT );
+ msg->addStringFast(_PREHASH_Reason, self->mKickMessage.c_str() );
+ gAgent.sendReliableMessage();
+ }
+}
+
+
+// static
+void LLPanelGridTools::onClickFlushMapVisibilityCaches(void* data)
+{
+ gViewerWindow->alertXml("FlushMapVisibilityCaches",
+ flushMapVisibilityCachesConfirm, data);
+}
+
+// static
+void LLPanelGridTools::flushMapVisibilityCachesConfirm(S32 option, void* data)
+{
+ if (option != 0) return;
+
+ LLPanelGridTools* self = (LLPanelGridTools*)data;
+ if (!self) return;
+
+ // HACK: Send this as an EstateOwnerRequest so it gets routed
+ // correctly by the spaceserver. JC
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "refreshmapvisibility");
+ msg->addUUID("Invoice", LLUUID::null);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", gAgent.getID().getString());
+ gAgent.sendReliableMessage();
+}
+
+
+//*****************************************************************************
+// LLPanelObjectTools
+//*****************************************************************************
+
+
+// || Object |_______________________________________________________
+// | |
+// | Sim Name: Foo |
+// | ^ ^ |
+// | LEFT R1 |
+// | |
+// | [X] Disable Scripts [X] Disable Collisions [X] Disable Physics |
+// | [ Apply ] |
+// | |
+// | [Set Target Avatar] Avatar Name |
+// | [Delete Target's Objects on Public Land ] |
+// | [Delete All Target's Objects ] |
+// | [Delete All Scripted Objects on Public Land] |
+// | [Get Top Colliders ] |
+// | [Get Top Scripts ] |
+// |_________________________________________________________________|
+// ^ ^
+// LEFT RIGHT
+
+// Default constructor
+LLPanelObjectTools::LLPanelObjectTools(const std::string& title)
+: LLPanel(title), mTargetAvatar()
+{
+}
+
+// Destroys the object
+LLPanelObjectTools::~LLPanelObjectTools()
+{
+ // base class will take care of everything
+}
+
+BOOL LLPanelObjectTools::postBuild()
+{
+ childSetCommitCallback("disable scripts", onChangeAnything, this);
+ childSetCommitCallback("disable collisions", onChangeAnything, this);
+ childSetCommitCallback("disable physics", onChangeAnything, this);
+
+ childSetAction("Apply", onApplyChanges, this);
+
+ childSetAction("Set Target", onClickSet, this);
+
+ childSetAction("Delete Target's Scripted Objects On Others Land", onClickDeletePublicOwnedBy, this);
+ childSetAction("Delete Target's Scripted Objects On *Any* Land", onClickDeleteAllScriptedOwnedBy, this);
+ childSetAction("Delete *ALL* Of Target's Objects", onClickDeleteAllOwnedBy, this);
+
+ childSetAction("Get Top Colliders", onGetTopColliders, this);
+ childSetAction("Get Top Scripts", onGetTopScripts, this);
+ childSetAction("Scripts digest", onGetScriptDigest, this);
+
+ return TRUE;
+}
+
+void LLPanelObjectTools::setTargetAvatar(const LLUUID &target_id)
+{
+ mTargetAvatar = target_id;
+ if (target_id.isNull())
+ {
+ childSetValue("target_avatar_name", "(no target)");
+ }
+}
+
+
+void LLPanelObjectTools::refresh()
+{
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ childSetText("region name", regionp->getName());
+ }
+}
+
+
+U32 LLPanelObjectTools::computeRegionFlags(U32 flags) const
+{
+ if (childGetValue("disable scripts").asBoolean())
+ {
+ flags |= REGION_FLAGS_SKIP_SCRIPTS;
+ }
+ else
+ {
+ flags &= ~REGION_FLAGS_SKIP_SCRIPTS;
+ }
+ if (childGetValue("disable collisions").asBoolean())
+ {
+ flags |= REGION_FLAGS_SKIP_COLLISIONS;
+ }
+ else
+ {
+ flags &= ~REGION_FLAGS_SKIP_COLLISIONS;
+ }
+ if (childGetValue("disable physics").asBoolean())
+ {
+ flags |= REGION_FLAGS_SKIP_PHYSICS;
+ }
+ else
+ {
+ flags &= ~REGION_FLAGS_SKIP_PHYSICS;
+ }
+ return flags;
+}
+
+
+void LLPanelObjectTools::setCheckFlags(U32 flags)
+{
+ childSetValue("disable scripts", flags & REGION_FLAGS_SKIP_SCRIPTS ? TRUE : FALSE);
+ childSetValue("disable collisions", flags & REGION_FLAGS_SKIP_COLLISIONS ? TRUE : FALSE);
+ childSetValue("disable physics", flags & REGION_FLAGS_SKIP_PHYSICS ? TRUE : FALSE);
+}
+
+
+void LLPanelObjectTools::clearAllWidgets()
+{
+ childSetValue("disable scripts", FALSE);
+ childDisable("disable scripts");
+
+ childDisable("Apply");
+ childDisable("Set Target");
+ childDisable("Delete Target's Scripted Objects On Others Land");
+ childDisable("Delete Target's Scripted Objects On *Any* Land");
+ childDisable("Delete *ALL* Of Target's Objects");
+}
+
+
+void LLPanelObjectTools::enableAllWidgets()
+{
+ childEnable("disable scripts");
+
+ childDisable("Apply"); // don't enable this one
+ childEnable("Set Target");
+ childEnable("Delete Target's Scripted Objects On Others Land");
+ childEnable("Delete Target's Scripted Objects On *Any* Land");
+ childEnable("Delete *ALL* Of Target's Objects");
+ childEnable("Get Top Colliders");
+ childEnable("Get Top Scripts");
+}
+
+
+// static
+void LLPanelObjectTools::onGetTopColliders(void* userdata)
+{
+ if (sGodTools
+ && gAgent.isGodlike())
+ {
+ LLFloaterTopObjects::show();
+ LLFloaterTopObjects::setMode(STAT_REPORT_TOP_COLLIDERS);
+ LLFloaterTopObjects::onRefresh(NULL);
+ }
+}
+
+// static
+void LLPanelObjectTools::onGetTopScripts(void* userdata)
+{
+ if (sGodTools
+ && gAgent.isGodlike())
+ {
+ LLFloaterTopObjects::show();
+ LLFloaterTopObjects::setMode(STAT_REPORT_TOP_SCRIPTS);
+ LLFloaterTopObjects::onRefresh(NULL);
+ }
+}
+
+// static
+void LLPanelObjectTools::onGetScriptDigest(void* userdata)
+{
+ if (sGodTools
+ && gAgent.isGodlike())
+ {
+ // get the list of scripts and number of occurences of each
+ // (useful for finding self-replicating objects)
+ LLPanelRequestTools::sendRequest("scriptdigest","0",gAgent.getRegionHost());
+ }
+}
+
+void LLPanelObjectTools::onClickDeletePublicOwnedBy(void* userdata)
+{
+ // Bring up view-modal dialog
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*)userdata;
+ if (!panelp->mTargetAvatar.isNull())
+ {
+ panelp->mSimWideDeletesFlags =
+ SWD_SCRIPTED_ONLY | SWD_OTHERS_LAND_ONLY;
+
+ LLStringBase<char>::format_map_t args;
+ args["[AVATAR_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+
+ gViewerWindow->alertXml( "GodDeleteAllScriptedPublicObjectsByUser",
+ args,
+ callbackSimWideDeletes,
+ userdata);
+ }
+}
+
+// static
+void LLPanelObjectTools::onClickDeleteAllScriptedOwnedBy(void* userdata)
+{
+ // Bring up view-modal dialog
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*)userdata;
+ if (!panelp->mTargetAvatar.isNull())
+ {
+ panelp->mSimWideDeletesFlags = SWD_SCRIPTED_ONLY;
+
+ LLStringBase<char>::format_map_t args;
+ args["[AVATAR_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+
+ gViewerWindow->alertXml( "GodDeleteAllScriptedObjectsByUser",
+ args,
+ callbackSimWideDeletes,
+ userdata);
+ }
+}
+
+// static
+void LLPanelObjectTools::onClickDeleteAllOwnedBy(void* userdata)
+{
+ // Bring up view-modal dialog
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*)userdata;
+ if (!panelp->mTargetAvatar.isNull())
+ {
+ panelp->mSimWideDeletesFlags = 0;
+
+ LLStringBase<char>::format_map_t args;
+ args["[AVATAR_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+
+ gViewerWindow->alertXml( "GodDeleteAllObjectsByUser",
+ args,
+ callbackSimWideDeletes,
+ userdata);
+ }
+}
+
+// static
+void LLPanelObjectTools::callbackSimWideDeletes( S32 option, void* userdata )
+{
+ if (option == 0)
+ {
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) userdata;
+ if (!object_tools->mTargetAvatar.isNull())
+ {
+ send_sim_wide_deletes(object_tools->mTargetAvatar,
+ object_tools->mSimWideDeletesFlags);
+ }
+ }
+}
+
+void LLPanelObjectTools::onClickSet(void* data)
+{
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*) data;
+ // grandparent is a floater, which can have a dependent
+ gFloaterView->getParentFloater(panelp)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, data));
+}
+
+void LLPanelObjectTools::onClickSetBySelection(void* data)
+{
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*) data;
+ if (!panelp) return;
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) node = gSelectMgr->getFirstNode();
+ if (!node) return;
+
+ LLString owner_name;
+ LLUUID owner_id;
+ gSelectMgr->selectGetOwner(owner_id, owner_name);
+
+ panelp->mTargetAvatar = owner_id;
+ LLString name = "Object " + node->mName + " owned by " + owner_name;
+ panelp->childSetValue("target_avatar_name", name);
+}
+
+// static
+void LLPanelObjectTools::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) data;
+ if (ids.empty() || names.empty()) return;
+ object_tools->mTargetAvatar = ids[0];
+ object_tools->childSetValue("target_avatar_name", names[0]);
+ object_tools->refresh();
+}
+
+
+// static
+void LLPanelObjectTools::onChangeAnything(LLUICtrl* ctrl, void* userdata)
+{
+ if (sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) userdata;
+ object_tools->childEnable("Apply");
+ }
+}
+
+// static
+void LLPanelObjectTools::onApplyChanges(void* userdata)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region
+ && sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) userdata;
+ // TODO -- implement this
+
+ object_tools->childDisable("Apply");
+ sGodTools->sendGodUpdateRegionInfo();
+ }
+}
+
+
+// --------------------
+// LLPanelRequestTools
+// --------------------
+
+const char SELECTION[] = "Selection";
+const char AGENT_REGION[] = "Agent Region";
+
+LLPanelRequestTools::LLPanelRequestTools(const std::string& name):
+ LLPanel(name)
+{
+}
+
+LLPanelRequestTools::~LLPanelRequestTools()
+{
+}
+
+BOOL LLPanelRequestTools::postBuild()
+{
+ childSetAction("Make Request", onClickRequest, this);
+
+ refresh();
+
+ return TRUE;
+}
+
+void LLPanelRequestTools::refresh()
+{
+ std::string buffer = childGetValue("destination");
+ LLCtrlListInterface *list = childGetListInterface("destination");
+ if (!list) return;
+
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ list->addSimpleElement(SELECTION);
+ list->addSimpleElement(AGENT_REGION);
+ LLViewerRegion* regionp;
+ for(regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp != NULL;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ LLString name = regionp->getName();
+ if (!name.empty())
+ {
+ list->addSimpleElement(name);
+ }
+ }
+ if(!buffer.empty())
+ {
+ list->selectByValue(buffer);
+ }
+ else
+ {
+ list->selectByValue(SELECTION);
+ }
+}
+
+
+// static
+void LLPanelRequestTools::sendRequest(const char *request,
+ const char *parameter,
+ const LLHost& host)
+{
+ llinfos << "Sending request '" << request << "', '"
+ << parameter << "' to " << host << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GodlikeMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", request);
+ msg->addUUID("Invoice", LLUUID::null);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", parameter);
+ msg->sendReliable(host);
+}
+
+// static
+void LLPanelRequestTools::onClickRequest(void* data)
+{
+ LLPanelRequestTools* self = (LLPanelRequestTools*)data;
+ const std::string dest = self->childGetValue("destination").asString();
+ if(dest == SELECTION)
+ {
+ std::string req = self->childGetValue("request");
+ req = req.substr(0, req.find_first_of(" "));
+ std::string param = self->childGetValue("parameter");
+ gSelectMgr->sendGodlikeRequest(req, param);
+ }
+ else if(dest == AGENT_REGION)
+ {
+ self->sendRequest(gAgent.getRegionHost());
+ }
+ else
+ {
+ // find region by name
+ LLViewerRegion* regionp;
+ for(regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp != NULL;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ if(dest == regionp->getName())
+ {
+ // found it
+ self->sendRequest(regionp->getHost());
+ }
+ }
+ }
+}
+
+void terrain_download_done(void** data, S32 status)
+{
+ LLNotifyBox::showXml("TerrainDownloaded");
+}
+
+
+void test_callback(const LLTSCode status)
+{
+ llinfos << "Test transfer callback returned!" << llendl;
+}
+
+
+void LLPanelRequestTools::sendRequest(const LLHost& host)
+{
+
+ // intercept viewer local actions here
+ std::string req = childGetValue("request");
+ if (req == "terrain download")
+ {
+ gXferManager->requestFile("terrain.raw", "terrain.raw", LL_PATH_NONE,
+ host,
+ FALSE,
+ terrain_download_done,
+ NULL);
+ }
+ else
+ {
+ req = req.substr(0, req.find_first_of(" "));
+ sendRequest(req.c_str(), childGetValue("parameter").asString().c_str(), host);
+ }
+}
+
+// Flags are SWD_ flags.
+void send_sim_wide_deletes(const LLUUID& owner_id, U32 flags)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SimWideDeletes);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_DataBlock);
+ msg->addUUIDFast(_PREHASH_TargetID, owner_id);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ gAgent.sendReliableMessage();
+}
diff --git a/indra/newview/llfloatergodtools.h b/indra/newview/llfloatergodtools.h
new file mode 100644
index 0000000000..f6034aee39
--- /dev/null
+++ b/indra/newview/llfloatergodtools.h
@@ -0,0 +1,262 @@
+/**
+ * @file llfloatergodtools.h
+ * @brief The on-screen rectangle with tool options.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERGODTOOLS_H
+#define LL_LLFLOATERGODTOOLS_H
+
+#include "llcoord.h"
+#include "llhost.h"
+#include "llframetimer.h"
+
+#include "llfloater.h"
+#include "llpanel.h"
+#include <vector>
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLUICtrl;
+class LLLineEditor;
+class LLPanelGridTools;
+class LLPanelRegionTools;
+class LLPanelObjectTools;
+class LLPanelRequestTools;
+//class LLSliderCtrl;
+class LLSpinCtrl;
+class LLTabContainer;
+class LLTextBox;
+class LLMessageSystem;
+
+class LLFloaterGodTools
+: public LLFloater
+{
+public:
+
+ static LLFloaterGodTools* instance();
+
+ enum EGodPanel
+ {
+ PANEL_GRID,
+ PANEL_REGION,
+ PANEL_OBJECT,
+ PANEL_REQUEST,
+ PANEL_COUNT
+ };
+
+ static void show(void *);
+ static void hide(void *);
+
+ static void* createPanelGrid(void *userdata);
+ static void* createPanelRegion(void *userdata);
+ static void* createPanelObjects(void *userdata);
+ static void* createPanelRequest(void *userdata);
+
+ static void refreshAll();
+
+ void showPanel(const LLString& panel_name);
+
+ virtual void onClose(bool app_quitting);
+
+ virtual void draw();
+
+ // call this once per frame to handle visibility, rect location,
+ // button highlights, etc.
+ void updatePopup(LLCoordGL center, MASK mask);
+
+ // Get data to populate UI.
+ void sendRegionInfoRequest();
+
+ // get and process region info if necessary.
+ static void processRegionInfo(LLMessageSystem* msg);
+
+ // Send possibly changed values to simulator.
+ void sendGodUpdateRegionInfo();
+
+ static void onTabChanged(void *data, bool from_click);
+
+protected:
+ U32 computeRegionFlags() const;
+
+protected:
+ LLFloaterGodTools();
+ ~LLFloaterGodTools();
+
+ void setStatusText(const std::string& text);
+
+ // When the floater is going away, reset any options that need to be
+ // cleared.
+ void resetToolState();
+
+ static LLFloaterGodTools* sInstance;
+
+public:
+ LLPanelRegionTools *mPanelRegionTools;
+ LLPanelObjectTools *mPanelObjectTools;
+
+ LLHost mCurrentHost;
+ LLFrameTimer mUpdateTimer;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelRegionTools
+//-----------------------------------------------------------------------------
+
+class LLPanelRegionTools
+: public LLPanel
+{
+public:
+ LLPanelRegionTools(const std::string& name);
+ /*virtual*/ ~LLPanelRegionTools();
+
+ BOOL postBuild();
+
+ /*virtual*/ void refresh();
+
+ static void onSaveState(void* data);
+ static void onChangeAnything(LLUICtrl* ctrl, void* userdata);
+ static void onChangePrelude(LLUICtrl* ctrl, void* data);
+ static void onChangeSimName(LLLineEditor* caller, void* userdata);
+ static void onApplyChanges(void* userdata);
+ static void onBakeTerrain(void *userdata);
+ static void onRevertTerrain(void *userdata);
+ static void onSwapTerrain(void *userdata);
+ static void onSelectRegion(void *userdata);
+ static void onRefresh(void* userdata);
+
+ // set internal checkboxes/spinners/combos
+ const std::string getSimName() const;
+ U32 getEstateID() const;
+ U32 getParentEstateID() const;
+ U32 getRegionFlags() const;
+ U32 getRegionFlagsMask() const;
+ F32 getBillableFactor() const;
+ S32 getPricePerMeter() const;
+ S32 getGridPosX() const;
+ S32 getGridPosY() const;
+ S32 getRedirectGridX() const;
+ S32 getRedirectGridY() const;
+
+ // set internal checkboxes/spinners/combos
+ void setSimName(char *name);
+ void setEstateID(U32 id);
+ void setParentEstateID(U32 id);
+ void setCheckFlags(U32 flags);
+ void setBillableFactor(F32 billable_factor);
+ void setPricePerMeter(S32 price);
+ void setGridPosX(S32 pos);
+ void setGridPosY(S32 pos);
+ void setRedirectGridX(S32 pos);
+ void setRedirectGridY(S32 pos);
+
+ U32 computeRegionFlags(U32 initial_flags) const;
+ void clearAllWidgets();
+ void enableAllWidgets();
+
+protected:
+ // gets from internal checkboxes/spinners/combos
+ void updateCurrentRegion() const;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelGridTools
+//-----------------------------------------------------------------------------
+
+class LLPanelGridTools
+: public LLPanel
+{
+public:
+ LLPanelGridTools(const std::string& name);
+ virtual ~LLPanelGridTools();
+
+ BOOL postBuild();
+
+ void refresh();
+
+ static void onClickKickAll(void *data);
+ static void confirmKick(S32 option, const LLString& text, void* userdata);
+ static void finishKick(S32 option, void* userdata);
+ static void onDragSunPhase(LLUICtrl *ctrl, void *userdata);
+ static void onClickFlushMapVisibilityCaches(void* data);
+ static void flushMapVisibilityCachesConfirm(S32 option, void* data);
+
+protected:
+ LLString mKickMessage; // Message to send on kick
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelObjectTools
+//-----------------------------------------------------------------------------
+
+class LLPanelObjectTools
+: public LLPanel
+{
+public:
+ LLPanelObjectTools(const std::string& name);
+ /*virtual*/ ~LLPanelObjectTools();
+
+ BOOL postBuild();
+
+ /*virtual*/ void refresh();
+
+ void setTargetAvatar(const LLUUID& target_id);
+ U32 computeRegionFlags(U32 initial_flags) const;
+ void clearAllWidgets();
+ void enableAllWidgets();
+ void setCheckFlags(U32 flags);
+
+ static void onChangeAnything(LLUICtrl* ctrl, void* data);
+ static void onApplyChanges(void* data);
+ static void onClickSet(void* data);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+ static void onClickDeletePublicOwnedBy(void* data);
+ static void onClickDeleteAllScriptedOwnedBy(void* data);
+ static void onClickDeleteAllOwnedBy(void* data);
+ static void callbackSimWideDeletes(S32 option, void* userdata);
+ static void onGetTopColliders(void* data);
+ static void onGetTopScripts(void* data);
+ static void onGetScriptDigest(void* data);
+ static void onClickSetBySelection(void* data);
+
+protected:
+ LLUUID mTargetAvatar;
+
+ // For all delete dialogs, store flags here for message.
+ U32 mSimWideDeletesFlags;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelRequestTools
+//-----------------------------------------------------------------------------
+
+class LLPanelRequestTools : public LLPanel
+{
+public:
+ LLPanelRequestTools(const std::string& name);
+ /*virtual*/ ~LLPanelRequestTools();
+
+ BOOL postBuild();
+
+ void refresh();
+
+ static void sendRequest(const char* request,
+ const char* parameter,
+ const LLHost& host);
+
+protected:
+ static void onClickRequest(void *data);
+ void sendRequest(const LLHost& host);
+};
+
+// Flags are SWD_ flags.
+void send_sim_wide_deletes(const LLUUID& owner_id, U32 flags);
+
+#endif
diff --git a/indra/newview/llfloatergroupinvite.cpp b/indra/newview/llfloatergroupinvite.cpp
new file mode 100644
index 0000000000..b21711a96f
--- /dev/null
+++ b/indra/newview/llfloatergroupinvite.cpp
@@ -0,0 +1,118 @@
+/**
+ * @file llfloatergroupinvite.cpp
+ * @brief Floater to invite new members into a group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergroupinvite.h"
+#include "llpanelgroupinvite.h"
+
+const char FLOATER_TITLE[] = "Group Invitation";
+const LLRect FGI_RECT(0, 380, 210, 0);
+
+class LLFloaterGroupInvite::impl
+{
+public:
+ impl(const LLUUID& group_id);
+ ~impl();
+
+ static void closeFloater(void* data);
+
+public:
+ LLUUID mGroupID;
+ LLPanelGroupInvite* mInvitePanelp;
+
+ static std::map<LLUUID, LLFloaterGroupInvite*> sInstances;
+};
+
+//
+// Globals
+//
+std::map<LLUUID, LLFloaterGroupInvite*> LLFloaterGroupInvite::impl::sInstances;
+
+LLFloaterGroupInvite::impl::impl(const LLUUID& group_id)
+{
+ mGroupID = group_id;
+}
+
+LLFloaterGroupInvite::impl::~impl()
+{
+}
+
+//static
+void LLFloaterGroupInvite::impl::closeFloater(void* data)
+{
+ LLFloaterGroupInvite* floaterp = (LLFloaterGroupInvite*) data;
+
+ if ( floaterp ) floaterp->close();
+}
+
+//-----------------------------------------------------------------------------
+// Implementation
+//-----------------------------------------------------------------------------
+LLFloaterGroupInvite::LLFloaterGroupInvite(const std::string& name,
+ const LLRect &rect,
+ const std::string& title,
+ const LLUUID& group_id)
+: LLFloater(name, rect, title)
+{
+ LLRect contents(mRect);
+ contents.mTop -= LLFLOATER_HEADER_SIZE;
+
+ mImpl = new impl(group_id);
+
+ mImpl->mInvitePanelp = new LLPanelGroupInvite("Group Invite Panel",
+ group_id);
+
+ mImpl->mInvitePanelp->setCloseCallback(impl::closeFloater, this);
+
+ mImpl->mInvitePanelp->setRect(contents);
+ addChild(mImpl->mInvitePanelp);
+}
+
+// virtual
+LLFloaterGroupInvite::~LLFloaterGroupInvite()
+{
+ if (mImpl->mGroupID.notNull())
+ {
+ impl::sInstances.erase(mImpl->mGroupID);
+ }
+
+ delete mImpl->mInvitePanelp;
+ delete mImpl;
+}
+
+// static
+void LLFloaterGroupInvite::showForGroup(const LLUUID& group_id)
+{
+ // Make sure group_id isn't null
+ if (group_id.isNull())
+ {
+ llwarns << "LLFloaterGroupInvite::showForGroup with null group_id!" << llendl;
+ return;
+ }
+
+ // If we don't have a floater for this group, create one.
+ LLFloaterGroupInvite *fgi = get_if_there(impl::sInstances,
+ group_id,
+ (LLFloaterGroupInvite*)NULL);
+ if (!fgi)
+ {
+ fgi = new LLFloaterGroupInvite("groupinfo",
+ FGI_RECT,
+ FLOATER_TITLE,
+ group_id);
+
+ impl::sInstances[group_id] = fgi;
+
+ fgi->mImpl->mInvitePanelp->clear();
+ }
+
+ fgi->center();
+ fgi->open();
+ fgi->mImpl->mInvitePanelp->update();
+}
diff --git a/indra/newview/llfloatergroupinvite.h b/indra/newview/llfloatergroupinvite.h
new file mode 100644
index 0000000000..fb47654476
--- /dev/null
+++ b/indra/newview/llfloatergroupinvite.h
@@ -0,0 +1,34 @@
+/**
+ * @file llfloatergroupinvite.h
+ * @brief This floater is just a wrapper for LLPanelGroupInvite, which
+ * is used to invite members to a specific group
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERGROUPINVITE_H
+#define LL_LLFLOATERGROUPINVITE_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+
+class LLFloaterGroupInvite
+: public LLFloater
+{
+public:
+ virtual ~LLFloaterGroupInvite();
+
+ static void showForGroup(const LLUUID &group_id);
+
+protected:
+ LLFloaterGroupInvite(const std::string& name,
+ const LLRect &rect,
+ const std::string& title,
+ const LLUUID& group_id = LLUUID::null);
+
+ class impl;
+ impl* mImpl;
+};
+
+#endif
diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp
new file mode 100644
index 0000000000..f03d0735a4
--- /dev/null
+++ b/indra/newview/llfloatergroups.cpp
@@ -0,0 +1,454 @@
+/**
+ * @file llfloatergroups.cpp
+ * @brief LLFloaterGroups class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shown from Edit -> Groups...
+ * Shows the agent's groups and allows the edit window to be invoked.
+ * Also overloaded to allow picking of a single group for assigning
+ * objects and land to groups.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergroups.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterdirectory.h"
+#include "llfocusmgr.h"
+#include "llalertdialog.h"
+#include "llselectmgr.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+const LLRect FLOATER_RECT(0, 258, 280, 0);
+const char FLOATER_TITLE[] = "Groups";
+
+// static
+LLMap<const LLUUID, LLFloaterGroups*> LLFloaterGroups::sInstances;
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterGroups
+///----------------------------------------------------------------------------
+
+//LLEventListener
+//virtual
+bool LLFloaterGroups::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+{
+ if (event->desc() == "new group")
+ {
+ reset();
+ return true;
+ }
+
+ return LLView::handleEvent(event, userdata);
+}
+
+// Call this with an agent id and AGENT_GROUPS for an agent's
+// groups, otherwise, call with an object id and SET_OBJECT_GROUP
+// when modifying an object.
+// static
+LLFloaterGroups* LLFloaterGroups::show(const LLUUID& id, EGroupDialog type)
+{
+ LLFloaterGroups* instance = NULL;
+ if(sInstances.checkData(id))
+ {
+ instance = sInstances.getData(id);
+ if (instance->getType() != type)
+ {
+ // not the type we want ==> destroy it and rebuild below
+ instance->destroy();
+ instance = NULL;
+ }
+ else
+ {
+ // Move the existing view to the front
+ instance->open();
+ }
+ }
+
+ if (!instance)
+ {
+ S32 left = 0;
+ S32 top = 0;
+ LLRect rect = FLOATER_RECT;
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ instance = new LLFloaterGroups("groups", rect, FLOATER_TITLE, id);
+ if(instance)
+ {
+ sInstances.addData(id, instance);
+ //instance->init(type);
+ instance->mType = type;
+ switch (type)
+ {
+ case AGENT_GROUPS:
+ gUICtrlFactory->buildFloater(instance, "floater_groups.xml");
+ break;
+ case CHOOSE_ONE:
+ gUICtrlFactory->buildFloater(instance, "floater_choose_group.xml");
+ break;
+ }
+ instance->center();
+ instance->open();
+ }
+ }
+ return instance;
+}
+
+// static
+LLFloaterGroups* LLFloaterGroups::getInstance(const LLUUID& id)
+{
+ if(sInstances.checkData(id))
+ {
+ return sInstances.getData(id);
+ }
+ return NULL;
+}
+
+// Default constructor
+LLFloaterGroups::LLFloaterGroups(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& id) :
+ LLFloater(name, rect, title),
+ mID(id),
+ mType(AGENT_GROUPS),
+ mOKCallback(NULL),
+ mCallbackUserdata(NULL)
+{
+}
+
+// Destroys the object
+LLFloaterGroups::~LLFloaterGroups()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ sInstances.removeData(mID);
+}
+
+// clear the group list, and get a fresh set of info.
+void LLFloaterGroups::reset()
+{
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ if (group_list)
+ {
+ group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ }
+ childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+ childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
+
+ initAgentGroups(gAgent.getGroupID());
+ enableButtons();
+}
+
+void LLFloaterGroups::setOkCallback(void (*callback)(LLUUID, void*),
+ void* userdata)
+{
+ mOKCallback = callback;
+ mCallbackUserdata = userdata;
+}
+
+BOOL LLFloaterGroups::postBuild()
+{
+ childSetCommitCallback("group list", onGroupList, this);
+
+ if(mType == AGENT_GROUPS)
+ {
+ childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+ childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
+
+ initAgentGroups(gAgent.getGroupID());
+
+ childSetAction("Activate", onBtnActivate, this);
+
+ childSetAction("Info", onBtnInfo, this);
+
+ childSetAction("Leave", onBtnLeave, this);
+
+ childSetAction("Create", onBtnCreate, this);
+
+ childSetAction("Search...", onBtnSearch, this);
+
+ childSetAction("Close", onBtnCancel, this);
+
+ setDefaultBtn("Info");
+
+ childSetDoubleClickCallback("group list", onBtnInfo);
+ childSetUserData("group list", this);
+ }
+ else
+ {
+ initAgentGroups(gAgent.getGroupID());
+
+ childSetAction("OK", onBtnOK, this);
+
+ childSetAction("Cancel", onBtnCancel, this);
+
+ setDefaultBtn("OK");
+
+ childSetDoubleClickCallback("group list", onBtnOK);
+ childSetUserData("group list", this);
+ }
+
+ enableButtons();
+
+ return TRUE;
+}
+
+void LLFloaterGroups::initAgentGroups(const LLUUID& highlight_id)
+{
+ S32 count = gAgent.mGroups.count();
+ LLUUID id;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ if (!group_list) return;
+
+ group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ id = gAgent.mGroups.get(i).mID;
+ LLGroupData* group_datap = &gAgent.mGroups.get(i);
+ LLString style = "NORMAL";
+ if(highlight_id == id)
+ {
+ style = "BOLD";
+ }
+
+ LLSD element;
+ element["id"] = id;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = group_datap->mName;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_SORTED);
+ }
+
+ {
+ LLString style = "NORMAL";
+ if (highlight_id.isNull())
+ {
+ style = "BOLD";
+ }
+ LLSD element;
+ element["id"] = LLUUID::null;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = "none";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_TOP);
+ }
+
+ group_list->selectByValue(highlight_id);
+
+ childSetFocus("group list");
+}
+
+void LLFloaterGroups::enableButtons()
+{
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
+ {
+ group_id = group_list->getCurrentID();
+ }
+ if(mType == AGENT_GROUPS)
+ {
+ if(group_id != gAgent.getGroupID())
+ {
+ childEnable("Activate");
+ }
+ else
+ {
+ childDisable("Activate");
+ }
+ if (group_id.notNull())
+ {
+ childEnable("Info");
+ childEnable("Leave");
+ }
+ else
+ {
+ childDisable("Info");
+ childDisable("Leave");
+ }
+ if(gAgent.mGroups.count() < MAX_AGENT_GROUPS)
+ {
+ childEnable("Create");
+ }
+ else
+ {
+ childDisable("Create");
+ }
+ }
+ else
+ {
+ childEnable("OK");
+ }
+}
+
+
+void LLFloaterGroups::onBtnCreate(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->create();
+}
+
+void LLFloaterGroups::onBtnActivate(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->activate();
+}
+
+void LLFloaterGroups::onBtnInfo(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->info();
+}
+
+void LLFloaterGroups::onBtnLeave(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->leave();
+}
+
+void LLFloaterGroups::onBtnSearch(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->search();
+}
+
+void LLFloaterGroups::onBtnOK(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->ok();
+}
+
+void LLFloaterGroups::onBtnCancel(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->close();
+}
+
+void LLFloaterGroups::onGroupList(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->highlightGroupList(ctrl);
+}
+
+void LLFloaterGroups::create()
+{
+ llinfos << "LLFloaterGroups::create" << llendl;
+ LLFloaterGroupInfo::showCreateGroup(NULL);
+}
+
+void LLFloaterGroups::activate()
+{
+ llinfos << "LLFloaterGroups::activate" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
+ {
+ group_id = group_list->getCurrentID();
+ }
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ActivateGroup);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID, group_id);
+ gAgent.sendReliableMessage();
+}
+
+void LLFloaterGroups::info()
+{
+ llinfos << "LLFloaterGroups::info" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list && (group_id = group_list->getCurrentID()).notNull())
+ {
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+}
+
+void LLFloaterGroups::leave()
+{
+ llinfos << "LLFloaterGroups::leave" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list && (group_id = group_list->getCurrentID()).notNull())
+ {
+ S32 count = gAgent.mGroups.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ if(gAgent.mGroups.get(i).mID == group_id)
+ break;
+ }
+ if(i < count)
+ {
+ LLUUID* cb_data = new LLUUID((const LLUUID&)group_id);
+ LLString::format_map_t args;
+ args["[GROUP]"] = gAgent.mGroups.get(i).mName;
+ gViewerWindow->alertXml("GroupLeaveConfirmMember", args, callbackLeaveGroup, (void*)cb_data);
+ }
+ }
+}
+
+void LLFloaterGroups::search()
+{
+ LLFloaterDirectory::showGroups();
+}
+
+// static
+void LLFloaterGroups::callbackLeaveGroup(S32 option, void* userdata)
+{
+ LLUUID* group_id = (LLUUID*)userdata;
+ if(option == 0 && group_id)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_LeaveGroupRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID, *group_id);
+ gAgent.sendReliableMessage();
+ }
+ delete group_id;
+}
+
+void LLFloaterGroups::ok()
+{
+ llinfos << "LLFloaterGroups::ok" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
+ {
+ group_id = group_list->getCurrentID();
+ }
+ if(mOKCallback)
+ {
+ mOKCallback(group_id, mCallbackUserdata);
+ }
+
+ close();
+}
+
+void LLFloaterGroups::highlightGroupList(LLUICtrl*)
+{
+ llinfos << "LLFloaterGroups::highlightGroupList" << llendl;
+ enableButtons();
+}
diff --git a/indra/newview/llfloatergroups.h b/indra/newview/llfloatergroups.h
new file mode 100644
index 0000000000..6f73c97553
--- /dev/null
+++ b/indra/newview/llfloatergroups.h
@@ -0,0 +1,110 @@
+/**
+ * @file llfloatergroups.h
+ * @brief LLFloaterGroups class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shown from Edit -> Groups...
+ * Shows the agent's groups and allows the edit window to be invoked.
+ * Also overloaded to allow picking of a single group for assigning
+ * objects and land to groups.
+ */
+
+#ifndef LL_LLFLOATERGROUPS_H
+#define LL_LLFLOATERGROUPS_H
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class llfloatergroups
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#include "lluuid.h"
+#include "llmap.h"
+#include "llevent.h"
+#include "llfloater.h"
+
+class LLUICtrl;
+class LLTextBox;
+class LLScrollListCtrl;
+class LLButton;
+
+class LLFloaterGroups : public LLFloater
+{
+public:
+ //LLEventListener
+ /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+
+ enum EGroupDialog
+ {
+ AGENT_GROUPS,
+ CHOOSE_ONE
+ };
+ // Call this with an agent id and AGENT_GROUPS for an agent's
+ // groups, otherwise, call with an object id and SET_OBJECT_GROUP
+ // when modifying an object.
+ static LLFloaterGroups* show(const LLUUID& id, EGroupDialog type);
+
+ // Return the instance requested if it already exists. Otherwise,
+ // return NULL.
+ static LLFloaterGroups* getInstance(const LLUUID& id);
+
+ // clear the group list, and get a fresh set of info.
+ void reset();
+
+ void setOkCallback( void (*callback)(LLUUID, void*),
+ void* userdata);
+
+ EGroupDialog getType() const { return mType; }
+
+protected:
+ // initialize based on the type
+ BOOL postBuild();
+
+ // highlight_id is a group id to highlight
+ void initAgentGroups(const LLUUID& highlight_id);
+ void enableButtons();
+
+ static void onBtnCreate(void* userdata);
+ static void onBtnActivate(void* userdata);
+ static void onBtnInfo(void* userdata);
+ static void onBtnLeave(void* userdata);
+ static void onBtnSearch(void* userdata);
+ static void onBtnVote(void* userdata);
+ static void onBtnOK(void* userdata);
+ static void onBtnCancel(void* userdata);
+ static void onGroupList(LLUICtrl* ctrl, void* userdata);
+ static void onDoubleClickGroup(void* userdata);
+
+ void create();
+ void activate();
+ void info();
+ void leave();
+ void search();
+ void callVote();
+ void ok();
+ void highlightGroupList(LLUICtrl*);
+
+ static void callbackLeaveGroup(S32 option, void* userdata);
+
+protected:
+ LLUUID mID;
+
+ EGroupDialog mType;
+
+ void (*mOKCallback)(LLUUID id, void* userdata);
+ void* mCallbackUserdata;
+
+protected:
+ static LLMap<const LLUUID, LLFloaterGroups*> sInstances;
+
+public:
+ // do not call these directly
+ LLFloaterGroups(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& id);
+ virtual ~LLFloaterGroups();
+};
+
+
+#endif // LL_LLFLOATERGROUPS_H
diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp
new file mode 100644
index 0000000000..68a5a90bcc
--- /dev/null
+++ b/indra/newview/llfloaterimagepreview.cpp
@@ -0,0 +1,679 @@
+/**
+ * @file llfloaterimagepreview.cpp
+ * @brief LLFloaterImagePreview class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterimagepreview.h"
+
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagejpeg.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "lltextbox.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+
+//static
+S32 LLFloaterImagePreview::sUploadAmount = 10;
+
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREF_BUTTON_HEIGHT = 16;
+const S32 PREVIEW_TEXTURE_HEIGHT = 300;
+
+//-----------------------------------------------------------------------------
+// LLFloaterImagePreview()
+//-----------------------------------------------------------------------------
+LLFloaterImagePreview::LLFloaterImagePreview(const char* filename) :
+ LLFloaterNameDesc(filename)
+{
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+ mGLName = 0;
+ loadImage(mFilenameAndPath.c_str());
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::postBuild()
+{
+ if (!LLFloaterNameDesc::postBuild())
+ {
+ return FALSE;
+ }
+
+ childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount));
+
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface)
+ {
+ iface->selectFirstItem();
+ }
+ childSetCommitCallback("clothing_type_combo", onPreviewTypeCommit, this);
+
+ mPreviewRect.set(PREVIEW_HPAD,
+ PREVIEW_TEXTURE_HEIGHT,
+ getRect().getWidth() - PREVIEW_HPAD,
+ PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f);
+
+ childHide("bad_image_text");
+
+ if (mRawImagep.notNull())
+ {
+ mAvatarPreview = new LLImagePreviewAvatar(256, 256);
+ mAvatarPreview->setPreviewTarget("mPelvis", "mUpperBodyMesh0", mRawImagep, 2.f, FALSE);
+ }
+ else
+ {
+ mAvatarPreview = NULL;
+ childShow("bad_image_text");
+ childDisable("clothing_type_combo");
+ childDisable("ok_btn");
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterImagePreview()
+//-----------------------------------------------------------------------------
+LLFloaterImagePreview::~LLFloaterImagePreview()
+{
+ mRawImagep = NULL;
+ delete mAvatarPreview;
+ if (mGLName)
+ {
+ glDeleteTextures(1, &mGLName );
+ }
+}
+
+//static
+//-----------------------------------------------------------------------------
+// onPreviewTypeCommit()
+//-----------------------------------------------------------------------------
+void LLFloaterImagePreview::onPreviewTypeCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterImagePreview *fp =(LLFloaterImagePreview *)userdata;
+
+ if (!fp->mAvatarPreview)
+ {
+ return;
+ }
+
+ S32 which_mode = 0;
+
+ LLCtrlSelectionInterface* iface = fp->childGetSelectionInterface("clothing_type_combo");
+ if (iface)
+ {
+ which_mode = iface->getFirstSelectedIndex();
+ }
+
+ switch(which_mode)
+ {
+ case 0:
+ break;
+ case 1:
+ fp->mAvatarPreview->setPreviewTarget("mSkull", "mHairMesh0", fp->mRawImagep, 0.4f, FALSE);
+ break;
+ case 2:
+ fp->mAvatarPreview->setPreviewTarget("mSkull", "mHeadMesh0", fp->mRawImagep, 0.4f, FALSE);
+ break;
+ case 3:
+ fp->mAvatarPreview->setPreviewTarget("mChest", "mUpperBodyMesh0", fp->mRawImagep, 1.0f, FALSE);
+ break;
+ case 4:
+ fp->mAvatarPreview->setPreviewTarget("mKneeLeft", "mLowerBodyMesh0", fp->mRawImagep, 1.2f, FALSE);
+ break;
+ case 5:
+ fp->mAvatarPreview->setPreviewTarget("mSkull", "mHeadMesh0", fp->mRawImagep, 0.4f, TRUE);
+ break;
+ case 6:
+ fp->mAvatarPreview->setPreviewTarget("mChest", "mUpperBodyMesh0", fp->mRawImagep, 1.2f, TRUE);
+ break;
+ case 7:
+ fp->mAvatarPreview->setPreviewTarget("mKneeLeft", "mLowerBodyMesh0", fp->mRawImagep, 1.2f, TRUE);
+ break;
+ case 8:
+ fp->mAvatarPreview->setPreviewTarget("mKneeLeft", "mSkirtMesh0", fp->mRawImagep, 1.3f, FALSE);
+ break;
+ default:
+ break;
+ }
+ fp->mAvatarPreview->refresh();
+ //gViewerWindow->requestFastFrame(fp);
+}
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLFloaterImagePreview::draw()
+{
+ LLFloater::draw();
+ LLRect r = getRect();
+
+ if (mRawImagep.notNull())
+ {
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ gl_rect_2d_checkerboard(mPreviewRect);
+ LLGLDisable gls_alpha(GL_ALPHA_TEST);
+
+ GLenum format_options[4] = { GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
+ GLenum format = format_options[mRawImagep->getComponents()-1];
+
+ GLenum internal_format_options[4] = { GL_LUMINANCE8, GL_LUMINANCE8_ALPHA8, GL_RGB8, GL_RGBA8 };
+ GLenum internal_format = internal_format_options[mRawImagep->getComponents()-1];
+
+ if (mGLName)
+ {
+ LLImageGL::bindExternalTexture( mGLName, 0, GL_TEXTURE_2D );
+ }
+ else
+ {
+ glGenTextures(1, &mGLName );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( mGLName, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, internal_format,
+ mRawImagep->getWidth(), mRawImagep->getHeight(),
+ 0, format, GL_UNSIGNED_BYTE, mRawImagep->getData());
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (mAvatarPreview)
+ {
+ mAvatarPreview->setTexture(mGLName);
+ }
+ }
+
+ glColor3f(1.f, 1.f, 1.f);
+ glBegin( GL_QUADS );
+ {
+ glTexCoord2f(mPreviewImageRect.mLeft, mPreviewImageRect.mTop);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ glTexCoord2f(mPreviewImageRect.mLeft, mPreviewImageRect.mBottom);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(mPreviewImageRect.mRight, mPreviewImageRect.mBottom);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(mPreviewImageRect.mRight, mPreviewImageRect.mTop);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ }
+ glEnd();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ stop_glerror();
+ }
+ else
+ {
+ if (mAvatarPreview)
+ {
+ glColor3f(1.f, 1.f, 1.f);
+ mAvatarPreview->bindTexture();
+
+ glBegin( GL_QUADS );
+ {
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ }
+ glEnd();
+
+ mAvatarPreview->unbindTexture();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// loadImage()
+//-----------------------------------------------------------------------------
+bool LLFloaterImagePreview::loadImage(const char *src_filename)
+{
+ // U32 length = strlen(src_filename);
+ const char* ext = strrchr(src_filename, '.');
+ char error_message[MAX_STRING];
+ error_message[0] = '\0';
+
+ U32 codec = IMG_CODEC_INVALID;
+ LLString temp_str;
+ if( 0 == strnicmp(ext, ".bmp", 4) )
+ {
+ codec = IMG_CODEC_BMP;
+ }
+ else if( 0 == strnicmp(ext, ".tga", 4) )
+ {
+ codec = IMG_CODEC_TGA;
+ }
+ else if( 0 == strnicmp(ext, ".jpg", 4) || 0 == strnicmp(ext, ".jpeg", 5))
+ {
+ codec = IMG_CODEC_JPEG;
+ }
+
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+
+ switch (codec)
+ {
+ case IMG_CODEC_BMP:
+ {
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+
+ if (!bmp_image->load(src_filename))
+ {
+ return false;
+ }
+
+ if (!bmp_image->decode(raw_image))
+ {
+ return false;
+ }
+ }
+ break;
+ case IMG_CODEC_TGA:
+ {
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ if (!tga_image->load(src_filename))
+ {
+ return false;
+ }
+
+ if (!tga_image->decode(raw_image))
+ {
+ return false;
+ }
+
+ if( (tga_image->getComponents() != 3) &&
+ (tga_image->getComponents() != 4) )
+ {
+ tga_image->setLastError( "Image files with less than 3 or more than 4 components are not supported." );
+ return false;
+ }
+ }
+ break;
+ case IMG_CODEC_JPEG:
+ {
+ LLPointer<LLImageJPEG> jpeg_image = new LLImageJPEG;
+
+ if (!jpeg_image->load(src_filename))
+ {
+ return false;
+ }
+
+ if (!jpeg_image->decode(raw_image))
+ {
+ return false;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ raw_image->biasedScaleToPowerOfTwo(1024);
+ mRawImagep = raw_image;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseDown()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mPreviewRect.pointInRect(x, y))
+ {
+ bringToFront( x, y );
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ gViewerWindow->hideCursor();
+ mLastMouseX = x;
+ mLastMouseY = y;
+ return TRUE;
+ }
+
+ return LLFloater::handleMouseDown(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseUp()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->setMouseCapture(FALSE, NULL);
+ gViewerWindow->showCursor();
+ return LLFloater::handleMouseUp(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleHover()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleHover(S32 x, S32 y, MASK mask)
+{
+ MASK local_mask = mask & ~MASK_ALT;
+
+ if (mAvatarPreview && gViewerWindow->hasMouseCapture(this))
+ {
+ if (local_mask == MASK_PAN)
+ {
+ // pan here
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ mPreviewImageRect.translate((F32)(x - mLastMouseX) * -0.005f * mPreviewImageRect.getWidth(),
+ (F32)(y - mLastMouseY) * -0.005f * mPreviewImageRect.getHeight());
+ }
+ else
+ {
+ mAvatarPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f);
+ }
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAvatarPreview->rotate(yaw_radians, pitch_radians);
+ }
+ else
+ {
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ F32 zoom_amt = (F32)(y - mLastMouseY) * -0.002f;
+ mPreviewImageRect.stretch(zoom_amt);
+ }
+ else
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAvatarPreview->rotate(yaw_radians, 0.f);
+ mAvatarPreview->zoom(zoom_amt);
+ }
+ }
+
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ if (mPreviewImageRect.getWidth() > 1.f)
+ {
+ mPreviewImageRect.stretch((1.f - mPreviewImageRect.getWidth()) * 0.5f);
+ }
+ else if (mPreviewImageRect.getWidth() < 0.1f)
+ {
+ mPreviewImageRect.stretch((0.1f - mPreviewImageRect.getWidth()) * 0.5f);
+ }
+
+ if (mPreviewImageRect.getHeight() > 1.f)
+ {
+ mPreviewImageRect.stretch((1.f - mPreviewImageRect.getHeight()) * 0.5f);
+ }
+ else if (mPreviewImageRect.getHeight() < 0.1f)
+ {
+ mPreviewImageRect.stretch((0.1f - mPreviewImageRect.getHeight()) * 0.5f);
+ }
+
+ if (mPreviewImageRect.mLeft < 0.f)
+ {
+ mPreviewImageRect.translate(-mPreviewImageRect.mLeft, 0.f);
+ }
+ else if (mPreviewImageRect.mRight > 1.f)
+ {
+ mPreviewImageRect.translate(1.f - mPreviewImageRect.mRight, 0.f);
+ }
+
+ if (mPreviewImageRect.mBottom < 0.f)
+ {
+ mPreviewImageRect.translate(0.f, -mPreviewImageRect.mBottom);
+ }
+ else if (mPreviewImageRect.mTop > 1.f)
+ {
+ mPreviewImageRect.translate(0.f, 1.f - mPreviewImageRect.mTop);
+ }
+ }
+ else
+ {
+ mAvatarPreview->refresh();
+ }
+
+ LLUI::setCursorPositionLocal(this, mLastMouseX, mLastMouseY);
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ if (!mPreviewRect.pointInRect(x, y) || !mAvatarPreview)
+ {
+ return LLFloater::handleHover(x, y, mask);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+ }
+ else if (local_mask == MASK_PAN)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (mPreviewRect.pointInRect(x, y) && mAvatarPreview)
+ {
+ mAvatarPreview->zoom((F32)clicks * -0.2f);
+ mAvatarPreview->refresh();
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// onMouseCaptureLost()
+//-----------------------------------------------------------------------------
+void LLFloaterImagePreview::onMouseCaptureLost(LLMouseHandler* handler)
+{
+ gViewerWindow->showCursor();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLImagePreviewAvatar
+//-----------------------------------------------------------------------------
+LLImagePreviewAvatar::LLImagePreviewAvatar(S32 width, S32 height) : LLDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE)
+{
+ mNeedsUpdate = TRUE;
+ mTargetJoint = NULL;
+ mTargetMesh = NULL;
+ mCameraDistance = 0.f;
+ mCameraYaw = 0.f;
+ mCameraPitch = 0.f;
+ mCameraZoom = 1.f;
+
+ mDummyAvatar = new LLVOAvatar(LLUUID::null, LL_PCODE_LEGACY_AVATAR, gAgent.getRegion());
+ mDummyAvatar->createDrawable(&gPipeline);
+ mDummyAvatar->mIsDummy = TRUE;
+ mDummyAvatar->mSpecialRenderMode = 2;
+ mDummyAvatar->setPositionAgent(LLVector3::zero);
+ mDummyAvatar->slamPosition();
+ mDummyAvatar->updateJointLODs();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ gPipeline.markVisible(mDummyAvatar->mDrawable);
+
+ mTextureName = 0;
+}
+
+LLImagePreviewAvatar::~LLImagePreviewAvatar()
+{
+ mDummyAvatar->markDead();
+}
+
+
+void LLImagePreviewAvatar::setPreviewTarget(const char* joint_name, const char* mesh_name, LLImageRaw* imagep, F32 distance, BOOL male)
+{
+ mTargetJoint = mDummyAvatar->mRoot.findJoint(joint_name);
+ // clear out existing test mesh
+ if (mTargetMesh)
+ {
+ mTargetMesh->setTestTexture(0);
+ }
+
+ if (male)
+ {
+ mDummyAvatar->setVisualParamWeight( "male", 1.f );
+ mDummyAvatar->updateVisualParams();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ }
+ else
+ {
+ mDummyAvatar->setVisualParamWeight( "male", 0.f );
+ mDummyAvatar->updateVisualParams();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ }
+ mDummyAvatar->mRoot.setVisible(FALSE, TRUE);
+
+ mTargetMesh = (LLViewerJointMesh*)mDummyAvatar->mRoot.findJoint(mesh_name);
+ mTargetMesh->setTestTexture(mTextureName);
+ mTargetMesh->setVisible(TRUE, FALSE);
+ mCameraDistance = distance;
+ mCameraZoom = 1.f;
+ mCameraPitch = 0.f;
+ mCameraYaw = 0.f;
+ mCameraOffset.clearVec();
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+BOOL LLImagePreviewAvatar::render()
+{
+ mNeedsUpdate = FALSE;
+ LLVOAvatar* avatarp = mDummyAvatar;
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSUIDefault def;
+ glColor4f(0.15f, 0.2f, 0.3f, 1.f);
+
+ gl_rect_2d_simple( mWidth, mHeight );
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ LLVector3 target_pos = mTargetJoint->getWorldPosition();
+
+ LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
+ LLQuaternion(mCameraYaw, LLVector3::z_axis);
+
+ LLQuaternion av_rot = avatarp->mPelvisp->getWorldRotation() * camera_rot;
+ gCamera->setOriginAndLookAt(
+ target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera
+ LLVector3::z_axis, // up
+ target_pos + (mCameraOffset * av_rot) ); // point of interest
+
+ stop_glerror();
+
+ gCamera->setView(gCamera->getDefaultFOV() / mCameraZoom);
+ gCamera->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mWidth, mHeight, FALSE);
+
+ if (avatarp->mDrawable.notNull())
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ // make sure alpha=0 shows avatar material color
+ LLGLDisable no_blend(GL_BLEND);
+
+ LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
+
+ gPipeline.unbindAGP();
+ avatarPoolp->syncAGP();
+ if (avatarPoolp->canUseAGP() && gPipeline.usingAGP())
+ {
+ gPipeline.bindAGP();
+ }
+ avatarPoolp->renderAvatars(avatarp, TRUE); // renders only one avatar (no shaders)
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// refresh()
+//-----------------------------------------------------------------------------
+void LLImagePreviewAvatar::refresh()
+{
+ mNeedsUpdate = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLImagePreviewAvatar::rotate(F32 yaw_radians, F32 pitch_radians)
+{
+ mCameraYaw = mCameraYaw + yaw_radians;
+
+ mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
+}
+
+//-----------------------------------------------------------------------------
+// zoom()
+//-----------------------------------------------------------------------------
+void LLImagePreviewAvatar::zoom(F32 zoom_amt)
+{
+ mCameraZoom = llclamp(mCameraZoom + zoom_amt, 1.f, 10.f);
+}
+
+void LLImagePreviewAvatar::pan(F32 right, F32 up)
+{
+ mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f);
+ mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f);
+}
diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h
new file mode 100644
index 0000000000..4978a6ea0b
--- /dev/null
+++ b/indra/newview/llfloaterimagepreview.h
@@ -0,0 +1,83 @@
+/**
+ * @file llfloaterimagepreview.h
+ * @brief LLFloaterImagePreview class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERIMAGEPREVIEW_H
+#define LL_LLFLOATERIMAGEPREVIEW_H
+
+#include "llfloaternamedesc.h"
+#include "lldynamictexture.h"
+#include "llquaternion.h"
+
+class LLComboBox;
+class LLJoint;
+class LLViewerJointMesh;
+class LLVOAvatar;
+class LLTextBox;
+
+class LLImagePreviewAvatar : public LLDynamicTexture
+{
+public:
+ LLImagePreviewAvatar(S32 width, S32 height);
+ virtual ~LLImagePreviewAvatar();
+
+ void setPreviewTarget(const char* joint_name, const char *mesh_name, LLImageRaw* imagep, F32 distance, BOOL male);
+ void setTexture(U32 name) { mTextureName = name; }
+
+ BOOL render();
+ void refresh();
+ void rotate(F32 yaw_radians, F32 pitch_radians);
+ void zoom(F32 zoom_amt);
+ void pan(F32 right, F32 up);
+ virtual BOOL needsRender() { return mNeedsUpdate; }
+
+protected:
+ BOOL mNeedsUpdate;
+ LLJoint* mTargetJoint;
+ LLViewerJointMesh* mTargetMesh;
+ F32 mCameraDistance;
+ F32 mCameraYaw;
+ F32 mCameraPitch;
+ F32 mCameraZoom;
+ LLVector3 mCameraOffset;
+ LLPointer<LLVOAvatar> mDummyAvatar;
+ U32 mTextureName;
+};
+
+class LLFloaterImagePreview : public LLFloaterNameDesc
+{
+public:
+ LLFloaterImagePreview(const char* filename);
+ virtual ~LLFloaterImagePreview();
+
+ virtual BOOL postBuild();
+
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
+ static void onMouseCaptureLost(LLMouseHandler*);
+ static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+
+protected:
+ static void onPreviewTypeCommit(LLUICtrl*,void*);
+ void draw();
+ bool loadImage(const char* filename);
+
+ LLPointer<LLImageRaw> mRawImagep;
+ LLImagePreviewAvatar* mAvatarPreview;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ LLRect mPreviewRect;
+ LLRectf mPreviewImageRect;
+ GLuint mGLName;
+
+ static S32 sUploadAmount;
+};
+
+#endif // LL_LLFLOATERIMAGEPREVIEW_H
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
new file mode 100644
index 0000000000..6897e49dd9
--- /dev/null
+++ b/indra/newview/llfloaterland.cpp
@@ -0,0 +1,3208 @@
+/**
+ * @file llfloaterland.cpp
+ * @brief "About Land" floater, allowing display and editing of land parcel properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <sstream>
+#include <time.h>
+
+#include "llfloaterland.h"
+
+#include "llcachename.h"
+#include "llfocusmgr.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfloaterauction.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroups.h"
+#include "llfloatergroupinfo.h"
+#include "lllineeditor.h"
+#include "llnamelistctrl.h"
+#include "llnotify.h"
+#include "llradiogroup.h"
+#include "llscrolllistctrl.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "lluiconstants.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewermessage.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llmediaengine.h"
+#include "llviewercontrol.h"
+#include "roles_constants.h"
+
+static const S32 EDIT_HEIGHT = 16;
+static const S32 LEFT = HPAD;
+static const S32 BOTTOM = VPAD;
+static const S32 RULER0 = LEFT;
+static const S32 RULER05 = RULER0 + 24;
+static const S32 RULER1 = RULER05 + 16;
+static const S32 RULER15 = RULER1 + 20;
+static const S32 RULER2 = RULER1 + 32;
+static const S32 RULER205= RULER2 + 32;
+static const S32 RULER20 = RULER2 + 64;
+static const S32 RULER21 = RULER20 + 16;
+static const S32 RULER22 = RULER21 + 32;
+static const S32 RULER225 = RULER20 + 64;
+static const S32 RULER23 = RULER22 + 64;
+static const S32 RULER24 = RULER23 + 26;
+static const S32 RULER3 = RULER2 + 102;
+static const S32 RULER4 = RULER3 + 8;
+static const S32 RULER5 = RULER4 + 50;
+static const S32 RULER6 = RULER5 + 52;
+static const S32 RULER7 = RULER6 + 24;
+static const S32 RIGHT = LEFT + 278;
+static const S32 FAR_RIGHT = LEFT + 324 + 40;
+
+static const char PRICE[] = "Price:";
+static const char NO_PRICE[] = "";
+static const char AREA[] = "Area:";
+
+static const char OWNER_ONLINE[] = "0";
+static const char OWNER_OFFLINE[] = "1";
+static const char OWNER_GROUP[] = "2";
+
+static const char NEED_TIER_TO_MODIFY_STRING[] = "You must approve your purchase to modify this land.";
+
+static const char WEB_PAGE[] = "Web page";
+static const char QUICKTIME_MOVIE[] = "QuickTime movie";
+static const char RAW_HTML[] = "Raw HTML";
+
+// constants used in callbacks below - syntactic sugar.
+static const BOOL BUY_GROUP_LAND = TRUE;
+static const BOOL BUY_PERSONAL_LAND = FALSE;
+
+// Statics
+LLFloaterLand* LLFloaterLand::sInstance = NULL;
+LLParcelSelectionObserver* LLFloaterLand::sObserver = NULL;
+S32 LLFloaterLand::sLastTab = 0;
+BOOL LLFloaterLand::sRequestReplyOnUpdate = TRUE;
+
+// Local classes
+class LLParcelSelectionObserver : public LLParcelObserver
+{
+public:
+ virtual void changed() { LLFloaterLand::refreshAll(); }
+};
+
+//---------------------------------------------------------------------------
+// LLFloaterLand
+//---------------------------------------------------------------------------
+
+void send_parcel_select_objects(S32 parcel_local_id, S32 return_type,
+ uuid_list_t* return_ids = NULL)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ // Since new highlight will be coming in, drop any highlights
+ // that exist right now.
+ gSelectMgr->unhighlightAll();
+
+ msg->newMessageFast(_PREHASH_ParcelSelectObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addS32Fast(_PREHASH_ReturnType, return_type);
+
+ // Throw all return ids into the packet.
+ // TODO: Check for too many ids.
+ if (return_ids)
+ {
+ uuid_list_t::iterator end = return_ids->end();
+ for (uuid_list_t::iterator it = return_ids->begin();
+ it != end;
+ ++it)
+ {
+ msg->nextBlockFast(_PREHASH_ReturnIDs);
+ msg->addUUIDFast(_PREHASH_ReturnID, (*it));
+ }
+ }
+ else
+ {
+ // Put in a null key so that the message is complete.
+ msg->nextBlockFast(_PREHASH_ReturnIDs);
+ msg->addUUIDFast(_PREHASH_ReturnID, LLUUID::null);
+ }
+
+ msg->sendReliable(region->getHost());
+}
+
+
+// static
+void LLFloaterLand::show()
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterLand();
+
+ // Select tab from last view
+ sInstance->mTabLand->selectTab(sLastTab);
+
+ sObserver = new LLParcelSelectionObserver();
+ gParcelMgr->addObserver( sObserver );
+ }
+
+ sInstance->open();
+
+ // Done automatically when the selected parcel's properties arrive
+ // (and hence we have the local id).
+ // gParcelMgr->sendParcelAccessListRequest(AL_ACCESS | AL_BAN | AL_RENTER);
+
+ // If we've already got the parcel data, fill the
+ // floater with it.
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (parcel)
+ {
+ sInstance->refresh();
+ }
+
+ sRequestReplyOnUpdate = TRUE;
+}
+
+//static
+LLPanelLandObjects* LLFloaterLand::getCurrentPanelLandObjects()
+{
+ if (!sInstance)
+ {
+ return NULL;
+ }
+
+ return sInstance->mPanelObjects;
+}
+
+//static
+LLPanelLandCovenant* LLFloaterLand::getCurrentPanelLandCovenant()
+{
+ if (!sInstance)
+ {
+ return NULL;
+ }
+
+ return sInstance->mPanelCovenant;
+}
+
+// static
+void LLFloaterLand::refreshAll()
+{
+ if (sInstance)
+ {
+ sInstance->refresh();
+ }
+}
+
+
+// virtual
+BOOL LLFloaterLand::canClose()
+{
+ // canClose is checked as the first step of attempting to close
+ // the window, before focus is released from controls. Since we're
+ // closing the window and deselecting the land, we
+ // don't want replies to the upstream messages that get sent
+ // (because the reply will cause the land to be selected again).
+ sRequestReplyOnUpdate = FALSE;
+ return TRUE;
+}
+
+// virtual
+void LLFloaterLand::onClose(bool app_quitting)
+{
+ gParcelMgr->removeObserver( sObserver );
+ delete sObserver;
+ sObserver = NULL;
+
+ // Must do this after removing observer, otherwise
+ // infinite loops notifying and closing.
+ gParcelMgr->deselectLand();
+
+ // Might have been showing owned objects
+ gSelectMgr->unhighlightAll();
+
+ // Save which panel we had open
+ sLastTab = mTabLand->getCurrentPanelIndex();
+
+ destroy();
+}
+
+
+LLFloaterLand::LLFloaterLand()
+: LLFloater("floaterland", "FloaterLandRect5", "About Land")
+{
+
+
+ std::map<LLString, LLCallbackMap> factory_map;
+ factory_map["land_general_panel"] = LLCallbackMap(createPanelLandGeneral, this);
+
+
+ factory_map["land_covenant_panel"] = LLCallbackMap(createPanelLandCovenant, this);
+ factory_map["land_objects_panel"] = LLCallbackMap(createPanelLandObjects, this);
+ factory_map["land_options_panel"] = LLCallbackMap(createPanelLandOptions, this);
+ factory_map["land_media_panel"] = LLCallbackMap(createPanelLandMedia, this);
+ factory_map["land_access_panel"] = LLCallbackMap(createPanelLandAccess, this);
+ factory_map["land_ban_panel"] = LLCallbackMap(createPanelLandBan, this);
+
+ gUICtrlFactory->buildFloater(this, "floater_about_land.xml", &factory_map);
+
+
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(this, "landtab");
+
+ mTabLand = (LLTabContainer*) tab;
+
+
+ if (tab)
+ {
+ tab->selectTab(sLastTab);
+ }
+}
+
+
+// virtual
+LLFloaterLand::~LLFloaterLand()
+{
+ sInstance = NULL;
+}
+
+
+// public
+void LLFloaterLand::refresh()
+{
+ mPanelGeneral->refresh();
+ mPanelObjects->refresh();
+ mPanelOptions->refresh();
+ mPanelMedia->refresh();
+ mPanelAccess->refresh();
+ mPanelBan->refresh();
+}
+
+
+
+void* LLFloaterLand::createPanelLandGeneral(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelGeneral = new LLPanelLandGeneral();
+ return self->mPanelGeneral;
+}
+
+// static
+
+
+void* LLFloaterLand::createPanelLandCovenant(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelCovenant = new LLPanelLandCovenant();
+ return self->mPanelCovenant;
+}
+
+
+// static
+void* LLFloaterLand::createPanelLandObjects(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelObjects = new LLPanelLandObjects();
+ return self->mPanelObjects;
+}
+
+// static
+void* LLFloaterLand::createPanelLandOptions(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelOptions = new LLPanelLandOptions();
+ return self->mPanelOptions;
+}
+
+// static
+void* LLFloaterLand::createPanelLandMedia(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelMedia = new LLPanelLandMedia();
+ return self->mPanelMedia;
+}
+
+// static
+void* LLFloaterLand::createPanelLandAccess(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelAccess = new LLPanelLandAccess();
+ return self->mPanelAccess;
+}
+
+// static
+void* LLFloaterLand::createPanelLandBan(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelBan = new LLPanelLandBan();
+ return self->mPanelBan;
+}
+
+
+//---------------------------------------------------------------------------
+// LLPanelLandGeneral
+//---------------------------------------------------------------------------
+
+
+LLPanelLandGeneral::LLPanelLandGeneral()
+: LLPanel("land_general_panel"),
+ mUncheckedSell(FALSE)
+{
+}
+
+BOOL LLPanelLandGeneral::postBuild()
+{
+
+ mEditName = LLUICtrlFactory::getLineEditorByName(this, "Name");
+ mEditName->setCommitCallback(onCommitAny);
+ childSetPrevalidate("Name", LLLineEditor::prevalidatePrintableNotPipe);
+ childSetUserData("Name", this);
+
+
+ mEditDesc = LLUICtrlFactory::getLineEditorByName(this, "Description");
+ mEditDesc->setCommitCallback(onCommitAny);
+ childSetPrevalidate("Description", LLLineEditor::prevalidatePrintableNotPipe);
+ childSetUserData("Description", this);
+
+
+ mTextSalePending = LLUICtrlFactory::getTextBoxByName(this, "SalePending");
+ mTextOwnerLabel = LLUICtrlFactory::getTextBoxByName(this, "Owner:");
+ mTextOwner = LLUICtrlFactory::getTextBoxByName(this, "OwnerText");
+
+
+ mBtnProfile = LLUICtrlFactory::getButtonByName(this, "Profile...");
+ mBtnProfile->setClickedCallback(onClickProfile, this);
+
+
+ mTextGroupLabel = LLUICtrlFactory::getTextBoxByName(this, "Group:");
+ mTextGroup = LLUICtrlFactory::getTextBoxByName(this, "GroupText");
+
+
+ mBtnSetGroup = LLUICtrlFactory::getButtonByName(this, "Set...");
+ mBtnSetGroup->setClickedCallback(onClickSetGroup, this);
+
+
+
+ mCheckDeedToGroup = LLUICtrlFactory::getCheckBoxByName(this, "check deed");
+ childSetCommitCallback("check deed", onCommitAny, this);
+
+
+ mBtnDeedToGroup = LLUICtrlFactory::getButtonByName(this, "Deed...");
+ mBtnDeedToGroup->setClickedCallback(onClickDeed, this);
+
+
+ mCheckContributeWithDeed = LLUICtrlFactory::getCheckBoxByName(this, "check contib");
+ childSetCommitCallback("check contib", onCommitAny, this);
+
+
+
+ mSaleInfoNotForSale = LLUICtrlFactory::getTextBoxByName(this, "Not for sale.");
+
+ mSaleInfoForSale1 = LLUICtrlFactory::getTextBoxByName(this, "For Sale: Price L$[PRICE].");
+
+
+ mBtnSellLand = LLUICtrlFactory::getButtonByName(this, "Sell Land...");
+ mBtnSellLand->setClickedCallback(onClickSellLand, this);
+
+ mSaleInfoForSale2 = LLUICtrlFactory::getTextBoxByName(this, "For sale to");
+
+ mSaleInfoForSaleObjects = LLUICtrlFactory::getTextBoxByName(this, "Sell with landowners objects in parcel.");
+
+ mSaleInfoForSaleNoObjects = LLUICtrlFactory::getTextBoxByName(this, "Selling with no objects in parcel.");
+
+
+ mBtnStopSellLand = LLUICtrlFactory::getButtonByName(this, "Cancel Land Sale");
+ mBtnStopSellLand->setClickedCallback(onClickStopSellLand, this);
+
+
+ mTextClaimDateLabel = LLUICtrlFactory::getTextBoxByName(this, "Claimed:");
+ mTextClaimDate = LLUICtrlFactory::getTextBoxByName(this, "DateClaimText");
+
+
+ mTextPriceLabel = LLUICtrlFactory::getTextBoxByName(this, "PriceLabel");
+ mTextPrice = LLUICtrlFactory::getTextBoxByName(this, "PriceText");
+
+
+ mTextDwell = LLUICtrlFactory::getTextBoxByName(this, "DwellText");
+
+
+ mBtnBuyLand = LLUICtrlFactory::getButtonByName(this, "Buy Land...");
+ mBtnBuyLand->setClickedCallback(onClickBuyLand, (void*)&BUY_PERSONAL_LAND);
+
+ mBtnBuyGroupLand = LLUICtrlFactory::getButtonByName(this, "Buy For Group...");
+ mBtnBuyGroupLand->setClickedCallback(onClickBuyLand, (void*)&BUY_GROUP_LAND);
+
+
+ static BOOL deselect_when_done = FALSE;
+ mBtnBuyPass = LLUICtrlFactory::getButtonByName(this, "Buy Pass...");
+ mBtnBuyPass->setClickedCallback(onClickBuyPass, &deselect_when_done);
+
+ mBtnReleaseLand = LLUICtrlFactory::getButtonByName(this, "Abandon Land...");
+ mBtnReleaseLand->setClickedCallback(onClickRelease, NULL);
+
+ mBtnReclaimLand = LLUICtrlFactory::getButtonByName(this, "Reclaim Land...");
+ mBtnReclaimLand->setClickedCallback(onClickReclaim, NULL);
+
+ mBtnStartAuction = LLUICtrlFactory::getButtonByName(this, "Linden Sale...");
+ mBtnStartAuction->setClickedCallback(onClickStartAuction, NULL);
+
+ return TRUE;
+}
+
+
+// virtual
+LLPanelLandGeneral::~LLPanelLandGeneral()
+{ }
+
+
+// public
+void LLPanelLandGeneral::refresh()
+{
+ mBtnStartAuction->setVisible(gAgent.isGodlike());
+
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ bool region_owner = false;
+ LLViewerRegion* regionp = gParcelMgr->getSelectionRegion();
+ if(regionp && (regionp->getOwner() == gAgent.getID()))
+ {
+ region_owner = true;
+ mBtnReleaseLand->setVisible(FALSE);
+ mBtnReclaimLand->setVisible(TRUE);
+ }
+ else
+ {
+ mBtnReleaseLand->setVisible(TRUE);
+ mBtnReclaimLand->setVisible(FALSE);
+ }
+ if (!parcel)
+ {
+ // nothing selected, disable panel
+ mEditName->setEnabled(FALSE);
+ mEditName->setText("");
+
+ mEditDesc->setEnabled(FALSE);
+ mEditDesc->setText("");
+
+ mTextSalePending->setText("");
+ mTextSalePending->setEnabled(FALSE);
+
+ mBtnDeedToGroup->setEnabled(FALSE);
+ mBtnSetGroup->setEnabled(FALSE);
+ mBtnStartAuction->setEnabled(FALSE);
+
+ mCheckDeedToGroup ->set(FALSE);
+ mCheckDeedToGroup ->setEnabled(FALSE);
+ mCheckContributeWithDeed->set(FALSE);
+ mCheckContributeWithDeed->setEnabled(FALSE);
+
+ mTextOwner->setText("");
+ mBtnProfile->setLabelSelected("Profile...");
+ mBtnProfile->setLabelUnselected("Profile...");
+ mBtnProfile->setEnabled(FALSE);
+
+ mTextClaimDate->setText("");
+ mTextGroup->setText("");
+ mTextPrice->setText("");
+
+ mSaleInfoForSale1->setVisible(FALSE);
+ mSaleInfoForSale2->setVisible(FALSE);
+ mSaleInfoForSaleObjects->setVisible(FALSE);
+ mSaleInfoForSaleNoObjects->setVisible(FALSE);
+ mSaleInfoNotForSale->setVisible(FALSE);
+ mBtnSellLand->setVisible(FALSE);
+ mBtnStopSellLand->setVisible(FALSE);
+
+ mTextPriceLabel->setText(NO_PRICE);
+ mTextDwell->setText("");
+
+ mBtnBuyLand->setEnabled(FALSE);
+ mBtnBuyGroupLand->setEnabled(FALSE);
+ mBtnReleaseLand->setEnabled(FALSE);
+ mBtnReclaimLand->setEnabled(FALSE);
+ mBtnBuyPass->setEnabled(FALSE);
+ }
+ else
+ {
+ // something selected, hooray!
+ BOOL is_leased = (LLParcel::OS_LEASED == parcel->getOwnershipStatus());
+ BOOL region_xfer = FALSE;
+ if(regionp
+ && !(regionp->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL))
+ {
+ region_xfer = TRUE;
+ }
+
+ // estate owner/manager cannot edit other parts of the parcel
+ BOOL estate_manager_sellable = !parcel->getAuctionID()
+ && gAgent.canManageEstate()
+ // estate manager/owner can only sell parcels owned by estate owner
+ && regionp
+ && (parcel->getOwnerID() == regionp->getOwner());
+ BOOL owner_sellable = region_xfer && !parcel->getAuctionID()
+ && LLViewerParcelMgr::isParcelModifiableByAgent(
+ parcel, GP_LAND_SET_SALE_INFO);
+ BOOL can_be_sold = owner_sellable || estate_manager_sellable;
+
+ const LLUUID &owner_id = parcel->getOwnerID();
+ BOOL is_public = parcel->isPublic();
+
+ // Is it owned?
+ if (is_public)
+ {
+ mTextSalePending->setText("");
+ mTextSalePending->setEnabled(FALSE);
+ mTextOwner->setText("(public)");
+ mTextOwner->setEnabled(FALSE);
+ mBtnProfile->setEnabled(FALSE);
+ mTextClaimDate->setText("");
+ mTextClaimDate->setEnabled(FALSE);
+ mTextGroup->setText("(none)");
+ mTextGroup->setEnabled(FALSE);
+ mBtnStartAuction->setEnabled(FALSE);
+ }
+ else
+ {
+ if(!is_leased && (owner_id == gAgent.getID()))
+ {
+ mTextSalePending->setText(NEED_TIER_TO_MODIFY_STRING);
+ mTextSalePending->setEnabled(TRUE);
+ }
+ else if(parcel->getAuctionID())
+ {
+ char auction_str[MAX_STRING];
+ sprintf(auction_str, "Auction ID: %u", parcel->getAuctionID());
+ mTextSalePending->setText(auction_str);
+ mTextSalePending->setEnabled(TRUE);
+ }
+ else
+ {
+ // not the owner, or it is leased
+ mTextSalePending->setText("");
+ mTextSalePending->setEnabled(FALSE);
+ }
+ //refreshNames();
+ mTextOwner->setEnabled(TRUE);
+
+ // We support both group and personal profiles
+ mBtnProfile->setEnabled(TRUE);
+
+ if (parcel->getGroupID().isNull())
+ {
+ // Not group owned, so "Profile"
+ mBtnProfile->setLabelSelected("Profile...");
+ mBtnProfile->setLabelUnselected("Profile...");
+
+ mTextGroup->setText("(none)");
+ mTextGroup->setEnabled(FALSE);
+ }
+ else
+ {
+ // Group owned, so "Info"
+ mBtnProfile->setLabelSelected("Info...");
+ mBtnProfile->setLabelUnselected("Info...");
+
+ //mTextGroup->setText("HIPPOS!");//parcel->getGroupName());
+ mTextGroup->setEnabled(TRUE);
+ }
+
+ // Display claim date
+ time_t claim_date = parcel->getClaimDate();
+ char time_buf[TIME_STR_LENGTH];
+ mTextClaimDate->setText(formatted_time(claim_date, time_buf));
+ mTextClaimDate->setEnabled(is_leased);
+
+ BOOL enable_auction = (gAgent.getGodLevel() >= GOD_LIAISON)
+ && (owner_id == GOVERNOR_LINDEN_ID)
+ && (parcel->getAuctionID() == 0);
+ mBtnStartAuction->setEnabled(enable_auction);
+ }
+
+ // Display options
+ BOOL can_edit_identity = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_IDENTITY);
+ mEditName->setEnabled(can_edit_identity);
+ mEditDesc->setEnabled(can_edit_identity);
+
+ BOOL can_edit_agent_only = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_NO_POWERS);
+ mBtnSetGroup->setEnabled(can_edit_agent_only && !parcel->getIsGroupOwned());
+
+ const LLUUID& group_id = parcel->getGroupID();
+
+ // Can only allow deeding if you own it and it's got a group.
+ BOOL enable_deed = (owner_id == gAgent.getID()
+ && group_id.notNull()
+ && gAgent.isInGroup(group_id));
+ // You don't need special powers to allow your object to
+ // be deeded to the group.
+ mCheckDeedToGroup->setEnabled(enable_deed);
+ mCheckDeedToGroup->set( parcel->getAllowDeedToGroup() );
+ mCheckContributeWithDeed->setEnabled(enable_deed && parcel->getAllowDeedToGroup());
+ mCheckContributeWithDeed->set(parcel->getContributeWithDeed());
+
+ // Actually doing the deeding requires you to have GP_LAND_DEED
+ // powers in the group.
+ BOOL can_deed = gAgent.hasPowerInGroup(group_id, GP_LAND_DEED);
+ mBtnDeedToGroup->setEnabled( parcel->getAllowDeedToGroup()
+ && group_id.notNull()
+ && can_deed
+ && !parcel->getIsGroupOwned()
+ );
+
+ mEditName->setText( parcel->getName() );
+ mEditDesc->setText( parcel->getDesc() );
+
+ BOOL for_sale = parcel->getForSale();
+
+ mBtnSellLand->setVisible(FALSE);
+ mBtnStopSellLand->setVisible(FALSE);
+
+ if (for_sale)
+ {
+ mSaleInfoForSale1->setVisible(TRUE);
+ mSaleInfoForSale2->setVisible(TRUE);
+ if (parcel->getSellWithObjects())
+ {
+ mSaleInfoForSaleObjects->setVisible(TRUE);
+ mSaleInfoForSaleNoObjects->setVisible(FALSE);
+ }
+ else
+ {
+ mSaleInfoForSaleObjects->setVisible(FALSE);
+ mSaleInfoForSaleNoObjects->setVisible(TRUE);
+ }
+ mSaleInfoNotForSale->setVisible(FALSE);
+ mSaleInfoForSale1->setTextArg("[PRICE]", llformat("%d", parcel->getSalePrice()));
+ if (can_be_sold)
+ {
+ mBtnStopSellLand->setVisible(TRUE);
+ }
+ }
+ else
+ {
+ mSaleInfoForSale1->setVisible(FALSE);
+ mSaleInfoForSale2->setVisible(FALSE);
+ mSaleInfoForSaleObjects->setVisible(FALSE);
+ mSaleInfoForSaleNoObjects->setVisible(FALSE);
+ mSaleInfoNotForSale->setVisible(TRUE);
+ if (can_be_sold)
+ {
+ mBtnSellLand->setVisible(TRUE);
+ }
+ }
+
+ refreshNames();
+
+ mBtnBuyLand->setEnabled(
+ gParcelMgr->canAgentBuyParcel(parcel, false));
+ mBtnBuyGroupLand->setEnabled(
+ gParcelMgr->canAgentBuyParcel(parcel, true));
+
+ // show pricing information
+ char price[64];
+ const char* label = NULL;
+ S32 area;
+ S32 claim_price;
+ S32 rent_price;
+ F32 dwell;
+ gParcelMgr->getDisplayInfo(&area,
+ &claim_price,
+ &rent_price,
+ &for_sale,
+ &dwell);
+
+ // Area
+ sprintf(price, "%d sq. m.", area);
+ label = AREA;
+
+ mTextPriceLabel->setText(label);
+ mTextPrice->setText(price);
+
+ sprintf(price, "%.0f", dwell);
+ mTextDwell->setText(price);
+
+ if(region_owner)
+ {
+ mBtnReclaimLand->setEnabled(
+ !is_public && (parcel->getOwnerID() != gAgent.getID()));
+ }
+ else
+ {
+ BOOL is_owner_release = LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_RELEASE);
+ BOOL is_manager_release = (gAgent.canManageEstate() &&
+ regionp &&
+ (parcel->getOwnerID() != regionp->getOwner()));
+ BOOL can_release = is_owner_release || is_manager_release;
+ mBtnReleaseLand->setEnabled( can_release );
+ }
+
+ BOOL use_pass = parcel->getParcelFlag(PF_USE_PASS_LIST) && !gParcelMgr->isCollisionBanned();;
+ mBtnBuyPass->setEnabled(use_pass);
+ }
+}
+
+// public
+void LLPanelLandGeneral::refreshNames()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ mTextOwner->setText("");
+ return;
+ }
+
+ char buffer[MAX_STRING];
+ if (parcel->getIsGroupOwned())
+ {
+ buffer[0] = '\0';
+ strcat(buffer, "(Group Owned)");
+ }
+ else
+ {
+ // Figure out the owner's name
+ char owner_first[MAX_STRING];
+ char owner_last[MAX_STRING];
+ gCacheName->getName(parcel->getOwnerID(), owner_first, owner_last);
+ sprintf(buffer, "%s %s", owner_first, owner_last);
+ }
+
+ if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus())
+ {
+ strcat(buffer, " (Sale Pending)");
+ }
+ mTextOwner->setText(buffer);
+
+ if(!parcel->getGroupID().isNull())
+ {
+ gCacheName->getGroupName(parcel->getGroupID(), buffer);
+ }
+ else
+ {
+ buffer[0] = '\0';
+ }
+ mTextGroup->setText(buffer);
+
+ const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID();
+ if(auth_buyer_id.notNull())
+ {
+ LLString name;
+ char firstname[MAX_STRING];
+ char lastname[MAX_STRING];
+ gCacheName->getName(auth_buyer_id, firstname, lastname);
+ name.assign(firstname);
+ name.append(" ");
+ name.append(lastname);
+
+ mSaleInfoForSale2->setTextArg("[BUYER]", name);
+ }
+ else if(parcel->getReservedForNewbie())
+ {
+ mSaleInfoForSale2->setTextArg("[BUYER]", childGetText("new users only"));
+ }
+ else
+ {
+ mSaleInfoForSale2->setTextArg("[BUYER]", childGetText("anyone"));
+ }
+}
+
+
+// virtual
+void LLPanelLandGeneral::draw()
+{
+ refreshNames();
+ LLPanel::draw();
+}
+
+// static
+void LLPanelLandGeneral::onClickSetGroup(void* userdata)
+{
+ LLFloaterGroups* fg;
+ fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ fg->setOkCallback( cbGroupID, userdata );
+}
+
+// static
+void LLPanelLandGeneral::onClickProfile(void* data)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ if (parcel->getIsGroupOwned())
+ {
+ const LLUUID& group_id = parcel->getGroupID();
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+ else
+ {
+ const LLUUID& avatar_id = parcel->getOwnerID();
+ LLFloaterAvatarInfo::showFromObject(avatar_id);
+ }
+}
+
+// static
+void LLPanelLandGeneral::cbGroupID(LLUUID group_id, void* userdata)
+{
+ LLPanelLandGeneral* self = (LLPanelLandGeneral*)userdata;
+ self->setGroup(group_id);
+}
+
+// public
+void LLPanelLandGeneral::setGroup(const LLUUID& group_id)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ // Set parcel properties and send message
+ parcel->setGroupID(group_id);
+ //parcel->setGroupName(group_name);
+ //mTextGroup->setText(group_name);
+
+ // Send update
+ gParcelMgr->sendParcelPropertiesUpdate(parcel, LLFloaterLand::sRequestReplyOnUpdate);
+
+ // Update UI
+ refresh();
+}
+
+// static
+void LLPanelLandGeneral::onClickBuyLand(void* data)
+{
+ BOOL* for_group = (BOOL*)data;
+ gParcelMgr->startBuyLand(*for_group);
+}
+
+BOOL LLPanelLandGeneral::enableDeedToGroup(void*)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ return (parcel != NULL) && (parcel->getParcelFlag(PF_ALLOW_DEED_TO_GROUP));
+}
+
+// static
+void LLPanelLandGeneral::onClickDeed(void*)
+{
+ //LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ //if (parcel)
+ //{
+ gParcelMgr->startDeedLandToGroup();
+ //}
+}
+
+// static
+void LLPanelLandGeneral::onClickRelease(void*)
+{
+ gParcelMgr->startReleaseLand();
+}
+
+// static
+void LLPanelLandGeneral::onClickReclaim(void*)
+{
+ lldebugs << "LLPanelLandGeneral::onClickReclaim()" << llendl;
+ gParcelMgr->reclaimParcel();
+}
+
+// static
+BOOL LLPanelLandGeneral::enableBuyPass(void*)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ return (parcel != NULL) && (parcel->getParcelFlag(PF_USE_PASS_LIST) && !gParcelMgr->isCollisionBanned());
+}
+
+
+// static
+void LLPanelLandGeneral::onClickBuyPass(void* deselect_when_done)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ S32 pass_price = parcel->getPassPrice();
+ const char* parcel_name = parcel->getName();
+ F32 pass_hours = parcel->getPassHours();
+
+ char cost[256], time[256];
+ sprintf(cost, "%d", pass_price);
+ sprintf(time, "%.2f", pass_hours);
+
+ LLStringBase<char>::format_map_t args;
+ args["[COST]"] = cost;
+ args["[PARCEL_NAME]"] = parcel_name;
+ args["[TIME]"] = time;
+
+ gViewerWindow->alertXml("LandBuyPass", args, cbBuyPass, deselect_when_done);
+}
+
+// static
+void LLPanelLandGeneral::onClickStartAuction(void*)
+{
+ LLParcel* parcelp = gParcelMgr->getSelectedParcel();
+ if(parcelp)
+ {
+ if(parcelp->getForSale())
+ {
+ gViewerWindow->alertXml("CannotStartAuctionAlreadForSale");
+ }
+ else
+ {
+ LLFloaterAuction::show();
+ }
+ }
+}
+
+// static
+void LLPanelLandGeneral::cbBuyPass(S32 option, void* data)
+{
+ BOOL deselect_when_done = (BOOL)(intptr_t)data;
+
+ if (0 == option)
+ {
+ // User clicked OK
+ gParcelMgr->buyPass();
+ }
+
+ if (deselect_when_done)
+ {
+ gParcelMgr->deselectLand();
+ }
+}
+
+
+// static
+void LLPanelLandGeneral::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandGeneral *panelp = (LLPanelLandGeneral *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ std::string name = panelp->mEditName->getText();
+ std::string desc = panelp->mEditDesc->getText();
+
+ // Valid data from UI
+
+ // Stuff data into selected parcel
+ parcel->setName(name.c_str());
+ parcel->setDesc(desc.c_str());
+
+ BOOL allow_deed_to_group= panelp->mCheckDeedToGroup->get();
+ BOOL contribute_with_deed = panelp->mCheckContributeWithDeed->get();
+
+ parcel->setParcelFlag(PF_ALLOW_DEED_TO_GROUP, allow_deed_to_group);
+ parcel->setContributeWithDeed(contribute_with_deed);
+
+ // Send update to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ panelp->refresh();
+}
+
+// static
+void LLPanelLandGeneral::onClickSellLand(void* data)
+{
+ gParcelMgr->startSellLand();
+}
+
+// static
+void LLPanelLandGeneral::onClickStopSellLand(void* data)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+
+ parcel->setParcelFlag(PF_FOR_SALE, FALSE);
+ parcel->setSalePrice(0);
+ parcel->setAuthorizedBuyerID(LLUUID::null);
+
+ gParcelMgr->sendParcelPropertiesUpdate(parcel, LLFloaterLand::sRequestReplyOnUpdate);
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandObjects
+//---------------------------------------------------------------------------
+LLPanelLandObjects::LLPanelLandObjects()
+: LLPanel("land_objects_panel")
+{
+}
+
+
+
+BOOL LLPanelLandObjects::postBuild()
+{
+
+ mFirstReply = TRUE;
+ mParcelObjectBonus = LLUICtrlFactory::getTextBoxByName(this, "Simulator Primitive Bonus Factor: 1.00");
+
+ mSWTotalObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Simulator primitive usage:");
+ mSWTotalObjects = LLUICtrlFactory::getTextBoxByName(this, "0 out of 0 available");
+
+ mObjectContributionLabel = LLUICtrlFactory::getTextBoxByName(this, "Primitives parcel supports:");
+ mObjectContribution = LLUICtrlFactory::getTextBoxByName(this, "object_contrib_text");
+
+
+ mTotalObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Primitives on parcel:");
+ mTotalObjects = LLUICtrlFactory::getTextBoxByName(this, "total_objects_text");
+
+
+ mOwnerObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Owned by parcel owner:");
+ mOwnerObjects = LLUICtrlFactory::getTextBoxByName(this, "owner_objects_text");
+
+
+ mBtnShowOwnerObjects = LLUICtrlFactory::getButtonByName(this, "ShowOwner");
+ mBtnShowOwnerObjects->setClickedCallback(onClickShowOwnerObjects, this);
+
+ mBtnReturnOwnerObjects = LLUICtrlFactory::getButtonByName(this, "ReturnOwner...");
+ mBtnReturnOwnerObjects->setClickedCallback(onClickReturnOwnerObjects, this);
+
+
+ mGroupObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Set to group:");
+ mGroupObjects = LLUICtrlFactory::getTextBoxByName(this, "group_objects_text");
+
+
+ mBtnShowGroupObjects = LLUICtrlFactory::getButtonByName(this, "ShowGroup");
+ mBtnShowGroupObjects->setClickedCallback(onClickShowGroupObjects, this);
+
+ mBtnReturnGroupObjects = LLUICtrlFactory::getButtonByName(this, "ReturnGroup...");
+ mBtnReturnGroupObjects->setClickedCallback(onClickReturnGroupObjects, this);
+
+
+ mOtherObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Owned by others:");
+ mOtherObjects = LLUICtrlFactory::getTextBoxByName(this, "other_objects_text");
+
+ mBtnShowOtherObjects = LLUICtrlFactory::getButtonByName(this, "ShowOther");
+ mBtnShowOtherObjects->setClickedCallback(onClickShowOtherObjects, this);
+
+ mBtnReturnOtherObjects = LLUICtrlFactory::getButtonByName(this, "ReturnOther...");
+ mBtnReturnOtherObjects->setClickedCallback(onClickReturnOtherObjects, this);
+
+ mSelectedObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Selected / sat upon:");
+ mSelectedObjects = LLUICtrlFactory::getTextBoxByName(this, "selected_objects_text");
+
+ mCleanOtherObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Autoreturn other resident's objects (minutes, 0 for off):");
+
+ mCleanOtherObjectsTime = LLUICtrlFactory::getLineEditorByName(this, "clean other time");
+ mCleanOtherObjectsTime->setFocusLostCallback(onLostFocus);
+ childSetPrevalidate("clean other time", LLLineEditor::prevalidatePrintableNotPipe);
+ childSetUserData("clean other time", this);
+
+ mOwnerListText = LLUICtrlFactory::getTextBoxByName(this, "Object Owners:");
+
+
+ mBtnRefresh = LLUICtrlFactory::getButtonByName(this, "Refresh List");
+ mBtnRefresh->setClickedCallback(onClickRefresh, this);
+
+
+ mBtnReturnOwnerList = LLUICtrlFactory::getButtonByName(this, "Return objects...");
+ mBtnReturnOwnerList->setClickedCallback(onClickReturnOwnerList, this);
+
+ LLUUID image_id;
+
+ image_id.set( gViewerArt.getString("icon_avatar_online.tga") );
+ mIconAvatarOnline = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_avatar_offline.tga") );
+ mIconAvatarOffline = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_group.tga") );
+ mIconGroup = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ mCurrentSortColumn = 3; // sort by number of objects by default.
+ mCurrentSortAscending = FALSE;
+
+ // Column widths for various columns
+ const S32 SORTER_WIDTH = 308;
+ const S32 DESC_BTN_WIDTH = 64;
+ const S32 ICON_WIDTH = 24;
+ mColWidth[0] = ICON_WIDTH; // type icon
+ mColWidth[1] = -1; // hidden type code
+ mColWidth[2] = SORTER_WIDTH - mColWidth[0] - DESC_BTN_WIDTH;
+ mColWidth[3] = DESC_BTN_WIDTH; // info
+ mColWidth[4] = -1; // type data 1
+ mColWidth[5] = -1;
+ mColWidth[6] = -1; // type data 3
+ mColWidth[7] = -1; // type data 4
+ mColWidth[8] = -1; // type data 5
+
+ // Adjust description for other widths
+ S32 sum = 0;
+ for (S32 i = 0; i < 8; i++)
+ {
+ if (mColWidth[i] > 0)
+ {
+ sum += mColWidth[i];
+ }
+ }
+ mColWidth[8] = mRect.getWidth() - HPAD - sum - HPAD - HPAD;
+
+ mBtnType = LLUICtrlFactory::getButtonByName(this, "Type");
+ mBtnType->setClickedCallback(onClickType, this);
+
+ mBtnName = LLUICtrlFactory::getButtonByName(this, "Name");
+ mBtnName->setClickedCallback(onClickName, this);
+
+ mBtnDescription = LLUICtrlFactory::getButtonByName(this, "Count");
+ mBtnDescription->setClickedCallback(onClickDesc, this);
+
+ mOwnerList = LLUICtrlFactory::getNameListByName(this, "owner list");
+ childSetCommitCallback("owner list", onCommitList, this);
+ mOwnerList->setDoubleClickCallback(onDoubleClickOwner);
+
+ return TRUE;
+}
+
+
+
+
+// virtual
+LLPanelLandObjects::~LLPanelLandObjects()
+{ }
+
+// static
+void LLPanelLandObjects::onDoubleClickOwner(void *userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects *)userdata;
+
+ LLScrollListItem* item = self->mOwnerList->getFirstSelected();
+ if (item)
+ {
+ LLUUID owner_id = item->getUUID();
+ // Look up the selected name, for future dialog box use.
+ const LLScrollListCell* cell;
+ cell = item->getColumn(1);
+ if (!cell)
+ {
+ return;
+ }
+ // Is this a group?
+ BOOL is_group = cell->getText() == OWNER_GROUP;
+ if (is_group)
+ {
+ LLFloaterGroupInfo::showFromUUID(owner_id);
+ }
+ else
+ {
+ LLFloaterAvatarInfo::showFromDirectory(owner_id);
+ }
+ }
+}
+
+// public
+void LLPanelLandObjects::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ mBtnShowOwnerObjects->setEnabled(FALSE);
+ mBtnShowGroupObjects->setEnabled(FALSE);
+ mBtnShowOtherObjects->setEnabled(FALSE);
+ mBtnReturnOwnerObjects->setEnabled(FALSE);
+ mBtnReturnGroupObjects->setEnabled(FALSE);
+ mBtnReturnOtherObjects->setEnabled(FALSE);
+ mCleanOtherObjectsTime->setEnabled(FALSE);
+ mBtnRefresh-> setEnabled(FALSE);
+ mBtnReturnOwnerList-> setEnabled(FALSE);
+
+ mSelectedOwners.clear();
+ mOwnerList->deleteAllItems();
+ mOwnerList->setEnabled(FALSE);
+
+ if (!parcel)
+ {
+ mSWTotalObjects->setText("0 out of 0 available");
+ mObjectContribution->setText("0");
+ mTotalObjects->setText("0");
+ mOwnerObjects->setText("0");
+ mGroupObjects->setText("0");
+ mOtherObjects->setText("0");
+ mSelectedObjects->setText("0");
+ }
+ else
+ {
+ char count[MAX_STRING];
+ S32 sw_max;
+ S32 sw_total;
+ S32 max;
+ S32 total;
+ S32 owned;
+ S32 group;
+ S32 other;
+ S32 selected;
+ F32 parcel_object_bonus;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total,
+ max, total, owned, group, other, selected,
+ parcel_object_bonus, mOtherTime);
+
+ // Can't have more than region max tasks, regardless of parcel
+ // object bonus factor.
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (region)
+ {
+ S32 max_tasks_per_region = (S32)region->getMaxTasks();
+ sw_max = llmin(sw_max, max_tasks_per_region);
+ max = llmin(max, max_tasks_per_region);
+ }
+
+ if (parcel_object_bonus != 1.0f)
+ {
+ sprintf(count, "Region Object Bonus Factor: %.2f",
+ parcel_object_bonus);
+ mParcelObjectBonus->setText(count);
+ }
+ else
+ {
+ mParcelObjectBonus->setText("");
+ }
+
+ if (sw_total > sw_max)
+ {
+ sprintf(count, "%d out of %d (%d will be deleted)",
+ sw_total, sw_max, sw_total - sw_max);
+ }
+ else
+ {
+ sprintf(count, "%d out of %d (%d available)",
+ sw_total, sw_max, sw_max - sw_total);
+ }
+ mSWTotalObjects->setText(count);
+
+ sprintf(count, "%d", max);
+ mObjectContribution->setText(count);
+
+ sprintf(count, "%d", total);
+ mTotalObjects->setText(count);
+
+ sprintf(count, "%d", owned);
+ mOwnerObjects->setText(count);
+
+ sprintf(count, "%d", group);
+ mGroupObjects->setText(count);
+
+ sprintf(count, "%d", other);
+ mOtherObjects->setText(count);
+
+ sprintf(count, "%d", selected);
+ mSelectedObjects->setText(count);
+
+ sprintf(count, "%d", mOtherTime);
+ mCleanOtherObjectsTime->setText(count);
+
+ BOOL can_return_owned = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_GROUP_OWNED);
+ BOOL can_return_group_set = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_GROUP_SET);
+ BOOL can_return_other = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_NON_GROUP);
+
+ if (can_return_owned || can_return_group_set || can_return_other)
+ {
+ if (owned && can_return_owned)
+ {
+ mBtnShowOwnerObjects->setEnabled(TRUE);
+ mBtnReturnOwnerObjects->setEnabled(TRUE);
+ }
+ if (group && can_return_group_set)
+ {
+ mBtnShowGroupObjects->setEnabled(TRUE);
+ mBtnReturnGroupObjects->setEnabled(TRUE);
+ }
+ if (other && can_return_other)
+ {
+ mBtnShowOtherObjects->setEnabled(TRUE);
+ mBtnReturnOtherObjects->setEnabled(TRUE);
+ }
+
+ mCleanOtherObjectsTime->setEnabled(TRUE);
+ mBtnRefresh->setEnabled(TRUE);
+ }
+ }
+}
+
+// virtual
+void LLPanelLandObjects::draw()
+{
+ LLPanel::draw();
+}
+
+void send_other_clean_time_message(S32 parcel_local_id, S32 other_clean_time)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ msg->newMessageFast(_PREHASH_ParcelSetOtherCleanTime);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addS32Fast(_PREHASH_OtherCleanTime, other_clean_time);
+
+ msg->sendReliable(region->getHost());
+}
+
+void send_return_objects_message(S32 parcel_local_id, S32 return_type,
+ uuid_list_t* owner_ids = NULL)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ msg->newMessageFast(_PREHASH_ParcelReturnObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addU32Fast(_PREHASH_ReturnType, (U32) return_type);
+
+ // Dummy task id, not used
+ msg->nextBlock("TaskIDs");
+ msg->addUUID("TaskID", LLUUID::null);
+
+ // Throw all return ids into the packet.
+ // TODO: Check for too many ids.
+ if (owner_ids)
+ {
+ uuid_list_t::iterator end = owner_ids->end();
+ for (uuid_list_t::iterator it = owner_ids->begin();
+ it != end;
+ ++it)
+ {
+ msg->nextBlockFast(_PREHASH_OwnerIDs);
+ msg->addUUIDFast(_PREHASH_OwnerID, (*it));
+ }
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_OwnerIDs);
+ msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null);
+ }
+
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLPanelLandObjects::callbackReturnOwnerObjects(S32 option, void* userdata)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ LLUUID owner_id = parcel->getOwnerID();
+ LLString::format_map_t args;
+ if (owner_id == gAgentID)
+ {
+ LLNotifyBox::showXml("OwnedObjectsReturned");
+ }
+ else
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first, last);
+ args["[FIRST]"] = first;
+ args["[LAST]"] = last;
+ LLNotifyBox::showXml("OtherObjectsReturned", args);
+ }
+ send_return_objects_message(parcel->getLocalID(), RT_OWNER);
+ }
+ }
+
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ lop->refresh();
+}
+
+// static
+void LLPanelLandObjects::callbackReturnGroupObjects(S32 option, void* userdata)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ LLString::format_map_t args;
+ args["[GROUPNAME]"] = group_name;
+ LLNotifyBox::showXml("GroupObjectsReturned", args);
+ send_return_objects_message(parcel->getLocalID(), RT_GROUP);
+ }
+ }
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ lop->refresh();
+}
+
+// static
+void LLPanelLandObjects::callbackReturnOtherObjects(S32 option, void* userdata)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ LLNotifyBox::showXml("UnOwnedObjectsReturned");
+ send_return_objects_message(parcel->getLocalID(), RT_OTHER);
+ }
+ }
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ lop->refresh();
+}
+
+// static
+void LLPanelLandObjects::callbackReturnOwnerList(S32 option, void* userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ // Make sure we have something selected.
+ uuid_list_t::iterator selected = self->mSelectedOwners.begin();
+ if (selected != self->mSelectedOwners.end())
+ {
+ LLString::format_map_t args;
+ if (self->mSelectedIsGroup)
+ {
+ args["[GROUPNAME]"] = self->mSelectedName;
+ LLNotifyBox::showXml("GroupObjectsReturned", args);
+ }
+ else
+ {
+ // XUI:translate NAME -> FIRST LAST
+ args["[NAME]"] = self->mSelectedName;
+ LLNotifyBox::showXml("OtherObjectsReturned2", args);
+ }
+
+ send_return_objects_message(parcel->getLocalID(), RT_LIST, &(self->mSelectedOwners));
+ }
+ }
+ }
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ self->refresh();
+}
+
+
+// static
+void LLPanelLandObjects::onClickReturnOwnerList(void* userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects *)userdata;
+
+ S32 sw_max, sw_total;
+ S32 max, total;
+ S32 owned, group, other, selected;
+ F32 parcel_object_bonus;
+ S32 other_time;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcelp = gParcelMgr->getSelectedParcel();
+ if (!parcelp) return;
+
+ // Make sure we have something selected.
+ if (self->mSelectedOwners.empty())
+ {
+ return;
+ }
+ //uuid_list_t::iterator selected_itr = self->mSelectedOwners.begin();
+ //if (selected_itr == self->mSelectedOwners.end()) return;
+
+ send_parcel_select_objects(parcelp->getLocalID(), RT_LIST, &(self->mSelectedOwners));
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = self->mSelectedName;
+ args["[N]"] = llformat("%d",self->mSelectedCount);
+ if (self->mSelectedIsGroup)
+ {
+ gViewerWindow->alertXml("ReturnObjectsDeededToGroup", args, callbackReturnOwnerList, userdata);
+ }
+ else
+ {
+ gViewerWindow->alertXml("ReturnObjectsOwnedByUser", args, callbackReturnOwnerList, userdata);
+ }
+}
+
+
+// static
+void LLPanelLandObjects::onClickRefresh(void* userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects*)userdata;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ // ready the list for results
+ self->mOwnerList->deleteAllItems();
+ self->mOwnerList->addSimpleItem("Searching...");
+ self->mOwnerList->setEnabled(FALSE);
+ self->mFirstReply = TRUE;
+
+ // send the message
+ msg->newMessageFast(_PREHASH_ParcelObjectOwnersRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID());
+
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLPanelLandObjects::processParcelObjectOwnersReply(LLMessageSystem *msg, void **)
+{
+ LLPanelLandObjects* self = LLFloaterLand::getCurrentPanelLandObjects();
+
+ const LLFontGL* FONT = LLFontGL::sSansSerif;
+
+ // Extract all of the owners.
+ S32 rows = msg->getNumberOfBlocksFast(_PREHASH_Data);
+ //uuid_list_t return_ids;
+ LLUUID owner_id;
+ BOOL is_group_owned;
+ S32 object_count;
+ BOOL is_online;
+ char object_count_str[MAX_STRING];
+ //BOOL b_need_refresh = FALSE;
+
+ // If we were waiting for the first reply, clear the "Searching..." text.
+ if (self->mFirstReply)
+ {
+ self->mOwnerList->deleteAllItems();
+ self->mFirstReply = FALSE;
+ }
+
+ for(S32 i = 0; i < rows; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_OwnerID, owner_id, i);
+ msg->getBOOLFast(_PREHASH_Data, _PREHASH_IsGroupOwned, is_group_owned, i);
+ msg->getS32Fast (_PREHASH_Data, _PREHASH_Count, object_count, i);
+ msg->getBOOLFast(_PREHASH_Data, _PREHASH_OnlineStatus, is_online, i);
+
+ if (owner_id.isNull())
+ {
+ continue;
+ }
+
+ LLScrollListItem *row = new LLScrollListItem( TRUE, NULL, owner_id);
+ if (is_group_owned)
+ {
+ row->addColumn(self->mIconGroup, self->mColWidth[0]);
+ row->addColumn(OWNER_GROUP, FONT, self->mColWidth[1]);
+ }
+ else if (is_online)
+ {
+ row->addColumn(self->mIconAvatarOnline, self->mColWidth[0]);
+ row->addColumn(OWNER_ONLINE, FONT, self->mColWidth[1]);
+ }
+ else // offline
+ {
+ row->addColumn(self->mIconAvatarOffline, self->mColWidth[0]);
+ row->addColumn(OWNER_OFFLINE, FONT, self->mColWidth[1]);
+ }
+ // Placeholder for name.
+ row->addColumn("", FONT, self->mColWidth[2]);
+
+ sprintf(object_count_str, "%d", object_count);
+ row->addColumn(object_count_str, FONT, self->mColWidth[3]);
+
+ if (is_group_owned)
+ {
+ self->mOwnerList->addGroupNameItem(row, ADD_BOTTOM);
+ }
+ else
+ {
+ self->mOwnerList->addNameItem(row, ADD_BOTTOM);
+ }
+
+ lldebugs << "object owner " << owner_id << " (" << (is_group_owned ? "group" : "agent")
+ << ") owns " << object_count << " objects." << llendl;
+ }
+ self->mOwnerList->sortByColumn(self->mCurrentSortColumn, self->mCurrentSortAscending);
+
+ // check for no results
+ if (0 == self->mOwnerList->getItemCount())
+ {
+ self->mOwnerList->addSimpleItem("None found.");
+ }
+ else
+ {
+ self->mOwnerList->setEnabled(TRUE);
+ }
+}
+
+void LLPanelLandObjects::sortBtnCore(S32 column)
+{
+ if (column == (S32)mCurrentSortColumn) // is this already our sorted column?
+ {
+ mCurrentSortAscending = !mCurrentSortAscending;
+ }
+ else // default to ascending first time a column is clicked
+ {
+ mCurrentSortColumn = column;
+ mCurrentSortAscending = TRUE;
+ }
+
+ mOwnerList->sortByColumn(column, mCurrentSortAscending);
+}
+
+// static
+void LLPanelLandObjects::onCommitList(LLUICtrl* ctrl, void* data)
+{
+ LLPanelLandObjects* self = (LLPanelLandObjects*)data;
+
+ if (FALSE == self->mOwnerList->getCanSelect())
+ {
+ return;
+ }
+ LLScrollListItem *item = self->mOwnerList->getFirstSelected();
+ if (item)
+ {
+ // Look up the selected name, for future dialog box use.
+ const LLScrollListCell* cell;
+ cell = item->getColumn(1);
+ if (!cell)
+ {
+ return;
+ }
+ // Is this a group?
+ self->mSelectedIsGroup = cell->getText() == OWNER_GROUP;
+ cell = item->getColumn(2);
+ self->mSelectedName = cell->getText();
+ cell = item->getColumn(3);
+ self->mSelectedCount = atoi(cell->getText().c_str());
+
+ // Set the selection, and enable the return button.
+ self->mSelectedOwners.clear();
+ self->mSelectedOwners.insert(item->getUUID());
+ self->mBtnReturnOwnerList->setEnabled(TRUE);
+
+ // Highlight this user's objects
+ clickShowCore(RT_LIST, &(self->mSelectedOwners));
+ }
+}
+
+// static
+void LLPanelLandObjects::onClickType(void* userdata)
+{
+ // Sort on hidden type column
+ LLPanelLandObjects* self = (LLPanelLandObjects*)userdata;
+ self->sortBtnCore(1);
+}
+
+// static
+void LLPanelLandObjects::onClickDesc(void* userdata)
+{
+ LLPanelLandObjects* self = (LLPanelLandObjects*)userdata;
+ self->sortBtnCore(3);
+}
+
+// static
+void LLPanelLandObjects::onClickName(void* userdata)
+{
+ LLPanelLandObjects* self = (LLPanelLandObjects*)userdata;
+ self->sortBtnCore(2);
+}
+
+// static
+void LLPanelLandObjects::clickShowCore(S32 return_type, uuid_list_t* list)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), return_type, list);
+}
+
+// static
+void LLPanelLandObjects::onClickShowOwnerObjects(void*)
+{
+ clickShowCore(RT_OWNER);
+}
+
+// static
+void LLPanelLandObjects::onClickShowGroupObjects(void*)
+{
+ clickShowCore(RT_GROUP);
+}
+
+// static
+void LLPanelLandObjects::onClickShowOtherObjects(void*)
+{
+ clickShowCore(RT_OTHER);
+}
+
+// static
+void LLPanelLandObjects::onClickReturnOwnerObjects(void* userdata)
+{
+ S32 sw_max=0, sw_total=0;
+ S32 max=0, total=0;
+ S32 owned=0, group=0, other=0, selected=0;
+ F32 parcel_object_bonus=0;
+ S32 other_time=0;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), RT_OWNER);
+
+ LLUUID owner_id = parcel->getOwnerID();
+
+ LLStringBase<char>::format_map_t args;
+ args["[N]"] = llformat("%d",owned);
+
+ if (owner_id == gAgent.getID())
+ {
+ gViewerWindow->alertXml("ReturnObjectsOwnedBySelf", args, callbackReturnOwnerObjects, userdata);
+ }
+ else
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first, last);
+ std::string name = first;
+ name += " ";
+ name += last;
+ args["[NAME]"] = name;
+ gViewerWindow->alertXml("ReturnObjectsOwnedByUser", args, callbackReturnOwnerObjects, userdata);
+ }
+}
+
+// static
+void LLPanelLandObjects::onClickReturnGroupObjects(void* userdata)
+{
+ S32 sw_max=0, sw_total=0;
+ S32 max=0, total=0;
+ S32 owned=0, group=0, other=0, selected=0;
+ F32 parcel_object_bonus=0;
+ S32 other_time=0;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), RT_GROUP);
+
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = group_name;
+ args["[N]"] = llformat("%d",group);
+
+ // create and show confirmation textbox
+ gViewerWindow->alertXml("ReturnObjectsDeededToGroup", args, callbackReturnGroupObjects, userdata);
+}
+
+// static
+void LLPanelLandObjects::onClickReturnOtherObjects(void* userdata)
+{
+ S32 sw_max=0, sw_total=0;
+ S32 max=0, total=0;
+ S32 owned=0, group=0, other=0, selected=0;
+ F32 parcel_object_bonus=0;
+ S32 other_time=0;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), RT_OTHER);
+
+ LLStringBase<char>::format_map_t args;
+ args["[N]"] = llformat("%d", other);
+
+ if (parcel->getIsGroupOwned())
+ {
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ args["[NAME]"] = group_name;
+
+ gViewerWindow->alertXml("ReturnObjectsNotOwnedByGroup", args, callbackReturnOtherObjects, userdata);
+ }
+ else
+ {
+ LLUUID owner_id = parcel->getOwnerID();
+
+ if (owner_id == gAgent.getID())
+ {
+ gViewerWindow->alertXml("ReturnObjectsNotOwnedBySelf", args, callbackReturnOtherObjects, userdata);
+ }
+ else
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first, last);
+ std::string name;
+ name += first;
+ name += " ";
+ name += last;
+ args["[NAME]"] = name;
+
+ gViewerWindow->alertXml("ReturnObjectsNotOwnedByUser", args, callbackReturnOtherObjects, userdata);
+ }
+ }
+}
+
+// static
+void LLPanelLandObjects::onLostFocus(LLLineEditor *caller, void* user_data)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)user_data;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (parcel)
+ {
+ lop->mOtherTime = atoi(lop->mCleanOtherObjectsTime->getText().c_str());
+
+ parcel->setCleanOtherTime(lop->mOtherTime);
+ send_other_clean_time_message(parcel->getLocalID(), lop->mOtherTime);
+ }
+}
+
+
+//---------------------------------------------------------------------------
+// LLPanelLandOptions
+//---------------------------------------------------------------------------
+
+LLPanelLandOptions::LLPanelLandOptions()
+: LLPanel("land_options_panel"),
+ mCheckEditObjects(NULL),
+ mCheckEditGroupObjects(NULL),
+ mCheckAllObjectEntry(NULL),
+ mCheckGroupObjectEntry(NULL),
+ mCheckEditLand(NULL),
+ mCheckSafe(NULL),
+ mCheckFly(NULL),
+ mCheckGroupScripts(NULL),
+ mCheckOtherScripts(NULL),
+ mCheckLandmark(NULL),
+ mCheckShowDirectory(NULL),
+ mCategoryCombo(NULL),
+ mLandingTypeCombo(NULL),
+ mSnapshotCtrl(NULL),
+ mLocationText(NULL),
+ mSetBtn(NULL),
+ mClearBtn(NULL),
+ mAllowPublishCtrl(NULL),
+ mMatureCtrl(NULL),
+ mPushRestrictionCtrl(NULL),
+ mPublishHelpButton(NULL)
+{
+
+}
+
+
+BOOL LLPanelLandOptions::postBuild()
+{
+
+
+ mCheckEditObjects = LLUICtrlFactory::getCheckBoxByName(this, "edit objects check");
+ childSetCommitCallback("edit objects check", onCommitAny, this);
+
+ mCheckEditGroupObjects = LLUICtrlFactory::getCheckBoxByName(this, "edit group objects check");
+ childSetCommitCallback("edit group objects check", onCommitAny, this);
+
+ mCheckAllObjectEntry = LLUICtrlFactory::getCheckBoxByName(this, "all object entry check");
+ childSetCommitCallback("all object entry check", onCommitAny, this);
+
+ mCheckGroupObjectEntry = LLUICtrlFactory::getCheckBoxByName(this, "group object entry check");
+ childSetCommitCallback("group object entry check", onCommitAny, this);
+
+ mCheckEditLand = LLUICtrlFactory::getCheckBoxByName(this, "edit land check");
+ childSetCommitCallback("edit land check", onCommitAny, this);
+
+
+ mCheckLandmark = LLUICtrlFactory::getCheckBoxByName(this, "check landmark");
+ childSetCommitCallback("check landmark", onCommitAny, this);
+
+
+ mCheckGroupScripts = LLUICtrlFactory::getCheckBoxByName(this, "check group scripts");
+ childSetCommitCallback("check group scripts", onCommitAny, this);
+
+
+ mCheckFly = LLUICtrlFactory::getCheckBoxByName(this, "check fly");
+ childSetCommitCallback("check fly", onCommitAny, this);
+
+
+ mCheckOtherScripts = LLUICtrlFactory::getCheckBoxByName(this, "check other scripts");
+ childSetCommitCallback("check other scripts", onCommitAny, this);
+
+
+ mCheckSafe = LLUICtrlFactory::getCheckBoxByName(this, "check safe");
+ childSetCommitCallback("check safe", onCommitAny, this);
+
+
+ mPushRestrictionCtrl = LLUICtrlFactory::getCheckBoxByName(this, "PushRestrictCheck");
+ childSetCommitCallback("PushRestrictCheck", onCommitAny, this);
+
+ mCheckShowDirectory = LLUICtrlFactory::getCheckBoxByName(this, "ShowDirectoryCheck");
+ childSetCommitCallback("ShowDirectoryCheck", onCommitAny, this);
+
+
+ mCategoryCombo = LLUICtrlFactory::getComboBoxByName(this, "land category");
+ childSetCommitCallback("land category", onCommitAny, this);
+
+ mAllowPublishCtrl = LLUICtrlFactory::getCheckBoxByName(this, "PublishCheck");
+ childSetCommitCallback("PublishCheck", onCommitAny, this);
+
+
+ mMatureCtrl = LLUICtrlFactory::getCheckBoxByName(this, "MatureCheck");
+ childSetCommitCallback("MatureCheck", onCommitAny, this);
+
+ mPublishHelpButton = LLUICtrlFactory::getButtonByName(this, "?");
+ mPublishHelpButton->setClickedCallback(onClickPublishHelp, this);
+
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Disable these buttons if they are PG (Teen) users
+ mAllowPublishCtrl->setVisible(FALSE);
+ mAllowPublishCtrl->setEnabled(FALSE);
+ mPublishHelpButton->setVisible(FALSE);
+ mPublishHelpButton->setEnabled(FALSE);
+ mMatureCtrl->setVisible(FALSE);
+ mMatureCtrl->setEnabled(FALSE);
+ }
+
+ // Load up the category list
+ //now in xml file
+ /*
+ S32 i;
+ for (i = 0; i < LLParcel::C_COUNT; i++)
+ {
+ LLParcel::ECategory cat = (LLParcel::ECategory)i;
+
+ // Selecting Linden Location when you're not a god
+ // is also blocked on the server.
+ BOOL enabled = TRUE;
+ if (!gAgent.isGodlike()
+ && i == LLParcel::C_LINDEN)
+ {
+ enabled = FALSE;
+ }
+
+ mCategoryCombo->add( LLParcel::getCategoryUIString(cat), ADD_BOTTOM, enabled );
+ }*/
+
+
+ mSnapshotCtrl = LLUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setCommitCallback( onCommitAny );
+ mSnapshotCtrl->setCallbackUserData( this );
+ mSnapshotCtrl->setAllowNoTexture ( TRUE );
+ mSnapshotCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+ mSnapshotCtrl->setNonImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+
+
+
+ mLocationText = LLUICtrlFactory::getTextBoxByName(this, "Landing Point: (none)");
+
+ mSetBtn = LLUICtrlFactory::getButtonByName(this, "Set");
+ mSetBtn->setClickedCallback(onClickSet, this);
+
+
+ mClearBtn = LLUICtrlFactory::getButtonByName(this, "Clear");
+ mClearBtn->setClickedCallback(onClickClear, this);
+
+
+ mLandingTypeCombo = LLUICtrlFactory::getComboBoxByName(this, "landing type");
+ childSetCommitCallback("landing type", onCommitAny, this);
+
+ return TRUE;
+}
+
+
+// virtual
+LLPanelLandOptions::~LLPanelLandOptions()
+{ }
+
+
+// public
+void LLPanelLandOptions::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (!parcel)
+ {
+ mCheckEditObjects ->set(FALSE);
+ mCheckEditObjects ->setEnabled(FALSE);
+
+ mCheckEditGroupObjects ->set(FALSE);
+ mCheckEditGroupObjects ->setEnabled(FALSE);
+
+ mCheckAllObjectEntry ->set(FALSE);
+ mCheckAllObjectEntry ->setEnabled(FALSE);
+
+ mCheckGroupObjectEntry ->set(FALSE);
+ mCheckGroupObjectEntry ->setEnabled(FALSE);
+
+ mCheckEditLand ->set(FALSE);
+ mCheckEditLand ->setEnabled(FALSE);
+
+ mCheckSafe ->set(FALSE);
+ mCheckSafe ->setEnabled(FALSE);
+
+ mCheckFly ->set(FALSE);
+ mCheckFly ->setEnabled(FALSE);
+
+ mCheckLandmark ->set(FALSE);
+ mCheckLandmark ->setEnabled(FALSE);
+
+ mCheckGroupScripts ->set(FALSE);
+ mCheckGroupScripts ->setEnabled(FALSE);
+
+ mCheckOtherScripts ->set(FALSE);
+ mCheckOtherScripts ->setEnabled(FALSE);
+
+ mCheckShowDirectory ->set(FALSE);
+ mCheckShowDirectory ->setEnabled(FALSE);
+
+ mPushRestrictionCtrl->set(FALSE);
+ mPushRestrictionCtrl->setEnabled(FALSE);
+
+ const char* none_string = LLParcel::getCategoryUIString(LLParcel::C_NONE);
+ mCategoryCombo->setSimple(none_string);
+ mCategoryCombo->setEnabled(FALSE);
+
+ mLandingTypeCombo->setCurrentByIndex(0);
+ mLandingTypeCombo->setEnabled(FALSE);
+
+ mSnapshotCtrl->setImageAssetID(LLUUID::null);
+ mSnapshotCtrl->setEnabled(FALSE);
+
+ mLocationText->setText("Landing Point: (none)");
+ mSetBtn->setEnabled(FALSE);
+ mClearBtn->setEnabled(FALSE);
+
+ mAllowPublishCtrl->setEnabled(FALSE);
+ mMatureCtrl->setEnabled(FALSE);
+ mPublishHelpButton->setEnabled(FALSE);
+ }
+ else
+ {
+ // something selected, hooray!
+
+ // Display options
+ BOOL can_change_options = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_OPTIONS);
+ mCheckEditObjects ->set( parcel->getAllowModify() );
+ mCheckEditObjects ->setEnabled( can_change_options );
+
+ mCheckEditGroupObjects ->set( parcel->getAllowGroupModify() || parcel->getAllowModify());
+ mCheckEditGroupObjects ->setEnabled( can_change_options && !parcel->getAllowModify() ); // If others edit is enabled, then this is explicitly enabled.
+
+ mCheckAllObjectEntry ->set( parcel->getAllowAllObjectEntry() );
+ mCheckAllObjectEntry ->setEnabled( can_change_options );
+
+ mCheckGroupObjectEntry ->set( parcel->getAllowGroupObjectEntry() || parcel->getAllowAllObjectEntry());
+ mCheckGroupObjectEntry ->setEnabled( can_change_options && !parcel->getAllowAllObjectEntry() );
+
+ BOOL can_change_terraform = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_EDIT);
+ mCheckEditLand ->set( parcel->getAllowTerraform() );
+ mCheckEditLand ->setEnabled( can_change_terraform );
+
+ mCheckSafe ->set( !parcel->getAllowDamage() );
+ mCheckSafe ->setEnabled( can_change_options );
+
+ mCheckFly ->set( parcel->getAllowFly() );
+ mCheckFly ->setEnabled( can_change_options );
+
+ mCheckLandmark ->set( parcel->getAllowLandmark() );
+ mCheckLandmark ->setEnabled( can_change_options );
+
+ mCheckGroupScripts ->set( parcel->getAllowGroupScripts() || parcel->getAllowOtherScripts());
+ mCheckGroupScripts ->setEnabled( can_change_options && !parcel->getAllowOtherScripts());
+
+ mCheckOtherScripts ->set( parcel->getAllowOtherScripts() );
+ mCheckOtherScripts ->setEnabled( can_change_options );
+
+ BOOL can_change_identity = LLViewerParcelMgr::isParcelModifiableByAgent(parcel,
+ GP_LAND_CHANGE_IDENTITY);
+ mCheckShowDirectory ->set( parcel->getParcelFlag(PF_SHOW_DIRECTORY));
+ mCheckShowDirectory ->setEnabled( can_change_identity );
+
+ mPushRestrictionCtrl->set( parcel->getRestrictPushObject() );
+ if(parcel->getRegionPushOverride())
+ {
+ mPushRestrictionCtrl->setLabel("Restrict Pushing (Region Override)");
+ mPushRestrictionCtrl->setEnabled(false);
+ mPushRestrictionCtrl->set(TRUE);
+ }
+ else
+ {
+ mPushRestrictionCtrl->setLabel("Restrict Pushing");
+ mPushRestrictionCtrl->setEnabled(can_change_options);
+ }
+
+ // Set by string in case the order in UI doesn't match the order
+ // by index.
+ LLParcel::ECategory cat = parcel->getCategory();
+ const char* category_string = LLParcel::getCategoryUIString(cat);
+ mCategoryCombo->setSimple(category_string);
+ mCategoryCombo->setEnabled( can_change_identity );
+
+ BOOL can_change_landing_point = LLViewerParcelMgr::isParcelModifiableByAgent(parcel,
+ GP_LAND_SET_LANDING_POINT);
+ mLandingTypeCombo->setCurrentByIndex((S32)parcel->getLandingType());
+ mLandingTypeCombo->setEnabled( can_change_landing_point );
+
+ mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
+ mSnapshotCtrl->setEnabled( can_change_identity );
+
+ LLVector3 pos = parcel->getUserLocation();
+ if (pos.isExactlyZero())
+ {
+ mLocationText->setText("Landing Point: (none)");
+ }
+ else
+ {
+ char buffer[256];
+ sprintf(buffer, "Landing Point: %d, %d, %d",
+ llround(pos.mV[VX]),
+ llround(pos.mV[VY]),
+ llround(pos.mV[VZ]));
+ mLocationText->setText(buffer);
+ }
+
+ mSetBtn->setEnabled( can_change_landing_point );
+ mClearBtn->setEnabled( can_change_landing_point );
+
+ mAllowPublishCtrl->set(parcel->getAllowPublish());
+ mAllowPublishCtrl->setEnabled( can_change_identity );
+ mMatureCtrl->set(parcel->getMaturePublish());
+ mMatureCtrl->setEnabled( can_change_identity );
+ mPublishHelpButton->setEnabled( can_change_identity );
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Disable these buttons if they are PG (Teen) users
+ mAllowPublishCtrl->setVisible(FALSE);
+ mAllowPublishCtrl->setEnabled(FALSE);
+ mPublishHelpButton->setVisible(FALSE);
+ mPublishHelpButton->setEnabled(FALSE);
+ mMatureCtrl->setVisible(FALSE);
+ mMatureCtrl->setEnabled(FALSE);
+ }
+ }
+}
+
+
+// static
+void LLPanelLandOptions::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandOptions *self = (LLPanelLandOptions *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL create_objects = self->mCheckEditObjects->get();
+ BOOL create_group_objects = self->mCheckEditGroupObjects->get() || self->mCheckEditObjects->get();
+ BOOL all_object_entry = self->mCheckAllObjectEntry->get();
+ BOOL group_object_entry = self->mCheckGroupObjectEntry->get() || self->mCheckAllObjectEntry->get();
+ BOOL allow_terraform = self->mCheckEditLand->get();
+ BOOL allow_damage = !self->mCheckSafe->get();
+ BOOL allow_fly = self->mCheckFly->get();
+ BOOL allow_landmark = self->mCheckLandmark->get();
+ BOOL allow_group_scripts = self->mCheckGroupScripts->get() || self->mCheckOtherScripts->get();
+ BOOL allow_other_scripts = self->mCheckOtherScripts->get();
+ BOOL allow_publish = self->mAllowPublishCtrl->get();
+ BOOL mature_publish = self->mMatureCtrl->get();
+ BOOL push_restriction = self->mPushRestrictionCtrl->get();
+ BOOL show_directory = self->mCheckShowDirectory->get();
+ S32 category_index = self->mCategoryCombo->getCurrentIndex();
+ S32 landing_type_index = self->mLandingTypeCombo->getCurrentIndex();
+ LLUUID snapshot_id = self->mSnapshotCtrl->getImageAssetID();
+ LLViewerRegion* region;
+ region = gParcelMgr->getSelectionRegion();
+
+ if (!allow_other_scripts && region && region->getAllowDamage())
+ {
+
+ gViewerWindow->alertXml("UnableToDisableOutsideScripts");
+ return;
+ }
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_CREATE_OBJECTS, create_objects);
+ parcel->setParcelFlag(PF_CREATE_GROUP_OBJECTS, create_group_objects);
+ parcel->setParcelFlag(PF_ALLOW_ALL_OBJECT_ENTRY, all_object_entry);
+ parcel->setParcelFlag(PF_ALLOW_GROUP_OBJECT_ENTRY, group_object_entry);
+ parcel->setParcelFlag(PF_ALLOW_TERRAFORM, allow_terraform);
+ parcel->setParcelFlag(PF_ALLOW_DAMAGE, allow_damage);
+ parcel->setParcelFlag(PF_ALLOW_FLY, allow_fly);
+ parcel->setParcelFlag(PF_ALLOW_LANDMARK, allow_landmark);
+ parcel->setParcelFlag(PF_ALLOW_GROUP_SCRIPTS, allow_group_scripts);
+ parcel->setParcelFlag(PF_ALLOW_OTHER_SCRIPTS, allow_other_scripts);
+ parcel->setParcelFlag(PF_SHOW_DIRECTORY, show_directory);
+ parcel->setParcelFlag(PF_ALLOW_PUBLISH, allow_publish);
+ parcel->setParcelFlag(PF_MATURE_PUBLISH, mature_publish);
+ parcel->setParcelFlag(PF_RESTRICT_PUSHOBJECT, push_restriction);
+ parcel->setCategory((LLParcel::ECategory)category_index);
+ parcel->setLandingType((LLParcel::ELandingType)landing_type_index);
+ parcel->setSnapshotID(snapshot_id);
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+
+// static
+void LLPanelLandOptions::onClickSet(void* userdata)
+{
+ LLPanelLandOptions* self = (LLPanelLandOptions*)userdata;
+
+ LLParcel* selected_parcel = gParcelMgr->getSelectedParcel();
+ if (!selected_parcel) return;
+
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (!agent_parcel) return;
+
+ if (agent_parcel->getLocalID() != selected_parcel->getLocalID())
+ {
+ gViewerWindow->alertXml("MustBeInParcel");
+ return;
+ }
+
+ LLVector3 pos_region = gAgent.getPositionAgent();
+ selected_parcel->setUserLocation(pos_region);
+ selected_parcel->setUserLookAt(gAgent.getFrameAgent().getAtAxis());
+
+ gParcelMgr->sendParcelPropertiesUpdate(selected_parcel, LLFloaterLand::sRequestReplyOnUpdate);
+
+ self->refresh();
+}
+
+void LLPanelLandOptions::onClickClear(void* userdata)
+{
+ LLPanelLandOptions* self = (LLPanelLandOptions*)userdata;
+
+ LLParcel* selected_parcel = gParcelMgr->getSelectedParcel();
+ if (!selected_parcel) return;
+
+ // yes, this magic number of 0,0,0 means that it is clear
+ LLVector3 zero_vec(0.f, 0.f, 0.f);
+ selected_parcel->setUserLocation(zero_vec);
+ selected_parcel->setUserLookAt(zero_vec);
+
+ gParcelMgr->sendParcelPropertiesUpdate(selected_parcel, LLFloaterLand::sRequestReplyOnUpdate);
+
+ self->refresh();
+}
+
+// static
+void LLPanelLandOptions::onClickPublishHelp(void*)
+{
+ gViewerWindow->alertXml("ClickPublishHelpLand");
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandMedia
+//---------------------------------------------------------------------------
+
+LLPanelLandMedia::LLPanelLandMedia()
+: LLPanel("land_media_panel")
+{
+}
+
+
+
+
+BOOL LLPanelLandMedia::postBuild()
+{
+
+ mCheckSoundLocal = LLUICtrlFactory::getCheckBoxByName(this, "check sound local");
+ childSetCommitCallback("check sound local", onCommitAny, this);
+
+ mMusicURLEdit = LLUICtrlFactory::getLineEditorByName(this, "music_url");
+ childSetCommitCallback("music_url", onCommitAny, this);
+
+
+ mMediaTextureCtrl = LLUICtrlFactory::getTexturePickerByName(this, "media texture");
+ mMediaTextureCtrl->setCommitCallback( onCommitAny );
+ mMediaTextureCtrl->setCallbackUserData( this );
+ mMediaTextureCtrl->setAllowNoTexture ( TRUE );
+ mMediaTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+ mMediaTextureCtrl->setNonImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+
+ mMediaAutoScaleCheck = LLUICtrlFactory::getCheckBoxByName(this, "media_auto_scale");
+ childSetCommitCallback("media_auto_scale", onCommitAny, this);
+
+ mMediaURLEdit = LLUICtrlFactory::getLineEditorByName(this, "media_url");
+ childSetCommitCallback("media_url", onCommitAny, this);
+
+ return TRUE;
+}
+
+
+// virtual
+LLPanelLandMedia::~LLPanelLandMedia()
+{ }
+
+
+// public
+void LLPanelLandMedia::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (!parcel)
+ {
+ mCheckSoundLocal->set(FALSE);
+ mCheckSoundLocal->setEnabled(FALSE);
+
+ mMusicURLEdit->setText("");
+ mMusicURLEdit->setEnabled(FALSE);
+
+ mMediaURLEdit->setText("");
+ mMediaURLEdit->setEnabled(FALSE);
+
+ mMediaAutoScaleCheck->set ( FALSE );
+ mMediaAutoScaleCheck->setEnabled(FALSE);
+
+ mMediaTextureCtrl->clear();
+ mMediaTextureCtrl->setEnabled(FALSE);
+
+ #if 0
+ mMediaStopButton->setEnabled ( FALSE );
+ mMediaStartButton->setEnabled ( FALSE );
+ #endif
+ }
+ else
+ {
+ // something selected, hooray!
+
+ // Display options
+ BOOL can_change_media = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_MEDIA);
+
+ mCheckSoundLocal->set( parcel->getSoundLocal() );
+ mCheckSoundLocal->setEnabled( can_change_media );
+
+ // don't display urls if you're not able to change it
+ // much requested change in forums so people can't 'steal' urls
+ // NOTE: bug#2009 means this is still vunerable - however, bug
+ // should be closed since this bug opens up major security issues elsewhere.
+ if ( can_change_media )
+ {
+ mMusicURLEdit->setDrawAsterixes ( FALSE );
+ mMediaURLEdit->setDrawAsterixes ( FALSE );
+ }
+ else
+ {
+ mMusicURLEdit->setDrawAsterixes ( TRUE );
+ mMediaURLEdit->setDrawAsterixes ( TRUE );
+ }
+
+ mMusicURLEdit->setText(parcel->getMusicURL());
+ mMusicURLEdit->setEnabled( can_change_media );
+
+ mMediaURLEdit->setText(parcel->getMediaURL());
+ mMediaURLEdit->setEnabled( can_change_media );
+
+ mMediaAutoScaleCheck->set ( parcel->getMediaAutoScale () );
+ mMediaAutoScaleCheck->setEnabled ( can_change_media );
+
+ LLUUID tmp = parcel->getMediaID();
+ mMediaTextureCtrl->setImageAssetID ( parcel->getMediaID() );
+ mMediaTextureCtrl->setEnabled( can_change_media );
+
+ #if 0
+ // there is a media url and a media texture selected
+ if ( ( ! ( std::string ( parcel->getMediaURL() ).empty () ) ) && ( ! ( parcel->getMediaID ().isNull () ) ) )
+ {
+ // turn on transport controls if allowed for this parcel
+ mMediaStopButton->setEnabled ( editable );
+ mMediaStartButton->setEnabled ( editable );
+ }
+ else
+ {
+ // no media url or no media texture
+ mMediaStopButton->setEnabled ( FALSE );
+ mMediaStartButton->setEnabled ( FALSE );
+ };
+ #endif
+ }
+}
+
+// static
+void LLPanelLandMedia::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandMedia *self = (LLPanelLandMedia *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL sound_local = self->mCheckSoundLocal->get();
+ std::string music_url = self->mMusicURLEdit->getText();
+ std::string media_url = self->mMediaURLEdit->getText();
+ U8 media_auto_scale = self->mMediaAutoScaleCheck->get();
+ LLUUID media_id = self->mMediaTextureCtrl->getImageAssetID();
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_SOUND_LOCAL, sound_local);
+ parcel->setMusicURL(music_url.c_str());
+ parcel->setMediaURL(media_url.c_str());
+ parcel->setMediaID(media_id);
+ parcel->setMediaAutoScale ( media_auto_scale );
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+void LLPanelLandMedia::onClickStopMedia ( void* data )
+{
+ LLMediaEngine::getInstance ()->stop ();
+}
+
+void LLPanelLandMedia::onClickStartMedia ( void* data )
+{
+ // force a commit
+ gFocusMgr.setKeyboardFocus ( NULL, NULL );
+
+ // force a reload
+ LLMediaEngine::getInstance ()->convertImageAndLoadUrl ( true, false, std::string());
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandAccess
+//---------------------------------------------------------------------------
+
+LLPanelLandAccess::LLPanelLandAccess()
+: LLPanel("land_access_panel")
+{
+}
+
+
+
+BOOL LLPanelLandAccess::postBuild()
+{
+
+
+ mCheckGroup = LLUICtrlFactory::getCheckBoxByName(this, "GroupCheck");
+ childSetCommitCallback("GroupCheck", onCommitAny, this);
+
+ mCheckAccess = LLUICtrlFactory::getCheckBoxByName(this, "AccessCheck");
+ childSetCommitCallback("AccessCheck", onCommitAny, this);
+
+ mListAccess = LLUICtrlFactory::getNameListByName(this, "AccessList");
+
+ mBtnAddAccess = LLUICtrlFactory::getButtonByName(this, "Add...");
+
+ mBtnAddAccess->setClickedCallback(onClickAdd, this);
+
+ mBtnRemoveAccess = LLUICtrlFactory::getButtonByName(this, "Remove");
+
+ mBtnRemoveAccess->setClickedCallback(onClickRemove, this);
+
+ mCheckPass = LLUICtrlFactory::getCheckBoxByName(this, "PassCheck");
+ childSetCommitCallback("PassCheck", onCommitAny, this);
+
+
+ mSpinPrice = LLUICtrlFactory::getSpinnerByName(this, "PriceSpin");
+ childSetCommitCallback("PriceSpin", onCommitAny, this);
+
+ mSpinHours = LLUICtrlFactory::getSpinnerByName(this, "HoursSpin");
+ childSetCommitCallback("HoursSpin", onCommitAny, this);
+
+
+ return TRUE;
+}
+
+
+LLPanelLandAccess::~LLPanelLandAccess()
+{ }
+
+void LLPanelLandAccess::refresh()
+{
+ mListAccess->deleteAllItems();
+
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (parcel)
+ {
+ char label[256];
+
+ // Display options
+ BOOL use_group = parcel->getParcelFlag(PF_USE_ACCESS_GROUP);
+ mCheckGroup->set( use_group );
+
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ sprintf(label, "Group: %s", group_name);
+ mCheckGroup->setLabel( label );
+
+ S32 count = parcel->mAccessList.size();
+
+ BOOL use_list = parcel->getParcelFlag(PF_USE_ACCESS_LIST);
+ mCheckAccess->set( use_list );
+ sprintf(label, "Avatars: (%d listed, %d max)",
+ count, PARCEL_MAX_ACCESS_LIST);
+ mCheckAccess->setLabel( label );
+
+ access_map_const_iterator cit = parcel->mAccessList.begin();
+ access_map_const_iterator end = parcel->mAccessList.end();
+
+ for (; cit != end; ++cit)
+ {
+ const LLAccessEntry& entry = (*cit).second;
+ LLString suffix;
+ if (entry.mTime != 0)
+ {
+ S32 now = time(NULL);
+ S32 seconds = entry.mTime - now;
+ if (seconds < 0) seconds = 0;
+ suffix.assign(" (");
+ if (seconds >= 120)
+ {
+ char buf[30];
+ sprintf(buf, "%d minutes", (seconds/60));
+ suffix.append(buf);
+ }
+ else if (seconds >= 60)
+ {
+ suffix.append("1 minute");
+ }
+ else
+ {
+ char buf[30];
+ sprintf(buf, "%d seconds", seconds);
+ suffix.append(buf);
+ }
+ suffix.append(" remaining)");
+ }
+ mListAccess->addNameItem(entry.mID, ADD_BOTTOM, TRUE, suffix);
+ }
+
+ BOOL can_manage_allowed = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_ALLOWED);
+
+ BOOL enable_add = can_manage_allowed && (count < PARCEL_MAX_ACCESS_LIST);
+ mBtnAddAccess->setEnabled(enable_add);
+
+ BOOL enable_remove = can_manage_allowed && (count > 0);
+ mBtnRemoveAccess->setEnabled(enable_remove);
+
+ // Can only sell passes when limiting the access.
+ BOOL can_manage_passes = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_PASSES);
+ mCheckPass->setEnabled( (use_group || use_list) && can_manage_passes );
+
+ BOOL use_pass = parcel->getParcelFlag(PF_USE_PASS_LIST);
+ mCheckPass->set( use_pass );
+
+ BOOL enable_pass = can_manage_passes && use_pass;
+ mSpinPrice->setEnabled( enable_pass );
+ mSpinHours->setEnabled( enable_pass );
+
+ S32 pass_price = parcel->getPassPrice();
+ mSpinPrice->set( F32(pass_price) );
+
+ F32 pass_hours = parcel->getPassHours();
+ mSpinHours->set( pass_hours );
+
+ mCheckGroup->setEnabled( can_manage_allowed );
+ mCheckAccess->setEnabled( can_manage_allowed );
+
+ }
+ else
+ {
+ mCheckGroup->set(FALSE);
+ mCheckGroup->setLabel("Group:");
+ mCheckAccess->set(FALSE);
+ mCheckAccess->setLabel("Avatars:");
+ mBtnAddAccess->setEnabled(FALSE);
+ mBtnRemoveAccess->setEnabled(FALSE);
+ mSpinPrice->set((F32)PARCEL_PASS_PRICE_DEFAULT);
+ mSpinPrice->setEnabled(FALSE);
+ mSpinHours->set( PARCEL_PASS_HOURS_DEFAULT );
+ mSpinHours->setEnabled(FALSE);
+ mCheckGroup->setEnabled(FALSE);
+ mCheckAccess->setEnabled(FALSE);
+ }
+}
+
+// public
+void LLPanelLandAccess::refreshNames()
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ group_name[0] = '\0';
+ if(parcel)
+ {
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ }
+ char label[MAX_STRING];
+ snprintf(label, MAX_STRING, "Group: %s", group_name);
+ mCheckGroup->setLabel(label);
+}
+
+
+// virtual
+void LLPanelLandAccess::draw()
+{
+ refreshNames();
+ LLPanel::draw();
+}
+
+
+void LLPanelLandAccess::onAccessLevelChange(LLUICtrl*, void *userdata)
+{
+ LLPanelLandAccess::onCommitAny(NULL, userdata);
+}
+
+// static
+void LLPanelLandAccess::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandAccess *self = (LLPanelLandAccess *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL use_access_group = self->mCheckGroup->get();
+ BOOL use_access_list = self->mCheckAccess->get();
+ BOOL use_pass_list = self->mCheckPass->get();
+
+
+
+ // Must be limiting access to sell passes
+ if (!use_access_group && !use_access_list)
+ {
+ use_pass_list = FALSE;
+ }
+
+ S32 pass_price = llfloor(self->mSpinPrice->get());
+ F32 pass_hours = self->mSpinHours->get();
+
+ // Validate extracted data
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_USE_ACCESS_GROUP, use_access_group);
+ parcel->setParcelFlag(PF_USE_ACCESS_LIST, use_access_list);
+ parcel->setParcelFlag(PF_USE_PASS_LIST, use_pass_list);
+
+ parcel->setPassPrice( pass_price );
+ parcel->setPassHours( pass_hours );
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+// static
+void LLPanelLandAccess::onClickAdd(void* data)
+{
+ LLPanelLandAccess* panelp = (LLPanelLandAccess*)data;
+ gFloaterView->getParentFloater(panelp)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, data) );
+}
+
+// static
+void LLPanelLandAccess::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ LLPanelLandAccess* self = (LLPanelLandAccess*)userdata;
+ if (names.empty() || ids.empty()) return;
+ self->addAvatar(ids[0]);
+}
+
+
+void LLPanelLandAccess::addAvatar(LLUUID id)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ parcel->addToAccessList(id, 0);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_ACCESS);
+
+ refresh();
+}
+
+
+// static
+void LLPanelLandAccess::onClickRemove(void* data)
+{
+ LLPanelLandAccess* self = (LLPanelLandAccess*)data;
+ if (!self) return;
+
+ LLScrollListItem* item = self->mListAccess->getFirstSelected();
+ if (!item) return;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ const LLUUID& agent_id = item->getUUID();
+
+ parcel->removeFromAccessList(agent_id);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_ACCESS);
+
+ self->refresh();
+}
+
+
+
+//---------------------------------------------------------------------------
+// LLPanelLandBan
+//---------------------------------------------------------------------------
+LLPanelLandBan::LLPanelLandBan()
+: LLPanel("land_ban_panel")
+{
+
+}
+
+
+
+BOOL LLPanelLandBan::postBuild()
+{
+
+ mCheck = LLUICtrlFactory::getCheckBoxByName(this, "LandBanCheck");
+ childSetCommitCallback("LandBanCheck", onCommitAny, this);
+
+ mList = LLUICtrlFactory::getNameListByName(this, "LandBanList");
+
+ mBtnAdd = LLUICtrlFactory::getButtonByName(this, "Add...");
+
+ mBtnAdd->setClickedCallback(onClickAdd, this);
+
+ mBtnRemove = LLUICtrlFactory::getButtonByName(this, "Remove");
+
+ mBtnRemove->setClickedCallback(onClickRemove, this);
+
+ mCheckDenyAnonymous = LLUICtrlFactory::getCheckBoxByName(this, "DenyAnonymousCheck");
+ childSetCommitCallback("DenyAnonymousCheck", onCommitAny, this);
+
+ mCheckDenyIdentified = LLUICtrlFactory::getCheckBoxByName(this, "DenyIdentifiedCheck");
+ childSetCommitCallback("DenyIdentifiedCheck", onCommitAny, this);
+
+ mCheckDenyTransacted = LLUICtrlFactory::getCheckBoxByName(this, "DenyTransactedCheck");
+ childSetCommitCallback("DenyTransactedCheck", onCommitAny, this);
+
+ return TRUE;
+
+}
+
+
+LLPanelLandBan::~LLPanelLandBan()
+{ }
+
+void LLPanelLandBan::refresh()
+{
+ mList->deleteAllItems();
+
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (parcel)
+ {
+ char label[256];
+
+ // Display options
+
+ S32 count = parcel->mBanList.size();
+
+ BOOL use_ban = parcel->getParcelFlag(PF_USE_BAN_LIST);
+ mCheck->set( use_ban );
+
+ sprintf(label, "Ban these avatars: (%d listed, %d max)",
+ count, PARCEL_MAX_ACCESS_LIST);
+ mCheck->setLabel( label );
+
+ access_map_const_iterator cit = parcel->mBanList.begin();
+ access_map_const_iterator end = parcel->mBanList.end();
+ for ( ; cit != end; ++cit)
+ {
+ const LLAccessEntry& entry = (*cit).second;
+ LLString suffix;
+ if (entry.mTime != 0)
+ {
+ S32 now = time(NULL);
+ S32 seconds = entry.mTime - now;
+ if (seconds < 0) seconds = 0;
+ suffix.assign(" (");
+ if (seconds >= 120)
+ {
+ char buf[30];
+ sprintf(buf, "%d minutes", (seconds/60));
+ suffix.append(buf);
+ }
+ else if (seconds >= 60)
+ {
+ suffix.append("1 minute");
+ }
+ else
+ {
+ char buf[30];
+ sprintf(buf, "%d seconds", seconds);
+ suffix.append(buf);
+ }
+ suffix.append(" remaining)");
+ }
+ mList->addNameItem(entry.mID, ADD_BOTTOM, TRUE, suffix);
+ }
+
+ BOOL can_manage_banned = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_BANNED);
+ mCheck->setEnabled( can_manage_banned );
+ mCheckDenyAnonymous->setEnabled( FALSE );
+ mCheckDenyIdentified->setEnabled( FALSE );
+ mCheckDenyTransacted->setEnabled( FALSE );
+
+ if(parcel->getRegionDenyAnonymousOverride())
+ {
+ mCheckDenyAnonymous->set(TRUE);
+ }
+ else if(can_manage_banned)
+ {
+ mCheckDenyAnonymous->setEnabled(TRUE);
+ mCheckDenyAnonymous->set(parcel->getParcelFlag(PF_DENY_ANONYMOUS));
+ }
+ if(parcel->getRegionDenyIdentifiedOverride())
+ {
+ mCheckDenyIdentified->set(TRUE);
+ }
+ else if(can_manage_banned)
+ {
+ mCheckDenyIdentified->setEnabled(TRUE);
+ mCheckDenyIdentified->set(parcel->getParcelFlag(PF_DENY_IDENTIFIED));
+ }
+ if(parcel->getRegionDenyTransactedOverride())
+ {
+ mCheckDenyTransacted->set(TRUE);
+ }
+ else if(can_manage_banned)
+ {
+ mCheckDenyTransacted->setEnabled(TRUE);
+ mCheckDenyTransacted->set(parcel->getParcelFlag(PF_DENY_TRANSACTED));
+ }
+
+
+ BOOL enable_add = can_manage_banned && (count < PARCEL_MAX_ACCESS_LIST);
+ mBtnAdd->setEnabled(enable_add);
+
+ BOOL enable_remove = can_manage_banned && (count > 0);
+ mBtnRemove->setEnabled(enable_remove);
+ }
+ else
+ {
+ mCheck->set(FALSE);
+ mCheck->setLabel("Ban these avatars:");
+ mCheck->setEnabled(FALSE);
+ mBtnAdd->setEnabled(FALSE);
+ mBtnRemove->setEnabled(FALSE);
+ mCheckDenyAnonymous->set(FALSE);
+ mCheckDenyAnonymous->setEnabled(FALSE);
+ mCheckDenyIdentified->set(FALSE);
+ mCheckDenyIdentified->setEnabled(FALSE);
+ mCheckDenyTransacted->set(FALSE);
+ mCheckDenyTransacted->setEnabled(FALSE);
+ }
+}
+
+// static
+void LLPanelLandBan::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandBan *self = (LLPanelLandBan*)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL use_ban_list = self->mCheck->get();
+ BOOL deny_access_anonymous = self->mCheckDenyAnonymous->get();
+ BOOL deny_access_identified = self->mCheckDenyIdentified->get();
+ BOOL deny_access_transacted = self->mCheckDenyTransacted->get();
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_USE_BAN_LIST, use_ban_list);
+ parcel->setParcelFlag(PF_DENY_ANONYMOUS, deny_access_anonymous);
+ parcel->setParcelFlag(PF_DENY_IDENTIFIED, deny_access_identified);
+ parcel->setParcelFlag(PF_DENY_TRANSACTED, deny_access_transacted);
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+// static
+void LLPanelLandBan::onClickAdd(void* data)
+{
+ LLPanelLandBan* panelp = (LLPanelLandBan*)data;
+ gFloaterView->getParentFloater(panelp)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, data) );
+}
+
+// static
+void LLPanelLandBan::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ LLPanelLandBan* self = (LLPanelLandBan*)userdata;
+ if (names.empty() || ids.empty()) return;
+ self->addAvatar(ids[0]);
+}
+
+
+void LLPanelLandBan::addAvatar(LLUUID id)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ parcel->addToBanList(id, 0);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_BAN);
+
+ refresh();
+}
+
+
+// static
+void LLPanelLandBan::onClickRemove(void* data)
+{
+ LLPanelLandBan* self = (LLPanelLandBan*)data;
+ if (!self) return;
+
+ LLScrollListItem* item = self->mList->getFirstSelected();
+ if (!item) return;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ const LLUUID& agent_id = item->getUUID();
+
+ parcel->removeFromBanList(agent_id);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_BAN);
+
+ self->refresh();
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandRenters
+//---------------------------------------------------------------------------
+LLPanelLandRenters::LLPanelLandRenters()
+: LLPanel("landrenters", LLRect(0,500,500,0))
+{
+ const S32 BTN_WIDTH = 64;
+
+ S32 x = LEFT;
+ S32 y = mRect.getHeight() - VPAD;
+
+ LLCheckBoxCtrl* check = NULL;
+ LLButton* btn = NULL;
+ LLNameListCtrl* list = NULL;
+
+ check = new LLCheckBoxCtrl("RentersCheck",
+ LLRect(RULER0, y, RIGHT, y-LINE),
+ "Rent space to these avatars:");
+ addChild(check);
+ mCheckRenters = check;
+
+ y -= VPAD + LINE;
+
+ const S32 ITEMS = 5;
+ const BOOL NO_MULTIPLE_SELECT = FALSE;
+
+ list = new LLNameListCtrl("RentersList",
+ LLRect(RULER05, y, RIGHT, y-LINE*ITEMS),
+ NULL, NULL,
+ NO_MULTIPLE_SELECT);
+ list->addSimpleItem("foo tester");
+ list->addSimpleItem("bar tester");
+ list->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ addChild(list);
+ mListRenters = list;
+
+ y -= VPAD + LINE*ITEMS;
+
+ x = RULER05;
+ btn = new LLButton("Add...",
+ LLRect(x, y, x+BTN_WIDTH, y-LINE),
+ "",
+ onClickAdd, this);
+ btn->setFont( LLFontGL::sSansSerifSmall );
+ btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ addChild(btn);
+ mBtnAddRenter = btn;
+
+ x += HPAD + BTN_WIDTH;
+
+ btn = new LLButton("Remove",
+ LLRect(x, y, x+BTN_WIDTH, y-LINE),
+ "",
+ onClickRemove, this);
+ btn->setFont( LLFontGL::sSansSerifSmall );
+ btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ addChild(btn);
+ mBtnRemoveRenter = btn;
+}
+
+LLPanelLandRenters::~LLPanelLandRenters()
+{ }
+
+void LLPanelLandRenters::refresh()
+{ }
+
+// static
+void LLPanelLandRenters::onClickAdd(void*)
+{
+}
+
+// static
+void LLPanelLandRenters::onClickRemove(void*)
+{
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandCovenant
+//---------------------------------------------------------------------------
+LLPanelLandCovenant::LLPanelLandCovenant()
+: LLPanel("land_covenant_panel")
+{
+}
+
+LLPanelLandCovenant::~LLPanelLandCovenant()
+{
+}
+
+BOOL LLPanelLandCovenant::postBuild()
+{
+ refresh();
+ return TRUE;
+}
+
+// virtual
+void LLPanelLandCovenant::refresh()
+{
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if(!region) return;
+
+ LLTextBox* region_name = (LLTextBox*)getChildByName("region_name_text");
+ if (region_name)
+ {
+ region_name->setText(region->getName());
+ }
+
+ LLTextBox* resellable_clause = (LLTextBox*)getChildByName("resellable_clause");
+ if (resellable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ {
+ resellable_clause->setText(childGetText("can_not_resell"));
+ }
+ else
+ {
+ resellable_clause->setText(childGetText("can_resell"));
+ }
+ }
+
+ LLTextBox* changeable_clause = (LLTextBox*)getChildByName("changeable_clause");
+ if (changeable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ changeable_clause->setText(childGetText("can_change"));
+ }
+ else
+ {
+ changeable_clause->setText(childGetText("can_not_change"));
+ }
+ }
+
+ // send EstateCovenantInfo message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("EstateCovenantRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLPanelLandCovenant::updateCovenantText(const std::string &string)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLViewerTextEditor* editor = (LLViewerTextEditor*)self->getChildByName("covenant_editor");
+ if (editor)
+ {
+ editor->setHandleEditKeysDirectly(TRUE);
+ editor->setText(string);
+ }
+ }
+}
+
+// static
+void LLPanelLandCovenant::updateEstateName(const std::string& name)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLTextBox* editor = (LLTextBox*)self->getChildByName("estate_name_text");
+ if (editor) editor->setText(name);
+ }
+}
+
+// static
+void LLPanelLandCovenant::updateLastModified(const std::string& text)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLTextBox* editor = (LLTextBox*)self->getChildByName("covenant_timestamp_text");
+ if (editor) editor->setText(text);
+ }
+}
+
+// static
+void LLPanelLandCovenant::updateEstateOwnerName(const std::string& name)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLTextBox* editor = (LLTextBox*)self->getChildByName("estate_owner_text");
+ if (editor) editor->setText(name);
+ }
+}
diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h
new file mode 100644
index 0000000000..72f1e8ca8d
--- /dev/null
+++ b/indra/newview/llfloaterland.h
@@ -0,0 +1,472 @@
+/**
+ * @file llfloaterland.h
+ * @author James Cook
+ * @brief "About Land" floater, allowing display and editing of land parcel properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERLAND_H
+#define LL_LLFLOATERLAND_H
+
+#include <set>
+#include <vector>
+
+#include "llfloater.h"
+#include "llviewerimagelist.h"
+
+typedef std::set<LLUUID, lluuid_less> uuid_list_t;
+const F32 CACHE_REFRESH_TIME = 2.5f;
+
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLButton;
+class LLNameListCtrl;
+class LLSpinCtrl;
+class LLLineEditor;
+class LLRadioGroup;
+class LLParcelSelectionObserver;
+class LLTabContainer;
+class LLTextureCtrl;
+class LLViewerTextEditor;
+
+class LLPanelLandGeneral;
+class LLPanelLandObjects;
+class LLPanelLandOptions;
+class LLPanelLandMedia;
+class LLPanelLandAccess;
+class LLPanelLandBan;
+class LLPanelLandRenters;
+class LLPanelLandCovenant;
+
+class LLFloaterLand
+: public LLFloater
+{
+public:
+ // Call show() to open a land floater.
+ // Will query the viewer parcel manager to see what is selected.
+ static void show();
+ static BOOL floaterVisible() { return sInstance && sInstance->getVisible(); }
+ static void refreshAll();
+
+ static LLPanelLandObjects* getCurrentPanelLandObjects();
+ static LLPanelLandCovenant* getCurrentPanelLandCovenant();
+
+ // Returns TRUE, but does some early prep work for closing the window.
+ virtual BOOL canClose();
+
+ // Destroys itself on close.
+ virtual void onClose(bool app_quitting);
+
+protected:
+ // Does its own instance management, so clients not allowed
+ // to allocate or destroy.
+ LLFloaterLand();
+ virtual ~LLFloaterLand();
+
+ void refresh();
+
+
+ static void* createPanelLandGeneral(void* data);
+ static void* createPanelLandCovenant(void* data);
+ static void* createPanelLandObjects(void* data);
+ static void* createPanelLandOptions(void* data);
+ static void* createPanelLandMedia(void* data);
+ static void* createPanelLandAccess(void* data);
+ static void* createPanelLandBan(void* data);
+
+
+protected:
+ static LLFloaterLand* sInstance;
+ static LLParcelSelectionObserver* sObserver;
+ static S32 sLastTab;
+
+ LLTabContainer* mTabLand;
+ LLPanelLandGeneral* mPanelGeneral;
+ LLPanelLandObjects* mPanelObjects;
+ LLPanelLandOptions* mPanelOptions;
+ LLPanelLandMedia* mPanelMedia;
+ LLPanelLandAccess* mPanelAccess;
+ LLPanelLandBan* mPanelBan;
+ LLPanelLandCovenant* mPanelCovenant;
+ LLPanelLandRenters* mPanelRenters;
+
+public:
+ // When closing the dialog, we want to deselect the land. But when
+ // we send an update to the simulator, it usually replies with the
+ // parcel information, causing the land to be reselected. This allows
+ // us to suppress that behavior.
+ static BOOL sRequestReplyOnUpdate;
+};
+
+
+class LLPanelLandGeneral
+: public LLPanel
+{
+public:
+ LLPanelLandGeneral();
+ virtual ~LLPanelLandGeneral();
+ void refresh();
+ void refreshNames();
+ virtual void draw();
+
+ void setGroup(const LLUUID& group_id);
+ static void onClickProfile(void*);
+ static void onClickSetGroup(void*);
+ static void cbGroupID(LLUUID group_id, void* userdata);
+ static BOOL enableDeedToGroup(void*);
+ static void onClickDeed(void*);
+ static void onClickBuyLand(void* data);
+ static void onClickRelease(void*);
+ static void onClickReclaim(void*);
+ static void onClickBuyPass(void* deselect_when_done);
+ static BOOL enableBuyPass(void*);
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void finalizeCommit(void * userdata);
+ static void onForSaleChange(LLUICtrl *ctrl, void * userdata);
+ static void finalizeSetSellChange(void * userdata);
+ static void onSalePriceChange(LLUICtrl *ctrl, void * userdata);
+
+ static void cbBuyPass(S32 option, void*);
+
+ static void onClickSellLand(void* data);
+ static void onClickStopSellLand(void* data);
+ static void onClickSet(void* data);
+ static void onClickClear(void* data);
+ static void onClickShow(void* data);
+ static void callbackAvatarPick(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+ static void finalizeAvatarPick(void* data);
+ static void callbackHighlightTransferable(S32 option, void* userdata);
+ static void onClickStartAuction(void*);
+ // sale change confirmed when "is for sale", "sale price", "sell to whom" fields are changed
+ static void confirmSaleChange(S32 landSize, S32 salePrice, std::string authorizedName, void(*callback)(void*), void* userdata);
+ static void callbackConfirmSaleChange(S32 option, void* userdata);
+
+ virtual BOOL postBuild();
+
+protected:
+ BOOL mUncheckedSell; // True only when verifying land information when land is for sale on sale info change
+
+ LLTextBox* mLabelName;
+ LLLineEditor* mEditName;
+ LLTextBox* mLabelDesc;
+ LLLineEditor* mEditDesc;
+
+ LLTextBox* mTextSalePending;
+
+ LLButton* mBtnDeedToGroup;
+ LLButton* mBtnSetGroup;
+
+ LLTextBox* mTextOwnerLabel;
+ LLTextBox* mTextOwner;
+ LLButton* mBtnProfile;
+
+ LLTextBox* mTextGroup;
+ LLTextBox* mTextGroupLabel;
+ LLTextBox* mTextClaimDateLabel;
+ LLTextBox* mTextClaimDate;
+
+ LLTextBox* mTextPriceLabel;
+ LLTextBox* mTextPrice;
+
+ LLCheckBoxCtrl* mCheckDeedToGroup;
+ LLCheckBoxCtrl* mCheckContributeWithDeed;
+
+ LLTextBox* mSaleInfoForSale1;
+ LLTextBox* mSaleInfoForSale2;
+ LLTextBox* mSaleInfoForSaleObjects;
+ LLTextBox* mSaleInfoForSaleNoObjects;
+ LLTextBox* mSaleInfoNotForSale;
+ LLButton* mBtnSellLand;
+ LLButton* mBtnStopSellLand;
+
+ LLTextBox* mTextDwell;
+
+ LLButton* mBtnBuyLand;
+ LLButton* mBtnBuyGroupLand;
+
+ // these buttons share the same location, but
+ // reclaim is in exactly the same visual place,
+ // ond is only shown for estate owners on their
+ // estate since they cannot release land.
+ LLButton* mBtnReleaseLand;
+ LLButton* mBtnReclaimLand;
+
+ LLButton* mBtnBuyPass;
+ LLButton* mBtnStartAuction;
+};
+
+class LLPanelLandObjects
+: public LLPanel
+{
+public:
+ LLPanelLandObjects();
+ virtual ~LLPanelLandObjects();
+ void refresh();
+ virtual void draw();
+
+ static void callbackReturnOwnerObjects(S32, void*);
+ static void callbackReturnGroupObjects(S32, void*);
+ static void callbackReturnOtherObjects(S32, void*);
+ static void callbackReturnOwnerList(S32, void*);
+
+ static void clickShowCore(S32 return_type, uuid_list_t* list = 0);
+ static void onClickShowOwnerObjects(void*);
+ static void onClickShowGroupObjects(void*);
+ static void onClickShowOtherObjects(void*);
+
+ static void onClickReturnOwnerObjects(void*);
+ static void onClickReturnGroupObjects(void*);
+ static void onClickReturnOtherObjects(void*);
+ static void onClickReturnOwnerList(void*);
+ static void onClickRefresh(void*);
+ static void onClickType(void*);
+ static void onClickName(void*);
+ static void onClickDesc(void*);
+
+ static void onDoubleClickOwner(void*);
+
+ static void onCommitList(LLUICtrl* ctrl, void* data);
+ static void onLostFocus(LLLineEditor* caller, void* user_data);
+
+ static void processParcelObjectOwnersReply(LLMessageSystem *msg, void **);
+
+ virtual BOOL postBuild();
+
+protected:
+ void sortBtnCore(S32 column);
+
+ LLTextBox *mSWTotalObjectsLabel;
+ LLTextBox *mSWTotalObjects;
+
+ LLTextBox *mParcelObjectBonus;
+
+ LLTextBox *mObjectContributionLabel;
+ LLTextBox *mObjectContribution;
+ LLTextBox *mTotalObjectsLabel;
+ LLTextBox *mTotalObjects;
+
+ LLTextBox *mOwnerObjectsLabel;
+ LLTextBox *mOwnerObjects;
+ LLButton *mBtnShowOwnerObjects;
+ LLButton *mBtnReturnOwnerObjects;
+
+ LLTextBox *mGroupObjectsLabel;
+ LLTextBox *mGroupObjects;
+ LLButton *mBtnShowGroupObjects;
+ LLButton *mBtnReturnGroupObjects;
+
+ LLTextBox *mOtherObjectsLabel;
+ LLTextBox *mOtherObjects;
+ LLButton *mBtnShowOtherObjects;
+ LLButton *mBtnReturnOtherObjects;
+
+ LLTextBox *mSelectedObjectsLabel;
+ LLTextBox *mSelectedObjects;
+
+ LLTextBox *mCleanOtherObjectsLabel;
+ LLLineEditor *mCleanOtherObjectsTime;
+ S32 mOtherTime;
+
+ LLTextBox *mOwnerListText;
+ LLButton *mBtnRefresh;
+ LLButton *mBtnReturnOwnerList;
+ LLButton *mBtnType; // column 0
+ LLButton *mBtnName; // column 2
+ LLButton *mBtnDescription; // column 3
+ LLNameListCtrl *mOwnerList;
+
+ LLPointer<LLViewerImage> mIconAvatarOnline;
+ LLPointer<LLViewerImage> mIconAvatarOffline;
+ LLPointer<LLViewerImage> mIconGroup;
+
+ U32 mCurrentSortColumn;
+ BOOL mCurrentSortAscending;
+ S32 mColWidth[12];
+
+ BOOL mFirstReply;
+
+ uuid_list_t mSelectedOwners;
+ LLString mSelectedName;
+ S32 mSelectedCount;
+ BOOL mSelectedIsGroup;
+};
+
+
+class LLPanelLandOptions
+: public LLPanel
+{
+public:
+ LLPanelLandOptions();
+ virtual ~LLPanelLandOptions();
+ void refresh();
+
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickSet(void* userdata);
+ static void onClickClear(void* userdata);
+ static void onClickPublishHelp(void*);
+
+ virtual BOOL postBuild();
+
+protected:
+ LLCheckBoxCtrl* mCheckEditObjects;
+ LLCheckBoxCtrl* mCheckEditGroupObjects;
+ LLCheckBoxCtrl* mCheckAllObjectEntry;
+ LLCheckBoxCtrl* mCheckGroupObjectEntry;
+ LLCheckBoxCtrl* mCheckEditLand;
+ LLCheckBoxCtrl* mCheckSafe;
+ LLCheckBoxCtrl* mCheckFly;
+ LLCheckBoxCtrl* mCheckGroupScripts;
+ LLCheckBoxCtrl* mCheckOtherScripts;
+ LLCheckBoxCtrl* mCheckLandmark;
+
+ LLCheckBoxCtrl* mCheckShowDirectory;
+ LLComboBox* mCategoryCombo;
+ LLComboBox* mLandingTypeCombo;
+
+ LLTextureCtrl* mSnapshotCtrl;
+
+ LLTextBox* mLocationText;
+ LLButton* mSetBtn;
+ LLButton* mClearBtn;
+
+ LLCheckBoxCtrl *mAllowPublishCtrl;
+ LLCheckBoxCtrl *mMatureCtrl;
+ LLCheckBoxCtrl *mPushRestrictionCtrl;
+ LLButton *mPublishHelpButton;
+};
+
+
+class LLPanelLandMedia
+: public LLPanel
+{
+public:
+ LLPanelLandMedia();
+ virtual ~LLPanelLandMedia();
+ void refresh();
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickStopMedia ( void* data );
+ static void onClickStartMedia ( void* data );
+
+ virtual BOOL postBuild();
+
+protected:
+ LLCheckBoxCtrl* mCheckSoundLocal;
+ LLLineEditor* mMusicURLEdit;
+ LLLineEditor* mMediaURLEdit;
+ LLTextureCtrl* mMediaTextureCtrl;
+ LLCheckBoxCtrl* mMediaAutoScaleCheck;
+ //LLButton* mMediaStopButton;
+ //LLButton* mMediaStartButton;
+};
+
+
+
+class LLPanelLandAccess
+: public LLPanel
+{
+public:
+ LLPanelLandAccess();
+ virtual ~LLPanelLandAccess();
+ void refresh();
+ void refreshNames();
+ virtual void draw();
+
+ void addAvatar(LLUUID id);
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickAdd(void*);
+ static void onClickRemove(void*);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+ static void onAccessLevelChange(LLUICtrl* ctrl, void* userdata);
+
+ virtual BOOL postBuild();
+
+protected:
+ LLTextBox* mLabelTitle;
+
+ LLCheckBoxCtrl* mCheckGroup;
+
+ LLCheckBoxCtrl* mCheckAccess;
+ LLNameListCtrl* mListAccess;
+ LLButton* mBtnAddAccess;
+ LLButton* mBtnRemoveAccess;
+
+ LLCheckBoxCtrl* mCheckPass;
+ LLSpinCtrl* mSpinPrice;
+ LLSpinCtrl* mSpinHours;
+
+ LLCheckBoxCtrl* mCheckIdentified;
+ LLCheckBoxCtrl* mCheckTransacted;
+ LLRadioGroup* mCheckStatusLevel;
+
+};
+
+
+class LLPanelLandBan
+: public LLPanel
+{
+public:
+ LLPanelLandBan();
+ virtual ~LLPanelLandBan();
+ void refresh();
+
+ void addAvatar(LLUUID id);
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickAdd(void*);
+ static void onClickRemove(void*);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+
+ virtual BOOL postBuild();
+
+protected:
+ LLTextBox* mLabelTitle;
+
+ LLCheckBoxCtrl* mCheck;
+ LLNameListCtrl* mList;
+ LLButton* mBtnAdd;
+ LLButton* mBtnRemove;
+ LLCheckBoxCtrl* mCheckDenyAnonymous;
+ LLCheckBoxCtrl* mCheckDenyIdentified;
+ LLCheckBoxCtrl* mCheckDenyTransacted;
+};
+
+
+class LLPanelLandRenters
+: public LLPanel
+{
+public:
+ LLPanelLandRenters();
+ virtual ~LLPanelLandRenters();
+ void refresh();
+
+ static void onClickAdd(void*);
+ static void onClickRemove(void*);
+
+protected:
+ LLCheckBoxCtrl* mCheckRenters;
+ LLNameListCtrl* mListRenters;
+ LLButton* mBtnAddRenter;
+ LLButton* mBtnRemoveRenter;
+};
+
+class LLPanelLandCovenant
+: public LLPanel
+{
+public:
+ LLPanelLandCovenant();
+ virtual ~LLPanelLandCovenant();
+ virtual BOOL postBuild();
+ void refresh();
+ static void updateCovenantText(const std::string& string);
+ static void updateEstateName(const std::string& name);
+ static void updateLastModified(const std::string& text);
+ static void updateEstateOwnerName(const std::string& name);
+};
+
+#endif
diff --git a/indra/newview/llfloaterlandholdings.cpp b/indra/newview/llfloaterlandholdings.cpp
new file mode 100644
index 0000000000..496c31d219
--- /dev/null
+++ b/indra/newview/llfloaterlandholdings.cpp
@@ -0,0 +1,305 @@
+/**
+ * @file llfloaterlandholdings.cpp
+ * @brief "My Land" floater showing all your land parcels.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterlandholdings.h"
+
+#include "indra_constants.h"
+#include "llfontgl.h"
+#include "llqueryflags.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterworldmap.h"
+//#include "llinventoryview.h" // for mOpenNextNewItem
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "llscrolllistctrl.h"
+#include "llbutton.h"
+#include "lluiconstants.h"
+#include "llviewermessage.h"
+#include "llvieweruictrlfactory.h"
+
+// statics
+LLFloaterLandHoldings* LLFloaterLandHoldings::sInstance = NULL;
+
+
+// static
+void LLFloaterLandHoldings::show(void*)
+{
+ LLFloaterLandHoldings* floater = new LLFloaterLandHoldings();
+ gUICtrlFactory->buildFloater(floater, "floater_land_holdings.xml");
+ floater->center();
+
+ // query_id null is known to be us
+ const LLUUID& query_id = LLUUID::null;
+
+ // look only for parcels we own
+ U32 query_flags = DFQ_AGENT_OWNED;
+
+ send_places_query(query_id,
+ LLUUID::null,
+ "",
+ query_flags,
+ LLParcel::C_ANY,
+ "");
+
+ // TODO: request updated money balance?
+ floater->open();
+}
+
+
+// protected
+LLFloaterLandHoldings::LLFloaterLandHoldings()
+: LLFloater("land holdings floater"),
+ mActualArea(0),
+ mBillableArea(0),
+ mFirstPacketReceived(FALSE),
+ mSortColumn(""),
+ mSortAscending(TRUE)
+{
+ // Instance management.
+ sInstance = this;
+}
+
+BOOL LLFloaterLandHoldings::postBuild()
+{
+ childSetAction("Teleport", onClickTeleport, this);
+ childSetAction("Show on Map", onClickMap, this);
+
+ // Grant list
+ childSetDoubleClickCallback("grant list", onGrantList);
+ childSetUserData("grant list", this);
+
+ LLCtrlListInterface *list = childGetListInterface("grant list");
+ if (!list) return TRUE;
+
+ S32 count = gAgent.mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID id(gAgent.mGroups.get(i).mID);
+
+ LLSD element;
+ element["id"] = id;
+ element["columns"][0]["column"] = "group";
+ element["columns"][0]["value"] = gAgent.mGroups.get(i).mName;
+ element["columns"][0]["font"] = "SANSSERIF";
+
+ element["columns"][1]["column"] = "area";
+ element["columns"][1]["value"] = llformat("%d sq. meters", gAgent.mGroups.get(i).mContribution);
+ element["columns"][1]["font"] = "SANSSERIF";
+
+ list->addElement(element, ADD_SORTED);
+ }
+
+ return TRUE;
+}
+
+
+// protected
+LLFloaterLandHoldings::~LLFloaterLandHoldings()
+{
+ sInstance = NULL;
+}
+
+
+void LLFloaterLandHoldings::draw()
+{
+ refresh();
+
+ LLFloater::draw();
+}
+
+
+// public
+void LLFloaterLandHoldings::refresh()
+{
+ LLCtrlSelectionInterface *list = childGetSelectionInterface("parcel list");
+ BOOL enable_btns = FALSE;
+ if (list && list->getFirstSelectedIndex()> -1)
+ {
+ enable_btns = TRUE;
+ }
+
+ childSetEnabled("Teleport", enable_btns);
+ childSetEnabled("Show on Map", enable_btns);
+
+ refreshAggregates();
+}
+
+
+// static
+void LLFloaterLandHoldings::processPlacesReply(LLMessageSystem* msg, void**)
+{
+ LLFloaterLandHoldings* self = sInstance;
+
+ // Is this packet from an old, closed window?
+ if (!self)
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = self->childGetListInterface("parcel list");
+ if (!list) return;
+
+ // If this is the first packet, clear out the "loading..." indicator
+ if (!self->mFirstPacketReceived)
+ {
+ self->mFirstPacketReceived = TRUE;
+ list->operateOnAll(LLCtrlSelectionInterface::OP_DELETE);
+ }
+
+ LLUUID owner_id;
+ char name[MAX_STRING];
+ char desc[MAX_STRING];
+ S32 actual_area;
+ S32 billable_area;
+ U8 flags;
+ F32 global_x;
+ F32 global_y;
+ char sim_name[MAX_STRING];
+
+ S32 i;
+ S32 count = msg->getNumberOfBlocks("QueryData");
+ for (i = 0; i < count; i++)
+ {
+ msg->getUUID("QueryData", "OwnerID", owner_id, i);
+ msg->getString("QueryData", "Name", MAX_STRING, name, i);
+ msg->getString("QueryData", "Desc", MAX_STRING, desc, i);
+ msg->getS32("QueryData", "ActualArea", actual_area, i);
+ msg->getS32("QueryData", "BillableArea", billable_area, i);
+ msg->getU8("QueryData", "Flags", flags, i);
+ msg->getF32("QueryData", "GlobalX", global_x, i);
+ msg->getF32("QueryData", "GlobalY", global_y, i);
+ msg->getString("QueryData", "SimName", MAX_STRING, sim_name, i);
+
+ self->mActualArea += actual_area;
+ self->mBillableArea += billable_area;
+
+ S32 region_x = llround(global_x) % REGION_WIDTH_UNITS;
+ S32 region_y = llround(global_y) % REGION_WIDTH_UNITS;
+
+ char location[MAX_STRING];
+ sprintf(location, "%s (%d, %d)", sim_name, region_x, region_y);
+
+ char area[MAX_STRING];
+ if(billable_area == actual_area)
+ {
+ sprintf(area, "%d", billable_area);
+ }
+ else
+ {
+ sprintf(area, "%d / %d", billable_area, actual_area);
+ }
+
+ char hidden[MAX_STRING];
+ sprintf(hidden, "%f %f", global_x, global_y);
+
+ LLSD element;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = name;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][1]["column"] = "location";
+ element["columns"][1]["value"] = location;
+ element["columns"][1]["font"] = "SANSSERIF";
+ element["columns"][2]["column"] = "area";
+ element["columns"][2]["value"] = area;
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][3]["column"] = "hidden";
+ element["columns"][3]["value"] = hidden;
+ element["columns"][3]["font"] = "SANSSERIF";
+
+ list->addElement(element);
+ }
+
+ self->refreshAggregates();
+}
+
+void LLFloaterLandHoldings::buttonCore(S32 which)
+{
+ LLScrollListCtrl *list = LLUICtrlFactory::getScrollListByName(this, "parcel list");
+ if (!list) return;
+
+ S32 index = list->getFirstSelectedIndex();
+ if (index < 0) return;
+
+ LLString location = list->getSimpleSelectedItem(3);
+
+ F32 global_x = 0.f;
+ F32 global_y = 0.f;
+ sscanf(location.c_str(), "%f %f", &global_x, &global_y);
+
+ // Hack: Use the agent's z-height
+ F64 global_z = gAgent.getPositionGlobal().mdV[VZ];
+
+ LLVector3d pos_global(global_x, global_y, global_z);
+
+ switch(which)
+ {
+ case 0:
+ gAgent.teleportViaLocation(pos_global);
+ gFloaterWorldMap->trackLocation(pos_global);
+ break;
+ case 1:
+ gFloaterWorldMap->trackLocation(pos_global);
+ LLFloaterWorldMap::show(NULL, TRUE);
+ break;
+ default:
+ break;
+ }
+}
+
+// static
+void LLFloaterLandHoldings::onClickTeleport(void* data)
+{
+ LLFloaterLandHoldings* self = (LLFloaterLandHoldings*)data;
+ self->buttonCore(0);
+ self->close();
+}
+
+
+// static
+void LLFloaterLandHoldings::onClickMap(void* data)
+{
+ LLFloaterLandHoldings* self = (LLFloaterLandHoldings*)data;
+ self->buttonCore(1);
+}
+
+// static
+void LLFloaterLandHoldings::onGrantList(void* data)
+{
+ LLFloaterLandHoldings* self = (LLFloaterLandHoldings*)data;
+ LLCtrlSelectionInterface *list = self->childGetSelectionInterface("grant list");
+ if (!list) return;
+ LLUUID group_id = list->getCurrentID();
+ if (group_id.notNull())
+ {
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+}
+
+void LLFloaterLandHoldings::refreshAggregates()
+{
+ S32 allowed_area = gStatusBar->getSquareMetersCredit();
+ S32 current_area = gStatusBar->getSquareMetersCommitted();
+ S32 available_area = gStatusBar->getSquareMetersLeft();
+
+ char buffer[MAX_STRING];
+
+ sprintf(buffer, "%d sq. meters", allowed_area);
+ childSetValue("allowed_text", LLSD(buffer));
+
+ sprintf(buffer, "%d sq. meters", current_area);
+ childSetValue("current_text", LLSD(buffer));
+
+ sprintf(buffer, "%d sq. meters", available_area);
+ childSetValue("available_text", LLSD(buffer));
+}
diff --git a/indra/newview/llfloaterlandholdings.h b/indra/newview/llfloaterlandholdings.h
new file mode 100644
index 0000000000..4fb3d60451
--- /dev/null
+++ b/indra/newview/llfloaterlandholdings.h
@@ -0,0 +1,62 @@
+/**
+ * @file llfloaterlandholdings.h
+ * @brief "My Land" floater showing all your land parcels.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERLANDHOLDINGS_H
+#define LL_LLFLOATERLANDHOLDINGS_H
+
+#include "llfloater.h"
+
+class LLMessageSystem;
+class LLTextBox;
+class LLScrollListCtrl;
+class LLButton;
+
+class LLFloaterLandHoldings
+: public LLFloater
+{
+public:
+ BOOL postBuild();
+
+ static void show(void*);
+
+ virtual void draw();
+
+ void refresh();
+
+ void buttonCore(S32 which);
+
+ static void processPlacesReply(LLMessageSystem* msg, void**);
+
+ static void onClickTeleport(void*);
+ static void onClickMap(void*);
+ static void onClickLandmark(void*);
+
+ static void onGrantList(void* data);
+
+protected:
+ LLFloaterLandHoldings();
+ virtual ~LLFloaterLandHoldings();
+
+ void refreshAggregates();
+
+protected:
+ static LLFloaterLandHoldings* sInstance;
+
+ // Sum up as packets arrive the total holdings
+ S32 mActualArea;
+ S32 mBillableArea;
+
+ // Has a packet of data been received?
+ // Used to clear out the mParcelList's "Loading..." indicator
+ BOOL mFirstPacketReceived;
+
+ LLString mSortColumn;
+ BOOL mSortAscending;
+};
+
+#endif
diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp
new file mode 100644
index 0000000000..bbac916331
--- /dev/null
+++ b/indra/newview/llfloatermap.cpp
@@ -0,0 +1,199 @@
+/**
+ * @file llfloatermap.cpp
+ * @brief The "mini-map" or radar in the upper right part of the screen.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// self include
+#include "llfloatermap.h"
+
+// Library includes
+#include "llfontgl.h"
+#include "llinventory.h"
+#include "message.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "lldraghandle.h"
+#include "lleconomy.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llnetmap.h"
+#include "llregionhandle.h"
+#include "llresizebar.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llsky.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "llstatgraph.h"
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "viewer.h"
+
+#include "llglheaders.h"
+
+//
+// Constants
+//
+const S32 LEGEND_SIZE = 16;
+
+const S32 HPAD = 4;
+
+const S32 MAP_COMBOBOX_WIDTH = 128;
+const S32 MAP_COMBOBOX_HEIGHT = 20;
+const S32 GO_BTN_WIDTH = 20;
+const S32 SLIDER_WIDTH = LEGEND_SIZE + MAP_COMBOBOX_WIDTH + HPAD + GO_BTN_WIDTH - HPAD;
+const S32 SLIDER_HEIGHT = 16;
+
+const S32 SIM_STAT_WIDTH = 8;
+
+const S32 MAP_SCALE_MIN = 35; // in pixels per sim
+const S32 MAP_SCALE_MAX = 180; // in pixels per sim
+const S32 MAP_SCALE_INCREMENT = 5;
+
+const char MAP_TITLE[] = "";
+
+const S32 NETMAP_MIN_WIDTH = 128;
+const S32 NETMAP_MIN_HEIGHT = 128;
+
+const S32 FLOATERMAP_MIN_WIDTH =
+ SLIDER_WIDTH +
+ 2 * (HPAD + LLPANEL_BORDER_WIDTH);
+
+const S32 MORE_BTN_VPAD = 2;
+
+const S32 SPIN_VPAD = 4;
+
+const S32 TRACKING_LABEL_HEIGHT = 16;
+
+const S32 FLOATERMAP_MIN_HEIGHT_LARGE = 60;
+
+//
+// Globals
+//
+LLFloaterMap *gFloaterMap = NULL;
+
+
+
+
+//
+// Member functions
+//
+
+LLFloaterMap::LLFloaterMap(const std::string& name)
+ :
+ LLFloater(name,
+ "FloaterMapRect",
+ MAP_TITLE,
+ TRUE,
+ FLOATERMAP_MIN_WIDTH,
+ FLOATERMAP_MIN_HEIGHT_LARGE,
+ FALSE,
+ FALSE,
+ TRUE) // close button
+{
+ const S32 LEFT = LLPANEL_BORDER_WIDTH;
+ const S32 TOP = mRect.getHeight();
+
+ S32 y = 0;
+
+ // Map itself
+ LLRect map_rect(
+ LEFT,
+ TOP - LLPANEL_BORDER_WIDTH,
+ mRect.getWidth() - LLPANEL_BORDER_WIDTH,
+ y );
+ LLColor4 bg_color = gColors.getColor( "NetMapBackgroundColor" );
+ mMap = new LLNetMap("Net Map", map_rect, bg_color);
+ mMap->setFollowsAll();
+ addChildAtEnd(mMap);
+
+ // Get the drag handle all the way in back
+ removeChild(mDragHandle);
+ addChildAtEnd(mDragHandle);
+
+ setIsChrome(TRUE);
+}
+
+
+LLFloaterMap::~LLFloaterMap()
+{
+}
+
+
+// virtual
+void LLFloaterMap::setVisible(BOOL visible)
+{
+ LLFloater::setVisible(visible);
+
+ gSavedSettings.setBOOL("ShowMiniMap", visible);
+}
+
+
+// virtual
+void LLFloaterMap::onClose(bool app_quitting)
+{
+ LLFloater::setVisible(FALSE);
+
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowMiniMap", FALSE);
+ }
+}
+
+BOOL LLFloaterMap::canClose()
+{
+ return !gQuit;
+}
+
+
+// virtual
+void LLFloaterMap::draw()
+{
+ if( getVisible() )
+ {
+ // Note: we can't just gAgent.check cameraMouselook() because the transition states are wrong.
+ if( gAgent.cameraMouselook())
+ {
+ setMouseOpaque(FALSE);
+ mDragHandle->setMouseOpaque(FALSE);
+
+ drawChild(mMap);
+ }
+ else
+ {
+ setMouseOpaque(TRUE);
+ mDragHandle->setMouseOpaque(TRUE);
+
+ LLFloater::draw();
+ }
+ }
+}
+
+// static
+void LLFloaterMap::toggle(void*)
+{
+ if( gFloaterMap )
+ {
+ if (gFloaterMap->getVisible())
+ {
+ gFloaterMap->close();
+ }
+ else
+ {
+ gFloaterMap->open();
+ }
+ }
+}
diff --git a/indra/newview/llfloatermap.h b/indra/newview/llfloatermap.h
new file mode 100644
index 0000000000..00a483d17c
--- /dev/null
+++ b/indra/newview/llfloatermap.h
@@ -0,0 +1,50 @@
+/**
+ * @file llfloatermap.h
+ * @brief The "mini-map" or radar in the upper right part of the screen.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERMAP_H
+#define LL_LLFLOATERMAP_H
+
+#include "llfloater.h"
+
+class LLNetMap;
+class LLSliderCtrl;
+class LLStatGraph;
+class LLTextBox;
+class LLUICtrl;
+class LLViewerImage;
+
+//
+// Classes
+//
+class LLFloaterMap
+: public LLFloater
+{
+public:
+ LLFloaterMap(const std::string& name);
+ virtual ~LLFloaterMap();
+
+ static void toggle(void*);
+
+ /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ void draw();
+ /*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ BOOL canClose();
+
+protected:
+ LLNetMap* mMap;
+};
+
+
+//
+// Globals
+//
+
+extern LLFloaterMap *gFloaterMap;
+
+
+#endif // LL_LLFLOATERMAP_H
diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp
new file mode 100644
index 0000000000..a8d90b082e
--- /dev/null
+++ b/indra/newview/llfloaternamedesc.cpp
@@ -0,0 +1,202 @@
+/**
+ * @file llfloaternamedesc.cpp
+ * @brief LLFloaterNameDesc class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaternamedesc.h"
+#include "lllineeditor.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llviewerwindow.h"
+#include "llfocusmgr.h"
+#include "llradiogroup.h"
+#include "llassetstorage.h"
+#include "lldbstrings.h"
+#include "lldir.h"
+#include "llviewercontrol.h"
+#include "llviewermenu.h"
+#include "llvieweruictrlfactory.h"
+
+const S32 PREVIEW_LINE_HEIGHT = 19;
+const S32 PREVIEW_CLOSE_BOX_SIZE = 16;
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_VPAD = 2;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREVIEW_HEADER_SIZE = 3 * PREVIEW_LINE_HEIGHT + PREVIEW_VPAD;
+const S32 PREF_BUTTON_WIDTH = 64;
+const S32 PREF_BUTTON_HEIGHT = 16;
+
+//-----------------------------------------------------------------------------
+// LLFloaterNameDesc()
+//-----------------------------------------------------------------------------
+LLFloaterNameDesc::LLFloaterNameDesc(const std::string& filename )
+ :
+ LLFloater("Name/Description Floater")
+{
+ mFilenameAndPath = filename;
+ mFilename.assign(strrchr( filename.c_str(), gDirUtilp->getDirDelimiter()[0]) + 1);
+ // SL-5521 Maintain capitalization of filename when making the inventory item. JC
+ //LLString::toLower(mFilename);
+ mIsAudio = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterNameDesc::postBuild()
+{
+ LLRect r;
+
+ LLString asset_name = mFilename;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+ else
+ if( !stricmp( end_p, ".wav") )
+ {
+ mIsAudio = TRUE;
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ setTitle(mFilename);
+
+ centerWindow();
+
+ S32 line_width = mRect.getWidth() - 2 * PREVIEW_HPAD;
+ S32 y = mRect.getHeight() - PREVIEW_LINE_HEIGHT;
+
+ r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT );
+ y -= PREVIEW_LINE_HEIGHT;
+
+ r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT );
+
+ childSetCommitCallback("name_form", doCommit, this);
+ childSetValue("name_form", LLSD(asset_name));
+
+ LLLineEditor *NameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "name_form");
+ if (NameEditor)
+ {
+ NameEditor->setMaxTextLength(DB_INV_ITEM_NAME_STR_LEN);
+ NameEditor->setPrevalidate(&LLLineEditor::prevalidatePrintableNotPipe);
+ }
+
+ y -= llfloor(PREVIEW_LINE_HEIGHT * 1.2f);
+ y -= PREVIEW_LINE_HEIGHT;
+
+ r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT );
+ childSetCommitCallback("description_form", doCommit, this);
+ LLLineEditor *DescEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "description_form");
+ if (DescEditor)
+ {
+ DescEditor->setMaxTextLength(DB_INV_ITEM_DESC_STR_LEN);
+ DescEditor->setPrevalidate(&LLLineEditor::prevalidatePrintableNotPipe);
+ }
+
+ y -= llfloor(PREVIEW_LINE_HEIGHT * 1.2f);
+
+ if (mIsAudio)
+ {
+ LLSD bitrate = gSavedSettings.getS32("AudioDefaultBitrate");
+
+ childSetValue("bitrate", bitrate);
+ }
+
+ // Cancel button
+ childSetAction("cancel_btn", onBtnCancel, this);
+
+ // OK button
+ childSetAction("ok_btn", onBtnOK, this);
+ setDefaultBtn("ok_btn");
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterNameDesc()
+//-----------------------------------------------------------------------------
+LLFloaterNameDesc::~LLFloaterNameDesc()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+}
+
+//-----------------------------------------------------------------------------
+// centerWindow()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::centerWindow()
+{
+ LLRect window_rect = gViewerWindow->getRootView()->getRect();
+
+ S32 dialog_left = window_rect.mLeft + (window_rect.getWidth() - mRect.getWidth()) / 2;
+ S32 dialog_bottom = window_rect.mBottom + (window_rect.getHeight() - mRect.getHeight()) / 2;
+
+ translate( dialog_left - mRect.mLeft, dialog_bottom - mRect.mBottom );
+}
+
+// Sub-classes should override this function if they allow editing
+//-----------------------------------------------------------------------------
+// onCommit()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::onCommit()
+{
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onCommit()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::doCommit( class LLUICtrl *, void* userdata )
+{
+ LLFloaterNameDesc* self = (LLFloaterNameDesc*) userdata;
+ self->onCommit();
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onBtnOK()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::onBtnOK( void* userdata )
+{
+ LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata;
+
+ fp->childDisable("ok_btn"); // don't allow inadvertent extra uploads
+
+ S32 bitrate = 0;
+
+ if (fp->mIsAudio)
+ {
+ bitrate = fp->childGetValue("bitrate").asInteger();
+ }
+ upload_new_resource(fp->mFilenameAndPath, // file
+ fp->childGetValue("name_form").asString(),
+ fp->childGetValue("description_form").asString(),
+ bitrate, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
+ fp->onClose(false);
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onBtnCancel()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::onBtnCancel( void* userdata )
+{
+ LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata;
+ fp->onClose(false);
+}
diff --git a/indra/newview/llfloaternamedesc.h b/indra/newview/llfloaternamedesc.h
new file mode 100644
index 0000000000..b6f4a489f3
--- /dev/null
+++ b/indra/newview/llfloaternamedesc.h
@@ -0,0 +1,42 @@
+/**
+ * @file llfloaternamedesc.h
+ * @brief LLFloaterNameDesc class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERNAMEDESC_H
+#define LL_LLFLOATERNAMEDESC_H
+
+#include "llfloater.h"
+#include "llresizehandle.h"
+#include "llstring.h"
+
+class LLLineEditor;
+class LLButton;
+class LLRadioGroup;
+
+class LLFloaterNameDesc : public LLFloater
+{
+public:
+ LLFloaterNameDesc(const std::string& filename);
+ virtual ~LLFloaterNameDesc();
+ virtual BOOL postBuild();
+
+ static void doCommit(class LLUICtrl *, void* userdata);
+protected:
+ virtual void onCommit();
+ virtual void centerWindow();
+
+protected:
+ BOOL mIsAudio;
+
+ LLString mFilenameAndPath;
+ LLString mFilename;
+
+ static void onBtnOK(void*);
+ static void onBtnCancel(void*);
+};
+
+#endif // LL_LLFLOATERNAMEDESC_H
diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp
new file mode 100644
index 0000000000..fb5c795ac3
--- /dev/null
+++ b/indra/newview/llfloateropenobject.cpp
@@ -0,0 +1,196 @@
+/**
+ * @file llfloateropenobject.cpp
+ * @brief LLFloaterOpenObject class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shows the contents of an object.
+ * A floater wrapper for llpanelinventory
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateropenobject.h"
+
+#include "llcachename.h"
+#include "llbutton.h"
+#include "lltextbox.h"
+
+#include "llagent.h" // for agent id
+#include "llalertdialog.h"
+#include "llinventoryview.h"
+#include "llinventorymodel.h"
+#include "llpanelinventory.h"
+#include "llselectmgr.h"
+#include "lluiconstants.h"
+#include "llviewerobject.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+
+LLFloaterOpenObject* LLFloaterOpenObject::sInstance = NULL;
+
+LLFloaterOpenObject::LLFloaterOpenObject()
+: LLFloater("object_contents"),
+ mPanelInventory(NULL),
+ mDirty(TRUE)
+{
+ LLCallbackMap::map_t factory_map;
+ factory_map["object_contents"] = LLCallbackMap(createPanelInventory, this);
+ gUICtrlFactory->buildFloater(this,"floater_openobject.xml",&factory_map);
+
+ childSetAction("copy_to_inventory_button", onClickMoveToInventory, this);
+ childSetAction("copy_and_wear_button", onClickMoveAndWear, this);
+ childSetTextArg("object_name", "[DESC]", "Object");
+}
+
+LLFloaterOpenObject::~LLFloaterOpenObject()
+{
+ gSelectMgr->deselectAll();
+ sInstance = NULL;
+}
+
+void LLFloaterOpenObject::refresh()
+{
+ mPanelInventory->refresh();
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node)
+ {
+ std::string name = node->mName;
+ childSetTextArg("object_name", "[DESC]", name);
+ }
+}
+
+void LLFloaterOpenObject::draw()
+{
+ if (mDirty)
+ {
+ refresh();
+ mDirty = FALSE;
+ }
+ LLFloater::draw();
+}
+
+// static
+void LLFloaterOpenObject::dirty()
+{
+ if (sInstance) sInstance->mDirty = TRUE;
+}
+
+// static
+void LLFloaterOpenObject::show()
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("UnableToViewContentsMoreThanOne");
+ return;
+ }
+
+ // Create a new instance only if needed
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterOpenObject();
+ sInstance->center();
+ }
+
+ sInstance->open();
+ sInstance->setFocus(TRUE);
+}
+
+
+// static
+void LLFloaterOpenObject::moveToInventory(bool wear)
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("OnlyCopyContentsOfSingleItem");
+ return;
+ }
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return;
+ LLViewerObject* object = node->getObject();
+ if (!object) return;
+
+ LLUUID object_id = object->getID();
+ std::string name = node->mName;
+
+ // Either create a sub-folder of clothing, or of the root folder.
+ LLUUID parent_category_id;
+ if (wear)
+ {
+ parent_category_id = gInventory.findCategoryUUIDForType(
+ LLAssetType::AT_CLOTHING);
+ }
+ else
+ {
+ parent_category_id = gAgent.getInventoryRootID();
+ }
+ LLUUID category_id = gInventory.createNewCategory(parent_category_id,
+ LLAssetType::AT_NONE,
+ name);
+
+ LLCatAndWear* data = new LLCatAndWear;
+ data->mCatID = category_id;
+ data->mWear = wear;
+
+ // Copy and/or move the items into the newly created folder.
+ // Ignore any "you're going to break this item" messages.
+ BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
+ callbackMoveInventory,
+ (void*)data);
+ if (!success)
+ {
+ delete data;
+ data = NULL;
+
+ gViewerWindow->alertXml("OpenObjectCannotCopy");
+ }
+}
+
+// static
+void LLFloaterOpenObject::callbackMoveInventory(S32 result, void* data)
+{
+ LLCatAndWear* cat = (LLCatAndWear*)data;
+
+ if (result == 0)
+ {
+ LLInventoryView::showAgentInventory();
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if (view)
+ {
+ view->getPanel()->setSelection(cat->mCatID, TAKE_FOCUS_NO);
+ }
+ }
+
+ delete cat;
+}
+
+
+// static
+void LLFloaterOpenObject::onClickMoveToInventory(void* data)
+{
+ LLFloaterOpenObject* self = (LLFloaterOpenObject*)data;
+ moveToInventory(false);
+ self->close();
+}
+
+// static
+void LLFloaterOpenObject::onClickMoveAndWear(void* data)
+{
+ LLFloaterOpenObject* self = (LLFloaterOpenObject*)data;
+ moveToInventory(true);
+ self->close();
+}
+
+//static
+void* LLFloaterOpenObject::createPanelInventory(void* data)
+{
+ LLFloaterOpenObject* floater = (LLFloaterOpenObject*)data;
+ floater->mPanelInventory = new LLPanelInventory("Object Contents", LLRect());
+ return floater->mPanelInventory;
+}
diff --git a/indra/newview/llfloateropenobject.h b/indra/newview/llfloateropenobject.h
new file mode 100644
index 0000000000..8de8832ca9
--- /dev/null
+++ b/indra/newview/llfloateropenobject.h
@@ -0,0 +1,56 @@
+/**
+ * @file llfloateropenobject.h
+ * @brief LLFloaterOpenObject class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shows the contents of an object and their permissions when you
+ * click "Buy..." on an object with "Sell Contents" checked.
+ */
+
+#ifndef LL_LLFLOATEROPENOBJECT_H
+#define LL_LLFLOATEROPENOBJECT_H
+
+#include "llfloater.h"
+
+
+
+class LLPanelInventory;
+
+class LLFloaterOpenObject
+: public LLFloater
+{
+public:
+ static void show();
+ static void dirty();
+
+ struct LLCatAndWear
+ {
+ LLUUID mCatID;
+ bool mWear;
+ };
+
+protected:
+ LLFloaterOpenObject();
+ ~LLFloaterOpenObject();
+
+ void refresh();
+ void draw();
+
+ static void onClickMoveToInventory(void* data);
+ static void onClickMoveAndWear(void* data);
+ static void moveToInventory(bool wear);
+ static void callbackMoveInventory(S32 result, void* data);
+ static void* createPanelInventory(void* data);
+
+protected:
+ static LLFloaterOpenObject* sInstance;
+
+ LLPanelInventory* mPanelInventory;
+ BOOL mDirty;
+};
+
+#endif
diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp
new file mode 100644
index 0000000000..18473c8d98
--- /dev/null
+++ b/indra/newview/llfloaterpostcard.cpp
@@ -0,0 +1,319 @@
+/**
+ * @file llfloaterpostcard.cpp
+ * @brief Postcard send floater, allows setting name, e-mail address, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterpostcard.h"
+
+#include "llfontgl.h"
+#include "llsys.h"
+#include "llgl.h"
+#include "v3dmath.h"
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llui.h"
+#include "lllineeditor.h"
+#include "llviewertexteditor.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llviewernetwork.h"
+#include "llvieweruictrlfactory.h"
+#include "lluploaddialog.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llstatusbar.h"
+#include "llviewerregion.h"
+#include "lleconomy.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llimagejpeg.h"
+#include "llimagej2c.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "viewer.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLLinkedList<LLFloaterPostcard> LLFloaterPostcard::sInstances;
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterPostcard
+///----------------------------------------------------------------------------
+
+LLFloaterPostcard::LLFloaterPostcard(LLImageJPEG* jpeg, LLImageGL *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global)
+: LLFloater("Postcard Floater"),
+ mViewerImage(img),
+ mJPEGImage(jpeg),
+ mImageScale(img_scale),
+ mPosTakenGlobal(pos_taken_global)
+{
+ init();
+}
+
+
+void LLFloaterPostcard::init()
+{
+ // pick up the user's up-to-date email address
+ if(!gAgent.getID().isNull())
+ {
+ // we're logged in, so we can get this info.
+ gMessageSystem->newMessageFast(_PREHASH_UserInfoRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ }
+
+ sInstances.addData(this);
+}
+
+// Destroys the object
+LLFloaterPostcard::~LLFloaterPostcard()
+{
+ sInstances.removeData(this);
+ mJPEGImage = NULL; // deletes image
+}
+
+BOOL LLFloaterPostcard::postBuild()
+{
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("send_btn", onClickSend, this);
+
+ childDisable("from_form");
+ childSetAction("publish_help_btn", onClickPublishHelp, this);
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Disable these buttons if they are PG (Teen) users
+ childDisable("allow_publish_check");
+ childHide("allow_publish_check");
+ childDisable("publish_help_btn");
+ childHide("publish_help_btn");
+ childDisable("mature_check");
+ childHide("mature_check");
+ }
+
+ LLString name_string;
+ gAgent.buildFullname(name_string);
+
+ childSetValue("name_form", LLSD(name_string));
+
+ // XUI:translate
+ LLString msg("Postcard from ");
+ msg += gSecondLife;
+ childSetValue("subject_form", LLSD(msg));
+
+ LLTextEditor *MsgField = LLUICtrlFactory::getTextEditorByName(this, "msg_form");
+ if (MsgField)
+ {
+ MsgField->setText("Check this out!");
+ MsgField->setWordWrap(TRUE);
+ }
+
+ childSetFocus("to_form", TRUE);
+
+ return TRUE;
+}
+
+
+// static
+LLFloaterPostcard* LLFloaterPostcard::showFromSnapshot(LLImageJPEG *jpeg, LLImageGL *img, const LLVector2 &image_scale, const LLVector3d& pos_taken_global)
+{
+ // Take the images from the caller
+ // It's now our job to clean them up
+ LLFloaterPostcard *instance = new LLFloaterPostcard(jpeg, img, image_scale, pos_taken_global);
+
+ gUICtrlFactory->buildFloater(instance, "floater_postcard.xml");
+
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ instance->setOrigin(left, top - instance->getRect().getHeight());
+
+ instance->open();
+
+ return instance;
+}
+
+void LLFloaterPostcard::draw()
+{
+ LLGLSUIDefault gls_ui;
+ LLFloater::draw();
+
+ if(getVisible() && !mMinimized && mViewerImage.notNull() && mJPEGImage.notNull())
+ {
+ LLRect rect(mRect);
+
+ // first set the max extents of our preview
+ rect.translate(-rect.mLeft, -rect.mBottom);
+ rect.mLeft += 280;
+ rect.mRight -= 10;
+ rect.mTop -= 20;
+ rect.mBottom = rect.mTop - 130;
+
+ // then fix the aspect ratio
+ F32 ratio = (F32)mJPEGImage->getWidth() / (F32)mJPEGImage->getHeight();
+ if ((F32)rect.getWidth() / (F32)rect.getHeight() >= ratio)
+ {
+ rect.mRight = (S32)((F32)rect.mLeft + ((F32)rect.getHeight() * ratio));
+ }
+ else
+ {
+ rect.mBottom = (S32)((F32)rect.mTop - ((F32)rect.getWidth() / ratio));
+ }
+ {
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
+ rect.stretch(-1);
+ }
+ {
+
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ {
+ glScalef(mImageScale.mV[VX], mImageScale.mV[VY], 1.f);
+ glMatrixMode(GL_MODELVIEW);
+ gl_draw_scaled_image(rect.mLeft,
+ rect.mBottom,
+ rect.getWidth(),
+ rect.getHeight(),
+ mViewerImage,
+ LLColor4::white);
+ }
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ }
+ }
+}
+
+// static
+void LLFloaterPostcard::onClickCancel(void* data)
+{
+ if (data)
+ {
+ LLFloaterPostcard *self = (LLFloaterPostcard *)data;
+
+ self->onClose(false);
+ }
+}
+
+// static
+void LLFloaterPostcard::onClickSend(void* data)
+{
+ if (data)
+ {
+ LLFloaterPostcard *self = (LLFloaterPostcard *)data;
+
+ LLString from(self->childGetValue("from_form").asString().c_str());
+ LLString to(self->childGetValue("to_form").asString().c_str());
+
+ if (to.empty() || to.find('@') == std::string::npos)
+ {
+ gViewerWindow->alertXml("PromptRecipientEmail");
+ return;
+ }
+
+ if (from.empty() || from.find('@') == std::string::npos)
+ {
+ gViewerWindow->alertXml("PromptSelfEmail");
+ return;
+ }
+
+ if (self->mJPEGImage.notNull())
+ {
+ // upload the image
+ self->mTransactionID.generate();
+ self->mAssetID = self->mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+ LLVFile::writeFile(self->mJPEGImage->getData(), self->mJPEGImage->getDataSize(), gVFS, self->mAssetID, LLAssetType::AT_IMAGE_JPEG);
+
+ gAssetStorage->storeAssetData(self->mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)self, FALSE);
+
+ LLUploadDialog::modalUploadDialog("Uploading...\n\nPostcard");
+
+ // don't destroy the window until the upload is done
+ // this way we keep the information in the form
+ self->setVisible(FALSE);
+
+ // also remove any dependency on another floater
+ // so that we can be sure to outlive it while we
+ // need to.
+ LLFloater* dependee = self->getDependee();
+ if (dependee)
+ dependee->removeDependentFloater(self);
+ }
+ else
+ {
+ gViewerWindow->alertXml("ErrorProcessingSnapshot");
+ }
+ }
+}
+
+// static
+void LLFloaterPostcard::onClickPublishHelp(void* data)
+{
+ gViewerWindow->alertXml("ClickPublishHelpPostcard");
+}
+
+// static
+void LLFloaterPostcard::uploadCallback(const LLUUID& asset_id, void *user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ LLFloaterPostcard *self = (LLFloaterPostcard *)user_data;
+
+ LLUploadDialog::modalUploadFinished();
+
+ if (result)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(result));
+ gViewerWindow->alertXml("ErrorUploadingPostcard", args);
+ }
+ else
+ {
+ // only create the postcard once the upload succeeds
+
+ // request the postcard
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("SendPostcard");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addUUID("AssetID", self->mAssetID);
+ msg->addVector3d("PosGlobal", self->mPosTakenGlobal);
+ msg->addString("To", self->childGetValue("to_form").asString());
+ msg->addString("From", self->childGetValue("from_form").asString());
+ msg->addString("Name", self->childGetValue("name_form").asString());
+ msg->addString("Subject", self->childGetValue("subject_form").asString());
+ msg->addString("Msg", self->childGetValue("msg_form").asString());
+ msg->addBOOL("AllowPublish", self->childGetValue("allow_publish_check").asBoolean());
+ msg->addBOOL("MaturePublish", self->childGetValue("mature_check").asBoolean());
+ gAgent.sendReliableMessage();
+ }
+
+ self->close();
+}
+
+// static
+void LLFloaterPostcard::updateUserInfo(const char *email)
+{
+ LLFloaterPostcard *instance;
+
+ sInstances.resetList();
+ while ((instance = sInstances.getNextData()))
+ {
+ const LLString& text = instance->childGetValue("from_form").asString();
+ if (text.empty())
+ {
+ // there's no text in this field yet, pre-populate
+ instance->childSetValue("from_form", LLSD(email));
+ }
+ }
+}
diff --git a/indra/newview/llfloaterpostcard.h b/indra/newview/llfloaterpostcard.h
new file mode 100644
index 0000000000..542d117403
--- /dev/null
+++ b/indra/newview/llfloaterpostcard.h
@@ -0,0 +1,58 @@
+/**
+ * @file llfloaterpostcard.h
+ * @brief Postcard send floater, allows setting name, e-mail address, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERPOSTCARD_H
+#define LL_LLFLOATERPOSTCARD_H
+
+#include "llfloater.h"
+#include "llcheckboxctrl.h"
+
+#include "llmemory.h"
+#include "llimagegl.h"
+
+class LLTextEditor;
+class LLLineEditor;
+class LLButton;
+
+class LLFloaterPostcard
+: public LLFloater
+{
+public:
+ LLFloaterPostcard(LLImageJPEG* jpeg, LLImageGL *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global);
+ virtual ~LLFloaterPostcard();
+
+ virtual void init();
+ virtual BOOL postBuild();
+ virtual void draw();
+
+ static LLFloaterPostcard* showFromSnapshot(LLImageJPEG *jpeg, LLImageGL *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global);
+
+ static void onClickCancel(void* data);
+ static void onClickSend(void* data);
+ static void onClickPublishHelp(void *data);
+
+ static void uploadCallback(const LLUUID& asset_id,
+ void *user_data,
+ S32 result);
+
+ static void updateUserInfo(const char *email);
+
+protected:
+
+ LLPointer<LLImageJPEG> mJPEGImage;
+ LLPointer<LLImageGL> mViewerImage;
+ LLTransactionID mTransactionID;
+ LLAssetID mAssetID;
+ LLVector2 mImageScale;
+ LLVector3d mPosTakenGlobal;
+
+ static LLLinkedList<LLFloaterPostcard> sInstances;
+};
+
+
+#endif // LL_LLFLOATERPOSTCARD_H
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
new file mode 100644
index 0000000000..ded8f1df4a
--- /dev/null
+++ b/indra/newview/llfloaterpreference.cpp
@@ -0,0 +1,365 @@
+/**
+ * @file llfloaterpreference.cpp
+ * @brief LLPreferenceCore class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * App-wide preferences. Note that these are not per-user,
+ * because we need to load many preferences before we have
+ * a login name.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterpreference.h"
+
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "lldir.h"
+#include "llfocusmgr.h"
+#include "llscrollbar.h"
+#include "llspinctrl.h"
+#include "message.h"
+
+#include "llfloaterabout.h"
+#include "llpanelnetwork.h"
+#include "llpanelaudioprefs.h"
+#include "llpaneldisplay.h"
+#include "llpaneldebug.h"
+#include "llpanelgeneral.h"
+#include "llpanelinput.h"
+#include "llpanelmsgs.h"
+//#include "llpanelweb.h"
+#include "llprefschat.h"
+#include "llprefsim.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llassetstorage.h"
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llviewernetwork.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+//#include "viewer.h"
+#include "llkeyboard.h"
+#include "llscrollcontainer.h"
+
+
+const S32 PREF_BORDER = 4;
+const S32 PREF_PAD = 5;
+const S32 PREF_BUTTON_WIDTH = 70;
+const S32 PREF_CATEGORY_WIDTH = 150;
+
+const S32 PREF_FLOATER_MIN_HEIGHT = 2 * SCROLLBAR_SIZE + 2 * LLPANEL_BORDER_WIDTH + 96;
+
+LLFloaterPreference* LLFloaterPreference::sInstance = NULL;
+
+// Must be done at run time, not compile time. JC
+S32 pref_min_width()
+{
+ return
+ 2 * PREF_BORDER +
+ 2 * PREF_BUTTON_WIDTH +
+ PREF_PAD + RESIZE_HANDLE_WIDTH +
+ PREF_CATEGORY_WIDTH +
+ PREF_PAD;
+}
+
+S32 pref_min_height()
+{
+ return
+ 2 * PREF_BORDER +
+ 3*(BTN_HEIGHT + PREF_PAD) +
+ PREF_FLOATER_MIN_HEIGHT;
+}
+
+
+LLPreferenceCore::LLPreferenceCore(LLTabContainerCommon* tab_container, LLButton * default_btn) :
+ mTabContainer(tab_container),
+ mGeneralPanel(NULL),
+ mInputPanel(NULL),
+ mNetworkPanel(NULL),
+ mDisplayPanel(NULL),
+ mDisplayPanel2(NULL),
+ mAudioPanel(NULL),
+ mMsgPanel(NULL)
+{
+ mGeneralPanel = new LLPanelGeneral();
+ mTabContainer->addTabPanel(mGeneralPanel, mGeneralPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mGeneralPanel->setDefaultBtn(default_btn);
+
+ mInputPanel = new LLPanelInput();
+ mTabContainer->addTabPanel(mInputPanel, mInputPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mInputPanel->setDefaultBtn(default_btn);
+
+ mNetworkPanel = new LLPanelNetwork();
+ mTabContainer->addTabPanel(mNetworkPanel, mNetworkPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mNetworkPanel->setDefaultBtn(default_btn);
+
+ mDisplayPanel = new LLPanelDisplay();
+ mTabContainer->addTabPanel(mDisplayPanel, mDisplayPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mDisplayPanel->setDefaultBtn(default_btn);
+
+ mDisplayPanel3 = new LLPanelDisplay3();
+ mTabContainer->addTabPanel(mDisplayPanel3, mDisplayPanel3->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mDisplayPanel3->setDefaultBtn(default_btn);
+
+ mDisplayPanel2 = new LLPanelDisplay2();
+ mTabContainer->addTabPanel(mDisplayPanel2, mDisplayPanel2->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mDisplayPanel2->setDefaultBtn(default_btn);
+
+ mAudioPanel = new LLPanelAudioPrefs();
+ mTabContainer->addTabPanel(mAudioPanel, mAudioPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mAudioPanel->setDefaultBtn(default_btn);
+
+ mPrefsChat = new LLPrefsChat();
+ mTabContainer->addTabPanel(mPrefsChat->getPanel(), mPrefsChat->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mPrefsChat->getPanel()->setDefaultBtn(default_btn);
+
+ mPrefsIM = new LLPrefsIM();
+ mTabContainer->addTabPanel(mPrefsIM->getPanel(), mPrefsIM->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mPrefsIM->getPanel()->setDefaultBtn(default_btn);
+
+ mMsgPanel = new LLPanelMsgs();
+ gUICtrlFactory->buildPanel(mMsgPanel, "panel_settings_msgbox.xml");
+ mTabContainer->addTabPanel(mMsgPanel, mMsgPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mMsgPanel->setDefaultBtn(default_btn);
+
+ mTabContainer->selectTab(gSavedSettings.getS32("LastPrefTab"));
+
+// Web prefs removed from Loopy build
+// mWebPanel = new LLPanelWeb();
+// gUICtrlFactory->buildPanel(mWebPanel, "panel_settings_web.xml");
+// addTabPanel(mWebPanel, "Web", FALSE, onTabChanged, this);
+}
+
+void LLPreferenceCore::apply()
+{
+ mGeneralPanel->apply();
+ mInputPanel->apply();
+ mNetworkPanel->apply();
+ mDisplayPanel->apply();
+ mDisplayPanel2->apply();
+ mDisplayPanel3->apply();
+ mAudioPanel->apply();
+ mPrefsChat->apply();
+ mPrefsIM->apply();
+ mMsgPanel->apply();
+// mWebPanel->apply();
+}
+
+
+void LLPreferenceCore::cancel()
+{
+ mGeneralPanel->cancel();
+ mInputPanel->cancel();
+ mNetworkPanel->cancel();
+ mDisplayPanel->cancel();
+ mDisplayPanel2->cancel();
+ mDisplayPanel3->cancel();
+ mAudioPanel->cancel();
+ mPrefsChat->cancel();
+ mPrefsIM->cancel();
+ mMsgPanel->cancel();
+// mWebPanel->cancel();
+}
+
+// static
+void LLPreferenceCore::onTabChanged(void* user_data, bool from_click)
+{
+ LLTabContainerCommon* self = (LLTabContainerCommon*)user_data;
+
+ gSavedSettings.setS32("LastPrefTab", self->getCurrentPanelIndex());
+}
+
+
+void LLPreferenceCore::setPersonalInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email)
+{
+ mPrefsIM->setPersonalInfo(visibility, im_via_email, email);
+}
+
+//////////////////////////////////////////////
+// LLFloaterPreference
+
+LLFloaterPreference::LLFloaterPreference()
+{
+ gUICtrlFactory->buildFloater(this, "floater_preferences.xml");
+}
+
+BOOL LLFloaterPreference::postBuild()
+{
+ requires("About...", WIDGET_TYPE_BUTTON);
+ requires("OK", WIDGET_TYPE_BUTTON);
+ requires("Cancel", WIDGET_TYPE_BUTTON);
+ requires("Apply", WIDGET_TYPE_BUTTON);
+ requires("pref core", WIDGET_TYPE_TAB_CONTAINER);
+
+ if (!checkRequirements())
+ {
+ return FALSE;
+ }
+
+ mAboutBtn = LLUICtrlFactory::getButtonByName(this, "About...");
+ mAboutBtn->setClickedCallback(onClickAbout, this);
+
+ mApplyBtn = LLUICtrlFactory::getButtonByName(this, "Apply");
+ mApplyBtn->setClickedCallback(onBtnApply, this);
+
+ mCancelBtn = LLUICtrlFactory::getButtonByName(this, "Cancel");
+ mCancelBtn->setClickedCallback(onBtnCancel, this);
+
+ mOKBtn = LLUICtrlFactory::getButtonByName(this, "OK");
+ mOKBtn->setClickedCallback(onBtnOK, this);
+
+ mPreferenceCore = new LLPreferenceCore(
+ LLUICtrlFactory::getTabContainerByName(this, "pref core"),
+ static_cast<LLButton *>(getChildByName("OK"))
+ );
+
+ sInstance = this;
+
+ return TRUE;
+}
+
+
+LLFloaterPreference::~LLFloaterPreference()
+{
+ sInstance = NULL;
+}
+
+
+void LLFloaterPreference::draw()
+{
+ if( getVisible() )
+ {
+ LLFloater::draw();
+ }
+}
+
+
+void LLFloaterPreference::apply()
+{
+ this->mPreferenceCore->apply();
+}
+
+
+void LLFloaterPreference::cancel()
+{
+ this->mPreferenceCore->cancel();
+}
+
+
+// static
+void LLFloaterPreference::show(void*)
+{
+ if (!sInstance)
+ {
+ new LLFloaterPreference();
+ sInstance->center();
+ }
+
+ sInstance->open();
+
+ if(!gAgent.getID().isNull())
+ {
+ // we're logged in, so we can get this info.
+ gMessageSystem->newMessageFast(_PREHASH_UserInfoRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ }
+}
+
+
+// static
+void LLFloaterPreference::onClickAbout(void*)
+{
+ LLFloaterAbout::show(NULL);
+}
+
+
+// static
+void LLFloaterPreference::onBtnOK( void* userdata )
+{
+ LLFloaterPreference *fp =(LLFloaterPreference *)userdata;
+ // commit any outstanding text entry
+ if (fp->hasFocus())
+ {
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus->acceptsTextInput())
+ {
+ cur_focus->onCommit();
+ }
+ }
+
+ if (fp->canClose())
+ {
+ fp->apply();
+ fp->onClose(false);
+
+ gSavedSettings.saveToFile( gSettingsFileName, TRUE );
+
+ std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+ // save all settings, even if equals defaults
+ gCrashSettings.saveToFile(crash_settings_filename.c_str(), FALSE);
+ }
+ else
+ {
+ // Show beep, pop up dialog, etc.
+ llinfos << "Can't close preferences!" << llendl;
+ }
+}
+
+
+// static
+void LLFloaterPreference::onBtnApply( void* userdata )
+{
+ LLFloaterPreference *fp =(LLFloaterPreference *)userdata;
+ if (fp->hasFocus())
+ {
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus->acceptsTextInput())
+ {
+ cur_focus->onCommit();
+ }
+ }
+ fp->apply();
+}
+
+
+// static
+void LLFloaterPreference::onBtnCancel( void* userdata )
+{
+ LLFloaterPreference *fp =(LLFloaterPreference *)userdata;
+ if (fp->hasFocus())
+ {
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus->acceptsTextInput())
+ {
+ cur_focus->onCommit();
+ }
+ }
+ fp->cancel();
+ fp->close();
+}
+
+
+// static
+void LLFloaterPreference::updateUserInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email)
+{
+ if(sInstance && sInstance->mPreferenceCore)
+ {
+ sInstance->mPreferenceCore->setPersonalInfo(
+ visibility, im_via_email, email);
+ }
+}
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
new file mode 100644
index 0000000000..8c33aba476
--- /dev/null
+++ b/indra/newview/llfloaterpreference.h
@@ -0,0 +1,106 @@
+/**
+ * @file llfloaterpreference.h
+ * @brief LLPreferenceCore class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * App-wide preferences. Note that these are not per-user,
+ * because we need to load many preferences before we have
+ * a login name.
+ */
+
+#ifndef LL_LLFLOATERPREFERENCE_H
+#define LL_LLFLOATERPREFERENCE_H
+
+#include "llfloater.h"
+#include "lltabcontainervertical.h"
+
+class LLPanelGeneral;
+class LLPanelInput;
+class LLPanelDisplay;
+class LLPanelDisplay2;
+class LLPanelDisplay3;
+class LLPanelAudioPrefs;
+class LLPanelDebug;
+class LLPanelNetwork;
+class LLMessageSystem;
+class LLPrefsChat;
+class LLPrefsIM;
+class LLPanelMsgs;
+//class LLPanelWeb; // Web prefs removed from Loopy build
+class LLScrollListCtrl;
+
+class LLPreferenceCore
+{
+
+public:
+ LLPreferenceCore(LLTabContainerCommon* tab_container, LLButton * default_btn);
+
+ void apply();
+ void cancel();
+
+ LLTabContainerCommon* getTabContainer() { return mTabContainer; }
+
+ void setPersonalInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email);
+
+ static void onTabChanged(void* user_data, bool from_click);
+
+private:
+ LLTabContainerCommon *mTabContainer;
+ LLPanelGeneral *mGeneralPanel;
+ LLPanelInput *mInputPanel;
+ LLPanelNetwork *mNetworkPanel;
+ LLPanelDisplay *mDisplayPanel;
+ LLPanelDisplay2 *mDisplayPanel2;
+ LLPanelDisplay3 *mDisplayPanel3;
+ LLPanelAudioPrefs *mAudioPanel;
+// LLPanelDebug *mDebugPanel;
+ LLPrefsChat *mPrefsChat;
+ LLPrefsIM *mPrefsIM;
+ LLPanelMsgs *mMsgPanel;
+// LLPanelWeb* mWebPanel;
+};
+
+// Floater to control preferences (display, audio, bandwidth, general.
+class LLFloaterPreference : public LLFloater
+{
+public:
+ LLFloaterPreference();
+ ~LLFloaterPreference();
+
+ void apply();
+ void cancel();
+ virtual BOOL postBuild();
+ static void show(void*);
+
+ // static data update, called from message handler
+ static void updateUserInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email);
+
+protected:
+ LLPreferenceCore *mPreferenceCore;
+
+ /*virtual*/ void draw();
+
+ LLButton* mAboutBtn;
+ LLButton *mOKBtn;
+ LLButton *mCancelBtn;
+ LLButton *mApplyBtn;
+
+ static void onClickAbout(void*);
+ static void onBtnOK(void*);
+ static void onBtnCancel(void*);
+ static void onBtnApply(void*);
+
+ static LLFloaterPreference* sInstance;
+};
+
+#endif // LL_LLPREFERENCEFLOATER_H
diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp
new file mode 100644
index 0000000000..4311a899c4
--- /dev/null
+++ b/indra/newview/llfloaterproperties.cpp
@@ -0,0 +1,927 @@
+/**
+ * @file llfloaterproperties.cpp
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterproperties.h"
+
+#include <algorithm>
+#include <functional>
+#include "llcachename.h"
+#include "lldbstrings.h"
+#include "llinventory.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroupinfo.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llresmgr.h"
+#include "roles_constants.h"
+#include "llselectmgr.h"
+#include "lltextbox.h"
+#include "lluiconstants.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+
+#include "llvieweruictrlfactory.h"
+
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const char* YOU_CAN = "You can:";
+const char* OWNER_CAN = "Owner can: ";
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPropertiesObserver
+//
+// helper class to watch the inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLPropertiesObserver : public LLInventoryObserver
+{
+public:
+ LLPropertiesObserver() {}
+ virtual ~LLPropertiesObserver() {}
+ virtual void changed(U32 mask);
+};
+
+LLPropertiesObserver* gPropertiesObserver = NULL;
+void LLPropertiesObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
+ {
+ LLFloaterProperties::dirtyAll();
+ }
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterProperties
+///----------------------------------------------------------------------------
+
+LLFloaterProperties::instance_map LLFloaterProperties::sInstances;
+
+// static
+LLFloaterProperties* LLFloaterProperties::find(const LLUUID& item_id,
+ const LLUUID& object_id)
+{
+ // for simplicity's sake, we key the properties window with a
+ // single uuid. However, the items are keyed by item and object
+ // (obj == null -> agent inventory). So, we xor the two ids, and
+ // use that as a lookup key
+ instance_map::iterator it = sInstances.find(item_id ^ object_id);
+ if(it != sInstances.end())
+ {
+ return (*it).second;
+ }
+ return NULL;
+}
+
+// static
+LLFloaterProperties* LLFloaterProperties::show(const LLUUID& item_id,
+ const LLUUID& object_id)
+{
+ LLFloaterProperties* instance = find(item_id, object_id);
+ if(instance)
+ {
+ if (LLFloater::getFloaterHost() && LLFloater::getFloaterHost() != instance->getHost())
+ {
+ // this properties window is being opened in a new context
+ // needs to be rehosted
+ LLFloater::getFloaterHost()->addFloater(instance, TRUE);
+ }
+
+ instance->refresh();
+ instance->open();
+ }
+ return instance;
+}
+
+void LLFloaterProperties::dirtyAll()
+{
+ // ...this is more clear. Possibly more correct, because the
+ // refresh method may delete the object.
+ for(instance_map::iterator it = sInstances.begin(); it!=sInstances.end(); )
+ {
+ (*it++).second->dirty();
+ }
+}
+
+// Default constructor
+LLFloaterProperties::LLFloaterProperties(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_id, const LLUUID& object_id) :
+ LLFloater(name, rect, title),
+ mItemID(item_id),
+ mObjectID(object_id),
+ mDirty(TRUE)
+{
+ gUICtrlFactory->buildFloater(this,"floater_inventory_item_properties.xml");
+
+ // hack to make sure these floaters are observing the inventory.
+ if(!gPropertiesObserver)
+ {
+ gPropertiesObserver = new LLPropertiesObserver;
+ gInventory.addObserver(gPropertiesObserver);
+ }
+ // add the object to the static structure
+ LLUUID key = mItemID ^ mObjectID;
+ sInstances.insert(instance_map::value_type(key, this));
+ // build the UI
+ // item name & description
+ childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidatePrintableNotPipe);
+ childSetCommitCallback("LabelItemName",onCommitName,this);
+ childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidatePrintableNotPipe);
+ // Creator information
+ childSetAction("BtnCreator",onClickCreator,this);
+ // owner information
+ childSetAction("BtnOwner",onClickOwner,this);
+ // acquired date
+ // owner permissions
+ // Permissions debug text
+ // group permissions
+ childSetCommitCallback("CheckShareWithGroup",&onCommitPermissions, this);
+ // everyone permissions
+ childSetCommitCallback("CheckEveryoneCopy",&onCommitPermissions, this);
+ // next owner permissions
+ childSetCommitCallback("CheckNextOwnerModify",&onCommitPermissions, this);
+ childSetCommitCallback("CheckNextOwnerCopy",&onCommitPermissions, this);
+ childSetCommitCallback("CheckNextOwnerTransfer",&onCommitPermissions, this);
+ // Mark for sale or not, and sale info
+ childSetCommitCallback("CheckPurchase",&onCommitSaleInfo, this);
+ childSetCommitCallback("RadioSaleType",&onCommitSaleType, this);
+ // "Price" label for edit
+ childSetCommitCallback("EditPrice",&onCommitSaleInfo, this);
+ // The UI has been built, now fill in all the values
+ refresh();
+}
+
+// Destroys the object
+LLFloaterProperties::~LLFloaterProperties()
+{
+ // clean up the static data.
+ instance_map::iterator it = sInstances.find(mItemID ^ mObjectID);
+ if(it != sInstances.end())
+ {
+ sInstances.erase(it);
+ }
+}
+
+void LLFloaterProperties::refresh()
+{
+ LLInventoryItem* item = findItem();
+ if(item)
+ {
+ refreshFromItem(item);
+ }
+ else
+ {
+ //RN: it is possible that the container object is in the middle of an inventory refresh
+ // causing findItem() to fail, so just temporarily disable everything
+
+ mDirty = TRUE;
+
+ const char* enableNames[]={
+ "LabelItemName",
+ "LabelItemDesc",
+ "LabelCreatorName",
+ "BtnCreator",
+ "LabelOwnerName",
+ "BtnOwner",
+ "CheckOwnerModify",
+ "CheckOwnerCopy",
+ "CheckOwnerTransfer",
+ "CheckShareWithGroup",
+ "CheckEveryoneCopy",
+ "CheckNextOwnerModify",
+ "CheckNextOwnerCopy",
+ "CheckNextOwnerTransfer",
+ "CheckPurchase",
+ "RadioSaleType",
+ "EditPrice"
+ };
+ for(int t=0;t<sizeof(enableNames)/sizeof(char*);t++)
+ {
+ childSetEnabled(enableNames[t],false);
+ }
+ const char* hideNames[]={
+ "BaseMaskDebug",
+ "OwnerMaskDebug",
+ "GroupMaskDebug",
+ "EveryoneMaskDebug",
+ "NextMaskDebug"
+ };
+ for(int t=0;t<sizeof(hideNames)/sizeof(char*);t++)
+ {
+ childSetVisible(hideNames[t],false);
+ }
+ }
+}
+
+void LLFloaterProperties::draw()
+{
+ if (mDirty)
+ {
+ // RN: clear dirty first because refresh can set dirty to TRUE
+ mDirty = FALSE;
+ refresh();
+ }
+
+ LLFloater::draw();
+}
+
+void LLFloaterProperties::refreshFromItem(LLInventoryItem* item)
+{
+ ////////////////////////
+ // PERMISSIONS LOOKUP //
+ ////////////////////////
+
+ // do not enable the UI for incomplete items.
+ LLViewerInventoryItem* i = (LLViewerInventoryItem*)item;
+ BOOL is_complete = i->isComplete();
+
+ const LLPermissions& perm = item->getPermissions();
+ BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm,
+ GP_OBJECT_MANIPULATE);
+ BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm,
+ GP_OBJECT_SET_SALE);
+
+ // You need permission to modify the object to modify an inventory
+ // item in it.
+ LLViewerObject* object = NULL;
+ if(!mObjectID.isNull()) object = gObjectList.findObject(mObjectID);
+ BOOL is_obj_modify = TRUE;
+ if(object)
+ {
+ is_obj_modify = object->permOwnerModify();
+ }
+
+ //////////////////////
+ // ITEM NAME & DESC //
+ //////////////////////
+ BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm,
+ GP_OBJECT_MANIPULATE)
+ && is_obj_modify && is_complete;
+
+ childSetEnabled("LabelItemNameTitle",TRUE);
+ childSetEnabled("LabelItemName",is_modifiable);
+ const char EMPTY_STRING[1] = "";
+ const char* txt = EMPTY_STRING;
+ if(!item->getName().empty())
+ {
+ txt = item->getName().c_str();
+ }
+ childSetText("LabelItemName",txt);
+ childSetEnabled("LabelItemDescTitle",TRUE);
+ childSetEnabled("LabelItemDesc",is_modifiable);
+ childSetVisible("IconLocked",!is_modifiable);
+ txt = EMPTY_STRING;
+ if(!item->getDescription().empty())
+ {
+ txt = item->getDescription().c_str();
+ }
+ childSetText("LabelItemDesc",txt);
+
+ //////////////////
+ // CREATOR NAME //
+ //////////////////
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_LAST_NAME_BUF_SIZE];
+ if(!gCacheName) return;
+ if(!gAgent.getRegion()) return;
+
+ if (item->getCreatorUUID().notNull())
+ {
+ gCacheName->getName(item->getCreatorUUID(), first_name, last_name);
+ LLString name(first_name);
+ name.append(1, ' ');
+ name.append(last_name);
+
+ childSetEnabled("BtnCreator",TRUE);
+ childSetEnabled("LabelCreatorTitle",TRUE);
+ childSetEnabled("LabelCreatorName",TRUE);
+ childSetText("LabelCreatorName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnCreator",FALSE);
+ childSetEnabled("LabelCreatorTitle",FALSE);
+ childSetEnabled("LabelCreatorName",FALSE);
+ childSetText("LabelCreatorName","(unknown)"); // XUI:translate
+ }
+
+ ////////////////
+ // OWNER NAME //
+ ////////////////
+ if(perm.isOwned())
+ {
+ LLString name;
+ if (perm.isGroupOwned())
+ {
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ gCacheName->getGroupName(perm.getGroup(), group_name);
+ name.assign(group_name);
+ }
+ else
+ {
+ gCacheName->getName(perm.getOwner(), first_name, last_name);
+ name.assign(first_name);
+ name.append(1, ' ');
+ name.append(last_name);
+ }
+ childSetEnabled("BtnOwner",TRUE);
+ childSetEnabled("LabelOwnerTitle",TRUE);
+ childSetEnabled("LabelOwnerName",TRUE);
+ childSetText("LabelOwnerName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnOwner",FALSE);
+ childSetEnabled("LabelOwnerTitle",FALSE);
+ childSetEnabled("LabelOwnerName",FALSE);
+ childSetText("LabelOwnerName","(public)"); // XUI:translate
+ }
+
+ //////////////////
+ // ACQUIRE DATE //
+ //////////////////
+ time_t time_utc = (time_t)item->getCreationDate();
+ if (0 == time_utc)
+ {
+ childSetText("LabelAcquiredDate","(unknown)");
+ }
+ else
+ {
+ childSetText("LabelAcquiredDate", ctime(&time_utc) );
+ }
+
+ ///////////////////////
+ // OWNER PERMISSIONS //
+ ///////////////////////
+ if(can_agent_manipulate)
+ {
+ childSetText("OwnerLabel",YOU_CAN);
+ }
+ else
+ {
+ childSetText("OwnerLabel",OWNER_CAN);
+ }
+
+ U32 base_mask = perm.getMaskBase();
+ U32 owner_mask = perm.getMaskOwner();
+ U32 group_mask = perm.getMaskGroup();
+ U32 everyone_mask = perm.getMaskEveryone();
+ U32 next_owner_mask = perm.getMaskNextOwner();
+
+ childSetEnabled("OwnerLabel",TRUE);
+ childSetEnabled("CheckOwnerModify",FALSE);
+ childSetValue("CheckOwnerModify",LLSD((BOOL)(owner_mask & PERM_MODIFY)));
+ childSetEnabled("CheckOwnerCopy",FALSE);
+ childSetValue("CheckOwnerCopy",LLSD((BOOL)(owner_mask & PERM_COPY)));
+ childSetEnabled("CheckOwnerTransfer",FALSE);
+ childSetValue("CheckOwnerTransfer",LLSD((BOOL)(owner_mask & PERM_TRANSFER)));
+
+ ///////////////////////
+ // DEBUG PERMISSIONS //
+ ///////////////////////
+
+ if( gSavedSettings.getBOOL("DebugPermissions") )
+ {
+ BOOL slam_perm = FALSE;
+ BOOL overwrite_group = FALSE;
+ BOOL overwrite_everyone = FALSE;
+
+ if (item->getType() == LLAssetType::AT_OBJECT)
+ {
+ U32 flags = item->getFlags();
+ slam_perm = flags & LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
+ overwrite_everyone = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ overwrite_group = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ }
+
+ char perm_string[11];
+
+ sprintf(perm_string, "B: ");
+ mask_to_string(base_mask, perm_string+3);
+ childSetText("BaseMaskDebug",perm_string);
+ childSetVisible("BaseMaskDebug",TRUE);
+
+ sprintf(perm_string, "O: ");
+ mask_to_string(owner_mask, perm_string+3);
+ childSetText("OwnerMaskDebug",perm_string);
+ childSetVisible("OwnerMaskDebug",TRUE);
+
+ sprintf(perm_string, "G%s: ", overwrite_group ? "*" : "");
+ mask_to_string(group_mask, perm_string + (overwrite_group ? 4 : 3));
+ childSetText("GroupMaskDebug",perm_string);
+ childSetVisible("GroupMaskDebug",TRUE);
+
+ sprintf(perm_string, "E%s: ", overwrite_everyone ? "*" : "");
+ mask_to_string(everyone_mask, perm_string + (overwrite_everyone ? 4 : 3));
+ childSetText("EveryoneMaskDebug",perm_string);
+ childSetVisible("EveryoneMaskDebug",TRUE);
+
+ sprintf(perm_string, "N%s: ", slam_perm ? "*" : "");
+ mask_to_string(next_owner_mask, perm_string + (slam_perm ? 4 : 3));
+ childSetText("NextMaskDebug",perm_string);
+ childSetVisible("NextMaskDebug",TRUE);
+ }
+ else
+ {
+ childSetVisible("BaseMaskDebug",FALSE);
+ childSetVisible("OwnerMaskDebug",FALSE);
+ childSetVisible("GroupMaskDebug",FALSE);
+ childSetVisible("EveryoneMaskDebug",FALSE);
+ childSetVisible("NextMaskDebug",FALSE);
+ }
+
+ /////////////
+ // SHARING //
+ /////////////
+
+ // Check for ability to change values.
+ if (is_obj_modify && can_agent_manipulate)
+ {
+ childSetEnabled("CheckShareWithGroup",TRUE);
+ childSetEnabled("CheckEveryoneCopy",(owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER));
+ }
+ else
+ {
+ childSetEnabled("CheckShareWithGroup",FALSE);
+ childSetEnabled("CheckEveryoneCopy",FALSE);
+ }
+
+ // Set values.
+ BOOL is_group_copy = (group_mask & PERM_COPY) ? TRUE : FALSE;
+ BOOL is_group_modify = (group_mask & PERM_MODIFY) ? TRUE : FALSE;
+ BOOL is_group_move = (group_mask & PERM_MOVE) ? TRUE : FALSE;
+
+ if (is_group_copy && is_group_modify && is_group_move)
+ {
+ childSetValue("CheckShareWithGroup",LLSD((BOOL)TRUE));
+
+ LLCheckBoxCtrl* ctl = LLUICtrlFactory::getCheckBoxByName(this,"CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(FALSE);
+ }
+ }
+ else if (!is_group_copy && !is_group_modify && !is_group_move)
+ {
+ childSetValue("CheckShareWithGroup",LLSD((BOOL)FALSE));
+ LLCheckBoxCtrl* ctl = LLUICtrlFactory::getCheckBoxByName(this,"CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(FALSE);
+ }
+ }
+ else
+ {
+ LLCheckBoxCtrl* ctl = LLUICtrlFactory::getCheckBoxByName(this,"CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(TRUE);
+ ctl->set(TRUE);
+ }
+ }
+
+ childSetValue("CheckEveryoneCopy",LLSD((BOOL)(everyone_mask & PERM_COPY)));
+
+ ///////////////
+ // SALE INFO //
+ ///////////////
+
+ const LLSaleInfo& sale_info = item->getSaleInfo();
+ BOOL is_for_sale = sale_info.isForSale();
+ // Check for ability to change values.
+ if (is_obj_modify && can_agent_sell
+ && gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE))
+ {
+ childSetEnabled("SaleLabel",is_complete);
+ childSetEnabled("CheckPurchase",is_complete);
+
+ childSetEnabled("NextOwnerLabel",TRUE);
+ childSetEnabled("CheckNextOwnerModify",base_mask & PERM_MODIFY);
+ childSetEnabled("CheckNextOwnerCopy",base_mask & PERM_COPY);
+ childSetEnabled("CheckNextOwnerTransfer",next_owner_mask & PERM_COPY);
+
+ childSetEnabled("RadioSaleType",is_complete && is_for_sale);
+ childSetEnabled("TextPrice",is_complete && is_for_sale);
+ childSetEnabled("EditPrice",is_complete && is_for_sale);
+ }
+ else
+ {
+ childSetEnabled("SaleLabel",FALSE);
+ childSetEnabled("CheckPurchase",FALSE);
+
+ childSetEnabled("NextOwnerLabel",FALSE);
+ childSetEnabled("CheckNextOwnerModify",FALSE);
+ childSetEnabled("CheckNextOwnerCopy",FALSE);
+ childSetEnabled("CheckNextOwnerTransfer",FALSE);
+
+ childSetEnabled("RadioSaleType",FALSE);
+ childSetEnabled("TextPrice",FALSE);
+ childSetEnabled("EditPrice",FALSE);
+ }
+
+ // Set values.
+ childSetValue("CheckPurchase", is_for_sale);
+ childSetValue("CheckNextOwnerModify",LLSD(BOOL(next_owner_mask & PERM_MODIFY)));
+ childSetValue("CheckNextOwnerCopy",LLSD(BOOL(next_owner_mask & PERM_COPY)));
+ childSetValue("CheckNextOwnerTransfer",LLSD(BOOL(next_owner_mask & PERM_TRANSFER)));
+
+ LLRadioGroup* radioSaleType = LLUICtrlFactory::getRadioGroupByName(this,"RadioSaleType");
+ if (is_for_sale)
+ {
+ radioSaleType->setSelectedIndex((S32)sale_info.getSaleType() - 1);
+ char numerical_price[MAX_STRING];
+ sprintf(numerical_price, "%d", sale_info.getSalePrice());
+ childSetText("EditPrice",numerical_price);
+ }
+ else
+ {
+ radioSaleType->setSelectedIndex(-1);
+ childSetText("EditPrice","");
+ }
+}
+
+// static
+void LLFloaterProperties::onClickCreator(void* data)
+{
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLInventoryItem* item = self->findItem();
+ if(!item) return;
+ if(!item->getCreatorUUID().isNull())
+ {
+ LLFloaterAvatarInfo::showFromObject(item->getCreatorUUID());
+ }
+}
+
+// static
+void LLFloaterProperties::onClickOwner(void* data)
+{
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLInventoryItem* item = self->findItem();
+ if(!item) return;
+ if(item->getPermissions().isGroupOwned())
+ {
+ LLFloaterGroupInfo::showFromUUID(item->getPermissions().getGroup());
+ }
+ else
+ {
+ if(!item->getPermissions().getOwner().isNull())
+ {
+ LLFloaterAvatarInfo::showFromObject(item->getPermissions().getOwner());
+ }
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitName(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitName()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self)
+ {
+ return;
+ }
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->findItem();
+ if(!item)
+ {
+ return;
+ }
+ LLLineEditor* labelItemName = LLUICtrlFactory::getLineEditorByName(self,"LabelItemName");
+
+ if(labelItemName&&
+ (item->getName() != labelItemName->getText()) &&
+ (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) )
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(labelItemName->getText());
+ if(self->mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(self->mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitDescription(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitDescription()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->findItem();
+ if(!item) return;
+
+ LLLineEditor* labelItemDesc = LLUICtrlFactory::getLineEditorByName(self,"LabelItemDesc");
+ if(!labelItemDesc)
+ {
+ return;
+ }
+ if((item->getDescription() != labelItemDesc->getText()) &&
+ (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+
+ new_item->setDescription(labelItemDesc->getText());
+ if(self->mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(self->mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitPermissions(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitPermissions()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->findItem();
+ if(!item) return;
+ LLPermissions perm(item->getPermissions());
+
+
+ LLCheckBoxCtrl* CheckShareWithGroup = LLUICtrlFactory::getCheckBoxByName(self,"CheckShareWithGroup");
+
+ if(CheckShareWithGroup)
+ {
+ perm.setGroupBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckShareWithGroup->get(),
+ PERM_MODIFY | PERM_MOVE | PERM_COPY);
+ }
+ LLCheckBoxCtrl* CheckEveryoneCopy = LLUICtrlFactory::getCheckBoxByName(self,"CheckEveryoneCopy");
+ if(CheckEveryoneCopy)
+ {
+ perm.setEveryoneBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckEveryoneCopy->get(), PERM_COPY);
+ }
+
+ LLCheckBoxCtrl* CheckNextOwnerModify = LLUICtrlFactory::getCheckBoxByName(self,"CheckNextOwnerModify");
+ if(CheckNextOwnerModify)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerModify->get(), PERM_MODIFY);
+ }
+ LLCheckBoxCtrl* CheckNextOwnerCopy = LLUICtrlFactory::getCheckBoxByName(self,"CheckNextOwnerCopy");
+ if(CheckNextOwnerCopy)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerCopy->get(), PERM_COPY);
+ }
+ LLCheckBoxCtrl* CheckNextOwnerTransfer = LLUICtrlFactory::getCheckBoxByName(self,"CheckNextOwnerTransfer");
+ if(CheckNextOwnerTransfer)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerTransfer->get(), PERM_TRANSFER);
+ }
+ if(perm != item->getPermissions()
+ && item->isComplete())
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setPermissions(perm);
+ U32 flags = new_item->getFlags();
+ // If next owner permissions have changed (and this is an object)
+ // then set the slam permissions flag so that they are applied on rez.
+ if((perm.getMaskNextOwner()!=item->getPermissions().getMaskNextOwner())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
+ }
+ // If everyone permissions have changed (and this is an object)
+ // then set the overwrite everyone permissions flag so they
+ // are applied on rez.
+ if ((perm.getMaskEveryone()!=item->getPermissions().getMaskEveryone())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ }
+ // If group permissions have changed (and this is an object)
+ // then set the overwrite group permissions flag so they
+ // are applied on rez.
+ if ((perm.getMaskGroup()!=item->getPermissions().getMaskGroup())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ }
+ new_item->setFlags(flags);
+ if(self->mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(self->mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+ else
+ {
+ // need to make sure we don't just follow the click
+ self->refresh();
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitSaleInfo(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitSaleInfo()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ self->updateSaleInfo();
+}
+
+// static
+void LLFloaterProperties::onCommitSaleType(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitSaleType()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ self->updateSaleInfo();
+}
+
+void LLFloaterProperties::updateSaleInfo()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if(!item) return;
+ LLSaleInfo sale_info(item->getSaleInfo());
+ if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE))
+ {
+ childSetValue("CheckPurchase",LLSD((BOOL)FALSE));
+ }
+
+ if((BOOL)childGetValue("CheckPurchase"))
+ {
+ // turn on sale info
+ LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY;
+
+ LLRadioGroup* RadioSaleType = LLUICtrlFactory::getRadioGroupByName(this,"RadioSaleType");
+ if(RadioSaleType)
+ {
+ switch (RadioSaleType->getSelectedIndex())
+ {
+ case 0:
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ break;
+ case 1:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ case 2:
+ sale_type = LLSaleInfo::FS_CONTENTS;
+ break;
+ default:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ }
+ }
+
+ if (sale_type == LLSaleInfo::FS_COPY
+ && !gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_SET_SALE))
+ {
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ }
+
+ LLLineEditor* EditPrice = LLUICtrlFactory::getLineEditorByName(this,"EditPrice");
+
+ S32 price = -1;
+ if(EditPrice)
+ {
+ price = atoi(EditPrice->getText().c_str());
+ }
+ // Invalid data - turn off the sale
+ if (price < 0)
+ {
+ sale_type = LLSaleInfo::FS_NOT;
+ price = 0;
+ }
+
+ sale_info.setSaleType(sale_type);
+ sale_info.setSalePrice(price);
+ }
+ else
+ {
+ sale_info.setSaleType(LLSaleInfo::FS_NOT);
+ }
+ if(sale_info != item->getSaleInfo()
+ && item->isComplete())
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setSaleInfo(sale_info);
+ if(mObjectID.isNull())
+ {
+ // This is in the agent's inventory.
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ // This is in an object's contents.
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+ else
+ {
+ // need to make sure we don't just follow the click
+ refresh();
+ }
+}
+
+LLInventoryItem* LLFloaterProperties::findItem() const
+{
+ LLInventoryItem* item = NULL;
+ if(mObjectID.isNull())
+ {
+ // it is in agent inventory
+ item = gInventory.getItem(mItemID);
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ item = (LLInventoryItem*)object->getInventoryObject(mItemID);
+ }
+ }
+ return item;
+}
+
+void LLFloaterProperties::closeByID(const LLUUID& item_id, const LLUUID &object_id)
+{
+ LLFloaterProperties* floaterp = find(item_id, object_id);
+
+ if (floaterp)
+ {
+ floaterp->close();
+ }
+}
+
+///----------------------------------------------------------------------------
+/// LLMultiProperties
+///----------------------------------------------------------------------------
+
+LLMultiProperties::LLMultiProperties(const LLRect &rect) : LLMultiFloater("Properties", rect)
+{
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/llfloaterproperties.h b/indra/newview/llfloaterproperties.h
new file mode 100644
index 0000000000..bd3c8b32c7
--- /dev/null
+++ b/indra/newview/llfloaterproperties.h
@@ -0,0 +1,82 @@
+/**
+ * @file llfloaterproperties.h
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERPROPERTIES_H
+#define LL_LLFLOATERPROPERTIES_H
+
+#include <map>
+#include "llfloater.h"
+#include "lliconctrl.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterProperties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLInventoryItem;
+class LLLineEditor;
+class LLRadioGroup;
+class LLTextBox;
+
+class LLFloaterProperties : public LLFloater
+{
+public:
+ static LLFloaterProperties* find(const LLUUID& item_id,
+ const LLUUID& object_id);
+ static LLFloaterProperties* show(const LLUUID& item_id,
+ const LLUUID& object_id);
+ static void dirtyAll();
+
+ static void closeByID(const LLUUID& item_id, const LLUUID& object_id);
+
+ LLFloaterProperties(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_id, const LLUUID& object_id);
+ virtual ~LLFloaterProperties();
+
+ // do everything necessary
+ void dirty() { mDirty = TRUE; }
+ void refresh();
+
+protected:
+ // ui callbacks
+ static void onClickCreator(void* data);
+ static void onClickOwner(void* data);
+ static void onCommitName(LLUICtrl* ctrl, void* data);
+ static void onCommitDescription(LLUICtrl* ctrl, void* data);
+ static void onCommitPermissions(LLUICtrl* ctrl, void* data);
+ static void onCommitSaleInfo(LLUICtrl* ctrl, void* data);
+ static void onCommitSaleType(LLUICtrl* ctrl, void* data);
+ void updateSaleInfo();
+
+ LLInventoryItem* findItem() const;
+
+ void refreshFromItem(LLInventoryItem* item);
+ virtual void draw();
+
+protected:
+ // The item id of the inventory item in question.
+ LLUUID mItemID;
+
+ // mObjectID will have a value if it is associated with a task in
+ // the world, and will be == LLUUID::null if it's in the agent
+ // inventory.
+ LLUUID mObjectID;
+
+ BOOL mDirty;
+
+ typedef std::map<LLUUID, LLFloaterProperties*, lluuid_less> instance_map;
+ static instance_map sInstances;
+};
+
+class LLMultiProperties : public LLMultiFloater
+{
+public:
+ LLMultiProperties(const LLRect& rect);
+};
+
+#endif // LL_LLFLOATERPROPERTIES_H
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
new file mode 100644
index 0000000000..4bd3a4ee49
--- /dev/null
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -0,0 +1,2947 @@
+/**
+ * @file llfloaterregioninfo.cpp
+ * @author Aaron Brashears
+ * @brief Implementation of the region info and controls floater and panels.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterregioninfo.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "llcachename.h"
+#include "lldir.h"
+#include "lldispatcher.h"
+#include "llglheaders.h"
+#include "llregionflags.h"
+#include "llstl.h"
+#include "indra_constants.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfilepicker.h"
+#include "llfloatergodtools.h" // for send_sim_wide_deletes()
+#include "llfloatergroups.h"
+#include "llfloatertelehub.h"
+#include "lllineeditor.h"
+#include "llalertdialog.h"
+#include "llnamelistctrl.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "llinventory.h"
+#include "lltexturectrl.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llvlcomposition.h"
+
+const S32 TERRAIN_TEXTURE_COUNT = 4;
+const S32 CORNER_COUNT = 4;
+
+#define LL_ENABLE_MAINLAND_VISIBLE_CONTROL 0
+
+///----------------------------------------------------------------------------
+/// Local class declaration
+///----------------------------------------------------------------------------
+
+/*
+class LLDispatchSetEstateOwner : public LLDispatchHandler
+{
+public:
+ LLDispatchSetEstateOwner() {}
+ virtual ~LLDispatchSetEstateOwner() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const sparam_t& strings,
+ const iparam_t& integers);
+};
+*/
+
+class LLDispatchEstateUpdateInfo : public LLDispatchHandler
+{
+public:
+ LLDispatchEstateUpdateInfo() {}
+ virtual ~LLDispatchEstateUpdateInfo() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings);
+};
+
+class LLDispatchSetEstateAccess : public LLDispatchHandler
+{
+public:
+ LLDispatchSetEstateAccess() {}
+ virtual ~LLDispatchSetEstateAccess() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings);
+};
+
+
+/*
+void unpack_request_params(
+ LLMessageSystem* msg,
+ LLDispatcher::sparam_t& strings,
+ LLDispatcher::iparam_t& integers)
+{
+ char str_buf[MAX_STRING];
+ S32 str_count = msg->getNumberOfBlocksFast(_PREHASH_StringData);
+ S32 i;
+ for (i = 0; i < str_count; ++i)
+ {
+ // we treat the SParam as binary data (since it might be an
+ // LLUUID in compressed form which may have embedded \0's,)
+ str_buf[0] = '\0';
+ S32 data_size = msg->getSizeFast(_PREHASH_StringData, i, _PREHASH_SParam);
+ msg->getBinaryDataFast(_PREHASH_StringData, _PREHASH_SParam,
+ str_buf, data_size, i, MAX_STRING - 1);
+ strings.push_back(std::string(str_buf, data_size));
+ }
+
+ U32 int_buf;
+ S32 int_count = msg->getNumberOfBlocksFast(_PREHASH_IntegerData);
+ for (i = 0; i < int_count; ++i)
+ {
+ msg->getU32("IntegerData", "IParam", int_buf, i);
+ integers.push_back(int_buf);
+ }
+}
+*/
+
+
+
+bool estate_dispatch_initialized = false;
+
+
+///----------------------------------------------------------------------------
+/// LLFloaterRegionInfo
+///----------------------------------------------------------------------------
+
+LLFloaterRegionInfo* LLFloaterRegionInfo::sInstance = NULL;
+//S32 LLFloaterRegionInfo::sRequestSerial = 0;
+LLUUID LLFloaterRegionInfo::sRequestInvoice;
+
+LLFloaterRegionInfo::LLFloaterRegionInfo(const LLRect& rect) :
+ LLFloater("regioninfo", rect, "Region/Estate")
+{
+ LLRect tr(0, rect.getHeight() - LLFLOATER_HEADER_SIZE, rect.getWidth(), 0);
+ mTab = new LLTabContainer("tab", tr, LLTabContainer::TOP, NULL,NULL,"");
+ mTab->setBorderVisible(FALSE);
+ addChild(mTab);
+
+ // contruct the panels
+ LLPanel* panel;
+ panel = new LLPanelRegionGeneralInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_general.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), TRUE);
+
+ panel = new LLPanelRegionDebugInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_debug.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelRegionTextureInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_texture.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelRegionTerrainInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_terrain.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelEstateInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_estate.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelEstateCovenant;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_covenant.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+}
+
+LLFloaterRegionInfo::~LLFloaterRegionInfo()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterRegionInfo::show(LLViewerRegion* region)
+{
+ if (!sInstance)
+ {
+ LLRect rect = gSavedSettings.getRect("FloaterRegionInfo");
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ rect.translate(left,top);
+ sInstance = new LLFloaterRegionInfo(rect);
+ gMessageSystem->setHandlerFunc(
+ "EstateOwnerMessage",
+ &processEstateOwnerRequest);
+ }
+ sInstance->open();
+ sInstance->refreshFromRegion(region);
+
+ // Must allow anyone to request the RegionInfo data
+ // so non-owners/non-gods can see the values.
+ // Therefore can't use an EstateOwnerMessage JC
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("RequestRegionInfo");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+}
+
+// static
+void LLFloaterRegionInfo::show(void*)
+{
+ show(gAgent.getRegion());
+}
+
+// static
+LLFloaterRegionInfo* LLFloaterRegionInfo::getInstance()
+{
+ return sInstance;
+}
+
+// static
+void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**)
+{
+ static LLDispatcher dispatch;
+ if(!sInstance) return;
+
+ if (!estate_dispatch_initialized)
+ {
+ LLPanelEstateInfo::initDispatch(dispatch);
+ }
+
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "tab");
+ if (!tab) return;
+
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab, "Estate");
+ if (!panel) return;
+
+ // unpack the message
+ std::string request;
+ LLUUID invoice;
+ LLDispatcher::sparam_t strings;
+ LLDispatcher::unpackMessage(msg, request, invoice, strings);
+ if(invoice != getLastInvoice())
+ {
+ llwarns << "Mismatched Estate message: " << request.c_str() << llendl;
+ return;
+ }
+
+ //dispatch the message
+ dispatch.dispatch(request, invoice, strings);
+}
+
+
+// static
+void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg)
+{
+ LLPanel* panel;
+
+ llinfos << "LLFloaterRegionInfo::processRegionInfo" << llendl;
+ if(!sInstance) return;
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "tab");
+ if(!tab) return;
+
+ // extract message
+ char sim_name[MAX_STRING];
+ U32 region_flags;
+ U8 agent_limit;
+ F32 object_bonus_factor;
+ U8 sim_access;
+ F32 water_height;
+ F32 terrain_raise_limit;
+ F32 terrain_lower_limit;
+ BOOL use_estate_sun;
+ F32 sun_hour;
+ msg->getString("RegionInfo", "SimName", MAX_STRING, sim_name);
+ msg->getU32("RegionInfo", "RegionFlags", region_flags);
+ msg->getU8("RegionInfo", "MaxAgents", agent_limit);
+ msg->getF32("RegionInfo", "ObjectBonusFactor", object_bonus_factor);
+ msg->getU8("RegionInfo", "SimAccess", sim_access);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_WaterHeight, water_height);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainRaiseLimit, terrain_raise_limit);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainLowerLimit, terrain_lower_limit);
+ msg->getBOOL("RegionInfo", "UseEstateSun", use_estate_sun);
+ // actually the "last set" sun hour, not the current sun hour. JC
+ msg->getF32("RegionInfo", "SunHour", sun_hour);
+
+ // GENERAL PANEL
+ panel = LLUICtrlFactory::getPanelByName(tab, "General");
+ if(!panel) return;
+ panel->childSetValue("region_text", LLSD(sim_name));
+
+ panel->childSetValue("block_terraform_check", (region_flags & REGION_FLAGS_BLOCK_TERRAFORM) ? TRUE : FALSE );
+ panel->childSetValue("block_fly_check", (region_flags & REGION_FLAGS_BLOCK_FLY) ? TRUE : FALSE );
+ panel->childSetValue("allow_damage_check", (region_flags & REGION_FLAGS_ALLOW_DAMAGE) ? TRUE : FALSE );
+ panel->childSetValue("restrict_pushobject", (region_flags & REGION_FLAGS_RESTRICT_PUSHOBJECT) ? TRUE : FALSE );
+ panel->childSetValue("allow_land_resell_check", (region_flags & REGION_FLAGS_BLOCK_LAND_RESELL) ? FALSE : TRUE );
+ panel->childSetValue("allow_parcel_changes_check", (region_flags & REGION_FLAGS_ALLOW_PARCEL_CHANGES) ? TRUE : FALSE );
+
+ panel->childSetValue("agent_limit_spin", LLSD((F32)agent_limit) );
+ panel->childSetValue("object_bonus_spin", LLSD(object_bonus_factor) );
+
+ panel->childSetValue("access_combo", LLSD(LLViewerRegion::accessToString(sim_access)) );
+
+ // DEBUG PANEL
+ panel = LLUICtrlFactory::getPanelByName(tab, "Debug");
+ if(!panel) return;
+
+ panel->childSetValue("region_text", LLSD(sim_name) );
+ panel->childSetValue("disable_scripts_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SKIP_SCRIPTS)) );
+ panel->childSetValue("disable_collisions_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SKIP_COLLISIONS)) );
+ panel->childSetValue("disable_physics_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SKIP_PHYSICS)) );
+
+ // TERRAIN PANEL
+ panel = LLUICtrlFactory::getPanelByName(tab, "Terrain");
+ if(!panel) return;
+
+ panel->childSetValue("region_text", LLSD(sim_name));
+ panel->childSetValue("water_height_spin", LLSD(water_height));
+ panel->childSetValue("terrain_raise_spin", LLSD(terrain_raise_limit));
+ panel->childSetValue("terrain_lower_spin", LLSD(terrain_lower_limit));
+ panel->childSetValue("use_estate_sun_check", LLSD(use_estate_sun));
+
+ LLViewerRegion* region = gAgent.getRegion();
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ panel->childSetValue("fixed_sun_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SUN_FIXED)));
+ panel->childSetEnabled("fixed_sun_check", allow_modify && !use_estate_sun);
+ panel->childSetValue("sun_hour_slider", LLSD(sun_hour));
+ panel->childSetEnabled("sun_hour_slider", allow_modify && !use_estate_sun);
+}
+
+// static
+LLPanelEstateInfo* LLFloaterRegionInfo::getPanelEstate()
+{
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return NULL;
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab");
+ if (!tab) return NULL;
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab,"Estate");
+ return panel;
+}
+
+// static
+LLPanelEstateCovenant* LLFloaterRegionInfo::getPanelCovenant()
+{
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return NULL;
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab");
+ if (!tab) return NULL;
+ LLPanelEstateCovenant* panel = (LLPanelEstateCovenant*)LLUICtrlFactory::getPanelByName(tab, "Covenant");
+ return panel;
+}
+
+void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ // call refresh from region on all panels
+ std::for_each(
+ mInfoPanels.begin(),
+ mInfoPanels.end(),
+ llbind2nd(
+#if LL_WINDOWS
+ std::mem_fun1(&LLPanelRegionInfo::refreshFromRegion),
+#else
+ std::mem_fun(&LLPanelRegionInfo::refreshFromRegion),
+#endif
+ region));
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Local class implementation
+///----------------------------------------------------------------------------
+
+//
+// LLPanelRegionInfo
+//
+
+// static
+void LLPanelRegionInfo::onBtnSet(void* user_data)
+{
+ LLPanelRegionInfo* panel = (LLPanelRegionInfo*)user_data;
+ if(!panel) return;
+ if (panel->sendUpdate())
+ {
+ panel->disableButton("apply_btn");
+ }
+}
+
+//static
+void LLPanelRegionInfo::onChangeChildCtrl(LLUICtrl* ctrl, void* user_data)
+{
+ if (ctrl)
+ {
+ LLPanelRegionInfo* panel = (LLPanelRegionInfo*) ctrl->getParent();
+ panel->updateChild(ctrl);
+ }
+}
+
+// static
+// Enables the "set" button if it is not already enabled
+void LLPanelRegionInfo::onChangeAnything(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelRegionInfo* panel = (LLPanelRegionInfo*)user_data;
+ if(panel)
+ {
+ panel->enableButton("apply_btn");
+ }
+}
+
+// virtual
+BOOL LLPanelRegionInfo::postBuild()
+{
+ childSetAction("apply_btn", onBtnSet, this);
+ childDisable("apply_btn");
+ return TRUE;
+}
+
+// virtual
+void LLPanelRegionInfo::updateChild(LLUICtrl* child_ctr)
+{
+}
+
+// virtual
+bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ if (region) mHost = region->getHost();
+ return true;
+}
+
+void LLPanelRegionInfo::sendEstateOwnerMessage(
+ LLMessageSystem* msg,
+ const char* request,
+ const LLUUID& invoice,
+ const strings_t& strings)
+{
+ llinfos << "Sending estate request '" << request << "'" << llendl;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", request);
+ msg->addUUID("Invoice", invoice);
+ if(strings.empty())
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", NULL);
+ }
+ else
+ {
+ strings_t::const_iterator it = strings.begin();
+ strings_t::const_iterator end = strings.end();
+ for(; it != end; ++it)
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", (*it).c_str());
+ }
+ }
+ msg->sendReliable(mHost);
+}
+
+void LLPanelRegionInfo::enableButton(const char* btn_name, BOOL enable)
+{
+ childSetEnabled(btn_name, enable);
+}
+
+void LLPanelRegionInfo::disableButton(const char* btn_name)
+{
+ childDisable(btn_name);
+}
+
+void LLPanelRegionInfo::initCtrl(const char* name)
+{
+ childSetCommitCallback(name, onChangeAnything, this);
+}
+
+void LLPanelRegionInfo::initHelpBtn(const char* name, const char* xml_alert)
+{
+ childSetAction(name, onClickHelp, (void*)xml_alert);
+}
+
+// static
+void LLPanelRegionInfo::onClickHelp(void* data)
+{
+ const char* xml_alert = (const char*)data;
+ gViewerWindow->alertXml(xml_alert);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionGeneralInfo
+//
+bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ setCtrlsEnabled(allow_modify);
+ childDisable("apply_btn");
+ childSetEnabled("access_text", allow_modify);
+ childSetEnabled("access_combo", allow_modify);
+ childSetEnabled("kick_btn", allow_modify);
+ childSetEnabled("kick_all_btn", allow_modify);
+ childSetEnabled("im_btn", allow_modify);
+ childSetEnabled("manage_telehub_btn", allow_modify);
+
+ // Data gets filled in by processRegionInfo
+
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+BOOL LLPanelRegionGeneralInfo::postBuild()
+{
+ // Enable the "Apply" button if something is changed. JC
+ initCtrl("block_terraform_check");
+ initCtrl("block_fly_check");
+ initCtrl("allow_damage_check");
+ initCtrl("allow_land_resell_check");
+ initCtrl("allow_parcel_changes_check");
+ initCtrl("agent_limit_spin");
+ initCtrl("object_bonus_spin");
+ initCtrl("access_combo");
+ initCtrl("restrict_pushobject");
+
+ initHelpBtn("terraform_help", "HelpRegionBlockTerraform");
+ initHelpBtn("fly_help", "HelpRegionBlockFly");
+ initHelpBtn("damage_help", "HelpRegionAllowDamage");
+ initHelpBtn("agent_limit_help", "HelpRegionAgentLimit");
+ initHelpBtn("object_bonus_help", "HelpRegionObjectBonus");
+ initHelpBtn("access_help", "HelpRegionMaturity");
+ initHelpBtn("restrict_pushobject_help", "HelpRegionRestrictPushObject");
+ initHelpBtn("land_resell_help", "HelpRegionLandResell");
+ initHelpBtn("parcel_changes_help", "HelpParcelChanges");
+
+ childSetAction("kick_btn", onClickKick, this);
+ childSetAction("kick_all_btn", onClickKickAll, this);
+ childSetAction("im_btn", onClickMessage, this);
+ childSetAction("manage_telehub_btn", onClickManageTelehub, this);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickKick(void* userdata)
+{
+ llinfos << "LLPanelRegionGeneralInfo::onClickKick" << llendl;
+ LLPanelRegionGeneralInfo* panelp = (LLPanelRegionGeneralInfo*)userdata;
+
+ // this depends on the grandparent view being a floater
+ // in order to set up floater dependency
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+ LLFloater* child_floater = LLFloaterAvatarPicker::show(onKickCommit, userdata, FALSE, TRUE);
+ parent_floater->addDependentFloater(child_floater);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onKickCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ if (names.empty() || ids.empty()) return;
+ if(ids[0].notNull())
+ {
+ LLPanelRegionGeneralInfo* self = (LLPanelRegionGeneralInfo*)userdata;
+ if(!self) return;
+ strings_t strings;
+ // [0] = our agent id
+ // [1] = target agent id
+ char buffer[MAX_STRING];
+ gAgent.getID().toString(buffer);
+ strings.push_back(buffer);
+
+ ids[0].toString(buffer);
+ strings.push_back(strings_t::value_type(buffer));
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "teleporthomeuser", invoice, strings);
+ }
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickKickAll(void* userdata)
+{
+ llinfos << "LLPanelRegionGeneralInfo::onClickKickAll" << llendl;
+ gViewerWindow->alertXml("KickUsersFromRegion", onKickAllCommit, userdata);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onKickAllCommit(S32 option, void* userdata)
+{
+ if (option == 0)
+ {
+ LLPanelRegionGeneralInfo* self = (LLPanelRegionGeneralInfo*)userdata;
+ if(!self) return;
+ strings_t strings;
+ // [0] = our agent id
+ char buffer[MAX_STRING];
+ gAgent.getID().toString(buffer);
+ strings.push_back(buffer);
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ // historical message name
+ self->sendEstateOwnerMessage(gMessageSystem, "teleporthomeallusers", invoice, strings);
+ }
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickMessage(void* userdata)
+{
+ llinfos << "LLPanelRegionGeneralInfo::onClickMessage" << llendl;
+ gViewerWindow->alertXmlEditText("MessageRegion", LLString::format_map_t(),
+ NULL, NULL,
+ onMessageCommit, userdata);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onMessageCommit(S32 option, const LLString& text, void* userdata)
+{
+ if(option != 0) return;
+ if(text.empty()) return;
+ LLPanelRegionGeneralInfo* self = (LLPanelRegionGeneralInfo*)userdata;
+ if(!self) return;
+ llinfos << "Message to everyone: " << text << llendl;
+ strings_t strings;
+ // [0] grid_x, unused here
+ // [1] grid_y, unused here
+ // [2] agent_id of sender
+ // [3] sender name
+ // [4] message
+ strings.push_back("-1");
+ strings.push_back("-1");
+ char buffer[MAX_STRING];
+ gAgent.getID().toString(buffer);
+ strings.push_back(buffer);
+ std::string name;
+ gAgent.buildFullname(name);
+ strings.push_back(strings_t::value_type(name));
+ strings.push_back(strings_t::value_type(text));
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "simulatormessage", invoice, strings);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickManageTelehub(void* data)
+{
+ LLFloaterRegionInfo::getInstance()->close();
+
+ LLFloaterTelehub::show();
+}
+
+// setregioninfo
+// strings[0] = 'Y' - block terraform, 'N' - not
+// strings[1] = 'Y' - block fly, 'N' - not
+// strings[2] = 'Y' - allow damage, 'N' - not
+// strings[3] = 'Y' - allow land sale, 'N' - not
+// strings[4] = agent limit
+// strings[5] = object bonus
+// strings[6] = sim access (0 = unknown, 13 = PG, 21 = Mature)
+// strings[7] = restrict pushobject
+// strings[8] = 'Y' - allow parcel subdivide, 'N' - not
+BOOL LLPanelRegionGeneralInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionGeneralInfo::sendUpdate()" << llendl;
+ strings_t strings;
+ //integers_t integers;
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%s", (childGetValue("block_terraform_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("block_fly_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("allow_damage_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("allow_land_resell_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ F32 value = (F32)childGetValue("agent_limit_spin").asReal();
+ sprintf(buffer, "%f", value);
+ strings.push_back(strings_t::value_type(buffer));
+
+ value = (F32)childGetValue("object_bonus_spin").asReal();
+ sprintf(buffer, "%f", value);
+ strings.push_back(strings_t::value_type(buffer));
+
+ U8 access = LLViewerRegion::stringToAccess(childGetValue("access_combo").asString().c_str());
+ sprintf(buffer, "%d", (S32)access);
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("restrict_pushobject").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("allow_parcel_changes_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ sendEstateOwnerMessage(gMessageSystem, "setregioninfo", invoice, strings);
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region
+ && access != region->getSimAccess() )
+ {
+ gViewerWindow->alertXml("RegionMaturityChange");
+ }
+
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionDebugInfo
+/////////////////////////////////////////////////////////////////////////////
+BOOL LLPanelRegionDebugInfo::postBuild()
+{
+ LLPanelRegionInfo::postBuild();
+ initCtrl("disable_scripts_check");
+ initCtrl("disable_collisions_check");
+ initCtrl("disable_physics_check");
+
+ initHelpBtn("disable_scripts_help", "HelpRegionDisableScripts");
+ initHelpBtn("disable_collisions_help", "HelpRegionDisableCollisions");
+ initHelpBtn("disable_physics_help", "HelpRegionDisablePhysics");
+ initHelpBtn("top_colliders_help", "HelpRegionTopColliders");
+ initHelpBtn("top_scripts_help", "HelpRegionTopScripts");
+ initHelpBtn("restart_help", "HelpRegionRestart");
+
+ childSetAction("choose_avatar_btn", onClickChooseAvatar, this);
+ childSetAction("return_scripted_other_land_btn", onClickReturnScriptedOtherLand, this);
+ childSetAction("return_scripted_all_btn", onClickReturnScriptedAll, this);
+ childSetAction("top_colliders_btn", onClickTopColliders, this);
+ childSetAction("top_scripts_btn", onClickTopScripts, this);
+ childSetAction("restart_btn", onClickRestart, this);
+ childSetAction("cancel_restart_btn", onClickCancelRestart, this);
+
+ return TRUE;
+}
+
+// virtual
+bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ setCtrlsEnabled(allow_modify);
+ childDisable("apply_btn");
+
+ childSetEnabled("choose_avatar_btn", allow_modify);
+ childSetEnabled("return_scripted_other_land_btn", allow_modify && !mTargetAvatar.isNull());
+ childSetEnabled("return_scripted_all_btn", allow_modify && !mTargetAvatar.isNull());
+ childSetEnabled("top_colliders_btn", allow_modify);
+ childSetEnabled("top_scripts_btn", allow_modify);
+ childSetEnabled("restart_btn", allow_modify);
+ childSetEnabled("cancel_restart_btn", allow_modify);
+
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+// virtual
+BOOL LLPanelRegionDebugInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionDebugInfo::sendUpdate" << llendl;
+ strings_t strings;
+ char buffer[MAX_STRING];
+
+ sprintf(buffer, "%s", (childGetValue("disable_scripts_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+
+ sprintf(buffer, "%s", (childGetValue("disable_collisions_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+
+ sprintf(buffer, "%s", (childGetValue("disable_physics_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ sendEstateOwnerMessage(gMessageSystem, "setregiondebug", invoice, strings);
+ return TRUE;
+}
+
+void LLPanelRegionDebugInfo::onClickChooseAvatar(void* data)
+{
+ LLFloaterAvatarPicker::show(callbackAvatarID, data, FALSE, TRUE);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*) data;
+ if (ids.empty() || names.empty()) return;
+ self->mTargetAvatar = ids[0];
+ self->childSetValue("target_avatar_name", LLSD(names[0]));
+ self->refreshFromRegion( gAgent.getRegion() );
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickReturnScriptedOtherLand(void* data)
+{
+ LLPanelRegionDebugInfo* panelp = (LLPanelRegionDebugInfo*) data;
+ if (panelp->mTargetAvatar.isNull()) return;
+
+ LLString::format_map_t args;
+ args["[USER_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+ gViewerWindow->alertXml("ReturnScriptedOnOthersLand", args, callbackReturnScriptedOtherLand, data);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackReturnScriptedOtherLand( S32 option, void* userdata )
+{
+ if (option != 0) return;
+
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*) userdata;
+ if (!self->mTargetAvatar.isNull())
+ {
+ U32 flags = 0;
+ flags = flags | SWD_OTHERS_LAND_ONLY;
+ flags = flags | SWD_ALWAYS_RETURN_OBJECTS;
+ flags |= SWD_SCRIPTED_ONLY;
+
+ send_sim_wide_deletes(self->mTargetAvatar, flags);
+ }
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickReturnScriptedAll(void* data)
+{
+ LLPanelRegionDebugInfo* panelp = (LLPanelRegionDebugInfo*) data;
+ if (panelp->mTargetAvatar.isNull()) return;
+
+
+ LLString::format_map_t args;
+ args["[USER_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+ gViewerWindow->alertXml("ReturnScriptedOnAllLand", args, callbackReturnScriptedAll, data);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackReturnScriptedAll( S32 option, void* userdata )
+{
+ if (option != 0) return;
+
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*) userdata;
+ if (!self->mTargetAvatar.isNull())
+ {
+ U32 flags = 0;
+ flags |= SWD_ALWAYS_RETURN_OBJECTS;
+ flags |= SWD_SCRIPTED_ONLY;
+
+ send_sim_wide_deletes(self->mTargetAvatar, flags);
+ }
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickTopColliders(void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("1"); // one physics step
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "colliders", invoice, strings);
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickTopScripts(void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("6"); // top 5 scripts
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "scripts", invoice, strings);
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickRestart(void* data)
+{
+ gViewerWindow->alertXml("ConfirmRestart", callbackRestart, data);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackRestart(S32 option, void* data)
+{
+ if (option != 0) return;
+
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("120");
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "restart", invoice, strings);
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickCancelRestart(void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("-1");
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "restart", invoice, strings);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionTextureInfo
+//
+LLPanelRegionTextureInfo::LLPanelRegionTextureInfo() : LLPanelRegionInfo()
+{
+ // nothing.
+}
+
+bool LLPanelRegionTextureInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ setCtrlsEnabled(allow_modify);
+ childDisable("apply_btn");
+
+ if (region)
+ {
+ childSetValue("region_text", LLSD(region->getName()));
+ }
+ else
+ {
+ childSetValue("region_text", LLSD(""));
+ }
+
+ if (!region) return LLPanelRegionInfo::refreshFromRegion(region);
+
+ LLVLComposition* compp = region->getComposition();
+ LLTextureCtrl* texture_ctrl;
+ char buffer[MAX_STRING];
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ sprintf(buffer, "texture_detail_%d", i);
+ texture_ctrl = LLViewerUICtrlFactory::getTexturePickerByName(this, buffer);
+ if(texture_ctrl)
+ {
+ lldebugs << "Detail Texture " << i << ": "
+ << compp->getDetailTextureID(i) << llendl;
+ LLUUID tmp_id(compp->getDetailTextureID(i));
+ texture_ctrl->setImageAssetID(tmp_id);
+ }
+ }
+
+ for(S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ sprintf(buffer, "height_start_spin_%d", i);
+ childSetValue(buffer, LLSD(compp->getStartHeight(i)));
+ sprintf(buffer, "height_range_spin_%d", i);
+ childSetValue(buffer, LLSD(compp->getHeightRange(i)));
+ }
+
+ // Call the parent for common book-keeping
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+
+BOOL LLPanelRegionTextureInfo::postBuild()
+{
+ LLPanelRegionInfo::postBuild();
+ char buffer[MAX_STRING];
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ sprintf(buffer, "texture_detail_%d", i);
+ initCtrl(buffer);
+ }
+
+ for(S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ sprintf(buffer, "height_start_spin_%d", i);
+ initCtrl(buffer);
+ sprintf(buffer, "height_range_spin_%d", i);
+ initCtrl(buffer);
+ }
+
+// LLButton* btn = new LLButton("dump", LLRect(0, 20, 100, 0), "", onClickDump, this);
+// btn->setFollows(FOLLOWS_TOP|FOLLOWS_LEFT);
+// addChild(btn);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+void LLPanelRegionTextureInfo::draw()
+{
+ if(getVisible())
+ {
+ LLPanel::draw();
+ }
+}
+
+BOOL LLPanelRegionTextureInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionTextureInfo::sendUpdate()" << llendl;
+
+ // Make sure user hasn't chosen wacky textures.
+ if (!validateTextureSizes())
+ {
+ return FALSE;
+ }
+
+ LLTextureCtrl* texture_ctrl;
+ char buffer[MAX_STRING];
+ char buffer2[MAX_STRING];
+ char id_str[UUID_STR_LENGTH];
+ LLMessageSystem* msg = gMessageSystem;
+ strings_t strings;
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ sprintf(buffer, "texture_detail_%d", i);
+ texture_ctrl = LLViewerUICtrlFactory::getTexturePickerByName(this, buffer);
+ if(texture_ctrl)
+ {
+ LLUUID tmp_id(texture_ctrl->getImageAssetID());
+ tmp_id.toString(id_str);
+ sprintf(buffer, "%d %s", i, id_str);
+ strings.push_back(strings_t::value_type(buffer));
+ }
+ }
+ sendEstateOwnerMessage(msg, "texturedetail", invoice, strings);
+ strings.clear();
+ for(S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ sprintf(buffer, "height_start_spin_%d", i);
+ sprintf(buffer2, "height_range_spin_%d", i);
+ sprintf(buffer, "%d %f %f", i, (F32)childGetValue(buffer).asReal(), (F32)childGetValue(buffer2).asReal());
+ strings.push_back(strings_t::value_type(buffer));
+ }
+ sendEstateOwnerMessage(msg, "textureheights", invoice, strings);
+ strings.clear();
+ sendEstateOwnerMessage(msg, "texturecommit", invoice, strings);
+ return TRUE;
+}
+
+BOOL LLPanelRegionTextureInfo::validateTextureSizes()
+{
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "texture_detail_%d", i);
+ LLTextureCtrl* texture_ctrl = LLViewerUICtrlFactory::getTexturePickerByName(this, buffer);
+ if (!texture_ctrl) continue;
+
+ LLUUID image_asset_id = texture_ctrl->getImageAssetID();
+ LLViewerImage* img = gImageList.getImage(image_asset_id);
+ S32 components = img->getComponents();
+ // Must ask for highest resolution version's width. JC
+ S32 width = img->getWidth(0);
+ S32 height = img->getHeight(0);
+
+ //llinfos << "texture detail " << i << " is " << width << "x" << height << "x" << components << llendl;
+
+ if (components != 3)
+ {
+ LLString::format_map_t args;
+ args["[TEXTURE_NUM]"] = llformat("%d",i+1);
+ args["[TEXTURE_BIT_DEPTH]"] = llformat("%d",components * 8);
+ gViewerWindow->alertXml("InvalidTerrainBitDepth", args);
+ return FALSE;
+ }
+
+ if (width > 512 || height > 512)
+ {
+
+ LLString::format_map_t args;
+ args["[TEXTURE_NUM]"] = i+1;
+ args["[TEXTURE_SIZE_X]"] = llformat("%d",width);
+ args["[TEXTURE_SIZE_Y]"] = llformat("%d",height);
+ gViewerWindow->alertXml("InvalidTerrainSize", args);
+ return FALSE;
+
+ }
+ }
+
+ return TRUE;
+}
+
+
+// static
+void LLPanelRegionTextureInfo::onClickDump(void* data)
+{
+ llinfos << "LLPanelRegionTextureInfo::onClickDump()" << llendl;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionTerrainInfo
+/////////////////////////////////////////////////////////////////////////////
+BOOL LLPanelRegionTerrainInfo::postBuild()
+{
+ LLPanelRegionInfo::postBuild();
+
+ initHelpBtn("water_height_help", "HelpRegionWaterHeight");
+ initHelpBtn("terrain_raise_help", "HelpRegionTerrainRaise");
+ initHelpBtn("terrain_lower_help", "HelpRegionTerrainLower");
+ initHelpBtn("upload_raw_help", "HelpRegionUploadRaw");
+ initHelpBtn("download_raw_help", "HelpRegionDownloadRaw");
+ initHelpBtn("use_estate_sun_help", "HelpRegionUseEstateSun");
+ initHelpBtn("fixed_sun_help", "HelpRegionFixedSun");
+ initHelpBtn("bake_terrain_help", "HelpRegionBakeTerrain");
+
+ initCtrl("water_height_spin");
+ initCtrl("terrain_raise_spin");
+ initCtrl("terrain_lower_spin");
+
+ initCtrl("fixed_sun_check");
+ childSetCommitCallback("fixed_sun_check", onChangeFixedSun, this);
+ childSetCommitCallback("use_estate_sun_check", onChangeUseEstateTime, this);
+ childSetCommitCallback("sun_hour_slider", onChangeSunHour, this);
+
+ childSetAction("download_raw_btn", onClickDownloadRaw, this);
+ childSetAction("upload_raw_btn", onClickUploadRaw, this);
+ childSetAction("bake_terrain_btn", onClickBakeTerrain, this);
+
+ return TRUE;
+}
+
+// virtual
+bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ llinfos << "LLPanelRegionTerrainInfo::refreshFromRegion" << llendl;
+
+ BOOL owner_or_god = gAgent.isGodlike()
+ || (region && (region->getOwner() == gAgent.getID()));
+ BOOL owner_or_god_or_manager = owner_or_god
+ || (region && region->isEstateManager());
+ setCtrlsEnabled(owner_or_god_or_manager);
+ childDisable("apply_btn");
+
+ childSetEnabled("download_raw_btn", owner_or_god);
+ childSetEnabled("upload_raw_btn", owner_or_god);
+ childSetEnabled("bake_terrain_btn", owner_or_god);
+
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+// virtual
+BOOL LLPanelRegionTerrainInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionTerrainInfo::sendUpdate" << llendl;
+ char buffer[MAX_STRING];
+ strings_t strings;
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+
+ sprintf(buffer, "%f", (F32)childGetValue("water_height_spin").asReal());
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", (F32)childGetValue("terrain_raise_spin").asReal());
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", (F32)childGetValue("terrain_lower_spin").asReal());
+ strings.push_back(buffer);
+ sprintf(buffer, "%s", (childGetValue("use_estate_sun_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+ sprintf(buffer, "%s", (childGetValue("fixed_sun_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", (F32)childGetValue("sun_hour_slider").asReal() );
+ strings.push_back(buffer);
+
+ // Grab estate information in case the user decided to set the
+ // region back to estate time. JC
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return true;
+
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab");
+ if (!tab) return true;
+
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab, "Estate");
+ if (!panel) return true;
+
+ BOOL estate_global_time = panel->getGlobalTime();
+ BOOL estate_fixed_sun = panel->getFixedSun();
+ F32 estate_sun_hour;
+ if (estate_global_time)
+ {
+ estate_sun_hour = 0.f;
+ }
+ else
+ {
+ estate_sun_hour = panel->getSunHour();
+ }
+
+ sprintf(buffer, "%s", (estate_global_time ? "Y" : "N") );
+ strings.push_back(buffer);
+ sprintf(buffer, "%s", (estate_fixed_sun ? "Y" : "N") );
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", estate_sun_hour);
+ strings.push_back(buffer);
+
+ sendEstateOwnerMessage(gMessageSystem, "setregionterrain", invoice, strings);
+ return TRUE;
+}
+
+// static
+void LLPanelRegionTerrainInfo::onChangeUseEstateTime(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelRegionTerrainInfo* panel = (LLPanelRegionTerrainInfo*) user_data;
+ if (!panel) return;
+ BOOL use_estate_sun = panel->childGetValue("use_estate_sun_check").asBoolean();
+ panel->childSetEnabled("fixed_sun_check", !use_estate_sun);
+ panel->childSetEnabled("sun_hour_slider", !use_estate_sun);
+ if (use_estate_sun)
+ {
+ panel->childSetValue("fixed_sun_check", LLSD(FALSE));
+ panel->childSetValue("sun_hour_slider", LLSD(0.f));
+ }
+ panel->childEnable("apply_btn");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onChangeFixedSun(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelRegionTerrainInfo* panel = (LLPanelRegionTerrainInfo*) user_data;
+ if (!panel) return;
+ // Just enable the apply button. We let the sun-hour slider be enabled
+ // for both fixed-sun and non-fixed-sun. JC
+ panel->childEnable("apply_btn");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onChangeSunHour(LLUICtrl* ctrl, void*)
+{
+ // can't use userdata to get panel, slider uses it internally
+ LLPanelRegionTerrainInfo* panel = (LLPanelRegionTerrainInfo*) ctrl->getParent();
+ if (!panel) return;
+ panel->childEnable("apply_btn");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onClickDownloadRaw(void* data)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_RAW, "terrain.raw"))
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ LLString filepath = picker.getFirstFile();
+
+ LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data;
+ strings_t strings;
+ strings.push_back("download filename");
+ strings.push_back(filepath);
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings);
+}
+
+// static
+void LLPanelRegionTerrainInfo::onClickUploadRaw(void* data)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getOpenFile(LLFilePicker::FFLOAD_RAW))
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ LLString filepath = picker.getFirstFile();
+
+ LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data;
+ strings_t strings;
+ strings.push_back("upload filename");
+ strings.push_back(filepath);
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings);
+
+ gViewerWindow->alertXml("RawUploadStarted");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onClickBakeTerrain(void* data)
+{
+ gViewerWindow->alertXml("ConfirmBakeTerrain",
+ callbackBakeTerrain, data);
+}
+
+// static
+void LLPanelRegionTerrainInfo::callbackBakeTerrain(S32 option, void* data)
+{
+ if (option != 0) return;
+
+ LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data;
+ strings_t strings;
+ strings.push_back("bake");
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelEstateInfo
+//
+
+LLPanelEstateInfo::LLPanelEstateInfo()
+: LLPanelRegionInfo(),
+ mEstateID(0) // invalid
+{
+}
+
+// static
+void LLPanelEstateInfo::initDispatch(LLDispatcher& dispatch)
+{
+ std::string name;
+
+// name.assign("setowner");
+// static LLDispatchSetEstateOwner set_owner;
+// dispatch.addHandler(name, &set_owner);
+
+ name.assign("estateupdateinfo");
+ static LLDispatchEstateUpdateInfo estate_update_info;
+ dispatch.addHandler(name, &estate_update_info);
+
+ name.assign("setaccess");
+ static LLDispatchSetEstateAccess set_access;
+ dispatch.addHandler(name, &set_access);
+
+ estate_dispatch_initialized = true;
+}
+
+// static
+// Disables the sun-hour slider and the use fixed time check if the use global time is check
+void LLPanelEstateInfo::onChangeUseGlobalTime(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*) user_data;
+ if (panel)
+ {
+ bool enabled = !panel->childGetValue("use_global_time_check").asBoolean();
+ panel->childSetEnabled("sun_hour_slider", enabled);
+ panel->childSetEnabled("fixed_sun_check", enabled);
+ panel->childSetValue("fixed_sun_check", LLSD(FALSE));
+ panel->enableButton("apply_btn");
+ }
+}
+
+// Enables the sun-hour slider if the fixed-sun checkbox is set
+void LLPanelEstateInfo::onChangeFixedSun(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*) user_data;
+ if (panel)
+ {
+ bool enabled = !panel->childGetValue("fixed_sun_check").asBoolean();
+ panel->childSetEnabled("use_global_time_check", enabled);
+ panel->childSetValue("use_global_time_check", LLSD(FALSE));
+ panel->enableButton("apply_btn");
+ }
+}
+
+//---------------------------------------------------------------------------
+// Add/Remove estate access button callbacks
+//---------------------------------------------------------------------------
+
+// static
+void LLPanelEstateInfo::onClickAddAllowedAgent(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("allowed_avatar_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_ACCESS_IDS)
+ {
+ //args
+
+ LLString::format_map_t args;
+ args["[MAX_AGENTS]"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
+ gViewerWindow->alertXml("MaxAllowedAgentOnRegion", args);
+ return;
+ }
+ accessAddCore(ESTATE_ACCESS_ALLOWED_AGENT_ADD, "EstateAllowedAgentAdd");
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveAllowedAgent(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_ALLOWED_AGENT_REMOVE, "EstateAllowedAgentRemove", "allowed_avatar_name_list");
+}
+
+// static
+void LLPanelEstateInfo::onClickAddAllowedGroup(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("allowed_group_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_ACCESS_IDS)
+ {
+ LLString::format_map_t args;
+ args["[MAX_GROUPS]"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
+ gViewerWindow->alertXml("MaxAllowedGroupsOnRegion", args);
+ return;
+ }
+ if (isLindenEstate())
+ {
+ gViewerWindow->alertXml("ChangeLindenAccess", addAllowedGroup, user_data);
+ }
+ else
+ {
+ addAllowedGroup(0, user_data);
+ }
+}
+
+// static
+void LLPanelEstateInfo::addAllowedGroup(S32 option, void* user_data)
+{
+ if (option != 0) return;
+
+ LLFloaterGroups* widget;
+ widget = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ if (widget)
+ {
+ widget->setOkCallback(addAllowedGroup2, user_data);
+ }
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveAllowedGroup(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_ALLOWED_GROUP_REMOVE, "EstateAllowedGroupRemove", "allowed_group_name_list");
+}
+
+// static
+void LLPanelEstateInfo::onClickAddBannedAgent(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("banned_avatar_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_ACCESS_IDS)
+ {
+ LLString::format_map_t args;
+ args["[MAX_BANNED]"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
+ gViewerWindow->alertXml("MaxBannedAgentsOnRegion", args);
+ return;
+ }
+ accessAddCore(ESTATE_ACCESS_BANNED_AGENT_ADD, "EstateBannedAgentAdd");
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveBannedAgent(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_BANNED_AGENT_REMOVE, "EstateBannedAgentRemove", "banned_avatar_name_list");
+}
+
+// static
+void LLPanelEstateInfo::onClickAddEstateManager(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("estate_manager_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_MANAGERS)
+ {
+ LLString::format_map_t args;
+ args["[MAX_MANAGER]"] = llformat("%d",ESTATE_MAX_MANAGERS);
+ gViewerWindow->alertXml("MaxManagersOnRegion", args);
+ return;
+ }
+ accessAddCore(ESTATE_ACCESS_MANAGER_ADD, "EstateManagerAdd");
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveEstateManager(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_MANAGER_REMOVE, "EstateManagerRemove", "estate_manager_name_list");
+}
+
+//---------------------------------------------------------------------------
+// Kick from estate methods
+//---------------------------------------------------------------------------
+struct LLKickFromEstateInfo
+{
+ LLPanelEstateInfo *mEstatePanelp;
+ LLString mDialogName;
+ LLUUID mAgentID;
+};
+
+void LLPanelEstateInfo::onClickKickUser(void *user_data)
+{
+ LLPanelEstateInfo* panelp = (LLPanelEstateInfo*)user_data;
+
+ // this depends on the grandparent view being a floater
+ // in order to set up floater dependency
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+ LLFloater* child_floater = LLFloaterAvatarPicker::show(LLPanelEstateInfo::onKickUserCommit, user_data, FALSE, TRUE);
+ parent_floater->addDependentFloater(child_floater);
+}
+
+void LLPanelEstateInfo::onKickUserCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ if (names.empty() || ids.empty()) return;
+
+ //check to make sure there is one valid user and id
+ if( (ids[0].isNull()) ||
+ (names[0].length() == 0) )
+ {
+ return;
+ }
+
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)userdata;
+ if(!self) return;
+
+ //keep track of what user they want to kick and other misc info
+ LLKickFromEstateInfo *kick_info = new LLKickFromEstateInfo();
+ kick_info->mEstatePanelp = self;
+ kick_info->mDialogName = "EstateKickUser";
+ kick_info->mAgentID = ids[0];
+
+ //Bring up a confirmation dialog
+ LLString::format_map_t args;
+ args["[EVIL_USER]"] = names[0];
+ gViewerWindow->alertXml(kick_info->mDialogName, args, LLPanelEstateInfo::kickUserConfirm, (void*)kick_info);
+
+}
+
+void LLPanelEstateInfo::kickUserConfirm(S32 option, void* userdata)
+{
+ //extract the callback parameter
+ LLKickFromEstateInfo *kick_info = (LLKickFromEstateInfo*) userdata;
+ if (!kick_info) return;
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ strings_t strings;
+ char buffer[MAX_STRING];
+
+ switch(option)
+ {
+ case 0:
+ //Kick User
+ kick_info->mAgentID.toString(buffer);
+ strings.push_back(strings_t::value_type(buffer));
+
+ kick_info->mEstatePanelp->sendEstateOwnerMessage(gMessageSystem, "kickestate", invoice, strings);
+ break;
+ default:
+ break;
+ }
+
+ delete kick_info;
+ kick_info = NULL;
+}
+
+//---------------------------------------------------------------------------
+// Core Add/Remove estate access methods
+//---------------------------------------------------------------------------
+std::string all_estates_text()
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return "(error)";
+
+ std::string owner = panel->getOwnerName();
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (gAgent.isGodlike())
+ {
+ return llformat("all estates\nowned by %s", owner.c_str());
+ }
+ else if (region && region->getOwner() == gAgent.getID())
+ {
+ return "all estates you own";
+ }
+ else if (region && region->isEstateManager())
+ {
+ return llformat("all estates that\nyou manage for %s", owner.c_str());
+ }
+ else
+ {
+ return "(error)";
+ }
+}
+
+// static
+bool LLPanelEstateInfo::isLindenEstate()
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return false;
+
+ U32 estate_id = panel->getEstateID();
+ return (estate_id <= ESTATE_LAST_LINDEN);
+}
+
+struct LLEstateAccessChangeInfo
+{
+ U32 mOperationFlag; // ESTATE_ACCESS_BANNED_AGENT_ADD, _REMOVE, etc.
+ LLString mDialogName;
+ LLUUID mAgentOrGroupID;
+};
+
+// Special case callback for groups, since it has different callback format than names
+// static
+void LLPanelEstateInfo::addAllowedGroup2(LLUUID id, void* user_data)
+{
+ LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo;
+ change_info->mOperationFlag = ESTATE_ACCESS_ALLOWED_GROUP_ADD;
+ change_info->mDialogName = "EstateAllowedGroupAdd";
+ change_info->mAgentOrGroupID = id;
+
+ if (isLindenEstate())
+ {
+ accessCoreConfirm(0, (void*)change_info);
+ }
+ else
+ {
+ LLString::format_map_t args;
+ args["[ALL_ESTATES]"] = all_estates_text();
+ gViewerWindow->alertXml(change_info->mDialogName, args, accessCoreConfirm, (void*)change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessAddCore(U32 operation_flag, const char* dialog_name)
+{
+ LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo;
+ change_info->mOperationFlag = operation_flag;
+ change_info->mDialogName = dialog_name;
+ // agent id filled in after avatar picker
+
+ if (isLindenEstate())
+ {
+ gViewerWindow->alertXml("ChangeLindenAccess", accessAddCore2, change_info);
+ }
+ else
+ {
+ // same as clicking "OK"
+ accessAddCore2(0, change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessAddCore2(S32 option, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ if (option != 0)
+ {
+ // abort change
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+
+ // avatar picker no multi-select, yes close-on-select
+ LLFloaterAvatarPicker::show(accessAddCore3, (void*)change_info, FALSE, TRUE);
+}
+
+// static
+void LLPanelEstateInfo::accessAddCore3(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ if (!change_info) return;
+ if (ids.empty())
+ {
+ // User didn't select a name.
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+ // User did select a name.
+ change_info->mAgentOrGroupID = ids[0];
+
+ // Can't put estate owner on ban list
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return;
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) return;
+
+ if ((change_info->mOperationFlag & ESTATE_ACCESS_BANNED_AGENT_ADD)
+ && (region->getOwner() == change_info->mAgentOrGroupID))
+ {
+ gViewerWindow->alertXml("OwnerCanNotBeDenied");
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+
+ if (isLindenEstate())
+ {
+ // just apply to this estate
+ accessCoreConfirm(0, (void*)change_info);
+ }
+ else
+ {
+ // ask if this estate or all estates with this owner
+ LLString::format_map_t args;
+ args["[ALL_ESTATES]"] = all_estates_text();
+ gViewerWindow->alertXml(change_info->mDialogName, args, accessCoreConfirm, (void*)change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessRemoveCore(U32 operation_flag, const char* dialog_name, const char* list_ctrl_name)
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return;
+ LLNameListCtrl* name_list = LLViewerUICtrlFactory::getNameListByName(panel, list_ctrl_name);
+ if (!name_list) return;
+ LLScrollListItem* item = name_list->getFirstSelected();
+ if (!item) return;
+ LLUUID agent_id = item->getUUID();
+
+ LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo;
+ change_info->mAgentOrGroupID = agent_id;
+ change_info->mOperationFlag = operation_flag;
+ change_info->mDialogName = dialog_name;
+
+ if (isLindenEstate())
+ {
+ // warn on change linden estate
+ gViewerWindow->alertXml("ChangeLindenAccess",
+ accessRemoveCore2,
+ (void*)change_info);
+ }
+ else
+ {
+ // just proceed, as if clicking OK
+ accessRemoveCore2(0, (void*)change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessRemoveCore2(S32 option, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ if (option != 0)
+ {
+ // abort
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+
+ // If Linden estate, can only apply to "this" estate, not all estates
+ // owned by NULL.
+ if (isLindenEstate())
+ {
+ accessCoreConfirm(0, (void*)change_info);
+ }
+ else
+ {
+ LLString::format_map_t args;
+ args["[ALL_ESTATES]"] = all_estates_text();
+ gViewerWindow->alertXml(change_info->mDialogName,
+ args,
+ accessCoreConfirm,
+ (void*)change_info);
+ }
+}
+
+// Used for both access add and remove operations, depending on the mOperationFlag
+// passed in (ESTATE_ACCESS_BANNED_AGENT_ADD, ESTATE_ACCESS_ALLOWED_AGENT_REMOVE, etc.)
+// static
+void LLPanelEstateInfo::accessCoreConfirm(S32 option, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ U32 flags = change_info->mOperationFlag;
+ switch(option)
+ {
+ case 0:
+ // This estate
+ sendEstateAccessDelta(flags, change_info->mAgentOrGroupID);
+ break;
+ case 1:
+ {
+ // All estates, either than I own or manage for this owner.
+ // This will be verified on simulator. JC
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) break;
+ if (region->getOwner() == gAgent.getID()
+ || gAgent.isGodlike())
+ {
+ flags |= ESTATE_ACCESS_APPLY_TO_ALL_ESTATES;
+ sendEstateAccessDelta(flags, change_info->mAgentOrGroupID);
+ }
+ else if (region->isEstateManager())
+ {
+ flags |= ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES;
+ sendEstateAccessDelta(flags, change_info->mAgentOrGroupID);
+ }
+ break;
+ }
+ case 2:
+ default:
+ break;
+ }
+ delete change_info;
+ change_info = NULL;
+}
+
+// key = "estateaccessdelta"
+// str(estate_id) will be added to front of list by forward_EstateOwnerRequest_to_dataserver
+// str[0] = str(agent_id) requesting the change
+// str[1] = str(flags) (ESTATE_ACCESS_DELTA_*)
+// str[2] = str(agent_id) to add or remove
+// static
+void LLPanelEstateInfo::sendEstateAccessDelta(U32 flags, const LLUUID& agent_or_group_id)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "estateaccessdelta");
+ msg->addUUID("Invoice", LLFloaterRegionInfo::getLastInvoice());
+
+ char buf[MAX_STRING];
+ gAgent.getID().toString(buf);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ sprintf(buf, "%u", flags);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ agent_or_group_id.toString(buf);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+
+ if (flags & (ESTATE_ACCESS_ALLOWED_AGENT_ADD | ESTATE_ACCESS_ALLOWED_AGENT_REMOVE |
+ ESTATE_ACCESS_BANNED_AGENT_ADD | ESTATE_ACCESS_BANNED_AGENT_REMOVE))
+ {
+
+ panel->clearAccessLists();
+ }
+
+ gAgent.sendReliableMessage();
+}
+
+
+bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL god = gAgent.isGodlike();
+ BOOL owner = (region && (region->getOwner() == gAgent.getID()));
+ BOOL manager = (region && region->isEstateManager());
+ setCtrlsEnabled(god || owner || manager);
+ childDisable("apply_btn");
+ childSetEnabled("add_allowed_avatar_btn", god || owner || manager);
+ childSetEnabled("remove_allowed_avatar_btn", god || owner || manager);
+ childSetEnabled("add_allowed_group_btn", god || owner || manager);
+ childSetEnabled("remove_allowed_group_btn", god || owner || manager);
+ childSetEnabled("add_banned_avatar_btn", god || owner || manager);
+ childSetEnabled("remove_banned_avatar_btn", god || owner || manager);
+ childSetEnabled("message_estate_btn", god || owner || manager);
+ childSetEnabled("kick_user_from_estate_btn", god || owner || manager);
+
+ // estate managers can't add estate managers
+ childSetEnabled("add_estate_manager_btn", god || owner);
+ childSetEnabled("remove_estate_manager_btn", god || owner);
+ childSetEnabled("estate_manager_name_list", god || owner);
+
+ // let the parent class handle the general data collection.
+ bool rv = LLPanelRegionInfo::refreshFromRegion(region);
+
+ // We want estate info. To make sure it works across region
+ // boundaries and multiple packets, we add a serial number to the
+ // integers and track against that on update.
+ strings_t strings;
+ //integers_t integers;
+ //LLFloaterRegionInfo::incrementSerial();
+ LLFloaterRegionInfo::nextInvoice();
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate();
+
+
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ panel->clearAccessLists();
+
+
+ sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings);
+
+
+
+
+ return rv;
+}
+
+void LLPanelEstateInfo::updateChild(LLUICtrl* child_ctrl)
+{
+ if (checkRemovalButton(child_ctrl->getName()))
+ {
+ // do nothing
+ }
+ else if (checkSunHourSlider(child_ctrl))
+ {
+ // do nothing
+ }
+}
+
+bool LLPanelEstateInfo::estateUpdate(LLMessageSystem* msg)
+{
+ llinfos << "LLPanelEstateInfo::estateUpdate()" << llendl;
+ return false;
+}
+
+
+BOOL LLPanelEstateInfo::postBuild()
+{
+ // set up the callbacks for the generic controls
+ initCtrl("externally_visible_check");
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ initCtrl("mainland_visible_check");
+#endif
+ initCtrl("use_global_time_check");
+ initCtrl("fixed_sun_check");
+ initCtrl("allow_direct_teleport");
+ initCtrl("deny_anonymous");
+ initCtrl("deny_identified");
+ initCtrl("deny_transacted");
+
+ initHelpBtn("estate_manager_help", "HelpEstateEstateManager");
+ initHelpBtn("use_global_time_help", "HelpEstateUseGlobalTime");
+ initHelpBtn("fixed_sun_help", "HelpEstateFixedSun");
+ initHelpBtn("externally_visible_help", "HelpEstateExternallyVisible");
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ initHelpBtn("mainland_visible_help", "HelpEstateMainlandVisible");
+#endif
+ initHelpBtn("allow_direct_teleport_help", "HelpEstateAllowDirectTeleport");
+ initHelpBtn("allow_resident_help", "HelpEstateAllowResident");
+ initHelpBtn("allow_group_help", "HelpEstateAllowGroup");
+ initHelpBtn("ban_resident_help", "HelpEstateBanResident");
+
+ // set up the use global time checkbox
+ childSetCommitCallback("use_global_time_check", onChangeUseGlobalTime, this);
+ childSetCommitCallback("fixed_sun_check", onChangeFixedSun, this);
+ childSetCommitCallback("sun_hour_slider", onChangeChildCtrl, this);
+
+ childSetCommitCallback("allowed_avatar_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl *avatar_name_list = LLViewerUICtrlFactory::getNameListByName(this, "allowed_avatar_name_list");
+ if (avatar_name_list)
+ {
+ avatar_name_list->setCommitOnSelectionChange(TRUE);
+ avatar_name_list->setMaxItemCount(ESTATE_MAX_ACCESS_IDS);
+ }
+
+ childSetAction("add_allowed_avatar_btn", onClickAddAllowedAgent, this);
+ childSetAction("remove_allowed_avatar_btn", onClickRemoveAllowedAgent, this);
+
+ childSetCommitCallback("allowed_group_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl* group_name_list = LLViewerUICtrlFactory::getNameListByName(this, "allowed_group_name_list");
+ if (group_name_list)
+ {
+ group_name_list->setCommitOnSelectionChange(TRUE);
+ group_name_list->setMaxItemCount(ESTATE_MAX_ACCESS_IDS);
+ }
+
+ childSetAction("add_allowed_group_btn", onClickAddAllowedGroup, this);
+ childSetAction("remove_allowed_group_btn", onClickRemoveAllowedGroup, this);
+
+ childSetCommitCallback("banned_avatar_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl* banned_name_list = LLViewerUICtrlFactory::getNameListByName(this, "banned_avatar_name_list");
+ if (banned_name_list)
+ {
+ banned_name_list->setCommitOnSelectionChange(TRUE);
+ banned_name_list->setMaxItemCount(ESTATE_MAX_ACCESS_IDS);
+ }
+
+ childSetAction("add_banned_avatar_btn", onClickAddBannedAgent, this);
+ childSetAction("remove_banned_avatar_btn", onClickRemoveBannedAgent, this);
+
+ childSetCommitCallback("estate_manager_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl* manager_name_list = LLViewerUICtrlFactory::getNameListByName(this, "estate_manager_name_list");
+ if (manager_name_list)
+ {
+ manager_name_list->setCommitOnSelectionChange(TRUE);
+ manager_name_list->setMaxItemCount(ESTATE_MAX_MANAGERS);
+ }
+
+ childSetAction("add_estate_manager_btn", onClickAddEstateManager, this);
+ childSetAction("remove_estate_manager_btn", onClickRemoveEstateManager, this);
+ childSetAction("message_estate_btn", onClickMessageEstate, this);
+ childSetAction("kick_user_from_estate_btn", onClickKickUser, this);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+
+BOOL LLPanelEstateInfo::sendUpdate()
+{
+ llinfos << "LLPanelEsateInfo::sendUpdate()" << llendl;
+
+ if (getEstateID() <= ESTATE_LAST_LINDEN)
+ {
+ // trying to change reserved estate, warn
+ gViewerWindow->alertXml("ChangeLindenEstate",
+ callbackChangeLindenEstate,
+ this);
+ }
+ else
+ {
+ // for normal estates, just make the change
+ callbackChangeLindenEstate(0, this);
+ }
+ return TRUE;
+}
+
+// static
+void LLPanelEstateInfo::callbackChangeLindenEstate(S32 option, void* data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)data;
+ switch(option)
+ {
+ case 0:
+ // send the update
+ LLFloaterRegionInfo::nextInvoice();
+ self->commitEstateInfo();
+ break;
+ case 1:
+ default:
+ // do nothing
+ break;
+ }
+}
+
+
+/*
+// Request = "getowner"
+// SParam[0] = "" (empty string)
+// IParam[0] = serial
+void LLPanelEstateInfo::getEstateOwner()
+{
+ // TODO -- disable the panel
+ // and call this function whenever we cross a region boundary
+ // re-enable when owner matches, and get new estate info
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_EstateOwnerRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+
+ msg->nextBlockFast(_PREHASH_RequestData);
+ msg->addStringFast(_PREHASH_Request, "getowner");
+
+ // we send an empty string so that the variable block is not empty
+ msg->nextBlockFast(_PREHASH_StringData);
+ msg->addStringFast(_PREHASH_SParam, "");
+
+ msg->nextBlockFast(_PREHASH_IntegerData);
+ msg->addS32Fast(_PREHASH_IParam, LLFloaterRegionInfo::getSerial());
+
+ gAgent.sendMessage();
+}
+*/
+
+// key = "estatechangeinfo"
+// strings[0] = str(estate_id) (added by simulator before relay - not here)
+// strings[1] = estate_name
+// strings[2] = str(estate_flags)
+// strings[3] = str((S32)(sun_hour * 1024.f))
+void LLPanelEstateInfo::commitEstateInfo()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "estatechangeinfo");
+ msg->addUUID("Invoice", LLFloaterRegionInfo::getLastInvoice());
+
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", getEstateName());
+
+ char buf[MAX_STRING];
+ sprintf(buf, "%u", computeEstateFlags());
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ F32 sun_hour = getSunHour();
+ if (childGetValue("use_global_time_check").asBoolean())
+ {
+ sun_hour = 0.f; // 0 = global time
+ }
+
+ sprintf(buf, "%d", (S32)(sun_hour*1024.0f));
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ gAgent.sendMessage();
+}
+
+void LLPanelEstateInfo::setEstateFlags(U32 flags)
+{
+ childSetValue("externally_visible_check", LLSD(flags & REGION_FLAGS_EXTERNALLY_VISIBLE ? TRUE : FALSE) );
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ childSetValue("mainland_visible_check", LLSD(flags & REGION_FLAGS_MAINLAND_VISIBLE ? TRUE : FALSE) );
+#endif
+ childSetValue("fixed_sun_check", LLSD(flags & REGION_FLAGS_SUN_FIXED ? TRUE : FALSE) );
+ childSetValue("allow_direct_teleport", LLSD(flags & REGION_FLAGS_ALLOW_DIRECT_TELEPORT ? TRUE : FALSE) );
+ childSetValue("deny_anonymous", LLSD(flags & REGION_FLAGS_DENY_ANONYMOUS ? TRUE : FALSE) );
+ childSetValue("deny_identified", LLSD(flags & REGION_FLAGS_DENY_IDENTIFIED ? TRUE : FALSE) );
+ childSetValue("deny_transacted", LLSD(flags & REGION_FLAGS_DENY_TRANSACTED ? TRUE : FALSE) );
+
+}
+
+U32 LLPanelEstateInfo::computeEstateFlags()
+{
+ U32 flags = 0;
+
+ if (childGetValue("externally_visible_check").asBoolean())
+ {
+ flags |= REGION_FLAGS_EXTERNALLY_VISIBLE;
+ }
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ // This flag is ignored by everything. 2006-11-17 Phoenix.
+ if (childGetValue("mainland_visible_check").asBoolean())
+ {
+ flags |= REGION_FLAGS_MAINLAND_VISIBLE;
+ }
+#else
+ flags |= REGION_FLAGS_MAINLAND_VISIBLE;
+#endif
+
+ if (childGetValue("allow_direct_teleport").asBoolean())
+ {
+ flags |= REGION_FLAGS_ALLOW_DIRECT_TELEPORT;
+ }
+
+ if (childGetValue("fixed_sun_check").asBoolean())
+ {
+ flags |= REGION_FLAGS_SUN_FIXED;
+ }
+
+ if (childGetValue("deny_anonymous").asBoolean())
+ {
+ flags |= REGION_FLAGS_DENY_ANONYMOUS;
+ }
+
+ if (childGetValue("deny_identified").asBoolean())
+ {
+ flags |= REGION_FLAGS_DENY_IDENTIFIED;
+ }
+
+ if (childGetValue("deny_transacted").asBoolean())
+ {
+ flags |= REGION_FLAGS_DENY_TRANSACTED;
+ }
+
+ return flags;
+}
+
+BOOL LLPanelEstateInfo::getGlobalTime()
+{
+ return childGetValue("use_global_time_check").asBoolean();
+}
+
+void LLPanelEstateInfo::setGlobalTime(bool b)
+{
+ childSetValue("use_global_time_check", LLSD(b));
+ childSetEnabled("fixed_sun_check", LLSD(!b));
+ childSetEnabled("sun_hour_slider", LLSD(!b));
+ if (b)
+ {
+ childSetValue("sun_hour_slider", LLSD(0.f));
+ }
+}
+
+
+BOOL LLPanelEstateInfo::getFixedSun()
+{
+ return childGetValue("fixed_sun_check").asBoolean();
+}
+
+void LLPanelEstateInfo::setSunHour(F32 sun_hour)
+{
+ if(sun_hour < 6.0f)
+ {
+ sun_hour = 24.0f + sun_hour;
+ }
+ childSetValue("sun_hour_slider", LLSD(sun_hour));
+}
+
+F32 LLPanelEstateInfo::getSunHour()
+{
+ if (childIsEnabled("sun_hour_slider"))
+ {
+ return (F32)childGetValue("sun_hour_slider").asReal();
+ }
+ return 0.f;
+}
+
+const std::string LLPanelEstateInfo::getEstateName() const
+{
+ return childGetValue("estate_name").asString();
+}
+
+void LLPanelEstateInfo::setEstateName(const std::string& name)
+{
+ childSetValue("estate_name", LLSD(name));
+}
+
+const std::string LLPanelEstateInfo::getOwnerName() const
+{
+ return childGetValue("estate_owner").asString();
+}
+
+void LLPanelEstateInfo::setOwnerName(const std::string& name)
+{
+ childSetValue("estate_owner", LLSD(name));
+}
+
+void LLPanelEstateInfo::setAccessAllowedEnabled(bool enable_agent,
+ bool enable_group,
+ bool enable_ban)
+{
+ childSetEnabled("allow_resident_label", enable_agent);
+ childSetEnabled("allowed_avatar_name_list", enable_agent);
+ childSetVisible("allowed_avatar_name_list", enable_agent);
+ childSetEnabled("add_allowed_avatar_btn", enable_agent);
+ childSetEnabled("remove_allowed_avatar_btn", enable_agent);
+
+ // Groups
+ childSetEnabled("allow_group_label", enable_group);
+ childSetEnabled("allowed_group_name_list", enable_group);
+ childSetVisible("allowed_group_name_list", enable_group);
+ childSetEnabled("add_allowed_group_btn", enable_group);
+ childSetEnabled("remove_allowed_group_btn", enable_group);
+
+ // Ban
+ childSetEnabled("ban_resident_label", enable_ban);
+ childSetEnabled("banned_avatar_name_list", enable_ban);
+ childSetVisible("banned_avatar_name_list", enable_ban);
+ childSetEnabled("add_banned_avatar_btn", enable_ban);
+ childSetEnabled("remove_banned_avatar_btn", enable_ban);
+
+ // Update removal buttons if needed
+ if (enable_agent)
+ {
+ checkRemovalButton("allowed_avatar_name_list");
+ }
+
+ if (enable_group)
+ {
+ checkRemovalButton("allowed_group_name_list");
+ }
+
+ if (enable_ban)
+ {
+ checkRemovalButton("banned_avatar_name_list");
+ }
+}
+
+// static
+void LLPanelEstateInfo::callbackCacheName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*)
+{
+ LLPanelEstateInfo* self = LLFloaterRegionInfo::getPanelEstate();
+ if (!self) return;
+
+ std::string name;
+
+ if (id.isNull())
+ {
+ name = "(none)";
+ }
+ else
+ {
+ name = first;
+ name += " ";
+ name += last;
+ }
+
+ self->setOwnerName(name);
+}
+
+void LLPanelEstateInfo::clearAccessLists()
+{
+ LLNameListCtrl* name_list = LLViewerUICtrlFactory::getNameListByName(this, "allowed_avatar_name_list");
+ if (name_list)
+ {
+ name_list->deleteAllItems();
+ }
+
+ name_list = LLViewerUICtrlFactory::getNameListByName(this, "banned_avatar_name_list");
+ if (name_list)
+ {
+ name_list->deleteAllItems();
+ }
+}
+
+// enables/disables the "remove" button for the various allow/ban lists
+BOOL LLPanelEstateInfo::checkRemovalButton(std::string name)
+{
+ std::string btn_name = "";
+ if (name == "allowed_avatar_name_list")
+ {
+ btn_name = "remove_allowed_avatar_btn";
+ }
+ else if (name == "allowed_group_name_list")
+ {
+ btn_name = "remove_allowed_group_btn";
+ }
+ else if (name == "banned_avatar_name_list")
+ {
+ btn_name = "remove_banned_avatar_btn";
+ }
+ else if (name == "estate_manager_name_list")
+ {
+ btn_name = "remove_estate_manager_btn";
+ }
+
+ // enable the remove button if something is selected
+ LLNameListCtrl* name_list = LLViewerUICtrlFactory::getNameListByName(this, name);
+ childSetEnabled(btn_name.c_str(), name_list && name_list->getFirstSelected() ? TRUE : FALSE);
+
+ return (btn_name != "");
+}
+
+BOOL LLPanelEstateInfo::checkSunHourSlider(LLUICtrl* child_ctrl)
+{
+ BOOL found_child_ctrl = FALSE;
+ if (child_ctrl->getName() == "sun_hour_slider")
+ {
+ enableButton("apply_btn");
+ found_child_ctrl = TRUE;
+ }
+ return found_child_ctrl;
+}
+
+// static
+void LLPanelEstateInfo::onClickMessageEstate(void* userdata)
+{
+ llinfos << "LLPanelEstateInfo::onClickMessageEstate" << llendl;
+ gViewerWindow->alertXmlEditText("MessageEstate", LLString::format_map_t(),
+ NULL, NULL,
+ onMessageCommit, userdata);
+}
+
+// static
+void LLPanelEstateInfo::onMessageCommit(S32 option, const LLString& text, void* userdata)
+{
+ if(option != 0) return;
+ if(text.empty()) return;
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)userdata;
+ if(!self) return;
+ llinfos << "Message to everyone: " << text << llendl;
+ strings_t strings;
+ //integers_t integers;
+ std::string name;
+ gAgent.buildFullname(name);
+ strings.push_back(strings_t::value_type(name));
+ strings.push_back(strings_t::value_type(text));
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "instantmessage", invoice, strings);
+}
+
+LLPanelEstateCovenant::LLPanelEstateCovenant()
+: mCovenantID(LLUUID::null)
+{
+}
+
+// virtual
+bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region)
+{
+ LLTextBox* region_name = (LLTextBox*)getChildByName("region_name_text");
+ if (region_name)
+ {
+ region_name->setText(region->getName());
+ }
+
+ LLTextBox* resellable_clause = (LLTextBox*)getChildByName("resellable_clause");
+ if (resellable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ {
+ resellable_clause->setText(childGetText("can_not_resell"));
+ }
+ else
+ {
+ resellable_clause->setText(childGetText("can_resell"));
+ }
+ }
+
+ LLTextBox* changeable_clause = (LLTextBox*)getChildByName("changeable_clause");
+ if (changeable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ changeable_clause->setText(childGetText("can_change"));
+ }
+ else
+ {
+ changeable_clause->setText(childGetText("can_not_change"));
+ }
+ }
+
+ // let the parent class handle the general data collection.
+ bool rv = LLPanelRegionInfo::refreshFromRegion(region);
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("EstateCovenantRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->sendReliable(region->getHost());
+ return rv;
+}
+
+// virtual
+bool LLPanelEstateCovenant::estateUpdate(LLMessageSystem* msg)
+{
+ llinfos << "LLPanelEstateCovenant::estateUpdate()" << llendl;
+ return true;
+}
+
+// virtual
+BOOL LLPanelEstateCovenant::postBuild()
+{
+ initHelpBtn("covenant_help", "HelpEstateCovenant");
+ mEstateNameText = (LLTextBox*)getChildByName("estate_name_text");
+ mEstateOwnerText = (LLTextBox*)getChildByName("estate_owner_text");
+ mLastModifiedText = (LLTextBox*)getChildByName("covenant_timestamp_text");
+ mEditor = (LLViewerTextEditor*)getChildByName("covenant_editor");
+ if (mEditor) mEditor->setHandleEditKeysDirectly(TRUE);
+ LLButton* reset_button = (LLButton*)getChildByName("reset_covenant");
+ reset_button->setEnabled(gAgent.canManageEstate());
+ reset_button->setClickedCallback(LLPanelEstateCovenant::resetCovenantID, NULL);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+// virtual
+void LLPanelEstateCovenant::updateChild(LLUICtrl* child_ctrl)
+{
+}
+
+// virtual
+BOOL LLPanelEstateCovenant::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ LLInventoryItem* item = (LLInventoryItem*)cargo_data;
+
+ if (!gAgent.canManageEstate())
+ {
+ *accept = ACCEPT_NO;
+ return TRUE;
+ }
+
+ switch(cargo_type)
+ {
+ case DAD_NOTECARD:
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if (item && drop)
+ {
+ gViewerWindow->alertXml("EstateChangeCovenant",
+ LLPanelEstateCovenant::confirmChangeCovenantCallback,
+ item);
+ }
+ break;
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+
+ return TRUE;
+}
+
+// static
+void LLPanelEstateCovenant::confirmChangeCovenantCallback(S32 option, void* userdata)
+{
+ LLInventoryItem* item = (LLInventoryItem*)userdata;
+ LLPanelEstateCovenant* self = LLFloaterRegionInfo::getPanelCovenant();
+
+ if (!item || !self) return;
+
+ switch(option)
+ {
+ case 0:
+ self->loadInvItem(item);
+ break;
+ default:
+ break;
+ }
+}
+
+// static
+void LLPanelEstateCovenant::resetCovenantID(void* userdata)
+{
+ gViewerWindow->alertXml("EstateChangeCovenant",
+ LLPanelEstateCovenant::confirmResetCovenantCallback,
+ NULL);
+}
+
+// static
+void LLPanelEstateCovenant::confirmResetCovenantCallback(S32 option, void* userdata)
+{
+ LLPanelEstateCovenant* self = LLFloaterRegionInfo::getPanelCovenant();
+ if (!self) return;
+
+ switch(option)
+ {
+ case 0:
+ self->loadInvItem(NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+void LLPanelEstateCovenant::loadInvItem(LLInventoryItem *itemp)
+{
+ const BOOL high_priority = TRUE;
+ if (itemp)
+ {
+ gAssetStorage->getInvItemAsset(gAgent.getRegionHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ itemp->getPermissions().getOwner(),
+ LLUUID::null,
+ itemp->getUUID(),
+ itemp->getAssetUUID(),
+ itemp->getType(),
+ onLoadComplete,
+ (void*)this,
+ high_priority);
+ mAssetStatus = ASSET_LOADING;
+ }
+ else
+ {
+ mAssetStatus = ASSET_LOADED;
+ setCovenantTextEditor("There is no Covenant provided for this Estate.");
+ sendChangeCovenantID(LLUUID::null);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "LLPanelEstateCovenant::onLoadComplete()" << llendl;
+ LLPanelEstateCovenant* panelp = (LLPanelEstateCovenant*)user_data;
+ if( panelp )
+ {
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+
+ if( (file_length > 19) && !strncmp( buffer, "Linden text version", 19 ) )
+ {
+ if( !panelp->mEditor->importBuffer( buffer ) )
+ {
+ llwarns << "Problem importing estate covenant." << llendl;
+ gViewerWindow->alertXml("ProblemImportingEstateCovenant");
+ }
+ else
+ {
+ panelp->sendChangeCovenantID(asset_uuid);
+ }
+ }
+ else
+ {
+ // Version 0 (just text, doesn't include version number)
+ panelp->sendChangeCovenantID(asset_uuid);
+ }
+ delete[] buffer;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ gViewerWindow->alertXml("MissingNotecardAssetID");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ gViewerWindow->alertXml("NotAllowedToViewNotecard");
+ }
+ else
+ {
+ gViewerWindow->alertXml("UnableToLoadNotecard");
+ }
+
+ llwarns << "Problem loading notecard: " << status << llendl;
+ }
+ panelp->mAssetStatus = ASSET_LOADED;
+ panelp->setCovenantID(asset_uuid);
+ }
+}
+
+// key = "estatechangecovenantid"
+// strings[0] = str(estate_id) (added by simulator before relay - not here)
+// strings[1] = str(covenant_id)
+void LLPanelEstateCovenant::sendChangeCovenantID(const LLUUID &asset_id)
+{
+ if (asset_id != getCovenantID())
+ {
+ setCovenantID(asset_id);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "estatechangecovenantid");
+ msg->addUUID("Invoice", LLFloaterRegionInfo::getLastInvoice());
+
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", getCovenantID().getString().c_str());
+ gAgent.sendReliableMessage();
+ }
+}
+
+// virtual
+BOOL LLPanelEstateCovenant::sendUpdate()
+{
+ return TRUE;
+}
+
+const std::string& LLPanelEstateCovenant::getEstateName() const
+{
+ return mEstateNameText->getText();
+}
+
+void LLPanelEstateCovenant::setEstateName(const std::string& name)
+{
+ mEstateNameText->setText(name);
+}
+
+// static
+void LLPanelEstateCovenant::updateCovenantText(const std::string& string, const LLUUID& asset_id)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mEditor->setText(string);
+ panelp->setCovenantID(asset_id);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::updateEstateName(const std::string& name)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mEstateNameText->setText(name);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::updateLastModified(const std::string& text)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mLastModifiedText->setText(text);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::updateEstateOwnerName(const std::string& name)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mEstateOwnerText->setText(name);
+ }
+}
+
+const std::string& LLPanelEstateCovenant::getOwnerName() const
+{
+ return mEstateOwnerText->getText();
+}
+
+void LLPanelEstateCovenant::setOwnerName(const std::string& name)
+{
+ mEstateOwnerText->setText(name);
+}
+
+void LLPanelEstateCovenant::setCovenantTextEditor(const std::string& text)
+{
+ mEditor->setText(text);
+}
+
+/*
+// AgentData = agent_id
+// RequestData = "setowner"
+// StringData[0] = compressed owner id
+// IntegerData[0] = serial
+bool LLDispatchSetEstateOwner::operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const sparam_t& strings,
+ const iparam_t& integers)
+{
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return true;
+
+ LLTabContainer* tab = (LLTabContainer*)(floater->getChildByName("tab"));
+ if (!tab) return true;
+
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)(tab->getChildByName("Estate"));
+ if (!panel) return true;
+
+ // TODO -- check owner, and disable floater if owner
+ // does not match
+
+ return true;
+}
+*/
+
+// key = "estateupdateinfo"
+// strings[0] = estate name
+// strings[1] = str(owner_id)
+// strings[2] = str(estate_id)
+// strings[3] = str(estate_flags)
+// strings[4] = str((S32)(sun_hour * 1024))
+// strings[5] = str(parent_estate_id)
+// strings[6] = str(covenant_id)
+// strings[7] = str(covenant_timestamp)
+// strings[8] = str(send_to_agent_only)
+bool LLDispatchEstateUpdateInfo::operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return true;
+
+ // NOTE: LLDispatcher extracts strings with an extra \0 at the
+ // end. If we pass the std::string direct to the UI/renderer
+ // it draws with a weird character at the end of the string.
+ std::string estate_name = strings[0].c_str();
+ panel->setEstateName(estate_name);
+
+
+ LLViewerRegion* regionp = gAgent.getRegion();
+
+ LLUUID owner_id(strings[1].c_str());
+ regionp->setOwner(owner_id);
+ // Update estate owner name in UI
+ const BOOL is_group = FALSE;
+ gCacheName->get(owner_id, is_group, LLPanelEstateInfo::callbackCacheName);
+
+ U32 estate_id = strtoul(strings[2].c_str(), NULL, 10);
+ panel->setEstateID(estate_id);
+
+ U32 flags = strtoul(strings[3].c_str(), NULL, 10);
+ panel->setEstateFlags(flags);
+
+ F32 sun_hour = ((F32)(strtod(strings[4].c_str(), NULL)))/1024.0f;
+ if(sun_hour == 0 && (flags & REGION_FLAGS_SUN_FIXED ? FALSE : TRUE))
+ {
+ panel->setGlobalTime(TRUE);
+ }
+ else
+ {
+ panel->setGlobalTime(FALSE);
+ panel->setSunHour(sun_hour);
+ }
+
+ bool visible_from_mainland = (bool)(flags & REGION_FLAGS_EXTERNALLY_VISIBLE);
+ bool god = gAgent.isGodlike();
+ bool linden_estate = (estate_id <= ESTATE_LAST_LINDEN);
+
+ // If visible from mainland, disable the access allowed
+ // UI, as anyone can teleport there.
+ // However, gods need to be able to edit the access list for
+ // linden estates, regardless of visibility, to allow object
+ // and money transfers.
+ bool enable_agent = (!visible_from_mainland || (god && linden_estate));
+ bool enable_group = enable_agent;
+ bool enable_ban = !linden_estate;
+ panel->setAccessAllowedEnabled(enable_agent, enable_group, enable_ban);
+
+ return true;
+}
+
+
+// key = "setaccess"
+// strings[0] = str(estate_id)
+// strings[1] = str(packed_access_lists)
+// strings[2] = str(num allowed agent ids)
+// strings[3] = str(num allowed group ids)
+// strings[4] = str(num banned agent ids)
+// strings[5] = str(num estate manager agent ids)
+// strings[6] = bin(uuid)
+// strings[7] = bin(uuid)
+// strings[8] = bin(uuid)
+// ...
+bool LLDispatchSetEstateAccess::operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return true;
+
+ S32 index = 1; // skip estate_id
+ U32 access_flags = strtoul(strings[index++].c_str(), NULL,10);
+ S32 num_allowed_agents = strtol(strings[index++].c_str(), NULL, 10);
+ S32 num_allowed_groups = strtol(strings[index++].c_str(), NULL, 10);
+ S32 num_banned_agents = strtol(strings[index++].c_str(), NULL, 10);
+ S32 num_estate_managers = strtol(strings[index++].c_str(), NULL, 10);
+
+ // sanity ckecks
+ if (num_allowed_agents > 0
+ && !(access_flags & ESTATE_ACCESS_ALLOWED_AGENTS))
+ {
+ llwarns << "non-zero count for allowed agents, but no corresponding flag" << llendl;
+ }
+ if (num_allowed_groups > 0
+ && !(access_flags & ESTATE_ACCESS_ALLOWED_GROUPS))
+ {
+ llwarns << "non-zero count for allowed groups, but no corresponding flag" << llendl;
+ }
+ if (num_banned_agents > 0
+ && !(access_flags & ESTATE_ACCESS_BANNED_AGENTS))
+ {
+ llwarns << "non-zero count for banned agents, but no corresponding flag" << llendl;
+ }
+ if (num_estate_managers > 0
+ && !(access_flags & ESTATE_ACCESS_MANAGERS))
+ {
+ llwarns << "non-zero count for managers, but no corresponding flag" << llendl;
+ }
+
+ // grab the UUID's out of the string fields
+ if (access_flags & ESTATE_ACCESS_ALLOWED_AGENTS)
+ {
+ LLNameListCtrl* allowed_agent_name_list;
+ allowed_agent_name_list = LLViewerUICtrlFactory::getNameListByName(panel, "allowed_avatar_name_list");
+
+ int totalAllowedAgents = num_allowed_agents;
+
+ if (allowed_agent_name_list)
+ {
+ totalAllowedAgents += allowed_agent_name_list->getItemCount();
+ }
+
+ std::string msg = llformat("Allowed residents: (%d, max %d)",
+ totalAllowedAgents,
+ ESTATE_MAX_ACCESS_IDS);
+ panel->childSetValue("allow_resident_label", LLSD(msg));
+
+ if (allowed_agent_name_list)
+ {
+ //allowed_agent_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_allowed_agents && i < ESTATE_MAX_ACCESS_IDS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ allowed_agent_name_list->addNameItem(id);
+ }
+ panel->childSetEnabled("remove_allowed_avatar_btn", allowed_agent_name_list->getFirstSelected() ? TRUE : FALSE);
+ allowed_agent_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ if (access_flags & ESTATE_ACCESS_ALLOWED_GROUPS)
+ {
+ LLNameListCtrl* allowed_group_name_list;
+ allowed_group_name_list = LLViewerUICtrlFactory::getNameListByName(panel, "allowed_group_name_list");
+
+ std::string msg = llformat("Allowed groups: (%d, max %d)",
+ num_allowed_groups,
+ (S32) ESTATE_MAX_GROUP_IDS);
+ panel->childSetValue("allow_group_label", LLSD(msg));
+
+ if (allowed_group_name_list)
+ {
+ allowed_group_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_allowed_groups && i < ESTATE_MAX_GROUP_IDS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ allowed_group_name_list->addGroupNameItem(id);
+ }
+ panel->childSetEnabled("remove_allowed_group_btn", allowed_group_name_list->getFirstSelected() ? TRUE : FALSE);
+ allowed_group_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ if (access_flags & ESTATE_ACCESS_BANNED_AGENTS)
+ {
+ LLNameListCtrl* banned_agent_name_list;
+ banned_agent_name_list = LLViewerUICtrlFactory::getNameListByName(panel, "banned_avatar_name_list");
+
+ int totalBannedAgents = num_banned_agents;
+
+ if (banned_agent_name_list)
+ {
+ totalBannedAgents += banned_agent_name_list->getItemCount();
+ }
+
+
+ std::string msg = llformat("Banned residents: (%d, max %d)",
+ totalBannedAgents,
+ ESTATE_MAX_ACCESS_IDS);
+ panel->childSetValue("ban_resident_label", LLSD(msg));
+
+ if (banned_agent_name_list)
+ {
+ //banned_agent_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_banned_agents && i < ESTATE_MAX_ACCESS_IDS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ banned_agent_name_list->addNameItem(id);
+ }
+ panel->childSetEnabled("remove_banned_avatar_btn", banned_agent_name_list->getFirstSelected() ? TRUE : FALSE);
+ banned_agent_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ if (access_flags & ESTATE_ACCESS_MANAGERS)
+ {
+ std::string msg = llformat("Estate Managers: (%d, max %d)",
+ num_estate_managers,
+ ESTATE_MAX_MANAGERS);
+ panel->childSetValue("estate_manager_label", LLSD(msg));
+
+ LLNameListCtrl* estate_manager_name_list =
+ LLViewerUICtrlFactory::getNameListByName(panel, "estate_manager_name_list");
+ if (estate_manager_name_list)
+ {
+ estate_manager_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_estate_managers && i < ESTATE_MAX_MANAGERS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ estate_manager_name_list->addNameItem(id);
+ }
+ panel->childSetEnabled("remove_estate_manager_btn", estate_manager_name_list->getFirstSelected() ? TRUE : FALSE);
+ estate_manager_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ return true;
+}
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
new file mode 100644
index 0000000000..8ab479d619
--- /dev/null
+++ b/indra/newview/llfloaterregioninfo.h
@@ -0,0 +1,387 @@
+/**
+ * @file llfloaterregioninfo.h
+ * @author Aaron Brashears
+ * @brief Declaration of the region info and controls floater and panels.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERREGIONINFO_H
+#define LL_LLFLOATERREGIONINFO_H
+
+#include <vector>
+#include "llfloater.h"
+#include "llpanel.h"
+
+class LLLineEditor;
+class LLMessageSystem;
+class LLPanelRegionInfo;
+class LLTabContainer;
+class LLViewerRegion;
+class LLViewerTextEditor;
+class LLInventoryItem;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLNameListCtrl;
+class LLSliderCtrl;
+class LLSpinCtrl;
+class LLTextBox;
+
+class LLPanelRegionGeneralInfo;
+class LLPanelRegionDebugInfo;
+class LLPanelRegionTextureInfo;
+class LLPanelRegionTerrainInfo;
+class LLPanelEstateInfo;
+class LLPanelEstateCovenant;
+
+class LLFloaterRegionInfo : public LLFloater
+{
+public:
+ ~LLFloaterRegionInfo();
+
+ static void show(LLViewerRegion* region);
+ static void show(void*);
+ static LLFloaterRegionInfo* getInstance();
+ static void processEstateOwnerRequest(LLMessageSystem* msg, void**);
+
+ // get and process region info if necessary.
+ static void processRegionInfo(LLMessageSystem* msg);
+
+ static const LLUUID& getLastInvoice() { return sRequestInvoice; }
+ static void nextInvoice() { sRequestInvoice.generate(); }
+ //static S32 getSerial() { return sRequestSerial; }
+ //static void incrementSerial() { sRequestSerial++; }
+
+ static LLPanelEstateInfo* getPanelEstate();
+ static LLPanelEstateCovenant* getPanelCovenant();
+
+protected:
+ LLFloaterRegionInfo(const LLRect& rect);
+ void refreshFromRegion(LLViewerRegion* region);
+
+ // static data
+ static LLFloaterRegionInfo* sInstance;
+
+ // member data
+ LLTabContainer* mTab;
+ typedef std::vector<LLPanelRegionInfo*> info_panels_t;
+ info_panels_t mInfoPanels;
+ //static S32 sRequestSerial; // serial # of last EstateOwnerRequest
+ static LLUUID sRequestInvoice;
+};
+
+
+// Base class for all region information panels.
+class LLPanelRegionInfo : public LLPanel
+{
+public:
+ LLPanelRegionInfo() : LLPanel("Region Info Panel") {}
+ static void onBtnSet(void* user_data);
+ static void onChangeChildCtrl(LLUICtrl* ctrl, void* user_data);
+ static void onChangeAnything(LLUICtrl* ctrl, void* user_data);
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+ virtual bool estateUpdate(LLMessageSystem* msg) { return true; }
+
+ virtual BOOL postBuild();
+ virtual void updateChild(LLUICtrl* child_ctrl);
+
+ void enableButton(const char* btn_name, BOOL enable = TRUE);
+ void disableButton(const char* btn_name);
+
+protected:
+ void initCtrl(const char* name);
+ void initHelpBtn(const char* name, const char* xml_alert);
+
+ // Callback for all help buttons, data is name of XML alert to show.
+ static void onClickHelp(void* data);
+
+ // Returns TRUE if update sent and apply button should be
+ // disabled.
+ virtual BOOL sendUpdate() { return TRUE; }
+
+ typedef std::vector<std::string> strings_t;
+ //typedef std::vector<U32> integers_t;
+ void sendEstateOwnerMessage(
+ LLMessageSystem* msg,
+ const char* request,
+ const LLUUID& invoice,
+ const strings_t& strings);
+
+ // member data
+ LLHost mHost;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// Actual panels start here
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionGeneralInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionGeneralInfo()
+ : LLPanelRegionInfo() {}
+ ~LLPanelRegionGeneralInfo() {}
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+ // LLPanel
+ virtual BOOL postBuild();
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onClickKick(void* userdata);
+ static void onKickCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+ static void onClickKickAll(void* userdata);
+ static void onKickAllCommit(S32 option, void* userdata);
+ static void onClickMessage(void* userdata);
+ static void onMessageCommit(S32 option, const LLString& text, void* userdata);
+ static void onClickManageTelehub(void* data);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionDebugInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionDebugInfo()
+ : LLPanelRegionInfo(), mTargetAvatar() {}
+ ~LLPanelRegionDebugInfo() {}
+ // LLPanel
+ virtual BOOL postBuild();
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onClickChooseAvatar(void*);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+ static void onClickReturnScriptedOtherLand(void*);
+ static void callbackReturnScriptedOtherLand(S32 option, void*);
+ static void onClickReturnScriptedAll(void*);
+ static void callbackReturnScriptedAll(S32 option, void*);
+ static void onClickTopColliders(void*);
+ static void onClickTopScripts(void*);
+ static void onClickRestart(void* data);
+ static void callbackRestart(S32 option, void* data);
+ static void onClickCancelRestart(void* data);
+
+private:
+ LLUUID mTargetAvatar;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionTextureInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionTextureInfo();
+ ~LLPanelRegionTextureInfo() {}
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+ // LLPanel && LLView
+ virtual BOOL postBuild();
+ virtual void draw();
+
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onClickDump(void* data);
+ BOOL validateTextureSizes();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionTerrainInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionTerrainInfo()
+ : LLPanelRegionInfo() {}
+ ~LLPanelRegionTerrainInfo() {}
+ // LLPanel
+ virtual BOOL postBuild();
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onChangeUseEstateTime(LLUICtrl* ctrl, void* user_data);
+ static void onChangeFixedSun(LLUICtrl* ctrl, void* user_data);
+ static void onChangeSunHour(LLUICtrl* ctrl, void*);
+
+ static void onClickDownloadRaw(void*);
+ static void onClickUploadRaw(void*);
+ static void onClickBakeTerrain(void*);
+ static void callbackBakeTerrain(S32 option, void* data);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelEstateInfo : public LLPanelRegionInfo
+{
+public:
+ static void initDispatch(LLDispatcher& dispatch);
+
+ static void onChangeFixedSun(LLUICtrl* ctrl, void* user_data);
+ static void onChangeUseGlobalTime(LLUICtrl* ctrl, void* user_data);
+
+ static void onClickAddAllowedAgent(void* user_data);
+ static void onClickRemoveAllowedAgent(void* user_data);
+ static void onClickAddAllowedGroup(void* user_data);
+ static void onClickRemoveAllowedGroup(void* user_data);
+ static void onClickAddBannedAgent(void* user_data);
+ static void onClickRemoveBannedAgent(void* user_data);
+ static void onClickAddEstateManager(void* user_data);
+ static void onClickRemoveEstateManager(void* user_data);
+ static void onClickKickUser(void* userdata);
+
+ // Group picker callback is different, can't use core methods below
+ static void addAllowedGroup(S32 option, void* data);
+ static void addAllowedGroup2(LLUUID id, void* data);
+
+ // Core methods for all above add/remove button clicks
+ static void accessAddCore(U32 operation_flag, const char* dialog_name);
+ static void accessAddCore2(S32 option, void* data);
+ static void accessAddCore3(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+
+ static void accessRemoveCore(U32 operation_flag, const char* dialog_name, const char* list_ctrl_name);
+ static void accessRemoveCore2(S32 option, void* data);
+
+ // used for both add and remove operations
+ static void accessCoreConfirm(S32 option, void* data);
+ static void kickUserConfirm(S32 option, void* userdata);
+
+ // Send the actual EstateOwnerRequest "estateaccessdelta" message
+ static void sendEstateAccessDelta(U32 flags, const LLUUID& agent_id);
+
+ static void onKickUserCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+ static void onClickMessageEstate(void* data);
+ static void onMessageCommit(S32 option, const LLString& text, void* data);
+
+ LLPanelEstateInfo();
+ ~LLPanelEstateInfo() {}
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+ virtual bool estateUpdate(LLMessageSystem* msg);
+
+ // LLPanel
+ virtual BOOL postBuild();
+ virtual void updateChild(LLUICtrl* child_ctrl);
+
+ U32 computeEstateFlags();
+ void setEstateFlags(U32 flags);
+
+ BOOL getGlobalTime();
+ void setGlobalTime(bool b);
+
+ BOOL getFixedSun();
+
+ F32 getSunHour();
+ void setSunHour(F32 sun_hour);
+
+ const std::string getEstateName() const;
+ void setEstateName(const std::string& name);
+
+ U32 getEstateID() const { return mEstateID; }
+ void setEstateID(U32 estate_id) { mEstateID = estate_id; }
+ static bool isLindenEstate();
+
+ const std::string getOwnerName() const;
+ void setOwnerName(const std::string& name);
+
+ // If visible from mainland, allowed agent and allowed groups
+ // are ignored, so must disable UI.
+ void setAccessAllowedEnabled(bool enable_agent, bool enable_group, bool enable_ban);
+
+ // this must have the same function signature as
+ // llmessage/llcachename.h:LLCacheNameCallback
+ static void callbackCacheName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*);
+
+protected:
+ virtual BOOL sendUpdate();
+ // confirmation dialog callback
+ static void callbackChangeLindenEstate(S32 opt, void* data);
+
+ void commitEstateInfo();
+ void commitEstateAccess();
+ void commitEstateManagers();
+
+ void clearAccessLists();
+ BOOL checkRemovalButton(std::string name);
+ BOOL checkSunHourSlider(LLUICtrl* child_ctrl);
+
+ U32 mEstateID;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelEstateCovenant : public LLPanelRegionInfo
+{
+public:
+ LLPanelEstateCovenant();
+ ~LLPanelEstateCovenant() {}
+
+ // LLPanel
+ virtual BOOL postBuild();
+ virtual void updateChild(LLUICtrl* child_ctrl);
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+ virtual bool estateUpdate(LLMessageSystem* msg);
+
+ // LLView overrides
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type,
+ void *cargo_data, EAcceptance *accept,
+ LLString& tooltip_msg);
+ static void confirmChangeCovenantCallback(S32 option, void* userdata);
+ static void resetCovenantID(void* userdata);
+ static void confirmResetCovenantCallback(S32 option, void* userdata);
+ void sendChangeCovenantID(const LLUUID &asset_id);
+ void loadInvItem(LLInventoryItem *itemp);
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ // Accessor functions
+ static void updateCovenantText(const std::string& string, const LLUUID& asset_id);
+ static void updateEstateName(const std::string& name);
+ static void updateLastModified(const std::string& text);
+ static void updateEstateOwnerName(const std::string& name);
+
+ const LLUUID& getCovenantID() const { return mCovenantID; }
+ void setCovenantID(const LLUUID& id) { mCovenantID = id; }
+ const std::string& getEstateName() const;
+ void setEstateName(const std::string& name);
+ const std::string& getOwnerName() const;
+ void setOwnerName(const std::string& name);
+ void setCovenantTextEditor(const std::string& text);
+
+ typedef enum e_asset_status
+ {
+ ASSET_ERROR,
+ ASSET_UNLOADED,
+ ASSET_LOADING,
+ ASSET_LOADED
+ } EAssetStatus;
+
+protected:
+ virtual BOOL sendUpdate();
+ LLTextBox* mEstateNameText;
+ LLTextBox* mEstateOwnerText;
+ LLTextBox* mLastModifiedText;
+ // CovenantID from sim
+ LLUUID mCovenantID;
+ LLViewerTextEditor* mEditor;
+ EAssetStatus mAssetStatus;
+};
+
+#endif
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
new file mode 100644
index 0000000000..3b76aa038b
--- /dev/null
+++ b/indra/newview/llfloaterreporter.cpp
@@ -0,0 +1,819 @@
+/**
+ * @file llfloaterreporter.cpp
+ * @brief Bug and abuse reports.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <sstream>
+
+// self include
+#include "llfloaterreporter.h"
+
+// linden library includes
+#include "llassetstorage.h"
+#include "llcachename.h"
+#include "llfontgl.h"
+#include "llgl.h" // for renderer
+#include "llinventory.h"
+#include "llstring.h"
+#include "llsys.h"
+#include "llversion.h"
+#include "message.h"
+#include "v3math.h"
+
+// viewer project includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llinventoryview.h"
+#include "lllineeditor.h"
+#include "lltexturectrl.h"
+#include "llscrolllistctrl.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llcombobox.h"
+#include "llfloaterrate.h"
+#include "lltooldraganddrop.h"
+#include "llfloatermap.h"
+#include "lluiconstants.h"
+#include "llcallingcard.h"
+#include "llviewerobjectlist.h"
+#include "llagent.h"
+#include "lltoolobjpicker.h"
+#include "lltoolmgr.h"
+#include "llviewermenu.h" // for LLResourceData
+#include "llviewerwindow.h"
+#include "llviewerimagelist.h"
+#include "llfilepicker.h"
+#include "llfloateravatarpicker.h"
+#include "lldir.h"
+#include "llselectmgr.h"
+#include "llviewerbuild.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h"
+
+
+const U32 INCLUDE_SCREENSHOT = 0x01 << 0;
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+
+// this map keeps track of current reporter instances
+// there can only be one instance of each reporter type
+LLMap< EReportType, LLFloaterReporter* > gReporterInstances;
+
+//-----------------------------------------------------------------------------
+// Member functions
+//-----------------------------------------------------------------------------
+LLFloaterReporter::LLFloaterReporter(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ EReportType report_type)
+ :
+ LLFloater(name, rect, title),
+ mReportType(report_type),
+ mObjectID( ),
+ mScreenID(),
+ mDeselectOnClose( FALSE ),
+ mPicking( FALSE),
+ mPosition(),
+ mCopyrightWarningSeen( FALSE )
+{
+ if (report_type == BUG_REPORT)
+ {
+ gUICtrlFactory->buildFloater(this, "floater_report_bug.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildFloater(this, "floater_report_abuse.xml");
+ }
+
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ childSetText("sim_field", regionp->getName() );
+ }
+ LLButton* pick_btn = LLUICtrlFactory::getButtonByName(this, "pick_btn");
+ if (pick_btn)
+ {
+ // XUI: Why aren't these in viewerart.ini?
+ pick_btn->setImages( "UIImgFaceUUID",
+ "UIImgFaceSelectedUUID" );
+ childSetAction("pick_btn", onClickObjPicker, this);
+ }
+
+ if (report_type != BUG_REPORT)
+ {
+ LLLineEditor* le = (LLLineEditor*)getCtrlByNameAndType("abuser_name_edit", WIDGET_TYPE_LINE_EDITOR);
+ le->setEnabled( FALSE );
+ }
+
+ childSetAction("select_abuser", onClickSelectAbuser, this);
+
+ childSetAction("send_btn", onClickSend, this);
+ childSetAction("cancel_btn", onClickCancel, this);
+
+ enableControls(TRUE);
+
+ // convert the position to a string
+ LLVector3d pos = gAgent.getPositionGlobal();
+ if (regionp)
+ {
+ pos -= regionp->getOriginGlobal();
+ }
+ setPosBox(pos);
+
+ gReporterInstances.addData(report_type, this);
+
+ // Upload a screenshot, but don't draw this floater.
+ setVisible(FALSE);
+ uploadScreenshot();
+ setVisible(TRUE);
+
+ // Default text to be blank
+ childSetText("object_name", "");
+ childSetText("owner_name", "");
+
+ childSetFocus("summary_edit");
+
+ mDefaultSummary = childGetText("details_edit");
+}
+
+
+// virtual
+LLFloaterReporter::~LLFloaterReporter()
+{
+ gReporterInstances.removeData(mReportType);
+ // child views automatically deleted
+ mObjectID = LLUUID::null;
+
+ if (mPicking)
+ {
+ closePickTool(this);
+ }
+
+ mPosition.setVec(0.0f, 0.0f, 0.0f);
+
+ std::for_each(mMCDList.begin(), mMCDList.end(), DeletePointer() );
+ mMCDList.clear();
+
+ if (gSelectMgr)
+ {
+ gSelectMgr->deselectTransient();
+ }
+}
+
+
+void LLFloaterReporter::enableControls(BOOL enable)
+{
+ childSetEnabled("category_combo", enable);
+ // bug reports never include the chat history
+ if (mReportType != BUG_REPORT)
+ {
+ childSetEnabled("chat_check", enable);
+ }
+ childSetEnabled("screen_check", enable);
+ childDisable("screenshot");
+ childSetEnabled("pick_btn", enable);
+ childSetEnabled("summary_edit", enable);
+ childSetEnabled("details_edit", enable);
+ childSetEnabled("send_btn", enable);
+ childSetEnabled("cancel_btn", enable);
+}
+
+
+void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)
+{
+ // TODO --
+ // 1 need to send to correct simulator if object is not
+ // in same simulator as agent
+ // 2 display info in widget window that gives feedback that
+ // we have recorded the object info
+ // 3 can pick avatar ==> might want to indicate when a picked
+ // object is an avatar, attachment, or other category
+
+ mObjectID = object_id;
+
+ if (LLUUID::null != mObjectID)
+ {
+ // get object info for the user's benefit
+ LLViewerObject* objectp = NULL;
+ objectp = gObjectList.findObject( mObjectID );
+ if (objectp)
+ {
+ if ( objectp->isAttachment() )
+ {
+ objectp = (LLViewerObject*)objectp->getRoot();
+ }
+
+ // correct the region and position information
+ LLViewerRegion *regionp = objectp->getRegion();
+ if (regionp)
+ {
+ childSetText("sim_field", regionp->getName());
+ LLVector3d global_pos;
+ global_pos.setVec(objectp->getPositionRegion());
+ setPosBox(global_pos);
+ }
+
+ if (objectp->isAvatar())
+ {
+ // we have the information we need
+ LLString object_owner;
+
+ LLNameValue* firstname = objectp->getNVPair("FirstName");
+ LLNameValue* lastname = objectp->getNVPair("LastName");
+ if (firstname && lastname)
+ {
+ object_owner.append(firstname->getString());
+ object_owner.append(1, ' ');
+ object_owner.append(lastname->getString());
+ }
+ else
+ {
+ object_owner.append("Unknown");
+ }
+ childSetText("object_name", object_owner);
+ childSetText("owner_name", object_owner);
+ }
+ else
+ {
+ // we have to query the simulator for information
+ // about this object
+ LLMessageSystem* msg = gMessageSystem;
+ U32 request_flags = (mReportType == BUG_REPORT) ? BUG_REPORT_REQUEST : COMPLAINT_REPORT_REQUEST;
+ msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_RequestFlags, request_flags );
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+ LLViewerRegion* regionp = objectp->getRegion();
+ msg->sendReliable( regionp->getHost() );
+ }
+ }
+ }
+}
+
+
+// static
+void LLFloaterReporter::onClickSelectAbuser(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ gFloaterView->getParentFloater(self)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, userdata, FALSE, TRUE ));
+}
+
+void LLFloaterReporter::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLFloaterReporter* self = (LLFloaterReporter*) data;
+
+ if (ids.empty() || names.empty()) return;
+
+ // this should never be called in a bug report but here for safety.
+ if ( self->mReportType != BUG_REPORT )
+ {
+ self->childSetText("abuser_name_edit", names[0] );
+
+ self->refresh();
+ };
+}
+
+// static
+void LLFloaterReporter::onClickSend(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ // only do this for abuse reports
+ if ( self->mReportType != BUG_REPORT )
+ {
+ if ( ! self->mCopyrightWarningSeen )
+ {
+ LLString details_lc = self->childGetText("details_edit");
+ LLString::toLower( details_lc );
+ LLString summary_lc = self->childGetText("summary_edit");
+ LLString::toLower( summary_lc );
+ if ( details_lc.find( "copyright" ) != std::string::npos ||
+ summary_lc.find( "copyright" ) != std::string::npos )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseContainsCopyright");
+ self->mCopyrightWarningSeen = TRUE;
+ return;
+ };
+ };
+ };
+
+ if (self->mPicking)
+ {
+ closePickTool(self);
+ }
+ self->sendReport();
+}
+
+
+// static
+void LLFloaterReporter::onClickCancel(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ // reset flag in case the next report also contains this text
+ self->mCopyrightWarningSeen = FALSE;
+
+ if (self->mPicking)
+ {
+ closePickTool(self);
+ }
+ self->close();
+}
+
+
+// static
+void LLFloaterReporter::onClickObjPicker(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+ gToolObjPicker->setExitCallback(LLFloaterReporter::closePickTool, self);
+ gToolMgr->setTransientTool(gToolObjPicker);
+ self->mPicking = TRUE;
+ self->childSetText("object_name", "");
+ self->childSetText("owner_name", "");
+ LLButton* pick_btn = LLUICtrlFactory::getButtonByName(self, "pick_btn");
+ if (pick_btn) pick_btn->setToggleState(TRUE);
+}
+
+
+// static
+void LLFloaterReporter::closePickTool(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ LLUUID object_id = gToolObjPicker->getObjectID();
+ self->getObjectInfo(object_id);
+
+ gToolMgr->clearTransientTool();
+ self->mPicking = FALSE;
+ LLButton* pick_btn = LLUICtrlFactory::getButtonByName(self, "pick_btn");
+ if (pick_btn) pick_btn->setToggleState(FALSE);
+}
+
+
+// static
+void LLFloaterReporter::showFromMenu(EReportType report_type)
+{
+ if (gReporterInstances.checkData(report_type))
+ {
+ // ...bring that window to front
+ LLFloaterReporter *f = gReporterInstances.getData(report_type);
+ f->open();
+ }
+ else
+ {
+ LLFloaterReporter *f;
+ if (BUG_REPORT == report_type)
+ {
+ f = LLFloaterReporter::createNewBugReporter();
+ }
+ else if (COMPLAINT_REPORT == report_type)
+ {
+ f = LLFloaterReporter::createNewAbuseReporter();
+ }
+ else
+ {
+ llwarns << "Unknown LLViewerReporter type : " << report_type << llendl;
+ return;
+ }
+
+ f->center();
+
+ if (report_type == BUG_REPORT)
+ {
+ gViewerWindow->alertXml("HelpReportBug");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportAbuse");
+ }
+
+ // grab the user's name
+ LLString fullname;
+ gAgent.buildFullname(fullname);
+ f->childSetText("reporter_field", fullname);
+ }
+}
+
+
+// static
+void LLFloaterReporter::showFromObject(const LLUUID& object_id)
+{
+ LLFloaterReporter* f = createNewAbuseReporter();
+ f->center();
+ f->setFocus(TRUE);
+
+ // grab the user's name
+ LLString fullname;
+ gAgent.buildFullname(fullname);
+ f->childSetText("reporter_field", fullname);
+
+ // Request info for this object
+ f->getObjectInfo(object_id);
+
+ // Need to deselect on close
+ f->mDeselectOnClose = TRUE;
+
+ f->open();
+}
+
+
+// static
+LLFloaterReporter* LLFloaterReporter::getReporter(EReportType report_type)
+{
+ LLFloaterReporter *self = NULL;
+ if (gReporterInstances.checkData(report_type))
+ {
+ // ...bring that window to front
+ self = gReporterInstances.getData(report_type);
+ }
+ return self;
+}
+
+LLFloaterReporter* LLFloaterReporter::createNewAbuseReporter()
+{
+ return new LLFloaterReporter("complaint_reporter",
+ LLRect(),
+ "Report Abuse",
+ COMPLAINT_REPORT);
+}
+
+//static
+LLFloaterReporter* LLFloaterReporter::createNewBugReporter()
+{
+ return new LLFloaterReporter("bug_reporter",
+ LLRect(),
+ "Report Bug",
+ BUG_REPORT);
+}
+
+
+
+void LLFloaterReporter::setPickedObjectProperties(const char *object_name, const char *owner_name)
+{
+ childSetText("object_name", object_name);
+ childSetText("owner_name", owner_name);
+}
+
+void LLFloaterReporter::sendReport()
+{
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (!regionp) return;
+ // Ensure user selected a category from the list
+ LLSD category_sd = childGetValue("category_combo");
+ U8 category = (U8)category_sd.asInteger();
+ if (category == 0)
+ {
+ if ( mReportType != BUG_REPORT )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseSelectCategory");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportBugSelectCategory");
+ }
+ return;
+ }
+
+ if ( mReportType != BUG_REPORT )
+ {
+ if ( childGetText("abuser_name_edit").empty() )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseAbuserNameEmpty");
+ return;
+ };
+
+ if ( childGetText("abuse_location_edit").empty() )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseAbuserLocationEmpty");
+ return;
+ };
+ };
+
+ if ( childGetText("summary_edit").empty() )
+ {
+ if ( mReportType != BUG_REPORT )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseSummaryEmpty");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportBugSummaryEmpty");
+ }
+ return;
+ };
+
+ if ( childGetText("details_edit") == mDefaultSummary )
+ {
+ if ( mReportType != BUG_REPORT )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseDetailsEmpty");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportBugDetailsEmpty");
+ }
+ return;
+ };
+
+ // reset flag in case the next report also contains this text
+ mCopyrightWarningSeen = FALSE;
+
+ U32 check_flags = 0;
+ if (childGetValue("screen_check"))
+ {
+ check_flags |= INCLUDE_SCREENSHOT;
+ }
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UserReport);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ReportData);
+ msg->addU8Fast(_PREHASH_ReportType, (U8) mReportType);
+ msg->addU8(_PREHASH_Category, category);
+ msg->addVector3Fast(_PREHASH_Position, mPosition);
+ msg->addU8Fast(_PREHASH_CheckFlags, (U8) check_flags);
+ LLSD screenshot_id = childGetValue("screenshot");
+ msg->addUUIDFast(_PREHASH_ScreenshotID, screenshot_id);
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+
+ std::ostringstream summary;
+ if (!gInProductionGrid)
+ {
+ summary << "Preview ";
+ }
+
+ LLString category_name;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "category_combo");
+ if (combo)
+ {
+ category_name = combo->getSimpleSelectedItem(); // want label, not value
+ }
+
+#if LL_WINDOWS
+ const char* platform = "Win";
+ const char* short_platform = "O:W";
+#elif LL_DARWIN
+ const char* platform = "Mac";
+ const char* short_platform = "O:M";
+#elif LL_LINUX
+ const char* platform = "Lnx";
+ const char* short_platform = "O:L";
+#else
+ const char* platform = "???";
+ const char* short_platform = "O:?";
+#endif
+
+
+ if ( mReportType == BUG_REPORT)
+ {
+ summary << short_platform << " V" << LL_VERSION_MAJOR << "."
+ << LL_VERSION_MINOR << "."
+ << LL_VERSION_PATCH << "."
+ << LL_VIEWER_BUILD
+ << " (" << regionp->getName() << ")"
+ << "[" << category_name << "] "
+ << "\"" << childGetValue("summary_edit").asString() << "\"";
+ }
+ else
+ {
+ summary << ""
+ << " |" << regionp->getName() << "|" // region reporter is currently in.
+ << " (" << childGetText("abuse_location_edit") << ")" // region abuse occured in (freeform text - no LLRegionPicker tool)
+ << " [" << category_name << "] " // updated category
+ << " {" << childGetText("abuser_name_edit") << "} " // name of abuse entered in report (chosen using LLAvatarPicker)
+ << " \"" << childGetValue("summary_edit").asString() << "\""; // summary as entered
+ };
+ msg->addStringFast(_PREHASH_Summary, summary.str().c_str());
+
+ std::ostringstream details;
+ if (mReportType != BUG_REPORT)
+ {
+ details << "V" << LL_VERSION_MAJOR << "." // client version moved to body of email for abuse reports
+ << LL_VERSION_MINOR << "."
+ << LL_VERSION_PATCH << "."
+ << LL_VIEWER_BUILD << std::endl << std::endl;
+ }
+ LLString object_name = childGetText("object_name");
+ LLString owner_name = childGetText("owner_name");
+ if (!object_name.empty() && !owner_name.empty())
+ {
+ details << "Object: " << object_name << "\n";
+ details << "Owner: " << owner_name << "\n";
+ }
+
+ if ( mReportType != BUG_REPORT )
+ {
+ details << "Abuser name: " << childGetText("abuser_name_edit") << " \n";
+ details << " Abuser location: " << childGetText("abuse_location_edit") << " \n";
+ };
+
+ details << childGetValue("details_edit").asString();
+ msg->addStringFast(_PREHASH_Details, details.str() );
+
+ char version_string[MAX_STRING];
+ sprintf(version_string,
+ "%d.%d.%d %s %s %s %s",
+ LL_VERSION_MAJOR,
+ LL_VERSION_MINOR,
+ LL_VERSION_PATCH,
+ platform,
+ gSysCPU.getFamily().c_str(),
+ gGLManager.mGLRenderer.c_str(),
+ gGLManager.mDriverVersionVendorString.c_str());
+ msg->addString("VersionString", version_string);
+
+ std::list<LLMeanCollisionData*>::iterator it;
+ for (it = mMCDList.begin(); it != mMCDList.end(); ++it)
+ {
+ LLMeanCollisionData *mcd = *it;
+ msg->nextBlockFast(_PREHASH_MeanCollision);
+ msg->addUUIDFast(_PREHASH_Perp, mcd->mPerp);
+ msg->addU32Fast(_PREHASH_Time, mcd->mTime);
+ msg->addF32Fast(_PREHASH_Mag, mcd->mMag);
+ msg->addU8Fast(_PREHASH_Type, mcd->mType);
+ }
+ msg->sendReliable(regionp->getHost());
+
+ close();
+}
+
+
+void LLFloaterReporter::uploadScreenshot()
+{
+ const S32 IMAGE_WIDTH = 1024;
+ const S32 IMAGE_HEIGHT = 768;
+ LLString filename("report_screenshot.bmp");
+
+ if( !gViewerWindow->saveSnapshot( filename, IMAGE_WIDTH, IMAGE_HEIGHT, TRUE, FALSE ) )
+ {
+ return;
+ }
+
+ // Generate the temporary filename
+ std::string temp_filename = gDirUtilp->getTempFilename();
+
+ // try to create the upload file
+ if (!LLViewerImageList::createUploadFile(filename,
+ temp_filename,
+ IMG_CODEC_BMP ))
+ {
+ llwarns << "Unable to upload report screenshot " << filename << ":\n\n" << LLImageBase::getLastError() << "\n" << llendl;
+ if(LLFile::remove(temp_filename.c_str()) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+ }
+ else
+ {
+ // create a resource data
+ LLResourceData* data = NULL;
+ data = new LLResourceData;
+ data->mInventoryType = LLInventoryType::IT_NONE;
+ data->mAssetInfo.mTransactionID.generate();
+ data->mAssetInfo.mUuid = data->mAssetInfo.mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+ if (BUG_REPORT == mReportType)
+ {
+ //data->mAssetInfo.mType = LLAssetType::AT_BUG_REPORT_SCREENSHOT;
+ data->mAssetInfo.mType = LLAssetType::AT_TEXTURE;
+ data->mPreferredLocation = LLAssetType::EType(-1);
+ }
+ else if (COMPLAINT_REPORT == mReportType)
+ {
+ //data->mAssetInfo.mType = LLAssetType::AT_COMPLAINT_REPORT_SCREENSHOT;
+ data->mAssetInfo.mType = LLAssetType::AT_TEXTURE;
+ data->mPreferredLocation = LLAssetType::EType(-2);
+ }
+ else
+ {
+ llwarns << "Unknown LLFloaterReporter type" << llendl;
+ }
+ data->mAssetInfo.mCreatorID = gAgentID;
+ data->mAssetInfo.setName("screenshot_name");
+ data->mAssetInfo.setDescription("screenshot_descr");
+
+ llinfos << "*** Uploading: " << llendl;
+ llinfos << "Type: " << LLAssetType::lookup(data->mAssetInfo.mType) << llendl;
+ llinfos << "File: " << filename << llendl;
+ llinfos << "Dest: " << temp_filename << llendl;
+ llinfos << "Name: " << data->mAssetInfo.getName() << llendl;
+ llinfos << "Desc: " << data->mAssetInfo.getDescription() << llendl;
+
+ gAssetStorage->storeAssetData(temp_filename.c_str(),
+ data->mAssetInfo.mTransactionID,
+ data->mAssetInfo.mType,
+ LLFloaterReporter::uploadDoneCallback,
+ (void*)data, TRUE);
+ }
+}
+
+
+// static
+void LLFloaterReporter::uploadDoneCallback(const LLUUID &uuid, void *user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ LLResourceData* data = (LLResourceData*)user_data;
+
+ if(result >= 0)
+ {
+ EReportType report_type = UNKNOWN_REPORT;
+ if (data->mPreferredLocation == -1)
+ {
+ report_type = BUG_REPORT;
+ }
+ else if (data->mPreferredLocation == -2)
+ {
+ report_type = COMPLAINT_REPORT;
+ }
+ else
+ {
+ llwarns << "Unknown report type : " << data->mPreferredLocation << llendl;
+ }
+
+ LLFloaterReporter *self = getReporter(report_type);
+ if (self)
+ {
+ LLTexturePicker* texture = LLUICtrlFactory::getTexturePickerByName(self, "screenshot");
+ if (texture)
+ {
+ texture->setImageAssetID(uuid);
+ texture->setDefaultImageAssetID(uuid);
+ texture->setCaption("Screenshot");
+ }
+ self->mScreenID = uuid;
+ llinfos << "Got screen shot " << uuid << llendl;
+ }
+ }
+ else // if(result >= 0)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(result));
+ gViewerWindow->alertXml("ErrorUploadingReportScreenshot", args);
+
+ std::string err_msg("There was a problem uploading a report screenshot");
+ err_msg += " due to the following reason: " + args["[REASON]"];
+ llwarns << err_msg << llendl;
+ }
+ delete data;
+}
+
+
+void LLFloaterReporter::setPosBox(const LLVector3d &pos)
+{
+ mPosition.setVec(pos);
+ LLString pos_string = llformat("{%.1f, %.1f, %.1f}",
+ mPosition.mV[VX],
+ mPosition.mV[VY],
+ mPosition.mV[VZ]);
+ childSetText("pos_field", pos_string);
+}
+
+void LLFloaterReporter::setDescription(const LLString& description, LLMeanCollisionData *mcd)
+{
+ LLFloaterReporter *self = gReporterInstances[COMPLAINT_REPORT];
+ if (self)
+ {
+ self->childSetText("details_edit", description);
+
+ for_each(self->mMCDList.begin(), self->mMCDList.end(), DeletePointer());
+ self->mMCDList.clear();
+ if (mcd)
+ {
+ self->mMCDList.push_back(new LLMeanCollisionData(mcd));
+ }
+ }
+}
+
+void LLFloaterReporter::addDescription(const LLString& description, LLMeanCollisionData *mcd)
+{
+ LLFloaterReporter *self = gReporterInstances[COMPLAINT_REPORT];
+ if (self)
+ {
+ LLTextEditor* text = LLUICtrlFactory::getTextEditorByName(self, "details_edit");
+ if (text)
+ {
+ text->insertText(description);
+ }
+ if (mcd)
+ {
+ self->mMCDList.push_back(new LLMeanCollisionData(mcd));
+ }
+ }
+}
diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h
new file mode 100644
index 0000000000..a23424f9b4
--- /dev/null
+++ b/indra/newview/llfloaterreporter.h
@@ -0,0 +1,107 @@
+/**
+ * @file llfloaterreporter.h
+ * @author Andrew Meadows
+ * @brief Bug and abuse reports.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERREPORTER_H
+#define LL_LLFLOATERREPORTER_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "v3math.h"
+
+class LLMessageSystem;
+class LLViewerImage;
+class LLInventoryItem;
+class LLViewerObject;
+class LLAgent;
+class LLToolObjPicker;
+class LLMeanCollisionData;
+
+// these flags are used to label info requests to the server
+const U32 BUG_REPORT_REQUEST = 0x01 << 0;
+const U32 COMPLAINT_REPORT_REQUEST = 0x01 << 1;
+
+// ************************************************************
+// THESE ENUMS ARE IN THE DATABASE!!!
+//
+// The process for adding a new report type is to:
+// 1. Issue a command to the database to insert the new value:
+// insert into user_report_type (description)
+// values ('${new type name}');
+// 2. Record the integer value assigned:
+// select type from user_report_type
+// where description='${new type name}';
+// 3. Add it here.
+// ${NEW TYPE NAME}_REPORT = ${type_number};
+//
+// Failure to follow this process WILL result in incorrect
+// queries on user reports.
+// ************************************************************
+enum EReportType
+{
+ NULL_REPORT = 0, // don't use this value anywhere
+ UNKNOWN_REPORT = 1,
+ BUG_REPORT = 2,
+ COMPLAINT_REPORT = 3,
+ CS_REQUEST_REPORT = 4
+};
+
+
+class LLFloaterReporter
+: public LLFloater
+{
+public:
+ LLFloaterReporter(const std::string& name,
+ const LLRect &rect,
+ const std::string& title,
+ EReportType = UNKNOWN_REPORT);
+ /*virtual*/ ~LLFloaterReporter();
+
+ // Enables all buttons
+ static void showFromMenu(EReportType report_type);
+
+ static void showFromObject(const LLUUID& object_id);
+
+ static void onClickSend (void *userdata);
+ static void onClickCancel (void *userdata);
+ static void onClickObjPicker (void *userdata);
+ static void onClickSelectAbuser (void *userdata);
+ static void closePickTool (void *userdata);
+ static void uploadDoneCallback(const LLUUID &uuid, void* user_data, S32 result);
+ static void addDescription(const LLString& description, LLMeanCollisionData *mcd = NULL);
+ static void setDescription(const LLString& description, LLMeanCollisionData *mcd = NULL);
+
+ // returns a pointer to reporter of report_type
+ static LLFloaterReporter* getReporter(EReportType report_type);
+ static LLFloaterReporter* createNewAbuseReporter();
+ static LLFloaterReporter* createNewBugReporter();
+
+ void setPickedObjectProperties(const char *object_name, const char *owner_name);
+ void uploadScreenshot();
+
+private:
+ void setReporterID();
+ void sendReport();
+ void setPosBox(const LLVector3d &pos);
+ void enableControls(BOOL own_avatar);
+ void getObjectInfo(const LLUUID& object_id);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+
+private:
+ EReportType mReportType;
+ LLUUID mObjectID;
+ LLUUID mScreenID;
+ BOOL mDeselectOnClose;
+ BOOL mPicking;
+ LLVector3 mPosition;
+ BOOL mCopyrightWarningSeen;
+ std::list<LLMeanCollisionData*> mMCDList;
+ LLString mDefaultSummary;
+};
+
+#endif
diff --git a/indra/newview/llfloaterscriptdebug.cpp b/indra/newview/llfloaterscriptdebug.cpp
new file mode 100644
index 0000000000..d80dbca310
--- /dev/null
+++ b/indra/newview/llfloaterscriptdebug.cpp
@@ -0,0 +1,230 @@
+/**
+ * @file llfloaterscriptdebug.cpp
+ * @brief Chat window for showing script errors and warnings
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvieweruictrlfactory.h"
+#include "llfloaterscriptdebug.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+
+// project include
+#include "llviewertexteditor.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+#include "llviewerimagelist.h"
+
+//
+// Statics
+//
+LLFloaterScriptDebug* LLFloaterScriptDebug::sInstance = NULL;
+
+//
+// Member Functions
+//
+LLFloaterScriptDebug::LLFloaterScriptDebug()
+: LLMultiFloater()
+{
+
+}
+
+LLFloaterScriptDebug::~LLFloaterScriptDebug()
+{
+ sInstance = NULL;
+}
+
+void LLFloaterScriptDebug::show(const LLUUID& object_id)
+{
+ LLFloater* floaterp = addOutputWindow(object_id);
+ if (sInstance)
+ {
+ sInstance->open();
+ sInstance->showFloater(floaterp);
+ }
+}
+
+BOOL LLFloaterScriptDebug::postBuild()
+{
+ LLMultiFloater::postBuild();
+
+ if (mTabContainer)
+ {
+ // FIXME: apparantly fails for tab containers?
+// mTabContainer->requires("all_scripts", WIDGET_TYPE_FLOATER);
+// mTabContainer->checkRequirements();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void* getOutputWindow(void* data)
+{
+ return new LLFloaterScriptDebugOutput();
+}
+
+LLFloater* LLFloaterScriptDebug::addOutputWindow(const LLUUID &object_id)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterScriptDebug();
+ LLCallbackMap::map_t factory_map;
+ factory_map["all_scripts"] = LLCallbackMap(getOutputWindow, NULL);
+ gUICtrlFactory->buildFloater(sInstance, "floater_script_debug.xml", &factory_map);
+ sInstance->setVisible(FALSE);
+ }
+
+ LLFloater* floaterp = NULL;
+ LLFloater::setFloaterHost(sInstance);
+ {
+ floaterp = LLFloaterScriptDebugOutput::show(object_id);
+ }
+ LLFloater::setFloaterHost(NULL);
+
+ return floaterp;
+}
+
+void LLFloaterScriptDebug::addScriptLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color, const LLUUID& source_id)
+{
+ LLViewerObject* objectp = gObjectList.findObject(source_id);
+ LLString floater_label;
+
+ if (objectp)
+ {
+ objectp->setIcon(gImageList.getImage(LLUUID(gViewerArt.getString("script_error.tga"))));
+ floater_label = llformat("%s(%.2f, %.2f)", user_name.c_str(), objectp->getPositionRegion().mV[VX], objectp->getPositionRegion().mV[VY]);
+ }
+ else
+ {
+ floater_label = user_name;
+ }
+
+ addOutputWindow(LLUUID::null);
+ addOutputWindow(source_id);
+
+ // add to "All" floater
+ LLFloaterScriptDebugOutput* floaterp = LLFloaterScriptDebugOutput::getFloaterByID(LLUUID::null);
+ floaterp->addLine(utf8mesg, user_name, color);
+
+ // add to specific script instance floater
+ floaterp = LLFloaterScriptDebugOutput::getFloaterByID(source_id);
+ floaterp->addLine(utf8mesg, floater_label, color);
+}
+
+//
+// LLFloaterScriptDebugOutput
+//
+
+std::map<LLUUID, LLFloaterScriptDebugOutput*> LLFloaterScriptDebugOutput::sInstanceMap;
+
+LLFloaterScriptDebugOutput::LLFloaterScriptDebugOutput()
+: mObjectID(LLUUID::null)
+{
+ sInstanceMap[mObjectID] = this;
+}
+
+LLFloaterScriptDebugOutput::LLFloaterScriptDebugOutput(const LLUUID& object_id)
+: LLFloater("script instance floater", LLRect(0, 200, 200, 0), "Script", TRUE), mObjectID(object_id)
+{
+ S32 y = mRect.getHeight() - LLFLOATER_HEADER_SIZE - LLFLOATER_VPAD;
+ S32 x = LLFLOATER_HPAD;
+ // History editor
+ // Give it a border on the top
+ LLRect history_editor_rect(
+ x,
+ y,
+ mRect.getWidth() - LLFLOATER_HPAD,
+ LLFLOATER_VPAD );
+ mHistoryEditor = new LLViewerTextEditor( "Chat History Editor",
+ history_editor_rect, S32_MAX, "", LLFontGL::sSansSerif);
+ mHistoryEditor->setWordWrap( TRUE );
+ mHistoryEditor->setFollowsAll();
+ mHistoryEditor->setEnabled( FALSE );
+ mHistoryEditor->setTakesFocus( TRUE ); // We want to be able to cut or copy from the history.
+ addChild(mHistoryEditor);
+}
+
+void LLFloaterScriptDebugOutput::init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn)
+{
+ LLFloater::init(title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+ S32 y = mRect.getHeight() - LLFLOATER_HEADER_SIZE - LLFLOATER_VPAD;
+ S32 x = LLFLOATER_HPAD;
+ // History editor
+ // Give it a border on the top
+ LLRect history_editor_rect(
+ x,
+ y,
+ mRect.getWidth() - LLFLOATER_HPAD,
+ LLFLOATER_VPAD );
+ mHistoryEditor = new LLViewerTextEditor( "Chat History Editor",
+ history_editor_rect, S32_MAX, "", LLFontGL::sSansSerif);
+ mHistoryEditor->setWordWrap( TRUE );
+ mHistoryEditor->setFollowsAll();
+ mHistoryEditor->setEnabled( FALSE );
+ mHistoryEditor->setTakesFocus( TRUE ); // We want to be able to cut or copy from the history.
+ addChild(mHistoryEditor);
+}
+
+LLFloaterScriptDebugOutput::~LLFloaterScriptDebugOutput()
+{
+ sInstanceMap.erase(mObjectID);
+}
+
+void LLFloaterScriptDebugOutput::addLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color)
+{
+ if (mObjectID.isNull())
+ {
+ //setTitle("[All scripts]");
+ setCanTearOff(FALSE);
+ setCanClose(FALSE);
+ }
+ else
+ {
+ setTitle(user_name);
+ }
+
+ mHistoryEditor->appendColoredText(utf8mesg, false, true, color);
+}
+
+//static
+LLFloaterScriptDebugOutput* LLFloaterScriptDebugOutput::show(const LLUUID& object_id)
+{
+ LLFloaterScriptDebugOutput* floaterp = NULL;
+ instance_map_t::iterator found_it = sInstanceMap.find(object_id);
+ if (found_it == sInstanceMap.end())
+ {
+ floaterp = new LLFloaterScriptDebugOutput(object_id);
+ sInstanceMap[object_id] = floaterp;
+ floaterp->open();
+ }
+ else
+ {
+ floaterp = found_it->second;
+ }
+
+ return floaterp;
+}
+
+//static
+LLFloaterScriptDebugOutput* LLFloaterScriptDebugOutput::getFloaterByID(const LLUUID& object_id)
+{
+ LLFloaterScriptDebugOutput* floaterp = NULL;
+ instance_map_t::iterator found_it = sInstanceMap.find(object_id);
+ if (found_it != sInstanceMap.end())
+ {
+ floaterp = found_it->second;
+ }
+
+ return floaterp;
+}
diff --git a/indra/newview/llfloaterscriptdebug.h b/indra/newview/llfloaterscriptdebug.h
new file mode 100644
index 0000000000..49a6a5c1a2
--- /dev/null
+++ b/indra/newview/llfloaterscriptdebug.h
@@ -0,0 +1,60 @@
+/**
+ * @file llfloaterscriptdebug.h
+ * @brief Shows error and warning output from scripts
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERSCRIPTDEBUG_H
+#define LL_LLFLOATERSCRIPTDEBUG_H
+
+#include "llfloater.h"
+
+class LLTextEditor;
+class LLUUID;
+
+class LLFloaterScriptDebug : public LLMultiFloater
+{
+public:
+ virtual ~LLFloaterScriptDebug();
+ virtual void onClose(bool app_quitting) { setVisible(FALSE); }
+ virtual BOOL postBuild();
+ static void show(const LLUUID& object_id);
+ static void addScriptLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color, const LLUUID& source_id);
+
+protected:
+ LLFloaterScriptDebug();
+
+ static LLFloater* addOutputWindow(const LLUUID& object_id);
+
+protected:
+ static LLFloaterScriptDebug* sInstance;
+};
+
+class LLFloaterScriptDebugOutput : public LLFloater
+{
+public:
+ LLFloaterScriptDebugOutput();
+ LLFloaterScriptDebugOutput(const LLUUID& object_id);
+ ~LLFloaterScriptDebugOutput();
+
+ virtual void init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn);
+
+ void addLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color);
+
+ static LLFloaterScriptDebugOutput* show(const LLUUID& object_id);
+ static LLFloaterScriptDebugOutput* getFloaterByID(const LLUUID& id);
+
+protected:
+ LLTextEditor* mHistoryEditor;
+
+ typedef std::map<LLUUID, LLFloaterScriptDebugOutput*> instance_map_t;
+ static instance_map_t sInstanceMap;
+
+ LLUUID mObjectID;
+};
+
+#endif // LL_LLFLOATERSCRIPTDEBUG_H
diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp
new file mode 100755
index 0000000000..3c63ad69cb
--- /dev/null
+++ b/indra/newview/llfloatersellland.cpp
@@ -0,0 +1,486 @@
+/**
+ * @file llfloatersellland.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatersellland.h"
+
+#include "llfloateravatarpicker.h"
+#include "llfloater.h"
+#include "llfloaterland.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llparcel.h"
+#include "llselectmgr.h"
+#include "lltexturectrl.h"
+#include "llviewercontrol.h"
+#include "llviewerparcelmgr.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+// defined in llfloaterland.cpp
+void send_parcel_select_objects(S32 parcel_local_id, S32 return_type,
+ uuid_list_t* return_ids = NULL);
+
+enum Badge { BADGE_OK, BADGE_NOTE, BADGE_WARN, BADGE_ERROR };
+
+class LLFloaterSellLandUI
+: public LLFloater
+{
+private:
+ LLFloaterSellLandUI();
+ virtual ~LLFloaterSellLandUI();
+
+ LLViewerRegion* mRegion;
+ LLParcel* mParcel;
+ bool mParcelIsForSale;
+ bool mSellToBuyer;
+ bool mChoseSellTo;
+ S32 mParcelPrice;
+ S32 mParcelActualArea;
+ LLUUID mParcelSnapshot;
+ LLUUID mAuthorizedBuyer;
+ bool mParcelSoldWithObjects;
+
+ void updateParcelInfo();
+ void refreshUI();
+ void setBadge(const char* id, Badge badge);
+
+ static LLFloaterSellLandUI* sInstance;
+
+ static void onChangeValue(LLUICtrl *ctrl, void *userdata);
+ static void doSelectAgent(void *userdata);
+ static void doCancel(void *userdata);
+ static void doSellLand(void *userdata);
+ static void onConfirmSale(S32 option, void *userdata);
+ static void doShowObjects(void *userdata);
+ static void callbackHighlightTransferable(S32 option, void* userdata);
+
+ static void callbackAvatarPick(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+
+public:
+ virtual BOOL postBuild();
+ virtual void onClose(bool app_quitting);
+
+ static LLFloaterSellLandUI* soleInstance(bool createIfNeeded);
+
+ bool setParcel(LLViewerRegion* region, LLParcel* parcel);
+};
+
+// static
+void LLFloaterSellLand::sellLand(
+ LLViewerRegion* region, LLParcel* parcel)
+{
+ LLFloaterSellLandUI* ui = LLFloaterSellLandUI::soleInstance(true);
+ if (ui->setParcel(region, parcel))
+ {
+ ui->open();
+ }
+}
+
+// static
+LLFloaterSellLandUI* LLFloaterSellLandUI::sInstance = NULL;
+
+// static
+LLFloaterSellLandUI* LLFloaterSellLandUI::soleInstance(bool createIfNeeded)
+{
+ if (!sInstance && createIfNeeded)
+ {
+ sInstance = new LLFloaterSellLandUI();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_sell_land.xml");
+ sInstance->center();
+ }
+
+ return sInstance;
+}
+
+LLFloaterSellLandUI::LLFloaterSellLandUI()
+: LLFloater("Sell Land"),
+ mRegion(0), mParcel(0)
+{
+}
+
+LLFloaterSellLandUI::~LLFloaterSellLandUI()
+{
+ if (sInstance == this)
+ {
+ sInstance = NULL;
+ }
+}
+
+
+void LLFloaterSellLandUI::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+ delete this;
+}
+
+BOOL LLFloaterSellLandUI::postBuild()
+{
+ childSetCommitCallback("sell_to", onChangeValue, this);
+ childSetCommitCallback("price", onChangeValue, this);
+ childSetPrevalidate("price", LLLineEditor::prevalidateNonNegativeS32);
+ childSetCommitCallback("sell_objects", onChangeValue, this);
+ childSetAction("sell_to_select_agent", doSelectAgent, this);
+ childSetAction("cancel_btn", doCancel, this);
+ childSetAction("sell_btn", doSellLand, this);
+ childSetAction("show_objects", doShowObjects, this);
+ return TRUE;
+}
+
+bool LLFloaterSellLandUI::setParcel(LLViewerRegion* region, LLParcel* parcel)
+{
+ if (!parcel) // || !can_agent_modify_parcel(parcel)) // can_agent_modify_parcel was deprecated by GROUPS
+ {
+ return false;
+ }
+
+ mRegion = region;
+ mParcel = parcel;
+ mChoseSellTo = false;
+
+ updateParcelInfo();
+ refreshUI();
+
+ return true;
+}
+
+void LLFloaterSellLandUI::updateParcelInfo()
+{
+ mParcelActualArea = mParcel->getArea();
+ mParcelIsForSale = mParcel->getForSale();
+ if (mParcelIsForSale)
+ {
+ mChoseSellTo = true;
+ }
+ mParcelPrice = mParcelIsForSale ? mParcel->getSalePrice() : 0;
+ mParcelSoldWithObjects = mParcel->getSellWithObjects();
+ if (mParcelIsForSale)
+ {
+ childSetValue("price", mParcelPrice);
+ if (mParcelSoldWithObjects)
+ {
+ childSetValue("sell_objects", "yes");
+ }
+ else
+ {
+ childSetValue("sell_objects", "no");
+ }
+ }
+ else
+ {
+ childSetValue("price", "");
+ childSetValue("sell_objects", "none");
+ }
+
+ mParcelSnapshot = mParcel->getSnapshotID();
+
+ mAuthorizedBuyer = mParcel->getAuthorizedBuyerID();
+ mSellToBuyer = mAuthorizedBuyer.notNull();
+
+ if(mSellToBuyer)
+ {
+ LLString name;
+ char firstname[MAX_STRING];
+ char lastname[MAX_STRING];
+ gCacheName->getName(mAuthorizedBuyer, firstname, lastname);
+ name.assign(firstname);
+ name.append(" ");
+ name.append(lastname);
+
+ childSetText("sell_to_agent", name);
+ }
+}
+
+void LLFloaterSellLandUI::setBadge(const char* id, Badge badge)
+{
+ static LLUUID badgeOK(gViewerArt.getString("badge_ok.tga"));
+ static LLUUID badgeNote(gViewerArt.getString("badge_note.tga"));
+ static LLUUID badgeWarn(gViewerArt.getString("badge_warn.tga"));
+ static LLUUID badgeError(gViewerArt.getString("badge_error.tga"));
+
+ LLUUID badgeUUID;
+ switch (badge)
+ {
+ default:
+ case BADGE_OK: badgeUUID = badgeOK; break;
+ case BADGE_NOTE: badgeUUID = badgeNote; break;
+ case BADGE_WARN: badgeUUID = badgeWarn; break;
+ case BADGE_ERROR: badgeUUID = badgeError; break;
+ }
+
+ childSetValue(id, badgeUUID);
+}
+
+void LLFloaterSellLandUI::refreshUI()
+{
+ LLTextureCtrl* snapshot = LLViewerUICtrlFactory::getTexturePickerByName(this, "info_image");
+ if (snapshot)
+ {
+ snapshot->setImageAssetID(mParcelSnapshot);
+ }
+
+ childSetText("info_parcel", mParcel->getName());
+ childSetTextArg("info_size", "[AREA]", llformat("%d", mParcelActualArea));
+
+ LLString price_str = childGetValue("price").asString();
+ bool valid_price = false;
+ valid_price = (price_str != "") && LLLineEditor::prevalidateNonNegativeS32(utf8str_to_wstring(price_str));
+
+ if (valid_price && mParcelActualArea > 0)
+ {
+ F32 per_meter_price = 0;
+ per_meter_price = F32(mParcelPrice) / F32(mParcelActualArea);
+ childSetTextArg("price_per_m", "[PER_METER]", llformat("%0.2f", per_meter_price));
+ childShow("price_per_m");
+
+ setBadge("step_price", BADGE_OK);
+ }
+ else
+ {
+ childHide("price_per_m");
+
+ if ("" == price_str)
+ {
+ setBadge("step_price", BADGE_NOTE);
+ }
+ else
+ {
+ setBadge("step_price", BADGE_ERROR);
+ }
+ }
+
+ if (mSellToBuyer)
+ {
+ childSetValue("sell_to", "user");
+ childShow("sell_to_agent");
+ childShow("sell_to_select_agent");
+ }
+ else
+ {
+ if (mChoseSellTo)
+ {
+ childSetValue("sell_to", "anyone");
+ }
+ else
+ {
+ childSetValue("sell_to", "select");
+ }
+ childHide("sell_to_agent");
+ childHide("sell_to_select_agent");
+ }
+
+ // Must select Sell To: Anybody, or User (with a specified username)
+ LLString sell_to = childGetValue("sell_to").asString();
+ bool valid_sell_to = "select" != sell_to &&
+ ("user" != sell_to || mAuthorizedBuyer.notNull());
+
+ if (!valid_sell_to)
+ {
+ setBadge("step_sell_to", BADGE_NOTE);
+ }
+ else
+ {
+ setBadge("step_sell_to", BADGE_OK);
+ }
+
+ bool valid_sell_objects = ("none" != childGetValue("sell_objects").asString());
+
+ if (!valid_sell_objects)
+ {
+ setBadge("step_sell_objects", BADGE_NOTE);
+ }
+ else
+ {
+ setBadge("step_sell_objects", BADGE_OK);
+ }
+
+ if (valid_sell_to && valid_price && valid_sell_objects)
+ {
+ childEnable("sell_btn");
+ }
+ else
+ {
+ childDisable("sell_btn");
+ }
+}
+
+// static
+void LLFloaterSellLandUI::onChangeValue(LLUICtrl *ctrl, void *userdata)
+{
+ LLFloaterSellLandUI *self = (LLFloaterSellLandUI *)userdata;
+
+ LLString sell_to = self->childGetValue("sell_to").asString();
+
+ if (sell_to == "user")
+ {
+ self->mChoseSellTo = true;
+ self->mSellToBuyer = true;
+ if (self->mAuthorizedBuyer.isNull())
+ {
+ doSelectAgent(self);
+ }
+ }
+ else if (sell_to == "anyone")
+ {
+ self->mChoseSellTo = true;
+ self->mSellToBuyer = false;
+ }
+
+ self->mParcelPrice = self->childGetValue("price");
+
+ if ("yes" == self->childGetValue("sell_objects").asString())
+ {
+ self->mParcelSoldWithObjects = true;
+ }
+ else
+ {
+ self->mParcelSoldWithObjects = false;
+ }
+
+ self->refreshUI();
+}
+
+// static
+void LLFloaterSellLandUI::doSelectAgent(void *userdata)
+{
+ LLFloaterSellLandUI* floaterp = (LLFloaterSellLandUI*)userdata;
+ // grandparent is a floater, in order to set up dependency
+ floaterp->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarPick, floaterp, FALSE, TRUE));
+}
+
+// static
+void LLFloaterSellLandUI::callbackAvatarPick(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLFloaterSellLandUI* floaterp = (LLFloaterSellLandUI*)data;
+ LLParcel* parcel = floaterp->mParcel;
+
+ if (names.empty() || ids.empty()) return;
+
+ LLUUID id = ids[0];
+ parcel->setAuthorizedBuyerID(id);
+
+ floaterp->mAuthorizedBuyer = ids[0];
+
+ floaterp->childSetText("sell_to_agent", names[0]);
+
+ floaterp->refreshUI();
+}
+
+// static
+void LLFloaterSellLandUI::doCancel(void *userdata)
+{
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+ self->close();
+}
+
+// static
+void LLFloaterSellLandUI::doShowObjects(void *userdata)
+{
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+ send_parcel_select_objects(self->mParcel->getLocalID(), RT_SELL);
+
+ LLNotifyBox::showXml("TransferObjectsHighlighted",
+ callbackHighlightTransferable,
+ userdata);
+}
+
+// static
+void LLFloaterSellLandUI::callbackHighlightTransferable(S32 option, void* userdata)
+{
+ gSelectMgr->unhighlightAll();
+}
+
+// static
+void LLFloaterSellLandUI::doSellLand(void *userdata)
+{
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+
+ LLParcel* parcel = self->mParcel;
+
+ // Do a confirmation
+ if (!parcel->getForSale())
+ {
+ S32 sale_price = self->childGetValue("price");
+ S32 area = parcel->getArea();
+ std::string authorizedBuyerName = "Anyone";
+ bool sell_to_anyone = true;
+ if ("user" == self->childGetValue("sell_to").asString())
+ {
+ authorizedBuyerName = self->childGetText("sell_to_agent");
+ sell_to_anyone = false;
+ }
+
+ // must sell to someone if indicating sale to anyone
+ if ((sale_price == 0) && sell_to_anyone)
+ {
+ gViewerWindow->alertXml("SalePriceRestriction");
+ return;
+ }
+
+ LLStringBase<char>::format_map_t args;
+ args["[LAND_SIZE]"] = llformat("%d",area);
+ args["[SALE_PRICE]"] = llformat("%d",sale_price);
+ args["[NAME]"] = authorizedBuyerName;
+
+ gViewerWindow->alertXml("ConfirmLandSaleChange", args, onConfirmSale, self);
+ }
+ else
+ {
+ onConfirmSale(-1, self);
+ }
+}
+
+// static
+void LLFloaterSellLandUI::onConfirmSale(S32 option, void *userdata)
+{
+ if (option != 0)
+ {
+ return;
+ }
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+ S32 sale_price = self->childGetValue("price");
+
+ // Valid extracted data
+ if (sale_price < 0)
+ {
+ // TomY TODO: Throw an error
+ return;
+ }
+
+ LLParcel* parcel = self->mParcel;
+
+ // can_agent_modify_parcel deprecated by GROUPS
+// if (!can_agent_modify_parcel(parcel))
+// {
+// self->close();
+// return;
+// }
+
+ parcel->setParcelFlag(PF_FOR_SALE, TRUE);
+ parcel->setSalePrice(sale_price);
+ bool sell_with_objects = false;
+ if ("yes" == self->childGetValue("sell_objects").asString())
+ {
+ sell_with_objects = true;
+ }
+ parcel->setSellWithObjects(sell_with_objects);
+ if ("user" == self->childGetValue("sell_to").asString())
+ {
+ parcel->setAuthorizedBuyerID(self->mAuthorizedBuyer);
+ }
+ else
+ {
+ parcel->setAuthorizedBuyerID(LLUUID::null);
+ }
+
+ // Send update to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ self->close();
+}
diff --git a/indra/newview/llfloatersellland.h b/indra/newview/llfloatersellland.h
new file mode 100755
index 0000000000..077a775ffc
--- /dev/null
+++ b/indra/newview/llfloatersellland.h
@@ -0,0 +1,21 @@
+/**
+ * @file llfloatersellland.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERSELLLAND_H
+#define LL_LLFLOATERSELLLAND_H
+
+class LLParcel;
+class LLViewerRegion;
+
+class LLFloaterSellLand
+{
+public:
+ static void sellLand(LLViewerRegion* region,
+ LLParcel* parcel);
+};
+
+#endif // LL_LLFLOATERSELLLAND_H
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
new file mode 100644
index 0000000000..8219ae8844
--- /dev/null
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -0,0 +1,1523 @@
+/**
+ * @file llfloatersnapshot.cpp
+ * @brief Snapshot preview window, allowing saving, e-mailing, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatersnapshot.h"
+
+#include "llfontgl.h"
+#include "llsys.h"
+#include "llgl.h"
+#include "v3dmath.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "llcriticaldamp.h"
+#include "llui.h"
+#include "llviewertexteditor.h"
+#include "llfocusmgr.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "llviewercontrol.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerstats.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llviewermenu.h"
+#include "llfloaterpostcard.h"
+#include "llcheckboxctrl.h"
+#include "llradiogroup.h"
+#include "lltoolfocus.h"
+#include "lltoolmgr.h"
+#include "llworld.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llimagejpeg.h"
+#include "llimagej2c.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLSnapshotFloaterView* gSnapshotFloaterView = NULL;
+
+LLFloaterSnapshot* LLFloaterSnapshot::sInstance = NULL;
+const F32 SNAPSHOT_TIME_DELAY = 1.f;
+
+F32 SHINE_TIME = 0.5f;
+F32 SHINE_WIDTH = 0.6f;
+F32 SHINE_OPACITY = 0.3f;
+F32 FALL_TIME = 0.6f;
+S32 BORDER_WIDTH = 6;
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+
+///----------------------------------------------------------------------------
+/// Class LLSnapshotLivePreview
+///----------------------------------------------------------------------------
+class LLSnapshotLivePreview : public LLView
+{
+public:
+ enum ESnapshotType
+ {
+ SNAPSHOT_POSTCARD,
+ SNAPSHOT_TEXTURE,
+ SNAPSHOT_BITMAP
+ };
+
+ LLSnapshotLivePreview(const LLRect& rect);
+ ~LLSnapshotLivePreview();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ void setSize(S32 w, S32 h);
+ void getSize(S32& w, S32& h) const;
+ S32 getDataSize() const { return mDataSize; }
+
+ ESnapshotType getSnapshotType() const { return mSnapshotType; }
+ BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; }
+ BOOL isSnapshotActive() { return mSnapshotActive; }
+ LLImageGL* getCurrentImage();
+ F32 getImageAspect();
+ LLRect getImageRect();
+ BOOL isImageScaled();
+
+ void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
+ void setSnapshotQuality(S32 quality);
+ void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
+ void updateSnapshot(BOOL new_snapshot);
+ LLFloaterPostcard* savePostcard();
+ void saveTexture();
+ void saveLocal();
+
+ static void onIdle( void* snapshot_preview );
+
+protected:
+ LLColor4 mColor;
+ LLPointer<LLImageGL> mViewerImage[2];
+ LLRect mImageRect[2];
+ S32 mWidth[2];
+ S32 mHeight[2];
+ BOOL mImageScaled[2];
+
+ S32 mCurImageIndex;
+ LLPointer<LLImageRaw> mRawImage;
+ LLPointer<LLImageRaw> mRawImageEncoded;
+ LLPointer<LLImageJPEG> mJPEGImage;
+ LLFrameTimer mSnapshotDelayTimer;
+ S32 mShineCountdown;
+ LLFrameTimer mShineAnimTimer;
+ F32 mFlashAlpha;
+ BOOL mNeedsFlash;
+ LLVector3d mPosTakenGlobal;
+ S32 mSnapshotQuality;
+ S32 mDataSize;
+ ESnapshotType mSnapshotType;
+ BOOL mSnapshotUpToDate;
+ LLFrameTimer mFallAnimTimer;
+ LLVector3 mCameraPos;
+ LLQuaternion mCameraRot;
+ BOOL mSnapshotActive;
+ LLViewerWindow::ESnapshotType mSnapshotBufferType;
+
+public:
+ static std::set<LLSnapshotLivePreview*> sList;
+};
+
+std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList;
+
+LLSnapshotLivePreview::LLSnapshotLivePreview (const LLRect& rect) :
+ LLView("snapshot_live_preview", rect, FALSE),
+ mColor(1.f, 0.f, 0.f, 0.5f),
+ mCurImageIndex(0),
+ mRawImage(NULL),
+ mRawImageEncoded(NULL),
+ mJPEGImage(NULL),
+ mShineCountdown(0),
+ mFlashAlpha(0.f),
+ mNeedsFlash(TRUE),
+ mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")),
+ mDataSize(0),
+ mSnapshotType(SNAPSHOT_POSTCARD),
+ mSnapshotUpToDate(FALSE),
+ mCameraPos(gCamera->getOrigin()),
+ mCameraRot(gCamera->getQuaternion()),
+ mSnapshotActive(FALSE),
+ mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
+{
+ mSnapshotDelayTimer.setTimerExpirySec(0.0f);
+ mSnapshotDelayTimer.start();
+// gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.insert(this);
+ setFollowsAll();
+ mWidth[0] = gViewerWindow->getWindowDisplayWidth();
+ mWidth[1] = gViewerWindow->getWindowDisplayWidth();
+ mHeight[0] = gViewerWindow->getWindowDisplayHeight();
+ mHeight[1] = gViewerWindow->getWindowDisplayHeight();
+ mImageScaled[0] = FALSE;
+ mImageScaled[1] = FALSE;
+}
+
+LLSnapshotLivePreview::~LLSnapshotLivePreview()
+{
+ // delete images
+ mRawImage = NULL;
+ mRawImageEncoded = NULL;
+ mJPEGImage = NULL;
+
+// gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.erase(this);
+}
+
+LLImageGL* LLSnapshotLivePreview::getCurrentImage()
+{
+ return mViewerImage[mCurImageIndex];
+}
+
+F32 LLSnapshotLivePreview::getImageAspect()
+{
+ if (!mViewerImage[mCurImageIndex])
+ {
+ return 0.f;
+ }
+
+ F32 image_aspect_ratio = ((F32)mWidth[mCurImageIndex]) / ((F32)mHeight[mCurImageIndex]);
+ F32 window_aspect_ratio = ((F32)mRect.getWidth()) / ((F32)mRect.getHeight());
+
+ if (gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ return image_aspect_ratio;
+ }
+ else
+ {
+ return window_aspect_ratio;
+ }
+}
+
+LLRect LLSnapshotLivePreview::getImageRect()
+{
+ return mImageRect[mCurImageIndex];
+}
+
+BOOL LLSnapshotLivePreview::isImageScaled()
+{
+ return mImageScaled[mCurImageIndex];
+}
+
+void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot)
+{
+ if (mSnapshotUpToDate)
+ {
+ S32 old_image_index = mCurImageIndex;
+ mCurImageIndex = (mCurImageIndex + 1) % 2;
+ mWidth[mCurImageIndex] = mWidth[old_image_index];
+ mHeight[mCurImageIndex] = mHeight[old_image_index];
+ mFallAnimTimer.start();
+ }
+ mSnapshotUpToDate = FALSE;
+ mShineAnimTimer.stop();
+ if (new_snapshot)
+ {
+ mSnapshotDelayTimer.start();
+ mSnapshotDelayTimer.setTimerExpirySec(SNAPSHOT_TIME_DELAY);
+ }
+
+ LLRect& rect = mImageRect[mCurImageIndex];
+ rect.set(0, mRect.getHeight(), mRect.getWidth(), 0);
+
+ F32 image_aspect_ratio = ((F32)mWidth[mCurImageIndex]) / ((F32)mHeight[mCurImageIndex]);
+ F32 window_aspect_ratio = ((F32)mRect.getWidth()) / ((F32)mRect.getHeight());
+
+ if (gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ if (image_aspect_ratio > window_aspect_ratio)
+ {
+ // trim off top and bottom
+ S32 new_height = llround((F32)mRect.getWidth() / image_aspect_ratio);
+ rect.mBottom += (mRect.getHeight() - new_height) / 2;
+ rect.mTop -= (mRect.getHeight() - new_height) / 2;
+ }
+ else if (image_aspect_ratio < window_aspect_ratio)
+ {
+ // trim off left and right
+ S32 new_width = llround((F32)mRect.getHeight() * image_aspect_ratio);
+ rect.mLeft += (mRect.getWidth() - new_width) / 2;
+ rect.mRight -= (mRect.getWidth() - new_width) / 2;
+ }
+ }
+}
+
+void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
+{
+ if (quality != mSnapshotQuality)
+ {
+ mSnapshotQuality = quality;
+ gSavedSettings.setS32("SnapshotQuality", quality);
+ }
+}
+
+EWidgetType LLSnapshotLivePreview::getWidgetType() const
+{
+ return WIDGET_TYPE_SNAPSHOT_LIVE_PREVIEW;
+}
+
+LLString LLSnapshotLivePreview::getWidgetTag() const
+{
+ return LL_SNAPSHOT_LIVE_PREVIEW_TAG;
+}
+
+void LLSnapshotLivePreview::draw()
+{
+ if(getVisible())
+ {
+ if (mViewerImage[mCurImageIndex].notNull() &&
+ mRawImageEncoded.notNull() &&
+ mSnapshotUpToDate)
+ {
+ LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f);
+ gl_rect_2d(mRect, bg_color);
+ LLRect &rect = mImageRect[mCurImageIndex];
+ LLRect shadow_rect = mImageRect[mCurImageIndex];
+ shadow_rect.stretch(BORDER_WIDTH);
+ gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10);
+
+ LLGLSTexture set_texture;
+ LLColor4 image_color(1.f, 1.f, 1.f, 1.f);
+ glColor4fv(image_color.mV);
+ LLViewerImage::bindTexture(mViewerImage[mCurImageIndex]);
+ // calculate UV scale
+ F32 uv_width = mImageScaled[mCurImageIndex] ? 1.f : llmin((F32)mWidth[mCurImageIndex] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f);
+ F32 uv_height = mImageScaled[mCurImageIndex] ? 1.f : llmin((F32)mHeight[mCurImageIndex] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f);
+ glPushMatrix();
+ {
+ glTranslatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f);
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(uv_width, uv_height);
+ glVertex2i(rect.getWidth(), rect.getHeight() );
+
+ glTexCoord2f(0.f, uv_height);
+ glVertex2i(0, rect.getHeight() );
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(uv_width, 0.f);
+ glVertex2i(rect.getWidth(), 0);
+ }
+ glEnd();
+ }
+ glPopMatrix();
+
+ glColor4f(1.f, 1.f, 1.f, mFlashAlpha);
+ gl_rect_2d(mRect);
+ if (mNeedsFlash)
+ {
+ if (mFlashAlpha < 1.f)
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f));
+ }
+ else
+ {
+ mNeedsFlash = FALSE;
+ }
+ }
+ else
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ if (mShineCountdown > 0)
+ {
+ mShineCountdown--;
+ if (mShineCountdown == 0)
+ {
+ mShineAnimTimer.start();
+ }
+ }
+ else if (mShineAnimTimer.getStarted())
+ {
+ //LLDebugVarMessageBox::show("Shine time", &SHINE_TIME, 10.f, 0.1f);
+ //LLDebugVarMessageBox::show("Shine width", &SHINE_WIDTH, 2.f, 0.05f);
+ //LLDebugVarMessageBox::show("Shine opacity", &SHINE_OPACITY, 1.f, 0.05f);
+
+ F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
+
+ // draw "shine" effect
+ LLGLEnable scissor_test(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+ {
+ // draw diagonal stripe with gradient that passes over screen
+ S32 x1 = gViewerWindow->getWindowWidth() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
+ S32 x2 = x1 + llround(gViewerWindow->getWindowWidth() * SHINE_WIDTH);
+ S32 x3 = x2 + llround(gViewerWindow->getWindowWidth() * SHINE_WIDTH);
+ S32 y1 = 0;
+ S32 y2 = gViewerWindow->getWindowHeight();
+
+ LLGLSNoTexture no_texture;
+ glBegin(GL_QUADS);
+ {
+ glColor4f(1.f, 1.f, 1.f, 0.f);
+ glVertex2i(x1, y1);
+ glVertex2i(x1 + gViewerWindow->getWindowWidth(), y2);
+ glColor4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ glVertex2i(x2 + gViewerWindow->getWindowWidth(), y2);
+ glVertex2i(x2, y1);
+
+ glColor4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ glVertex2i(x2, y1);
+ glVertex2i(x2 + gViewerWindow->getWindowWidth(), y2);
+ glColor4f(1.f, 1.f, 1.f, 0.f);
+ glVertex2i(x3 + gViewerWindow->getWindowWidth(), y2);
+ glVertex2i(x3, y1);
+ }
+ glEnd();
+ }
+
+ if (mShineAnimTimer.getElapsedTimeF32() > SHINE_TIME)
+ {
+ mShineAnimTimer.stop();
+ }
+ }
+ }
+
+ // draw framing rectangle
+ {
+ LLGLSNoTexture no_texture;
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ LLRect outline_rect = mImageRect[mCurImageIndex];
+ glBegin(GL_QUADS);
+ {
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ glVertex2i(outline_rect.mRight, outline_rect.mTop);
+ glVertex2i(outline_rect.mLeft, outline_rect.mTop);
+
+ glVertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ glVertex2i(outline_rect.mRight, outline_rect.mBottom);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+
+ glVertex2i(outline_rect.mLeft, outline_rect.mTop);
+ glVertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+
+ glVertex2i(outline_rect.mRight, outline_rect.mBottom);
+ glVertex2i(outline_rect.mRight, outline_rect.mTop);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ }
+ glEnd();
+ }
+
+ // draw old image dropping away
+ if (mFallAnimTimer.getStarted())
+ {
+ S32 old_image_index = (mCurImageIndex + 1) % 2;
+ if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME)
+ {
+ LLGLSTexture texture_set;
+
+ F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME;
+ F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f);
+ LLColor4 image_color(1.f, 1.f, 1.f, alpha);
+ glColor4fv(image_color.mV);
+ LLViewerImage::bindTexture(mViewerImage[old_image_index]);
+ // calculate UV scale
+ //FIXME get this to work with old image
+ BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull();
+ F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f;
+ F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f;
+ glPushMatrix();
+ {
+ LLRect& rect = mImageRect[old_image_index];
+ glTranslatef((F32)rect.mLeft, (F32)rect.mBottom - llround(mRect.getHeight() * 2.f * (fall_interp * fall_interp)), 0.f);
+ glRotatef(-45.f * fall_interp, 0.f, 0.f, 1.f);
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(uv_width, uv_height);
+ glVertex2i(rect.getWidth(), rect.getHeight() );
+
+ glTexCoord2f(0.f, uv_height);
+ glVertex2i(0, rect.getHeight() );
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(uv_width, 0.f);
+ glVertex2i(rect.getWidth(), 0);
+ }
+ glEnd();
+ }
+ glPopMatrix();
+ }
+ }
+ }
+}
+
+/*virtual*/
+void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLRect old_rect = mRect;
+ LLView::reshape(width, height, called_from_parent);
+ if (old_rect.getWidth() != width || old_rect.getHeight() != height)
+ {
+ updateSnapshot(getSnapshotUpToDate());
+ }
+}
+
+//static
+void LLSnapshotLivePreview::onIdle( void* snapshot_preview )
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
+
+ LLVector3 new_camera_pos = gCamera->getOrigin();
+ LLQuaternion new_camera_rot = gCamera->getQuaternion();
+ if (gSavedSettings.getBOOL("FreezeTime") &&
+ (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
+ {
+ previewp->mCameraPos = new_camera_pos;
+ previewp->mCameraRot = new_camera_rot;
+ // request a new snapshot whenever the camera moves, with a time delay
+ previewp->updateSnapshot(gSavedSettings.getBOOL("AutoSnapshot"));
+ }
+
+ previewp->mSnapshotActive = (previewp->mSnapshotDelayTimer.getStarted() &&
+ previewp->mSnapshotDelayTimer.hasExpired());
+
+ // don't take snapshots while ALT-zoom active
+ if (gFocusMgr.getMouseCapture() == gToolCamera)
+ {
+ previewp->mSnapshotActive = FALSE;
+ }
+
+ if (previewp->mSnapshotActive)
+ {
+ if (!previewp->mRawImage)
+ {
+ previewp->mRawImage = new LLImageRaw;
+ }
+
+ if (!previewp->mRawImageEncoded)
+ {
+ previewp->mRawImageEncoded = new LLImageRaw;
+ }
+
+ previewp->setVisible(FALSE);
+ previewp->setEnabled(FALSE);
+
+ previewp->getWindow()->incBusyCount();
+ previewp->mImageScaled[previewp->mCurImageIndex] = FALSE;
+
+ // do update
+ if(gViewerWindow->rawSnapshot(previewp->mRawImage,
+ previewp->mWidth[previewp->mCurImageIndex],
+ previewp->mHeight[previewp->mCurImageIndex],
+ !gSavedSettings.getBOOL("KeepAspectForSnapshot"),
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ previewp->mSnapshotBufferType))
+ {
+ previewp->mRawImageEncoded->resize(previewp->mRawImage->getWidth(), previewp->mRawImage->getHeight(), previewp->mRawImage->getComponents());
+
+ if (previewp->getSnapshotType() == SNAPSHOT_POSTCARD)
+ {
+ //FIXME: just resize and reuse existing jpeg?
+ previewp->mJPEGImage = NULL; // deletes image
+ previewp->mJPEGImage = new LLImageJPEG();
+ previewp->mJPEGImage->setEncodeQuality(llclamp(previewp->mSnapshotQuality, 0, 100));
+ if (previewp->mJPEGImage->encode(previewp->mRawImage))
+ {
+ previewp->mDataSize = previewp->mJPEGImage->getDataSize();
+ previewp->mJPEGImage->decode(previewp->mRawImageEncoded);
+ }
+ }
+ else if (previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
+ {
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(previewp->mRawImage->getData(),
+ previewp->mRawImage->getWidth(),
+ previewp->mRawImage->getHeight(),
+ previewp->mRawImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(512);
+ previewp->mImageScaled[previewp->mCurImageIndex] = TRUE;
+ if (formatted->encode(scaled))
+ {
+ previewp->mDataSize = formatted->getDataSize();
+ formatted->decode(previewp->mRawImageEncoded);
+ }
+ }
+ else
+ {
+ previewp->mRawImageEncoded->copy(previewp->mRawImage);
+ previewp->mDataSize = previewp->mRawImage->getDataSize();
+ }
+
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(previewp->mRawImageEncoded->getData(),
+ previewp->mRawImageEncoded->getWidth(),
+ previewp->mRawImageEncoded->getHeight(),
+ previewp->mRawImageEncoded->getComponents());
+
+ // leave original image dimensions, just scale up texture buffer
+ if (previewp->mRawImageEncoded->getWidth() > 1024 || previewp->mRawImageEncoded->getHeight() > 1024)
+ {
+ // go ahead and shrink image to appropriate power of 2 for display
+ scaled->biasedScaleToPowerOfTwo(1024);
+ previewp->mImageScaled[previewp->mCurImageIndex] = TRUE;
+ }
+ else
+ {
+ // expand image but keep original image data intact
+ scaled->expandToPowerOfTwo(1024, FALSE);
+ }
+
+ previewp->mViewerImage[previewp->mCurImageIndex] = new LLImageGL(scaled, FALSE);
+ previewp->mViewerImage[previewp->mCurImageIndex]->setMipFilterNearest(previewp->getSnapshotType() != SNAPSHOT_TEXTURE);
+ LLViewerImage::bindTexture(previewp->mViewerImage[previewp->mCurImageIndex]);
+ previewp->mViewerImage[previewp->mCurImageIndex]->setClamp(TRUE, TRUE);
+
+ previewp->mSnapshotUpToDate = TRUE;
+
+ previewp->mPosTakenGlobal = gAgent.getCameraPositionGlobal();
+ previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
+ }
+ previewp->getWindow()->decBusyCount();
+ // only show fullscreen preview when in freeze frame mode
+ previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
+ previewp->mSnapshotDelayTimer.stop();
+ previewp->mSnapshotActive = FALSE;
+ }
+}
+
+void LLSnapshotLivePreview::setSize(S32 w, S32 h)
+{
+ mWidth[mCurImageIndex] = w;
+ mHeight[mCurImageIndex] = h;
+}
+
+void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
+{
+ w = mWidth[mCurImageIndex];
+ h = mHeight[mCurImageIndex];
+}
+
+LLFloaterPostcard* LLSnapshotLivePreview::savePostcard()
+{
+ // calculate and pass in image scale in case image data only use portion
+ // of viewerimage buffer
+ LLVector2 image_scale(1.f, 1.f);
+ if (!isImageScaled())
+ {
+ image_scale.setVec(llmin(1.f, (F32)mWidth[mCurImageIndex] / (F32)getCurrentImage()->getWidth()), llmin(1.f, (F32)mHeight[mCurImageIndex] / (F32)getCurrentImage()->getHeight()));
+ }
+
+
+ LLFloaterPostcard* floater = LLFloaterPostcard::showFromSnapshot(mJPEGImage, mViewerImage[mCurImageIndex], image_scale, mPosTakenGlobal);
+ // relinquish lifetime of viewerimage and jpeg image to postcard floater
+ mViewerImage[mCurImageIndex] = NULL;
+ mJPEGImage = NULL;
+
+ return floater;
+}
+
+void LLSnapshotLivePreview::saveTexture()
+{
+ // gen a new uuid for this asset
+ LLTransactionID tid;
+ tid.generate();
+ LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(mRawImage->getData(),
+ mRawImage->getWidth(),
+ mRawImage->getHeight(),
+ mRawImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(512);
+
+ if (formatted->encode(scaled))
+ {
+ LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE);
+ std::string pos_string;
+ gAgent.buildLocationString(pos_string);
+ upload_new_resource(tid, // tid
+ LLAssetType::AT_TEXTURE,
+ "Snapshot",
+ pos_string,
+ 0,
+ LLAssetType::AT_SNAPSHOT_CATEGORY,
+ LLInventoryType::IT_SNAPSHOT,
+ PERM_ALL,
+ "Snapshot");
+ }
+ else
+ {
+ gViewerWindow->alertXml("ErrorEncodingSnapshot");
+ llwarns << "Error encoding snapshot" << llendl;
+ }
+
+ gViewerStats->incStat(LLViewerStats::ST_SNAPSHOT_COUNT );
+}
+
+void LLSnapshotLivePreview::saveLocal()
+{
+ gViewerWindow->saveImageNumbered(mRawImage);
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterSnapshot::Impl
+///----------------------------------------------------------------------------
+
+class LLFloaterSnapshot::Impl
+{
+public:
+ Impl() : mLastToolset(NULL)
+ {
+ }
+ ~Impl()
+ {
+ //unpause avatars
+ mAvatarPauseHandles.clear();
+
+ }
+ static void onClickDiscard(void* data);
+ static void onClickKeep(void* data);
+ static void onClickNewSnapshot(void* data);
+ static void onClickAutoSnap(LLUICtrl *ctrl, void* data);
+ static void onClickUICheck(LLUICtrl *ctrl, void* data);
+ static void onClickHUDCheck(LLUICtrl *ctrl, void* data);
+ static void onClickKeepOpenCheck(LLUICtrl *ctrl, void* data);
+ static void onClickKeepAspectCheck(LLUICtrl *ctrl, void* data);
+ static void onCommitQuality(LLUICtrl* ctrl, void* data);
+ static void onCommitResolution(LLUICtrl* ctrl, void* data);
+ static void onCommitFreezeFrame(LLUICtrl* ctrl, void* data);
+ static void onCommitLayerTypes(LLUICtrl* ctrl, void*data);
+ static void onCommitSnapshotType(LLUICtrl* ctrl, void* data);
+ static void onCommitCustomResolution(LLUICtrl *ctrl, void* data);
+
+ static LLSnapshotLivePreview* getPreviewView(LLFloaterSnapshot *floater);
+ static void setResolution(LLFloaterSnapshot* floater, const std::string& comboname);
+ static void updateControls(LLFloaterSnapshot* floater);
+ static void updateLayout(LLFloaterSnapshot* floater);
+
+ static LLViewHandle sPreviewHandle;
+
+private:
+ static LLSnapshotLivePreview::ESnapshotType getTypeIndex(LLFloaterSnapshot* floater);
+ static LLViewerWindow::ESnapshotType getLayerType(LLFloaterSnapshot* floater);
+ static void comboSetCustom(LLFloaterSnapshot *floater, const std::string& comboname);
+ static void checkAutoSnapshot(LLSnapshotLivePreview* floater);
+
+public:
+ std::vector<LLAnimPauseRequest> mAvatarPauseHandles;
+
+ LLToolset* mLastToolset;
+};
+
+// static
+LLViewHandle LLFloaterSnapshot::Impl::sPreviewHandle;
+
+// static
+LLSnapshotLivePreview* LLFloaterSnapshot::Impl::getPreviewView(LLFloaterSnapshot *floater)
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)LLView::getViewByHandle(sPreviewHandle);
+ return previewp;
+}
+
+// static
+LLSnapshotLivePreview::ESnapshotType LLFloaterSnapshot::Impl::getTypeIndex(LLFloaterSnapshot* floater)
+{
+ LLSnapshotLivePreview::ESnapshotType index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;
+ LLSD value = floater->childGetValue("snapshot_type_radio");
+ const std::string id = value.asString();
+ if (id == "postcard")
+ index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;
+ else if (id == "texture")
+ index = LLSnapshotLivePreview::SNAPSHOT_TEXTURE;
+ else if (id == "local")
+ index = LLSnapshotLivePreview::SNAPSHOT_BITMAP;
+ return index;
+}
+
+// static
+LLViewerWindow::ESnapshotType LLFloaterSnapshot::Impl::getLayerType(LLFloaterSnapshot* floater)
+{
+ LLViewerWindow::ESnapshotType type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ LLSD value = floater->childGetValue("layer_types");
+ const std::string id = value.asString();
+ if (id == "colors")
+ type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ else if (id == "depth")
+ type = LLViewerWindow::SNAPSHOT_TYPE_DEPTH;
+ else if (id == "objects")
+ type = LLViewerWindow::SNAPSHOT_TYPE_OBJECT_ID;
+ return type;
+}
+
+// static
+void LLFloaterSnapshot::Impl::setResolution(LLFloaterSnapshot* floater, const std::string& comboname)
+{
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(floater, comboname);
+ if (combo)
+ {
+ combo->setVisible(TRUE);
+ onCommitResolution(combo, floater);
+ }
+}
+
+//static
+void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)
+{
+ LLSnapshotLivePreview* previewp = getPreviewView(floaterp);
+ if (floaterp->childGetValue("freeze_frame_check").asBoolean())
+ {
+ // stop all mouse events at fullscreen preview layer
+ floaterp->getParent()->setMouseOpaque(TRUE);
+
+ // shrink to smaller layout
+ floaterp->reshape(floaterp->mRect.getWidth(), 410);
+
+ // can see and interact with fullscreen preview now
+ if (previewp)
+ {
+ previewp->setVisible(TRUE);
+ previewp->setEnabled(TRUE);
+ }
+
+ //RN: freeze all avatars
+ LLCharacter* avatarp;
+ for (avatarp = LLCharacter::sInstances.getFirstData(); avatarp; avatarp = LLCharacter::sInstances.getNextData())
+ {
+ sInstance->impl.mAvatarPauseHandles.push_back(avatarp->requestPause());
+ }
+
+ // freeze everything else
+ gSavedSettings.setBOOL("FreezeTime", TRUE);
+
+ if (gCurrentToolset != gCameraToolset)
+ {
+ sInstance->impl.mLastToolset = gCurrentToolset;
+ gCurrentToolset = gCameraToolset;
+ if (gToolMgr)
+ {
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+ }
+ }
+ else // turning off freeze frame mode
+ {
+ floaterp->getParent()->setMouseOpaque(FALSE);
+ floaterp->reshape(floaterp->mRect.getWidth(), 510);
+ if (previewp)
+ {
+ previewp->setVisible(FALSE);
+ previewp->setEnabled(FALSE);
+ }
+
+ //RN: thaw all avatars
+ sInstance->impl.mAvatarPauseHandles.clear();
+
+ // thaw everything else
+ gSavedSettings.setBOOL("FreezeTime", FALSE);
+
+ // restore last tool (e.g. pie menu, etc)
+ if (sInstance->impl.mLastToolset)
+ {
+ gCurrentToolset = sInstance->impl.mLastToolset;
+ if (gToolMgr)
+ {
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+ }
+ }
+}
+
+
+// static
+void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
+{
+ LLRadioGroup* snapshot_type_radio = LLUICtrlFactory::getRadioGroupByName(floater, "snapshot_type_radio");
+ snapshot_type_radio->setSelectedIndex(gSavedSettings.getS32("LastSnapshotType"));
+ LLSnapshotLivePreview::ESnapshotType shot_type = getTypeIndex(floater);
+ LLViewerWindow::ESnapshotType layer_type = getLayerType(floater);
+
+ floater->childSetVisible("postcard_size_combo", FALSE);
+ floater->childSetVisible("texture_size_combo", FALSE);
+ floater->childSetVisible("local_size_combo", FALSE);
+
+ LLComboBox* combo;
+ combo = LLUICtrlFactory::getComboBoxByName(floater, "postcard_size_combo");
+ if (combo) combo->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution"));
+ combo = LLUICtrlFactory::getComboBoxByName(floater, "texture_size_combo");
+ if (combo) combo->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution"));
+ combo = LLUICtrlFactory::getComboBoxByName(floater, "local_size_combo");
+ if (combo) combo->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution"));
+
+
+ floater->childSetVisible("upload_btn", FALSE);
+ floater->childSetVisible("send_btn", FALSE);
+ floater->childSetVisible("save_btn", FALSE);
+
+ switch(shot_type)
+ {
+ case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:
+ layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ floater->childSetValue("layer_types", "colors");
+ floater->childSetEnabled("layer_types", FALSE);
+ floater->childSetEnabled("image_quality_slider", TRUE);
+ setResolution(floater, "postcard_size_combo");
+ floater->childSetVisible("send_btn", TRUE);
+ break;
+ case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
+ layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ floater->childSetValue("layer_types", "colors");
+ floater->childSetEnabled("layer_types", FALSE);
+ floater->childSetEnabled("image_quality_slider", FALSE);
+ setResolution(floater, "texture_size_combo");
+ floater->childSetVisible("upload_btn", TRUE);
+ break;
+ case LLSnapshotLivePreview::SNAPSHOT_BITMAP:
+ floater->childSetEnabled("layer_types", TRUE);
+ floater->childSetEnabled("image_quality_slider", FALSE);
+ setResolution(floater, "local_size_combo");
+ floater->childSetVisible("save_btn", TRUE);
+ break;
+ default:
+ break;
+ }
+ LLSnapshotLivePreview* previewp = getPreviewView(floater);
+ if (previewp)
+ {
+ previewp->setSnapshotType(shot_type);
+ previewp->setSnapshotBufferType(layer_type);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::checkAutoSnapshot(LLSnapshotLivePreview* previewp)
+{
+ if (previewp)
+ {
+ previewp->updateSnapshot(gSavedSettings.getBOOL("AutoSnapshot"));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickDiscard(void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ view->close();
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickKeep(void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+
+ if (previewp)
+ {
+ if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_POSTCARD)
+ {
+ LLFloaterPostcard* floater = previewp->savePostcard();
+ // if still in snapshot mode, put postcard floater in snapshot floaterview
+ // and link it to snapshot floater
+ if (!gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
+ {
+ gFloaterView->removeChild(floater);
+ gSnapshotFloaterView->addChild(floater);
+ view->addDependentFloater(floater, FALSE);
+ }
+ }
+ else if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE)
+ {
+ previewp->saveTexture();
+ }
+ else
+ {
+ previewp->saveLocal();
+ }
+
+ if (gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
+ {
+ view->close();
+ // only plays sound and anim when keeping a snapshot, and closing the snapshot UI
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ else
+ {
+ checkAutoSnapshot(previewp);
+ }
+ }
+
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickNewSnapshot(void* data)
+{
+ LLSnapshotLivePreview* previewp = getPreviewView((LLFloaterSnapshot *)data);
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (previewp && view)
+ {
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickAutoSnap(LLUICtrl *ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "AutoSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickUICheck(LLUICtrl *ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "RenderUIInSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickHUDCheck(LLUICtrl *ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "RenderHUDInSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickKeepOpenCheck(LLUICtrl* ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+
+ gSavedSettings.setBOOL( "CloseSnapshotOnKeep", !check->get() );
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickKeepAspectCheck(LLUICtrl* ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "KeepAspectForSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitQuality(LLUICtrl* ctrl, void* data)
+{
+ LLSliderCtrl* slider = (LLSliderCtrl*)ctrl;
+ S32 quality_val = llfloor((F32)slider->getValue().asReal());
+
+ LLSnapshotLivePreview* previewp = getPreviewView((LLFloaterSnapshot *)data);
+ if (previewp)
+ {
+ previewp->setSnapshotQuality(quality_val);
+ }
+ checkAutoSnapshot(previewp);
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitFreezeFrame(LLUICtrl* ctrl, void* data)
+{
+ LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+
+ if (!view || !check_box)
+ {
+ return;
+ }
+
+ gSavedSettings.setBOOL("UseFreezeFrame", check_box->get());
+
+ updateLayout(view);
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitResolution(LLUICtrl* ctrl, void* data)
+{
+ LLComboBox* combobox = (LLComboBox*)ctrl;
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+
+ if (!view || !combobox)
+ {
+ return;
+ }
+
+ // save off all selected resolution values
+ LLComboBox* combo;
+ combo = LLUICtrlFactory::getComboBoxByName(view, "postcard_size_combo");
+ gSavedSettings.setS32("SnapshotPostcardLastResolution", combo->getCurrentIndex());
+ combo = LLUICtrlFactory::getComboBoxByName(view, "texture_size_combo");
+ gSavedSettings.setS32("SnapshotTextureLastResolution", combo->getCurrentIndex());
+ combo = LLUICtrlFactory::getComboBoxByName(view, "local_size_combo");
+ gSavedSettings.setS32("SnapshotLocalLastResolution", combo->getCurrentIndex());
+
+ std::string sdstring = combobox->getSimpleSelectedValue();
+ LLSD sdres;
+ std::stringstream sstream(sdstring);
+ LLSDSerialize::fromNotation(sdres, sstream);
+
+ S32 width = sdres[0];
+ S32 height = sdres[1];
+
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp && combobox->getCurrentIndex() >= 0)
+ {
+ if (width == 0 || height == 0)
+ {
+ previewp->setSize(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight());
+ }
+ else if (width == -1 || height == -1)
+ {
+ // leave width and height when entering custom value
+ }
+ else
+ {
+ previewp->setSize(width, height);
+ }
+
+ previewp->getSize(width, height);
+ view->childSetValue("snapshot_width", width);
+ view->childSetValue("snapshot_height", height);
+ // hide old preview as the aspect ratio could be wrong
+ checkAutoSnapshot(previewp);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitLayerTypes(LLUICtrl* ctrl, void*data)
+{
+ LLComboBox* combobox = (LLComboBox*)ctrl;
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+
+ if (view)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp)
+ {
+ previewp->setSnapshotBufferType((LLViewerWindow::ESnapshotType)combobox->getCurrentIndex());
+ }
+ checkAutoSnapshot(previewp);
+ }
+}
+
+//static
+void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ gSavedSettings.setS32("LastSnapshotType", getTypeIndex(view));
+ getPreviewView(view)->updateSnapshot(TRUE);
+ updateControls(view);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::comboSetCustom(LLFloaterSnapshot* floater, const std::string& comboname)
+{
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(floater, comboname);
+ if (combo)
+ {
+ combo->setCurrentByIndex(combo->getItemCount() - 1);
+ }
+}
+
+//static
+void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp)
+ {
+ S32 curw,curh;
+ previewp->getSize(curw, curh);
+
+ S32 w = llfloor((F32)view->childGetValue("snapshot_width").asReal());
+ S32 h = llfloor((F32)view->childGetValue("snapshot_height").asReal());
+
+ if (w != curw || h != curh)
+ {
+ previewp->setSize(w,h);
+ checkAutoSnapshot(previewp);
+ comboSetCustom(view, "postcard_size_combo");
+ comboSetCustom(view, "texture_size_combo");
+ comboSetCustom(view, "local_size_combo");
+ }
+ }
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterSnapshot
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFloaterSnapshot::LLFloaterSnapshot()
+ : LLFloater("Snapshot Floater"),
+ impl (*(new Impl))
+{
+}
+
+// Destroys the object
+LLFloaterSnapshot::~LLFloaterSnapshot()
+{
+ if (sInstance == this)
+ {
+ delete LLView::getViewByHandle(Impl::sPreviewHandle);
+ Impl::sPreviewHandle = LLViewHandle::sDeadHandle;
+ sInstance = NULL;
+ }
+
+ //unfreeze everything else
+ gSavedSettings.setBOOL("FreezeTime", FALSE);
+
+ if (impl.mLastToolset)
+ {
+ gCurrentToolset = impl.mLastToolset;
+ if (gToolMgr && gCurrentToolset)
+ {
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+ }
+
+ delete &impl;
+}
+
+BOOL LLFloaterSnapshot::postBuild()
+{
+ childSetCommitCallback("snapshot_type_radio", Impl::onCommitSnapshotType, this);
+
+ childSetAction("new_snapshot_btn", Impl::onClickNewSnapshot, this);
+
+ childSetValue("auto_snapshot_check", gSavedSettings.getBOOL("AutoSnapshot"));
+ childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this);
+
+ childSetAction("upload_btn", Impl::onClickKeep, this);
+ childSetAction("send_btn", Impl::onClickKeep, this);
+ childSetAction("save_btn", Impl::onClickKeep, this);
+ childSetAction("discard_btn", Impl::onClickDiscard, this);
+
+ childSetCommitCallback("image_quality_slider", Impl::onCommitQuality, this);
+ childSetValue("image_quality_slider", gSavedSettings.getS32("SnapshotQuality"));
+
+ childSetCommitCallback("snapshot_width", Impl::onCommitCustomResolution, this);
+
+ childSetCommitCallback("snapshot_height", Impl::onCommitCustomResolution, this);
+
+ childSetCommitCallback("ui_check", Impl::onClickUICheck, this);
+
+ childSetCommitCallback("hud_check", Impl::onClickHUDCheck, this);
+ childSetValue("hud_check", gSavedSettings.getBOOL("RenderHUDInSnapshot"));
+
+ childSetCommitCallback("keep_open_check", Impl::onClickKeepOpenCheck, this);
+ childSetValue("keep_open_check", !gSavedSettings.getBOOL("CloseSnapshotOnKeep"));
+
+ childSetCommitCallback("keep_aspect_check", Impl::onClickKeepAspectCheck, this);
+ childSetValue("keep_aspect_check", gSavedSettings.getBOOL("KeepAspectForSnapshot"));
+
+ childSetCommitCallback("layer_types", Impl::onCommitLayerTypes, this);
+ childSetValue("layer_types", "colors");
+ childSetEnabled("layer_types", FALSE);
+
+ childSetValue("snapshot_width", gViewerWindow->getWindowDisplayWidth());
+ childSetValue("snapshot_height", gViewerWindow->getWindowDisplayHeight());
+
+ childSetValue("freeze_frame_check", gSavedSettings.getBOOL("UseFreezeFrame"));
+ childSetCommitCallback("freeze_frame_check", Impl::onCommitFreezeFrame, this);
+
+ childSetCommitCallback("postcard_size_combo", Impl::onCommitResolution, this);
+ childSetCommitCallback("texture_size_combo", Impl::onCommitResolution, this);
+ childSetCommitCallback("local_size_combo", Impl::onCommitResolution, this);
+
+ // create preview window
+ LLRect full_screen_rect = sInstance->getRootView()->getRect();
+ LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(full_screen_rect);
+ sInstance->getRootView()->removeChild(gSnapshotFloaterView);
+ // make sure preview is below snapshot floater
+ sInstance->getRootView()->addChild(previewp);
+ sInstance->getRootView()->addChild(gSnapshotFloaterView);
+
+ Impl::sPreviewHandle = previewp->mViewHandle;
+
+ impl.updateControls(this);
+
+ return TRUE;
+}
+
+void LLFloaterSnapshot::draw()
+{
+ LLSnapshotLivePreview* previewp = impl.getPreviewView(this);
+
+ if (previewp && previewp->isSnapshotActive())
+ {
+ // don't render snapshot window in snapshot, even if "show ui" is turned on
+ return;
+ }
+
+ if(getVisible() && !mMinimized)
+ {
+ if (previewp && previewp->getDataSize() > 0)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ LLString bytes_string;
+ if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_POSTCARD &&
+ previewp->getDataSize() > MAX_POSTCARD_DATASIZE)
+ {
+ childSetColor("file_size_label", LLColor4::red);
+ childSetEnabled("send_btn", FALSE);
+ }
+ else
+ {
+ childSetColor("file_size_label", gColors.getColor( "LabelTextColor" ));
+ childSetEnabled("send_btn", previewp->getSnapshotUpToDate());
+ }
+
+ //XUI:translate
+ if (previewp->getSnapshotUpToDate())
+ {
+ LLString bytes_string;
+ gResMgr->getIntegerString(bytes_string, previewp->getDataSize());
+ childSetTextArg("file_size_label", "[SIZE]", llformat("%s bytes", bytes_string.c_str()));
+ }
+ else
+ {
+ childSetTextArg("file_size_label", "[SIZE]", "unknown");
+ childSetColor("file_size_label", gColors.getColor( "LabelTextColor" ));
+ }
+ childSetEnabled("upload_btn", previewp->getSnapshotUpToDate());
+ childSetEnabled("save_btn", previewp->getSnapshotUpToDate());
+
+ }
+ else
+ {
+ childSetTextArg("file_size_label", "[SIZE]", "unknown");
+ childSetEnabled("upload_btn", FALSE);
+ childSetEnabled("send_btn", FALSE);
+ childSetEnabled("save_btn", FALSE);
+ }
+
+ BOOL ui_in_snapshot = gSavedSettings.getBOOL("RenderUIInSnapshot");
+ childSetValue("ui_check", ui_in_snapshot);
+ childSetToolTip("ui_check", "If selected shows the UI in the snapshot");
+ }
+
+ LLFloater::draw();
+
+ // draw snapshot thumbnail if not in fullscreen preview mode
+ if (!gSavedSettings.getBOOL("UseFreezeFrame") && previewp && previewp->getCurrentImage() && previewp->getSnapshotUpToDate())
+ {
+ F32 aspect = previewp->getImageAspect();
+ // UI size for thumbnail
+ S32 max_width = mRect.getWidth() - 20;
+ S32 max_height = 90;
+
+ S32 img_render_width = 0;
+ S32 img_render_height = 0;
+ if (aspect > max_width / max_height)
+ {
+ // image too wide, shrink to width
+ img_render_width = max_width;
+ img_render_height = llround((F32)max_width / aspect);
+ }
+ else
+ {
+ // image too tall, shrink to height
+ img_render_height = max_height;
+ img_render_width = llround((F32)max_height * aspect);
+ }
+ S32 image_width, image_height;
+ previewp->getSize(image_width, image_height);
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ {
+ // handle case where image is only a portion of image buffer
+ if (!previewp->isImageScaled())
+ {
+ glScalef(llmin(1.f, (F32)image_width / (F32)previewp->getCurrentImage()->getWidth()), llmin(1.f, (F32)image_height / (F32)previewp->getCurrentImage()->getHeight()), 1.f);
+ }
+ glMatrixMode(GL_MODELVIEW);
+ gl_draw_scaled_image((mRect.getWidth() - img_render_width) / 2, 35 + (max_height - img_render_height) / 2, img_render_width, img_render_height, previewp->getCurrentImage(), LLColor4::white);
+ }
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ }
+}
+
+void LLFloaterSnapshot::onClose(bool app_quitting)
+{
+ gSnapshotFloaterView->setEnabled(FALSE);
+ destroy();
+}
+
+// static
+void LLFloaterSnapshot::show(void*)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterSnapshot();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_snapshot.xml", NULL, FALSE);
+ //move snapshot floater to special purpose snapshotfloaterview
+ gFloaterView->removeChild(sInstance);
+ gSnapshotFloaterView->addChild(sInstance);
+
+ sInstance->impl.updateLayout(sInstance);
+ }
+
+ sInstance->open();
+ sInstance->focusFirstItem(FALSE);
+ gSnapshotFloaterView->setEnabled(TRUE);
+ gSnapshotFloaterView->adjustToFitScreen(sInstance, FALSE);
+}
+
+void LLFloaterSnapshot::hide(void*)
+{
+ if (sInstance && !sInstance->isDead())
+ {
+ sInstance->close();
+ }
+}
+
+//static
+void LLFloaterSnapshot::update()
+{
+ for (std::set<LLSnapshotLivePreview*>::iterator iter = LLSnapshotLivePreview::sList.begin();
+ iter != LLSnapshotLivePreview::sList.end(); ++iter)
+ {
+ LLSnapshotLivePreview::onIdle(*iter);
+ }
+}
+
+//============================================================================
+
+LLSnapshotFloaterView::LLSnapshotFloaterView( const LLString& name, const LLRect& rect ) : LLFloaterView(name, rect)
+{
+ setMouseOpaque(TRUE);
+ setEnabled(FALSE);
+}
+
+LLSnapshotFloaterView::~LLSnapshotFloaterView()
+{
+}
+
+BOOL LLSnapshotFloaterView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleKey(key, mask, called_from_parent);
+ }
+
+ if (!getEnabled())
+ {
+ return FALSE;
+ }
+ else
+ {
+ if (called_from_parent)
+ {
+ // pass all keystrokes down
+ LLFloaterView::handleKey(key, mask, called_from_parent);
+ }
+ else
+ {
+ // bounce keystrokes back down
+ LLFloaterView::handleKey(key, mask, TRUE);
+ }
+ return TRUE;
+ }
+}
+
+BOOL LLSnapshotFloaterView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleMouseDown(x, y, mask);
+ }
+ // give floater a change to handle mouse, else camera tool
+ if (childrenHandleMouseDown(x, y, mask) == NULL)
+ {
+ gToolMgr->getCurrentTool(mask)->handleMouseDown( x, y, mask );
+ }
+ return TRUE;
+}
+
+BOOL LLSnapshotFloaterView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleMouseUp(x, y, mask);
+ }
+ // give floater a change to handle mouse, else camera tool
+ if (childrenHandleMouseUp(x, y, mask) == NULL)
+ {
+ gToolMgr->getCurrentTool(mask)->handleMouseUp( x, y, mask );
+ }
+ return TRUE;
+}
+
+BOOL LLSnapshotFloaterView::handleHover(S32 x, S32 y, MASK mask)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleHover(x, y, mask);
+ }
+ // give floater a change to handle mouse, else camera tool
+ if (childrenHandleHover(x, y, mask) == NULL)
+ {
+ gToolMgr->getCurrentTool(mask)->handleHover( x, y, mask );
+ }
+ return TRUE;
+}
diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h
new file mode 100644
index 0000000000..85b18a99d3
--- /dev/null
+++ b/indra/newview/llfloatersnapshot.h
@@ -0,0 +1,53 @@
+/**
+ * @file llfloatersnapshot.h
+ * @brief Snapshot preview window, allowing saving, e-mailing, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERSNAPSHOT_H
+#define LL_LLFLOATERSNAPSHOT_H
+
+#include "llfloater.h"
+
+#include "llmemory.h"
+#include "llimagegl.h"
+#include "llcharacter.h"
+
+class LLFloaterSnapshot : public LLFloater
+{
+public:
+ LLFloaterSnapshot();
+ virtual ~LLFloaterSnapshot();
+
+ virtual BOOL postBuild();
+ virtual void draw();
+ virtual void onClose(bool app_quitting);
+
+ static void show(void*);
+ static void hide(void*);
+
+ static void update();
+
+private:
+ class Impl;
+ Impl& impl;
+
+ static LLFloaterSnapshot* sInstance;
+};
+
+class LLSnapshotFloaterView : public LLFloaterView
+{
+public:
+ LLSnapshotFloaterView( const LLString& name, const LLRect& rect );
+ virtual ~LLSnapshotFloaterView();
+
+ /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+};
+
+extern LLSnapshotFloaterView* gSnapshotFloaterView;
+#endif // LL_LLFLOATERSNAPSHOT_H
diff --git a/indra/newview/llfloatertelehub.cpp b/indra/newview/llfloatertelehub.cpp
new file mode 100644
index 0000000000..f27cf55b3a
--- /dev/null
+++ b/indra/newview/llfloatertelehub.cpp
@@ -0,0 +1,289 @@
+/**
+ * @file llfloatertelehub.cpp
+ * @author James Cook
+ * @brief LLFloaterTelehub class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertelehub.h"
+
+#include "message.h"
+#include "llfontgl.h"
+
+#include "llagent.h"
+#include "llfloatertools.h"
+#include "llscrolllistctrl.h"
+#include "llselectmgr.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llvieweruictrlfactory.h"
+
+LLFloaterTelehub* LLFloaterTelehub::sInstance = NULL;
+
+
+// static
+void LLFloaterTelehub::show()
+{
+ if (sInstance)
+ {
+ sInstance->setVisibleAndFrontmost();
+ return;
+ }
+
+ sInstance = new LLFloaterTelehub();
+
+ // Show tools floater by selecting translate (select) tool
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolTranslate );
+
+ // Find tools floater, glue to bottom
+ if (gFloaterTools)
+ {
+ gFloaterTools->showMore(FALSE);
+ LLRect tools_rect = gFloaterTools->getRect();
+ S32 our_width = sInstance->getRect().getWidth();
+ S32 our_height = sInstance->getRect().getHeight();
+ LLRect our_rect;
+ our_rect.setLeftTopAndSize(tools_rect.mLeft, tools_rect.mBottom, our_width, our_height);
+ sInstance->setRect(our_rect);
+ }
+
+ sInstance->sendTelehubInfoRequest();
+}
+
+LLFloaterTelehub::LLFloaterTelehub()
+: LLFloater("telehub"),
+ mTelehubObjectID(),
+ mTelehubObjectName(),
+ mTelehubPos(),
+ mTelehubRot(),
+ mNumSpawn(0)
+{
+ sInstance = this;
+
+ gMessageSystem->setHandlerFunc("TelehubInfo", processTelehubInfo);
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_telehub.xml");
+
+ childSetAction("connect_btn", onClickConnect, this);
+ childSetAction("disconnect_btn", onClickDisconnect, this);
+ childSetAction("add_spawn_point_btn", onClickAddSpawnPoint, this);
+ childSetAction("remove_spawn_point_btn", onClickRemoveSpawnPoint, this);
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "spawn_points_list");
+ if (list)
+ {
+ // otherwise you can't walk with arrow keys while floater is up
+ list->setAllowKeyboardMovement(FALSE);
+ }
+}
+
+LLFloaterTelehub::~LLFloaterTelehub()
+{
+ sInstance = NULL;
+
+ // no longer interested in this message
+ gMessageSystem->setHandlerFunc("TelehubInfo", NULL);
+}
+
+void LLFloaterTelehub::draw()
+{
+ if (getVisible() && !isMinimized())
+ {
+ refresh();
+ }
+ LLFloater::draw();
+}
+
+// Per-frame updates, because we don't have a selection manager observer.
+void LLFloaterTelehub::refresh()
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ }
+
+ BOOL have_selection = (object != NULL);
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+ childSetEnabled("connect_btn", have_selection && all_volume);
+
+ BOOL have_telehub = mTelehubObjectID.notNull();
+ childSetEnabled("disconnect_btn", have_telehub);
+
+ BOOL space_avail = (mNumSpawn < MAX_SPAWNPOINTS_PER_TELEHUB);
+ childSetEnabled("add_spawn_point_btn", have_selection && all_volume && space_avail);
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "spawn_points_list");
+ if (list)
+ {
+ BOOL enable_remove = (list->getFirstSelected() != NULL);
+ childSetEnabled("remove_spawn_point_btn", enable_remove);
+ }
+}
+
+// static
+BOOL LLFloaterTelehub::renderBeacons()
+{
+ // only render if we've got a telehub
+ return sInstance && sInstance->mTelehubObjectID.notNull();
+}
+
+// static
+void LLFloaterTelehub::addBeacons()
+{
+ if (!sInstance) return;
+
+ // Find the telehub position, either our cached old position, or
+ // an updated one based on the actual object position.
+ LLVector3 hub_pos_region = sInstance->mTelehubPos;
+ LLQuaternion hub_rot = sInstance->mTelehubRot;
+ LLViewerObject* obj = gObjectList.findObject(sInstance->mTelehubObjectID);
+ if (obj)
+ {
+ hub_pos_region = obj->getPositionRegion();
+ hub_rot = obj->getRotationRegion();
+ }
+ // Draw nice thick 3-pixel lines.
+ gObjectList.addDebugBeacon(hub_pos_region, "", LLColor4::yellow, LLColor4::white, 4);
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "spawn_points_list");
+ if (list)
+ {
+ S32 spawn_index = list->getFirstSelectedIndex();
+ if (spawn_index >= 0)
+ {
+ LLVector3 spawn_pos = hub_pos_region + (sInstance->mSpawnPointPos[spawn_index] * hub_rot);
+ gObjectList.addDebugBeacon(spawn_pos, "", LLColor4::orange, LLColor4::white, 4);
+ }
+ }
+}
+
+void LLFloaterTelehub::sendTelehubInfoRequest()
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "info ui");
+}
+
+// static
+void LLFloaterTelehub::onClickConnect(void* data)
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "connect");
+}
+
+// static
+void LLFloaterTelehub::onClickDisconnect(void* data)
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "delete");
+}
+
+// static
+void LLFloaterTelehub::onClickAddSpawnPoint(void* data)
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "spawnpoint add");
+ gSelectMgr->deselectAll();
+}
+
+// static
+void LLFloaterTelehub::onClickRemoveSpawnPoint(void* data)
+{
+ if (!sInstance) return;
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "spawn_points_list");
+ if (!list) return;
+
+ S32 spawn_index = list->getFirstSelectedIndex();
+ if (spawn_index < 0) return; // nothing selected
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ // Could be god or estate owner. If neither, server will reject message.
+ if (gAgent.isGodlike())
+ {
+ msg->newMessage("GodlikeMessage");
+ }
+ else
+ {
+ msg->newMessage("EstateOwnerMessage");
+ }
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "telehub");
+ msg->addUUID("Invoice", LLUUID::null);
+
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", "spawnpoint remove");
+
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d", spawn_index);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+
+ gAgent.sendReliableMessage();
+}
+
+// static
+void LLFloaterTelehub::processTelehubInfo(LLMessageSystem* msg, void**)
+{
+ if (sInstance)
+ {
+ sInstance->unpackTelehubInfo(msg);
+ }
+}
+
+void LLFloaterTelehub::unpackTelehubInfo(LLMessageSystem* msg)
+{
+ char buffer[MAX_STRING];
+
+ msg->getUUID("TelehubBlock", "ObjectID", mTelehubObjectID);
+ msg->getString("TelehubBlock", "ObjectName", MAX_STRING, buffer);
+ mTelehubObjectName = buffer;
+ msg->getVector3("TelehubBlock", "TelehubPos", mTelehubPos);
+ msg->getQuat("TelehubBlock", "TelehubRot", mTelehubRot);
+
+ mNumSpawn = msg->getNumberOfBlocks("SpawnPointBlock");
+ for (S32 i = 0; i < mNumSpawn; i++)
+ {
+ msg->getVector3("SpawnPointBlock", "SpawnPointPos", mSpawnPointPos[i], i);
+ }
+
+ // Update parts of the UI that change only when message received.
+
+ if (mTelehubObjectID.isNull())
+ {
+ childSetVisible("status_text_connected", false);
+ childSetVisible("status_text_not_connected", true);
+ childSetVisible("help_text_connected", false);
+ childSetVisible("help_text_not_connected", true);
+ }
+ else
+ {
+ childSetTextArg("status_text_connected", "[OBJECT]", mTelehubObjectName);
+ childSetVisible("status_text_connected", true);
+ childSetVisible("status_text_not_connected", false);
+ childSetVisible("help_text_connected", true);
+ childSetVisible("help_text_not_connected", false);
+ }
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "spawn_points_list");
+ if (list)
+ {
+ list->deleteAllItems();
+ for (S32 i = 0; i < mNumSpawn; i++)
+ {
+ LLString pos = llformat("%.1f, %.1f, %.1f",
+ mSpawnPointPos[i].mV[VX],
+ mSpawnPointPos[i].mV[VY],
+ mSpawnPointPos[i].mV[VZ]);
+ list->addSimpleItem(pos);
+ }
+ list->selectNthItem(mNumSpawn - 1);
+ }
+}
diff --git a/indra/newview/llfloatertelehub.h b/indra/newview/llfloatertelehub.h
new file mode 100644
index 0000000000..39f12f9443
--- /dev/null
+++ b/indra/newview/llfloatertelehub.h
@@ -0,0 +1,57 @@
+/**
+ * @file llfloatertelehub.h
+ * @author James Cook
+ * @brief LLFloaterTelehub class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTELEHUB_H
+#define LL_LLFLOATERTELEHUB_H
+
+#include "llfloater.h"
+
+class LLMessageSystem;
+
+const S32 MAX_SPAWNPOINTS_PER_TELEHUB = 16;
+
+class LLFloaterTelehub : public LLFloater
+{
+public:
+ // Opens the floater on screen.
+ static void show();
+
+ virtual void draw();
+
+ static BOOL renderBeacons();
+ static void addBeacons();
+
+private:
+ LLFloaterTelehub();
+ ~LLFloaterTelehub();
+
+ void refresh();
+ void sendTelehubInfoRequest();
+
+ static void onClickConnect(void* data);
+ static void onClickDisconnect(void* data);
+ static void onClickAddSpawnPoint(void* data);
+ static void onClickRemoveSpawnPoint(void* data);
+
+ static void processTelehubInfo(LLMessageSystem* msg, void**);
+ void unpackTelehubInfo(LLMessageSystem* msg);
+
+private:
+ LLUUID mTelehubObjectID; // null if no telehub
+ LLString mTelehubObjectName;
+ LLVector3 mTelehubPos; // region local, fallback if viewer can't see the object
+ LLQuaternion mTelehubRot;
+
+ S32 mNumSpawn;
+ LLVector3 mSpawnPointPos[MAX_SPAWNPOINTS_PER_TELEHUB];
+
+ static LLFloaterTelehub* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
new file mode 100644
index 0000000000..6f675236b3
--- /dev/null
+++ b/indra/newview/llfloatertools.cpp
@@ -0,0 +1,931 @@
+/**
+ * @file llfloatertools.cpp
+ * @brief The edit tools, including move, position, land, etc.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertools.h"
+
+#include "llfontgl.h"
+#include "llcoord.h"
+#include "llgl.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldraghandle.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloateropenobject.h"
+#include "llfocusmgr.h"
+#include "llmenugl.h"
+#include "llpanelcontents.h"
+#include "llpanelface.h"
+#include "llpanelland.h"
+#include "llpanelinventory.h"
+#include "llpanelobject.h"
+#include "llpanelvolume.h"
+#include "llpanelpermissions.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltoolbrush.h"
+#include "lltoolcomp.h"
+#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolgrab.h"
+#include "lltoolindividual.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolpipette.h"
+#include "lltoolplacer.h"
+#include "lltoolselect.h"
+#include "lltoolselectland.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llvolumesliderctrl.h"
+#include "viewer.h"
+
+#include "llvieweruictrlfactory.h"
+
+// Globals
+LLFloaterTools *gFloaterTools = NULL;
+
+
+const LLString PANEL_NAMES[LLFloaterTools::PANEL_COUNT] =
+{
+ LLString("General"), // PANEL_GENERAL,
+ LLString("Object"), // PANEL_OBJECT,
+ LLString("Features"), // PANEL_FEATURES,
+ LLString("Texture"), // PANEL_FACE,
+ LLString("Content"), // PANEL_CONTENTS,
+};
+
+// Local prototypes
+void commit_select_tool(LLUICtrl *ctrl, void *data);
+void commit_select_component(LLUICtrl *ctrl, void *data);
+void click_show_more(void*);
+void click_popup_info(void*);
+void click_popup_done(void*);
+void click_popup_minimize(void*);
+void click_popup_grab_drag(LLUICtrl *, void*);
+void click_popup_grab_lift(LLUICtrl *, void*);
+void click_popup_grab_spin(LLUICtrl *, void*);
+void click_popup_rotate_left(void*);
+void click_popup_rotate_reset(void*);
+void click_popup_rotate_right(void*);
+void click_popup_dozer_mode(LLUICtrl *, void *user);
+void click_popup_dozer_size(LLUICtrl *, void *user);
+void click_dozer_size(LLUICtrl *, void*);
+void click_apply_to_selection(void*);
+void commit_radio_zoom(LLUICtrl *, void*);
+void commit_radio_orbit(LLUICtrl *, void*);
+void commit_radio_pan(LLUICtrl *, void*);
+void commit_grid_mode(LLUICtrl *, void*);
+void commit_slider_zoom(LLUICtrl *, void*);
+
+
+//static
+void* LLFloaterTools::createPanelPermissions(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelPermissions = new LLPanelPermissions("General");
+ return floater->mPanelPermissions;
+}
+//static
+void* LLFloaterTools::createPanelObject(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelObject = new LLPanelObject("Object");
+ return floater->mPanelObject;
+}
+
+//static
+void* LLFloaterTools::createPanelVolume(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelVolume = new LLPanelVolume("Features");
+ return floater->mPanelVolume;
+}
+
+//static
+void* LLFloaterTools::createPanelFace(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelFace = new LLPanelFace("Texture");
+ return floater->mPanelFace;
+}
+
+//static
+void* LLFloaterTools::createPanelContents(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelContents = new LLPanelContents("Contents");
+ return floater->mPanelContents;
+}
+
+//static
+void* LLFloaterTools::createPanelContentsInventory(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelContents->mPanelInventory = new LLPanelInventory("ContentsInventory", LLRect());
+ return floater->mPanelContents->mPanelInventory;
+}
+
+//static
+void* LLFloaterTools::createPanelLandInfo(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelLandInfo = new LLPanelLandInfo("land info panel");
+ return floater->mPanelLandInfo;
+}
+
+BOOL LLFloaterTools::postBuild()
+{
+
+ // Hide until tool selected
+ setVisible(FALSE);
+
+ // Since we constantly show and hide this during drags, don't
+ // make sounds on visibility changes.
+ setSoundFlags(LLView::SILENT);
+
+ mDragHandle->setEnabled( !gSavedSettings.getBOOL("ToolboxAutoMove") );
+
+ LLRect rect;
+ mBtnFocus = LLUICtrlFactory::getButtonByName(this,"button focus");//btn;
+ childSetAction("button focus",select_tool, (void*)gToolCamera);
+ mBtnMove = LLUICtrlFactory::getButtonByName(this,"button move");
+ childSetAction("button move",select_tool, (void*)gToolGrab);
+ mBtnEdit = LLUICtrlFactory::getButtonByName(this,"button edit");
+ childSetAction("button edit",select_tool, (void*)gToolTranslate);
+ mBtnCreate = LLUICtrlFactory::getButtonByName(this,"button create");
+ childSetAction("button create",select_tool, (void*)gToolCreate);
+ mBtnLand = LLUICtrlFactory::getButtonByName(this, "button land" );
+ childSetAction("button land",select_tool, (void*)gToolParcel);
+ mTextStatus = LLUICtrlFactory::getTextBoxByName(this,"text status");
+ mRadioZoom = LLUICtrlFactory::getCheckBoxByName(this,"radio zoom");
+ mSliderZoom = LLViewerUICtrlFactory::getVolumeSliderByName(this,"slider zoom");
+ childSetCommitCallback("slider zoom",commit_slider_zoom,this);
+ mRadioOrbit = LLUICtrlFactory::getCheckBoxByName(this,"radio orbit");
+ childSetCommitCallback("radio orbit",commit_radio_orbit,this);
+ mRadioPan = LLUICtrlFactory::getCheckBoxByName(this,"radio pan");
+ childSetCommitCallback("radio pan",commit_radio_pan,this);
+ mRadioMove = LLUICtrlFactory::getCheckBoxByName(this,"radio move");
+ childSetCommitCallback("radio move",click_popup_grab_drag,this);
+ mRadioLift = LLUICtrlFactory::getCheckBoxByName(this,"radio lift");
+ childSetCommitCallback("radio lift",click_popup_grab_lift,this);
+ mRadioSpin = LLUICtrlFactory::getCheckBoxByName(this,"radio spin");
+ childSetCommitCallback("radio spin",click_popup_grab_spin,NULL);
+ mRadioPosition = LLUICtrlFactory::getCheckBoxByName(this,"radio position");
+ childSetCommitCallback("radio position",commit_select_tool,gToolTranslate);
+ mRadioRotate = LLUICtrlFactory::getCheckBoxByName(this,"radio rotate");
+ childSetCommitCallback("radio rotate",commit_select_tool,gToolRotate);
+ mRadioStretch = LLUICtrlFactory::getCheckBoxByName(this,"radio stretch");
+ childSetCommitCallback("radio stretch",commit_select_tool,gToolStretch);
+ mRadioSelectFace = LLUICtrlFactory::getCheckBoxByName(this,"radio select face");
+ childSetCommitCallback("radio select face",commit_select_tool,gToolFace);
+ mCheckSelectIndividual = LLUICtrlFactory::getCheckBoxByName(this,"checkbox edit linked parts");
+ childSetValue("checkbox edit linked parts",(BOOL)!gSavedSettings.getBOOL("SelectLinkedSet"));
+ childSetCommitCallback("checkbox edit linked parts",commit_select_component,this);
+ mCheckSnapToGrid = LLUICtrlFactory::getCheckBoxByName(this,"checkbox snap to grid");
+ childSetValue("checkbox snap to grid",(BOOL)gSavedSettings.getBOOL("SnapEnabled"));
+ mBtnGridOptions = LLUICtrlFactory::getButtonByName(this,"Options...");
+ childSetAction("Options...",onClickGridOptions, this);
+ mCheckStretchUniform = LLUICtrlFactory::getCheckBoxByName(this,"checkbox uniform");
+ childSetValue("checkbox uniform",(BOOL)gSavedSettings.getBOOL("ScaleUniform"));
+ mCheckStretchTexture = LLUICtrlFactory::getCheckBoxByName(this,"checkbox stretch textures");
+ childSetValue("checkbox stretch textures",(BOOL)gSavedSettings.getBOOL("ScaleStretchTextures"));
+ mTextGridMode = LLUICtrlFactory::getTextBoxByName(this,"text ruler mode");
+ mComboGridMode = LLUICtrlFactory::getComboBoxByName(this,"combobox grid mode");
+ childSetCommitCallback("combobox grid mode",commit_grid_mode, this);
+ //
+ // Create Buttons
+ //
+
+ static const LLString toolNames[]={
+ "ToolCube",
+ "ToolPrism",
+ "ToolPyramid",
+ "ToolTetrahedron",
+ "ToolCylinder",
+ "ToolHemiCylinder",
+ "ToolCone",
+ "ToolHemiCone",
+ "ToolSphere",
+ "ToolHemiSphere",
+ "ToolTorus",
+ "ToolTube",
+ "ToolRing",
+ "ToolTree",
+ "ToolGrass"};
+ void* toolData[]={
+ &LLToolPlacerPanel::sCube,
+ &LLToolPlacerPanel::sPrism,
+ &LLToolPlacerPanel::sPyramid,
+ &LLToolPlacerPanel::sTetrahedron,
+ &LLToolPlacerPanel::sCylinder,
+ &LLToolPlacerPanel::sCylinderHemi,
+ &LLToolPlacerPanel::sCone,
+ &LLToolPlacerPanel::sConeHemi,
+ &LLToolPlacerPanel::sSphere,
+ &LLToolPlacerPanel::sSphereHemi,
+ &LLToolPlacerPanel::sTorus,
+ &LLToolPlacerPanel::sSquareTorus,
+ &LLToolPlacerPanel::sTriangleTorus,
+ &LLToolPlacerPanel::sTree,
+ &LLToolPlacerPanel::sGrass};
+ for(int t=0;t<sizeof(toolNames)/sizeof(toolNames[0]);t++)
+ {
+ LLButton *found = LLViewerUICtrlFactory::getButtonByName(this,toolNames[t]);
+ if(found)
+ {
+ found->setClickedCallback(setObjectType,toolData[t]);
+ mButtons.push_back( found );
+ }else{
+ llwarns << "Tool button not found! DOA Pending." << llendl;
+ }
+ }
+ mCheckCopySelection = LLUICtrlFactory::getCheckBoxByName(this,"checkbox copy selection");
+ childSetValue("checkbox copy selection",(BOOL)gSavedSettings.getBOOL("CreateToolCopySelection"));
+ mCheckSticky = LLUICtrlFactory::getCheckBoxByName(this,"checkbox sticky");
+ childSetValue("checkbox sticky",(BOOL)gSavedSettings.getBOOL("CreateToolKeepSelected"));
+ mCheckCopyCenters = LLUICtrlFactory::getCheckBoxByName(this,"checkbox copy centers");
+ childSetValue("checkbox copy centers",(BOOL)gSavedSettings.getBOOL("CreateToolCopyCenters"));
+ mCheckCopyRotates = LLUICtrlFactory::getCheckBoxByName(this,"checkbox copy rotates");
+ childSetValue("checkbox copy rotates",(BOOL)gSavedSettings.getBOOL("CreateToolCopyRotates"));
+ mRadioSelectLand = LLUICtrlFactory::getCheckBoxByName(this,"radio select land");
+ childSetCommitCallback("radio select land",commit_select_tool, gToolParcel);
+ mRadioDozerFlatten = LLUICtrlFactory::getCheckBoxByName(this,"radio flatten");
+ childSetCommitCallback("radio flatten",click_popup_dozer_mode, (void*)0);
+ mRadioDozerRaise = LLUICtrlFactory::getCheckBoxByName(this,"radio raise");
+ childSetCommitCallback("radio raise",click_popup_dozer_mode, (void*)1);
+ mRadioDozerLower = LLUICtrlFactory::getCheckBoxByName(this,"radio lower");
+ childSetCommitCallback("radio lower",click_popup_dozer_mode, (void*)2);
+ mRadioDozerSmooth = LLUICtrlFactory::getCheckBoxByName(this,"radio smooth");
+ childSetCommitCallback("radio smooth",click_popup_dozer_mode, (void*)3);
+ mRadioDozerNoise = LLUICtrlFactory::getCheckBoxByName(this,"radio noise");
+ childSetCommitCallback("radio noise",click_popup_dozer_mode, (void*)4);
+ mRadioDozerRevert = LLUICtrlFactory::getCheckBoxByName(this,"radio revert");
+ childSetCommitCallback("radio revert",click_popup_dozer_mode, (void*)5);
+ mComboDozerSize = LLUICtrlFactory::getComboBoxByName(this,"combobox brush size");
+ childSetCommitCallback("combobox brush size",click_dozer_size, (void*)0);
+ if(mComboDozerSize) mComboDozerSize->setCurrentByIndex(0);
+ mBtnApplyToSelection = LLUICtrlFactory::getButtonByName(this,"button apply to selection");
+ childSetAction("button apply to selection",click_apply_to_selection, (void*)0);
+ mCheckShowOwners = LLUICtrlFactory::getCheckBoxByName(this,"checkbox show owners");
+ childSetValue("checkbox show owners",gSavedSettings.getBOOL("ShowParcelOwners"));
+ childSetAction("button more", click_show_more, this);
+ childSetAction("button less", click_show_more, this);
+ mTab = LLUICtrlFactory::getTabContainerByName(this,"Object Info Tabs");
+ if(mTab)
+ {
+ mTab->setVisible( gSavedSettings.getBOOL("ToolboxShowMore") );
+ mTab->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ mTab->setVisible( gSavedSettings.getBOOL("ToolboxShowMore") );
+ mTab->setBorderVisible(FALSE);
+ }
+ mTab->selectFirstTab();
+ return TRUE;
+}
+
+// Create the popupview with a dummy center. It will be moved into place
+// during LLViewerWindow's per-frame hover processing.
+LLFloaterTools::LLFloaterTools()
+: LLFloater("toolbox floater"),
+ mBtnFocus(NULL),
+ mBtnMove(NULL),
+ mBtnEdit(NULL),
+ mBtnCreate(NULL),
+ mBtnLand(NULL),
+ mTextStatus(NULL),
+
+ mRadioOrbit(NULL),
+ mRadioZoom(NULL),
+ mRadioPan(NULL),
+
+ mRadioMove(NULL),
+ mRadioLift(NULL),
+ mRadioSpin(NULL),
+
+ mRadioPosition(NULL),
+ mRadioRotate(NULL),
+ mRadioStretch(NULL),
+ mRadioSelectFace(NULL),
+ mCheckSelectIndividual(NULL),
+
+ mCheckSnapToGrid(NULL),
+ mBtnGridOptions(NULL),
+ mTextGridMode(NULL),
+ mComboGridMode(NULL),
+ mCheckStretchUniform(NULL),
+ mCheckStretchTexture(NULL),
+
+ mBtnRotateLeft(NULL),
+ mBtnRotateReset(NULL),
+ mBtnRotateRight(NULL),
+
+ mBtnDelete(NULL),
+ mBtnDuplicate(NULL),
+ mBtnDuplicateInPlace(NULL),
+
+ mCheckSticky(NULL),
+ mCheckCopySelection(NULL),
+ mCheckCopyCenters(NULL),
+ mCheckCopyRotates(NULL),
+ mRadioSelectLand(NULL),
+ mRadioDozerFlatten(NULL),
+ mRadioDozerRaise(NULL),
+ mRadioDozerLower(NULL),
+ mRadioDozerSmooth(NULL),
+ mRadioDozerNoise(NULL),
+ mRadioDozerRevert(NULL),
+ mComboDozerSize(NULL),
+ mBtnApplyToSelection(NULL),
+ mCheckShowOwners(NULL),
+
+
+ mTab(NULL),
+ mPanelPermissions(NULL),
+ mPanelObject(NULL),
+ mPanelVolume(NULL),
+ mPanelContents(NULL),
+ mPanelFace(NULL),
+ mPanelLandInfo(NULL),
+
+ mTabLand(NULL),
+ mDirty(TRUE)
+{
+ mAutoFocus = FALSE;
+ LLCallbackMap::map_t factory_map;
+ factory_map["General"] = LLCallbackMap(createPanelPermissions, this);//LLPanelPermissions
+ factory_map["Object"] = LLCallbackMap(createPanelObject, this);//LLPanelObject
+ factory_map["Features"] = LLCallbackMap(createPanelVolume, this);//LLPanelVolume
+ factory_map["Texture"] = LLCallbackMap(createPanelFace, this);//LLPanelFace
+ factory_map["Contents"] = LLCallbackMap(createPanelContents, this);//LLPanelContents
+ factory_map["ContentsInventory"] = LLCallbackMap(createPanelContentsInventory, this);//LLPanelContents
+ factory_map["land info panel"] = LLCallbackMap(createPanelLandInfo, this);//LLPanelLandInfo
+
+ gUICtrlFactory->buildFloater(this,"floater_tools.xml",&factory_map);
+
+ mLargeHeight = getRect().getHeight();
+ mSmallHeight = mLargeHeight;
+ if (mTab) mSmallHeight -= mTab->getRect().getHeight();
+
+ gSavedSettings.setBOOL("ToolboxShowMore", TRUE); // force a toggle initially
+ showMore(FALSE);
+}
+
+LLFloaterTools::~LLFloaterTools()
+{
+ showMore(FALSE);
+ // children automatically deleted
+}
+
+
+void LLFloaterTools::setStatusText(const LLString& text)
+{
+ mTextStatus->setText(text);
+}
+
+void LLFloaterTools::refresh()
+{
+ const S32 INFO_WIDTH = mRect.getWidth();
+ const S32 INFO_HEIGHT = 384;
+ LLRect object_info_rect(0, 0, INFO_WIDTH, -INFO_HEIGHT);
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+
+ S32 idx_features = mTab->getPanelIndexByTitle(PANEL_NAMES[PANEL_FEATURES]);
+ S32 idx_face = mTab->getPanelIndexByTitle(PANEL_NAMES[PANEL_FACE]);
+ S32 idx_contents = mTab->getPanelIndexByTitle(PANEL_NAMES[PANEL_CONTENTS]);
+
+ S32 selected_index = mTab->getCurrentPanelIndex();
+
+ if (!all_volume && (selected_index == idx_features || selected_index == idx_face ||
+ selected_index == idx_contents))
+ {
+ mTab->selectFirstTab();
+ }
+
+ mTab->enableTabButton(idx_features, all_volume);
+ mTab->enableTabButton(idx_face, all_volume);
+ mTab->enableTabButton(idx_contents, all_volume);
+
+ mPanelPermissions->refresh();
+ mPanelObject->refresh();
+ mPanelVolume->refresh();
+ mPanelFace->refresh();
+ mPanelContents->refresh();
+ mPanelLandInfo->refresh();
+}
+
+void LLFloaterTools::draw()
+{
+ if (mDirty)
+ {
+ refresh();
+ mDirty = FALSE;
+ }
+
+ mCheckSelectIndividual->set(!gSavedSettings.getBOOL("SelectLinkedSet"));
+ LLFloater::draw();
+}
+
+void LLFloaterTools::dirty()
+{
+ mDirty = TRUE;
+ LLFloaterOpenObject::dirty();
+}
+
+// Clean up any tool state that should not persist when the
+// floater is closed.
+void LLFloaterTools::resetToolState()
+{
+ gCameraBtnOrbit = FALSE;
+ gCameraBtnPan = FALSE;
+
+ gGrabBtnSpin = FALSE;
+ gGrabBtnVertical = FALSE;
+}
+
+void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)
+{
+ LLTool *tool = gToolMgr->getCurrentTool( mask );
+
+ // HACK to allow seeing the buttons when you have the app in a window.
+ // Keep the visibility the same as it
+ if (tool == gToolNull)
+ {
+ return;
+ }
+
+ // Focus buttons
+ BOOL focus_visible = ( tool == gToolCamera );
+
+ mBtnFocus ->setToggleState( focus_visible );
+
+ mRadioZoom ->setVisible( focus_visible );
+ mRadioOrbit ->setVisible( focus_visible );
+ mRadioPan ->setVisible( focus_visible );
+ mSliderZoom ->setVisible( focus_visible );
+
+ mRadioZoom ->set( !gCameraBtnOrbit &&
+ !gCameraBtnPan &&
+ !(mask == MASK_ORBIT) &&
+ !(mask == (MASK_ORBIT | MASK_ALT)) &&
+ !(mask == MASK_PAN) &&
+ !(mask == (MASK_PAN | MASK_ALT)) );
+
+ mRadioOrbit ->set( gCameraBtnOrbit ||
+ (mask == MASK_ORBIT) ||
+ (mask == (MASK_ORBIT | MASK_ALT)) );
+
+ mRadioPan ->set( gCameraBtnPan ||
+ (mask == MASK_PAN) ||
+ (mask == (MASK_PAN | MASK_ALT)) );
+
+ // multiply by correction factor because volume sliders go [0, 0.5]
+ mSliderZoom ->setValue( gAgent.getCameraZoomFraction() * 0.5f);
+
+ // Move buttons
+ BOOL move_visible = (tool == gToolGrab);
+
+ if (mBtnMove) mBtnMove ->setToggleState( move_visible );
+
+ // HACK - highlight buttons for next click
+ if (mRadioMove)
+ {
+ mRadioMove ->setVisible( move_visible );
+ mRadioMove ->set( !gGrabBtnSpin &&
+ !gGrabBtnVertical &&
+ !(mask == MASK_VERTICAL) &&
+ !(mask == MASK_SPIN) );
+ }
+
+ if (mRadioLift)
+ {
+ mRadioLift ->setVisible( move_visible );
+ mRadioLift ->set( gGrabBtnVertical ||
+ (mask == MASK_VERTICAL) );
+ }
+
+ if (mRadioSpin)
+ {
+ mRadioSpin ->setVisible( move_visible );
+ mRadioSpin ->set( gGrabBtnSpin ||
+ (mask == MASK_SPIN) );
+ }
+
+ // Edit buttons
+ BOOL edit_visible = tool == gToolTranslate ||
+ tool == gToolRotate ||
+ tool == gToolStretch ||
+ tool == gToolFace ||
+ tool == gToolIndividual ||
+ tool == gToolPipette;
+
+ mBtnEdit ->setToggleState( edit_visible );
+
+ mRadioPosition ->setVisible( edit_visible );
+ mRadioRotate ->setVisible( edit_visible );
+ mRadioStretch ->setVisible( edit_visible );
+ if (mRadioSelectFace)
+ {
+ mRadioSelectFace->setVisible( edit_visible );
+ mRadioSelectFace->set( tool == gToolFace );
+ }
+
+ if (mCheckSelectIndividual)
+ {
+ mCheckSelectIndividual->setVisible(edit_visible);
+ mCheckSelectIndividual->set(!gSavedSettings.getBOOL("SelectLinkedSet"));
+ }
+
+ mRadioPosition ->set( tool == gToolTranslate );
+ mRadioRotate ->set( tool == gToolRotate );
+ mRadioStretch ->set( tool == gToolStretch );
+
+ if (mComboGridMode)
+ {
+ mComboGridMode ->setVisible( edit_visible );
+ S32 index = mComboGridMode->getCurrentIndex();
+ mComboGridMode->removeall();
+
+ switch (gSelectMgr->getSelectType())
+ {
+ case SELECT_TYPE_HUD:
+ mComboGridMode->add("Screen");
+ mComboGridMode->add("Local");
+ //mComboGridMode->add("Reference");
+ break;
+ case SELECT_TYPE_WORLD:
+ mComboGridMode->add("World");
+ mComboGridMode->add("Local");
+ mComboGridMode->add("Reference");
+ break;
+ case SELECT_TYPE_ATTACHMENT:
+ mComboGridMode->add("Attachment");
+ mComboGridMode->add("Local");
+ mComboGridMode->add("Reference");
+ break;
+ }
+
+ mComboGridMode->setCurrentByIndex(index);
+ }
+ if (mTextGridMode) mTextGridMode->setVisible( edit_visible );
+
+ // Snap to grid disabled for grab tool - very confusing
+ if (mCheckSnapToGrid) mCheckSnapToGrid->setVisible( edit_visible /* || tool == gToolGrab */ );
+ if (mBtnGridOptions) mBtnGridOptions->setVisible( edit_visible /* || tool == gToolGrab */ );
+
+ //mCheckSelectLinked ->setVisible( edit_visible );
+ if (mCheckStretchUniform) mCheckStretchUniform->setVisible( edit_visible );
+ if (mCheckStretchTexture) mCheckStretchTexture->setVisible( edit_visible );
+
+ // Create buttons
+ BOOL create_visible = (tool == gToolCreate);
+
+ mBtnCreate ->setToggleState( tool == gToolCreate );
+
+ if (mCheckCopySelection
+ && mCheckCopySelection->get())
+ {
+ // don't highlight any placer button
+ for (std::vector<LLButton*>::size_type i = 0; i < mButtons.size(); i++)
+ {
+ mButtons[i]->setToggleState(FALSE);
+ mButtons[i]->setVisible( create_visible );
+ }
+ }
+ else
+ {
+ // Highlight the correct placer button
+ for( std::vector<LLButton*>::size_type i = 0; i < mButtons.size(); i++ )
+ {
+ LLPCode pcode = LLToolPlacer::getObjectType();
+ void *userdata = mButtons[i]->getCallbackUserData();
+ LLPCode *cur = (LLPCode*) userdata;
+
+ BOOL state = (pcode == *cur);
+ mButtons[i]->setToggleState( state );
+ mButtons[i]->setVisible( create_visible );
+ }
+ }
+
+ if (mCheckSticky) mCheckSticky ->setVisible( create_visible );
+ if (mCheckCopySelection) mCheckCopySelection ->setVisible( create_visible );
+ if (mCheckCopyCenters) mCheckCopyCenters ->setVisible( create_visible );
+ if (mCheckCopyRotates) mCheckCopyRotates ->setVisible( create_visible );
+
+ if (mCheckCopyCenters) mCheckCopyCenters->setEnabled( mCheckCopySelection->get() );
+ if (mCheckCopyRotates) mCheckCopyRotates->setEnabled( mCheckCopySelection->get() );
+
+ // Land buttons
+ BOOL land_visible = (tool == gToolLand || tool == gToolParcel );
+
+ if (mBtnLand) mBtnLand ->setToggleState( land_visible );
+
+ // mRadioEditLand ->set( tool == gToolLand );
+ if (mRadioSelectLand) mRadioSelectLand->set( tool == gToolParcel );
+
+ // mRadioEditLand ->setVisible( land_visible );
+ if (mRadioSelectLand) mRadioSelectLand->setVisible( land_visible );
+
+ S32 dozer_mode = gSavedSettings.getS32("RadioLandBrushAction");
+ S32 dozer_size = gSavedSettings.getS32("RadioLandBrushSize");
+
+ if (mRadioDozerFlatten)
+ {
+ mRadioDozerFlatten ->set( tool == gToolLand && dozer_mode == 0);
+ mRadioDozerFlatten ->setVisible( land_visible );
+ }
+ if (mRadioDozerRaise)
+ {
+ mRadioDozerRaise ->set( tool == gToolLand && dozer_mode == 1);
+ mRadioDozerRaise ->setVisible( land_visible );
+ }
+ if (mRadioDozerLower)
+ {
+ mRadioDozerLower ->set( tool == gToolLand && dozer_mode == 2);
+ mRadioDozerLower ->setVisible( land_visible );
+ }
+ if (mRadioDozerSmooth)
+ {
+ mRadioDozerSmooth ->set( tool == gToolLand && dozer_mode == 3);
+ mRadioDozerSmooth ->setVisible( land_visible );
+ }
+ if (mRadioDozerNoise)
+ {
+ mRadioDozerNoise ->set( tool == gToolLand && dozer_mode == 4);
+ mRadioDozerNoise ->setVisible( land_visible );
+ }
+ if (mRadioDozerRevert)
+ {
+ mRadioDozerRevert ->set( tool == gToolLand && dozer_mode == 5);
+ mRadioDozerRevert ->setVisible( land_visible );
+ }
+ if (mComboDozerSize)
+ {
+ mComboDozerSize ->setCurrentByIndex(dozer_size);
+ mComboDozerSize ->setVisible( land_visible );
+ mComboDozerSize ->setEnabled( tool == gToolLand );
+ }
+ if (mBtnApplyToSelection)
+ {
+ mBtnApplyToSelection->setVisible( land_visible );
+ mBtnApplyToSelection->setEnabled( land_visible && !gParcelMgr->selectionEmpty() && tool != gToolParcel);
+ }
+ if (mCheckShowOwners)
+ {
+ mCheckShowOwners ->setVisible( land_visible );
+ }
+
+ //
+ // More panel visibility
+ //
+ BOOL show_more = gSavedSettings.getBOOL("ToolboxShowMore");
+
+ mTab->setVisible(show_more && tool != gToolLand && tool != gToolParcel);
+ mPanelLandInfo->setVisible(show_more && (tool == gToolLand || tool == gToolParcel));
+}
+
+
+// virtual
+BOOL LLFloaterTools::canClose()
+{
+ // don't close when quitting, so camera will stay put
+ return !gQuit;
+}
+
+// virtual
+void LLFloaterTools::onClose(bool app_quitting)
+{
+ setMinimized(FALSE);
+ setVisible(FALSE);
+ mTab->setVisible(FALSE);
+
+ // Different from handle_reset_view in that it doesn't actually
+ // move the camera if EditCameraMovement is not set.
+ gAgent.resetView(gSavedSettings.getBOOL("EditCameraMovement"));
+
+ // exit component selection mode
+ gSelectMgr->promoteSelectionToRoot();
+ gSavedSettings.setBOOL("SelectLinkedSet", TRUE);
+
+ gViewerWindow->showCursor();
+
+ resetToolState();
+
+ // Switch back to basic toolset
+ gCurrentToolset = gBasicToolset;
+ gBasicToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gBasicToolset );
+}
+
+void LLFloaterTools::showMore(BOOL show_more)
+{
+ BOOL showing_more = gSavedSettings.getBOOL("ToolboxShowMore");
+ if (show_more == showing_more)
+ {
+ return;
+ }
+
+ gSavedSettings.setBOOL("ToolboxShowMore", show_more);
+
+ // Visibility updated next frame - JC
+ // mTab->setVisible(show_more);
+
+ if (show_more)
+ {
+ reshape( mRect.getWidth(), mLargeHeight, TRUE);
+ translate( 0, mSmallHeight - mLargeHeight );
+ childSetVisible("button less", true);
+ childSetVisible("button more", false);
+ }
+ else
+ {
+ reshape( mRect.getWidth(), mSmallHeight, TRUE);
+ translate( 0, mLargeHeight - mSmallHeight );
+ childSetVisible("button less", false);
+ childSetVisible("button more", true);
+ }
+}
+
+void LLFloaterTools::showPanel(EInfoPanel panel)
+{
+ llassert(panel >= 0 && panel < PANEL_COUNT);
+ mTab->selectTabByName(PANEL_NAMES[panel]);
+ showMore(TRUE);
+}
+
+void click_show_more(void *userdata)
+{
+ LLFloaterTools *f = (LLFloaterTools *)userdata;
+ BOOL show_more = !gSavedSettings.getBOOL("ToolboxShowMore");
+ f->showMore( show_more );
+}
+
+void click_popup_info(void*)
+{
+// gBuildView->setPropertiesPanelOpen(TRUE);
+}
+
+void click_popup_done(void*)
+{
+ handle_reset_view();
+}
+
+void click_popup_grab_drag(LLUICtrl*, void*)
+{
+ gGrabBtnVertical = FALSE;
+ gGrabBtnSpin = FALSE;
+}
+
+void click_popup_grab_lift(LLUICtrl*, void*)
+{
+ gGrabBtnVertical = TRUE;
+ gGrabBtnSpin = FALSE;
+}
+
+void click_popup_grab_spin(LLUICtrl*, void*)
+{
+ gGrabBtnVertical = FALSE;
+ gGrabBtnSpin = TRUE;
+}
+
+void commit_radio_zoom(LLUICtrl *, void*)
+{
+ gCameraBtnOrbit = FALSE;
+ gCameraBtnPan = FALSE;
+}
+
+void commit_radio_orbit(LLUICtrl *, void*)
+{
+ gCameraBtnOrbit = TRUE;
+ gCameraBtnPan = FALSE;
+}
+
+void commit_radio_pan(LLUICtrl *, void*)
+{
+ gCameraBtnOrbit = FALSE;
+ gCameraBtnPan = TRUE;
+}
+
+void commit_slider_zoom(LLUICtrl *ctrl, void*)
+{
+ LLVolumeSliderCtrl* slider = (LLVolumeSliderCtrl*)ctrl;
+ // renormalize value, since max "volume" level is 0.5 for some reason
+ F32 zoom_level = (F32)slider->getValue().asReal() * 2.f; // / 0.5f;
+ gAgent.setCameraZoomFraction(zoom_level);
+}
+
+void click_popup_rotate_left(void*)
+{
+ gSelectMgr->selectionRotateAroundZ( 45.f );
+ dialog_refresh_all();
+}
+
+void click_popup_rotate_reset(void*)
+{
+ gSelectMgr->selectionResetRotation();
+ dialog_refresh_all();
+}
+
+void click_popup_rotate_right(void*)
+{
+ gSelectMgr->selectionRotateAroundZ( -45.f );
+ dialog_refresh_all();
+}
+
+
+void click_popup_dozer_mode(LLUICtrl *, void *user)
+{
+ S32 show_owners = gSavedSettings.getBOOL("ShowParcelOwners");
+ S32 mode = (S32)(intptr_t) user;
+ select_tool( gToolLand );
+ gSavedSettings.setS32("RadioLandBrushAction", mode);
+ gSavedSettings.setBOOL("ShowParcelOwners", show_owners);
+}
+
+void click_popup_dozer_size(LLUICtrl *, void *user)
+{
+ S32 size = (S32)(intptr_t) user;
+ gSavedSettings.setS32("RadioLandBrushSize", size);
+}
+
+void click_dozer_size(LLUICtrl *ctrl, void *user)
+{
+ S32 size = ((LLComboBox*) ctrl)->getCurrentIndex();
+ gSavedSettings.setS32("RadioLandBrushSize", size);
+}
+
+void click_apply_to_selection(void* user)
+{
+ gToolLand->modifyLandInSelectionGlobal();
+}
+
+void commit_select_tool(LLUICtrl *ctrl, void *data)
+{
+ S32 show_owners = gSavedSettings.getBOOL("ShowParcelOwners");
+ select_tool(data);
+ gSavedSettings.setBOOL("ShowParcelOwners", show_owners);
+}
+
+void commit_select_component(LLUICtrl *ctrl, void *data)
+{
+ LLFloaterTools* floaterp = (LLFloaterTools*)data;
+
+ //forfeit focus
+ if (gFocusMgr.childHasKeyboardFocus(floaterp))
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ BOOL select_individuals = floaterp->mCheckSelectIndividual->get();
+ gSavedSettings.setBOOL("SelectLinkedSet", !select_individuals);
+ floaterp->dirty();
+
+ if (select_individuals)
+ {
+ gSelectMgr->demoteSelectionToIndividuals();
+ }
+ else
+ {
+ gSelectMgr->promoteSelectionToRoot();
+ }
+}
+
+void commit_grid_mode(LLUICtrl *ctrl, void *data)
+{
+ LLComboBox* combo = (LLComboBox*)ctrl;
+
+ gSelectMgr->setGridMode((EGridMode)combo->getCurrentIndex());
+}
+
+// static
+void LLFloaterTools::setObjectType( void* data )
+{
+ LLPCode pcode = *(LLPCode*) data;
+ LLToolPlacer::setObjectType( pcode );
+ gSavedSettings.setBOOL("CreateToolCopySelection", FALSE);
+ gViewerWindow->setMouseCapture(NULL, NULL);
+}
+
+// static
+void LLFloaterTools::onClickGridOptions(void* data)
+{
+ //LLFloaterTools* floaterp = (LLFloaterTools*)data;
+ LLFloaterBuildOptions::show(NULL);
+ // RN: this makes grid options dependent on build tools window
+ //floaterp->addDependentFloater(LLFloaterBuildOptions::getInstance(), FALSE);
+}
diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h
new file mode 100644
index 0000000000..092a8d7715
--- /dev/null
+++ b/indra/newview/llfloatertools.h
@@ -0,0 +1,164 @@
+/**
+ * @file llfloatertools.h
+ * @brief The edit tools, including move, position, land, etc.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTOOLS_H
+#define LL_LLFLOATERTOOLS_H
+
+#include "llfloater.h"
+#include "llcoord.h"
+
+class LLButton;
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLTabContainer;
+class LLPanelPermissions;
+class LLPanelObject;
+class LLPanelVolume;
+class LLPanelContents;
+class LLPanelFace;
+class LLPanelLandInfo;
+class LLComboBox;
+class LLVolumeSliderCtrl;
+
+class LLFloaterTools
+: public LLFloater
+{
+public:
+ virtual BOOL postBuild();
+ static void* createPanelPermissions(void* vdata);
+ static void* createPanelObject(void* vdata);
+ static void* createPanelVolume(void* vdata);
+ static void* createPanelFace(void* vdata);
+ static void* createPanelContents(void* vdata);
+ static void* createPanelContentsInventory(void* vdata);
+ static void* createPanelLandInfo(void* vdata);
+
+ LLFloaterTools();
+ virtual ~LLFloaterTools();
+
+ virtual void onClose(bool app_quitting);
+ virtual BOOL canClose();
+
+ // call this once per frame to handle visibility, rect location,
+ // button highlights, etc.
+ void updatePopup(LLCoordGL center, MASK mask);
+
+ // When the floater is going away, reset any options that need to be
+ // cleared.
+ void resetToolState();
+
+ enum EInfoPanel
+ {
+ PANEL_GENERAL=0,
+ PANEL_OBJECT,
+ PANEL_FEATURES,
+ PANEL_FACE,
+ PANEL_CONTENTS,
+ PANEL_COUNT
+ };
+
+ /*virtual*/ void draw();
+
+ void dirty();
+ void showMore(BOOL show_more);
+ void showPanel(EInfoPanel panel);
+
+ void setStatusText(const LLString& text);
+
+private:
+ static void setObjectType( void* data );
+ void refresh();
+
+ static void onClickGridOptions(void* data);
+
+public:
+
+ LLButton *mBtnFocus;
+ LLButton *mBtnMove;
+ LLButton *mBtnEdit;
+ LLButton *mBtnCreate;
+ LLButton *mBtnLand;
+
+ LLTextBox *mTextStatus;
+
+ // Focus buttons
+ LLCheckBoxCtrl *mRadioOrbit;
+ LLCheckBoxCtrl *mRadioZoom;
+ LLCheckBoxCtrl *mRadioPan;
+ LLVolumeSliderCtrl *mSliderZoom;
+
+ // Move buttons
+ LLCheckBoxCtrl *mRadioMove;
+ LLCheckBoxCtrl *mRadioLift;
+ LLCheckBoxCtrl *mRadioSpin;
+
+ // Edit buttons
+ LLCheckBoxCtrl *mRadioPosition;
+ LLCheckBoxCtrl *mRadioRotate;
+ LLCheckBoxCtrl *mRadioStretch;
+ LLCheckBoxCtrl *mRadioSelectFace;
+
+ LLCheckBoxCtrl *mCheckSelectIndividual;
+
+ LLCheckBoxCtrl* mCheckSnapToGrid;
+ LLButton* mBtnGridOptions;
+ LLTextBox* mTextGridMode;
+ LLComboBox* mComboGridMode;
+ LLCheckBoxCtrl* mCheckStretchUniform;
+ LLCheckBoxCtrl* mCheckStretchTexture;
+
+ LLButton *mBtnRotateLeft;
+ LLButton *mBtnRotateReset;
+ LLButton *mBtnRotateRight;
+
+ LLButton *mBtnDelete;
+ LLButton *mBtnDuplicate;
+ LLButton *mBtnDuplicateInPlace;
+
+ // Create buttons
+ LLCheckBoxCtrl *mCheckSticky;
+ LLCheckBoxCtrl *mCheckCopySelection;
+ LLCheckBoxCtrl *mCheckCopyCenters;
+ LLCheckBoxCtrl *mCheckCopyRotates;
+
+ // Land buttons
+// LLCheckBoxCtrl *mRadioEditLand;
+ LLCheckBoxCtrl *mRadioSelectLand;
+
+ LLCheckBoxCtrl *mRadioDozerFlatten;
+ LLCheckBoxCtrl *mRadioDozerRaise;
+ LLCheckBoxCtrl *mRadioDozerLower;
+ LLCheckBoxCtrl *mRadioDozerSmooth;
+ LLCheckBoxCtrl *mRadioDozerNoise;
+ LLCheckBoxCtrl *mRadioDozerRevert;
+
+ LLComboBox *mComboDozerSize;
+ LLButton *mBtnApplyToSelection;
+ LLCheckBoxCtrl *mCheckShowOwners;
+
+ std::vector<LLButton*> mButtons;//[ 15 ];
+
+ LLTabContainerCommon *mTab;
+ LLPanelPermissions *mPanelPermissions;
+ LLPanelObject *mPanelObject;
+ LLPanelVolume *mPanelVolume;
+ LLPanelContents *mPanelContents;
+ LLPanelFace *mPanelFace;
+ LLPanelLandInfo *mPanelLandInfo;
+
+ LLTabContainer* mTabLand;
+
+private:
+ BOOL mDirty;
+ S32 mSmallHeight;
+ S32 mLargeHeight;
+};
+
+extern LLFloaterTools *gFloaterTools;
+
+#endif
diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp
new file mode 100644
index 0000000000..f7850f38db
--- /dev/null
+++ b/indra/newview/llfloatertopobjects.cpp
@@ -0,0 +1,441 @@
+/**
+ * @file llfloatertopobjects.cpp
+ * @brief Shows top colliders, top scripts, etc.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertopobjects.h"
+
+#include "message.h"
+#include "llfontgl.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloatergodtools.h"
+#include "llparcel.h"
+#include "llscrolllistctrl.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "lltracker.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+LLFloaterTopObjects* LLFloaterTopObjects::sInstance = NULL;
+
+
+// static
+void LLFloaterTopObjects::show()
+{
+ if (sInstance)
+ {
+ sInstance->setVisibleAndFrontmost();
+ return;
+ }
+
+ sInstance = new LLFloaterTopObjects();
+ gUICtrlFactory->buildFloater(sInstance, "floater_top_objects.xml");
+ sInstance->center();
+}
+
+LLFloaterTopObjects::LLFloaterTopObjects()
+: LLFloater("top_objects"),
+ mInitialized(FALSE),
+ mtotalScore(0.f)
+{
+ sInstance = this;
+}
+
+LLFloaterTopObjects::~LLFloaterTopObjects()
+{
+ sInstance = NULL;
+}
+
+// virtual
+BOOL LLFloaterTopObjects::postBuild()
+{
+ childSetCommitCallback("objects_list", onCommitObjectsList, this);
+ childSetDoubleClickCallback("objects_list", onDoubleClickObjectsList);
+ childSetFocus("objects_list");
+ LLScrollListCtrl *objects_list = LLUICtrlFactory::getScrollListByName(this, "objects_list");
+ if (objects_list)
+ {
+ objects_list->setCommitOnSelectionChange(TRUE);
+ }
+
+ childSetAction("show_beacon_btn", onClickShowBeacon, this);
+ setDefaultBtn("show_beacon_btn");
+
+ childSetAction("return_selected_btn", onReturnSelected, this);
+ childSetAction("return_all_btn", onReturnAll, this);
+ childSetAction("disable_selected_btn", onDisableSelected, this);
+ childSetAction("disable_all_btn", onDisableAll, this);
+ childSetAction("refresh_btn", onRefresh, this);
+
+
+ childSetAction("filter_object_btn", onGetByObjectNameClicked, this);
+ childSetAction("filter_owner_btn", onGetByOwnerNameClicked, this);
+
+
+ /*
+ LLLineEditor* line_editor = LLUICtrlFactory::getLineEditorByName(this, "owner_name_editor");
+ if (line_editor)
+ {
+ line_editor->setCommitOnFocusLost(FALSE);
+ line_editor->setCommitCallback(onGetByOwnerName);
+ line_editor->setCallbackUserData(this);
+ }
+
+ line_editor = LLUICtrlFactory::getLineEditorByName(this, "object_name_editor");
+ if (line_editor)
+ {
+ line_editor->setCommitOnFocusLost(FALSE);
+ line_editor->setCommitCallback(onGetByObjectName);
+ line_editor->setCallbackUserData(this);
+ }*/
+
+ mCurrentMode = STAT_REPORT_TOP_SCRIPTS;
+ mFlags = 0;
+ mFilter = "";
+
+ return TRUE;
+}
+
+void LLFloaterTopObjects::handle_land_reply(LLMessageSystem* msg, void** data)
+{
+ // Make sure dialog is on screen
+ show();
+ sInstance->handleReply(msg, data);
+
+ //HACK: for some reason sometimes top scripts originally comes back
+ //with no results even though they're there
+ if (!sInstance->mObjectListIDs.size() && !sInstance->mInitialized)
+ {
+ sInstance->onRefresh(NULL);
+ sInstance->mInitialized = TRUE;
+ }
+
+}
+
+void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data)
+{
+ U32 request_flags;
+ U32 total_count;
+
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_RequestFlags, request_flags);
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_TotalObjectCount, total_count);
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ReportType, mCurrentMode);
+
+ LLCtrlListInterface *list = childGetListInterface("objects_list");
+ if (!list) return;
+
+ S32 block_count = msg->getNumberOfBlocks("ReportData");
+ for (S32 block = 0; block < block_count; ++block)
+ {
+ U32 task_local_id;
+ LLUUID task_id;
+ F32 location_x, location_y, location_z;
+ F32 score;
+ char name_buf[MAX_STRING];
+ char owner_buf[MAX_STRING];
+
+ msg->getU32Fast(_PREHASH_ReportData, _PREHASH_TaskLocalID, task_local_id, block);
+ msg->getUUIDFast(_PREHASH_ReportData, _PREHASH_TaskID, task_id, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_LocationX, location_x, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_LocationY, location_y, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_LocationZ, location_z, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_Score, score, block);
+ msg->getStringFast(_PREHASH_ReportData, _PREHASH_TaskName, MAX_STRING, name_buf, block);
+ msg->getStringFast(_PREHASH_ReportData, _PREHASH_OwnerName, MAX_STRING, owner_buf, block);
+
+ LLSD element;
+
+ element["id"] = task_id;
+ element["object_name"] = LLString(name_buf);
+ element["owner_name"] = LLString(owner_buf);
+ element["columns"][0]["column"] = "score";
+ element["columns"][0]["value"] = llformat("%0.3f", score);
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][1]["column"] = "name";
+ element["columns"][1]["value"] = name_buf;
+ element["columns"][1]["font"] = "SANSSERIF";
+ element["columns"][2]["column"] = "owner";
+ element["columns"][2]["value"] = owner_buf;
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][3]["column"] = "location";
+ element["columns"][3]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z);
+ element["columns"][3]["font"] = "SANSSERIF";
+
+ list->addElement(element);
+
+ mObjectListData.append(element);
+ mObjectListIDs.push_back(task_id);
+
+ mtotalScore += score;
+ }
+
+ if (total_count == 0 && list->getItemCount() == 0)
+ {
+ LLSD element;
+ element["id"] = LLUUID::null;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = childGetText("none_descriptor");
+ element["columns"][0]["font"] = "SANSSERIF";
+
+ list->addElement(element);
+ }
+ else
+ {
+ list->selectFirstItem();
+ }
+
+ if (mCurrentMode == STAT_REPORT_TOP_SCRIPTS)
+ {
+ setTitle(childGetText("top_scripts_title"));
+ list->setColumnLabel("score", childGetText("scripts_score_label"));
+
+ LLUIString format = childGetText("top_scripts_text");
+ format.setArg("[COUNT]", llformat("%d", total_count));
+ format.setArg("[TIME]", llformat("%0.1f", mtotalScore));
+ childSetValue("title_text", LLSD(format));
+ }
+ else
+ {
+ setTitle(childGetText("top_colliders_title"));
+ list->setColumnLabel("score", childGetText("colliders_score_label"));
+ LLUIString format = childGetText("top_colliders_text");
+ format.setArg("[COUNT]", llformat("%d", total_count));
+ childSetValue("title_text", LLSD(format));
+ }
+}
+
+// static
+void LLFloaterTopObjects::onCommitObjectsList(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterTopObjects* self = (LLFloaterTopObjects*)data;
+
+ self->updateSelectionInfo();
+}
+
+void LLFloaterTopObjects::updateSelectionInfo()
+{
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "objects_list");
+
+ if (!list) return;
+
+ LLUUID object_id = list->getCurrentID();
+ if (object_id.isNull()) return;
+
+ LLString object_id_string = object_id.getString();
+
+ childSetValue("id_editor", LLSD(object_id_string));
+ childSetValue("object_name_editor", list->getFirstSelected()->getColumn(1)->getText());
+ childSetValue("owner_name_editor", list->getFirstSelected()->getColumn(2)->getText());
+}
+
+// static
+void LLFloaterTopObjects::onDoubleClickObjectsList(void* data)
+{
+ LLFloaterTopObjects* self = (LLFloaterTopObjects*)data;
+ self->showBeacon();
+}
+
+// static
+void LLFloaterTopObjects::onClickShowBeacon(void* data)
+{
+ LLFloaterTopObjects* self = (LLFloaterTopObjects*)data;
+ if (!self) return;
+ self->showBeacon();
+}
+
+void LLFloaterTopObjects::doToObjects(int action, bool all)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) return;
+
+ LLCtrlListInterface *list = childGetListInterface("objects_list");
+ if (!list || list->getItemCount() == 0) return;
+
+ std::vector<LLUUID>::iterator id_itor;
+
+ bool start_message = true;
+
+ for (id_itor = mObjectListIDs.begin(); id_itor != mObjectListIDs.end(); ++id_itor)
+ {
+ LLUUID task_id = *id_itor;
+ if (!all && !list->isSelected(task_id))
+ {
+ // Selected only
+ continue;
+ }
+ if (start_message)
+ {
+ if (action == ACTION_RETURN)
+ {
+ msg->newMessageFast(_PREHASH_ParcelReturnObjects);
+ }
+ else
+ {
+ msg->newMessageFast(_PREHASH_ParcelDisableObjects);
+ }
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1); // Whole region
+ msg->addS32Fast(_PREHASH_ReturnType, RT_NONE);
+ start_message = false;
+ }
+
+ msg->nextBlockFast(_PREHASH_TaskIDs);
+ msg->addUUIDFast(_PREHASH_TaskID, task_id);
+
+ if (msg->isSendFullFast(_PREHASH_TaskIDs))
+ {
+ msg->sendReliable(region->getHost());
+ start_message = true;
+ }
+ }
+
+ if (!start_message)
+ {
+ msg->sendReliable(region->getHost());
+ }
+}
+
+//static
+void LLFloaterTopObjects::callbackReturnAll(S32 option, void* userdata)
+{
+ if (option == 0)
+ {
+ sInstance->doToObjects(ACTION_RETURN, true);
+ }
+}
+
+void LLFloaterTopObjects::onReturnAll(void* data)
+{
+ gViewerWindow->alertXml("ReturnAllTopObjects", callbackReturnAll, NULL);
+}
+
+
+void LLFloaterTopObjects::onReturnSelected(void* data)
+{
+ sInstance->doToObjects(ACTION_RETURN, false);
+}
+
+
+//static
+void LLFloaterTopObjects::callbackDisableAll(S32 option, void* userdata)
+{
+ if (option == 0)
+ {
+ sInstance->doToObjects(ACTION_DISABLE, true);
+ }
+}
+
+void LLFloaterTopObjects::onDisableAll(void* data)
+{
+ gViewerWindow->alertXml("DisableAllTopObjects", callbackDisableAll, NULL);
+}
+
+void LLFloaterTopObjects::onDisableSelected(void* data)
+{
+ sInstance->doToObjects(ACTION_DISABLE, false);
+}
+
+void LLFloaterTopObjects::clearList()
+{
+ LLCtrlListInterface *list = childGetListInterface("objects_list");
+
+ if (list)
+ {
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ }
+
+ mObjectListData.clear();
+ mObjectListIDs.clear();
+}
+
+void LLFloaterTopObjects::onRefresh(void* data)
+{
+ U32 mode = STAT_REPORT_TOP_SCRIPTS;
+ U32 flags = 0;
+ LLString filter = "";
+
+ if (sInstance)
+ {
+ mode = sInstance->mCurrentMode;
+ flags = sInstance->mFlags;
+ filter = sInstance->mFilter;
+ sInstance->clearList();
+ }
+
+ sInstance->mtotalScore = 0.f;
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_LandStatRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_RequestData);
+ msg->addU32Fast(_PREHASH_ReportType, mode);
+ msg->addU32Fast(_PREHASH_RequestFlags, flags);
+ msg->addStringFast(_PREHASH_Filter, filter);
+ msg->addS32Fast(_PREHASH_ParcelLocalID, 0);
+
+ msg->sendReliable(gAgent.getRegionHost());
+
+ if (sInstance)
+ {
+ sInstance->mFilter = "";
+ sInstance->mFlags = 0;
+ }
+}
+
+void LLFloaterTopObjects::onGetByObjectName(LLUICtrl* ctrl, void* data)
+{
+ if (sInstance)
+ {
+ sInstance->mFlags = STAT_FILTER_BY_OBJECT;
+ sInstance->mFilter = sInstance->childGetText("object_name_editor");
+ onRefresh(NULL);
+ }
+}
+
+void LLFloaterTopObjects::onGetByOwnerName(LLUICtrl* ctrl, void* data)
+{
+ if (sInstance)
+ {
+ sInstance->mFlags = STAT_FILTER_BY_OWNER;
+ sInstance->mFilter = sInstance->childGetText("owner_name_editor");
+ onRefresh(NULL);
+ }
+}
+
+void LLFloaterTopObjects::showBeacon()
+{
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "objects_list");
+ if (!list) return;
+
+ LLScrollListItem* first_selected = list->getFirstSelected();
+ if (!first_selected) return;
+
+ LLString name = first_selected->getColumn(1)->getText();
+ LLString pos_string = first_selected->getColumn(3)->getText();
+
+ F32 x, y, z;
+ S32 matched = sscanf(pos_string.c_str(), "<%g,%g,%g>", &x, &y, &z);
+ if (matched != 3) return;
+
+ LLVector3 pos_agent(x, y, z);
+ LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent);
+ LLString tooltip("");
+ LLTracker::trackLocation(pos_global, name, tooltip, LLTracker::LOCATION_ITEM);
+}
diff --git a/indra/newview/llfloatertopobjects.h b/indra/newview/llfloatertopobjects.h
new file mode 100644
index 0000000000..071e0bc5cd
--- /dev/null
+++ b/indra/newview/llfloatertopobjects.h
@@ -0,0 +1,86 @@
+/**
+ * @file llfloatertopobjects.h
+ * @brief Shows top colliders or top scripts
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTOPOBJECTS_H
+#define LL_LLFLOATERTOPOBJECTS_H
+
+#include "llfloater.h"
+
+class LLUICtrl;
+
+class LLFloaterTopObjects : public LLFloater
+{
+public:
+ // Opens the floater on screen.
+ static void show();
+
+ // Opens the floater if it's not on-screen.
+ // Juggles the UI based on method = "scripts" or "colliders"
+ static void handle_land_reply(LLMessageSystem* msg, void** data);
+ void handleReply(LLMessageSystem* msg, void** data);
+
+ void clearList();
+ void updateSelectionInfo();
+ virtual BOOL postBuild();
+
+ static void onRefresh(void* data);
+
+ static void setMode(U32 mode) { if (sInstance) sInstance->mCurrentMode = mode; }
+
+private:
+ LLFloaterTopObjects();
+ ~LLFloaterTopObjects();
+
+ void initColumns(LLCtrlListInterface *list);
+
+ static void onCommitObjectsList(LLUICtrl* ctrl, void* data);
+ static void onDoubleClickObjectsList(void* data);
+ static void onClickShowBeacon(void* data);
+
+ void doToObjects(int action, bool all);
+
+ static void onReturnAll(void* data);
+ static void onReturnSelected(void* data);
+ static void onDisableAll(void* data);
+ static void onDisableSelected(void* data);
+
+ static void callbackReturnAll(S32 option, void* userdata);
+ static void callbackDisableAll(S32 option, void* userdata);
+
+ static void onGetByOwnerName(LLUICtrl* ctrl, void* data);
+ static void onGetByObjectName(LLUICtrl* ctrl, void* data);
+
+ static void onGetByOwnerNameClicked(void* data) { onGetByOwnerName(NULL, data); };
+ static void onGetByObjectNameClicked(void* data) { onGetByObjectName(NULL, data); };
+
+ void showBeacon();
+
+private:
+ LLString mMethod;
+
+ LLSD mObjectListData;
+ std::vector<LLUUID> mObjectListIDs;
+
+ U32 mCurrentMode;
+ U32 mFlags;
+ LLString mFilter;
+
+ BOOL mInitialized;
+
+ F32 mtotalScore;
+
+ enum
+ {
+ ACTION_RETURN = 0,
+ ACTION_DISABLE
+ };
+
+ static LLFloaterTopObjects* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp
new file mode 100644
index 0000000000..149a585822
--- /dev/null
+++ b/indra/newview/llfloatertos.cpp
@@ -0,0 +1,269 @@
+/**
+ * @file llfloatertos.cpp
+ * @brief Terms of Service Agreement dialog
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertos.h"
+
+#include "llbutton.h"
+#include "llradiogroup.h"
+#include "llvfile.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "viewer.h"
+#include "llstartup.h"
+#include "message.h"
+#include "llagent.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llviewerstats.h"
+#include "llui.h"
+#include "llhttpclient.h"
+#include "llradiogroup.h"
+#include "llwebbrowserctrl.h"
+
+// static
+LLFloaterTOS* LLFloaterTOS::sInstance = NULL;
+
+// static
+LLFloaterTOS* LLFloaterTOS::show(ETOSType type, const std::string & message)
+{
+ if( !LLFloaterTOS::sInstance )
+ {
+ LLFloaterTOS::sInstance = new LLFloaterTOS(type, message);
+ }
+
+ if (type == TOS_TOS)
+ {
+ gUICtrlFactory->buildFloater(LLFloaterTOS::sInstance, "floater_tos.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildFloater(LLFloaterTOS::sInstance, "floater_critical.xml");
+ }
+
+ return LLFloaterTOS::sInstance;
+}
+
+
+LLFloaterTOS::LLFloaterTOS(ETOSType type, const std::string & message)
+: LLModalDialog( " ", 100, 100 ),
+ mType(type),
+ mMessage(message),
+ mWebBrowserWindowId( 0 ),
+ mLoadCompleteCount( 0 )
+{
+}
+
+// helper class that trys to download a URL from a web site and calls a method
+// on parent class indicating if the web server is working or not
+class LLIamHere : public LLHTTPClient::Responder
+{
+ private:
+ LLIamHere( LLFloaterTOS* parent ) :
+ mParent( parent )
+ {}
+
+ LLFloaterTOS* mParent;
+
+ public:
+
+ static boost::intrusive_ptr< LLIamHere > build( LLFloaterTOS* parent )
+ {
+ return boost::intrusive_ptr< LLIamHere >( new LLIamHere( parent ) );
+ };
+
+ virtual void setParent( LLFloaterTOS* parentIn )
+ {
+ mParent = parentIn;
+ };
+
+ virtual void result( const LLSD& content )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( true );
+ };
+
+ virtual void error( U32 status, const std::string& reason )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( false );
+ };
+};
+
+// this is global and not a class member to keep crud out of the header file
+namespace {
+ boost::intrusive_ptr< LLIamHere > gResponsePtr = 0;
+};
+
+BOOL LLFloaterTOS::postBuild()
+{
+ childSetAction("Continue", onContinue, this);
+ childSetAction("Cancel", onCancel, this);
+ childSetCommitCallback("tos_agreement", updateAgree, this);
+
+ // this displays the critical message
+ if ( mType != TOS_TOS )
+ {
+ LLTextEditor *Editor = LLUICtrlFactory::getTextEditorByName(this, "tos_text");
+ if (Editor)
+ {
+ Editor->setHandleEditKeysDirectly( TRUE );
+ Editor->setEnabled( FALSE );
+ Editor->setReadOnlyFgColor(LLColor4::white);
+ Editor->setWordWrap(TRUE);
+ Editor->setFocus(TRUE);
+ }
+ childSetValue("tos_text", LLSD(mMessage));
+ };
+
+ #if LL_LIBXUL_ENABLED
+ // disable Agree to TOS radio button until the page has fully loaded
+ LLRadioGroup* tos_agreement = LLUICtrlFactory::getRadioGroupByName(this, "tos_agreement");
+ if ( tos_agreement )
+ {
+ tos_agreement->setEnabled( false );
+ };
+
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "tos_html");
+ if ( web_browser )
+ {
+ // save the window id for this web browser widget
+ mWebBrowserWindowId = web_browser->getEmbeddedBrowserWindowId();
+
+ // start to observe it so we see navigate complete events
+ if ( mWebBrowserWindowId )
+ LLMozLib::getInstance()->addObserver( mWebBrowserWindowId, this );
+
+ gResponsePtr = LLIamHere::build( this );
+ LLHTTPClient::get( childGetValue( "real_url" ).asString(), gResponsePtr );
+ };
+ #else
+ LLTextEditor *Editor = LLUICtrlFactory::getTextEditorByName(this, "tos_text");
+ if (Editor)
+ {
+ Editor->setHandleEditKeysDirectly( TRUE );
+ Editor->setEnabled( FALSE );
+ Editor->setReadOnlyFgColor(LLColor4::white);
+ Editor->setWordWrap(TRUE);
+ Editor->setFocus(TRUE);
+ }
+ childSetValue("tos_text", LLSD(mMessage));
+ #endif
+ return TRUE;
+}
+
+void LLFloaterTOS::setSiteIsAlive( bool alive )
+{
+ // only do this for TOS pages
+ if ( mType == TOS_TOS )
+ {
+#if LL_LIBXUL_ENABLED
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "tos_html");
+ // if the contents of the site was retrieved
+ if ( alive )
+ {
+ if ( web_browser )
+ {
+ // navigate to the "real" page
+ web_browser->navigateTo( childGetValue( "real_url" ).asString() );
+ };
+ }
+ else
+ {
+ // normally this is set when navigation to TOS page navigation completes (so you can't accept before TOS loads)
+ // but if the page is unavailable, we need to do this now
+ LLRadioGroup* tos_agreement = LLUICtrlFactory::getRadioGroupByName(this, "tos_agreement");
+ if ( tos_agreement )
+ {
+ tos_agreement->setEnabled( true );
+ };
+
+ if ( web_browser )
+ {
+ // hide browser control (revealing default text message)
+ web_browser->setVisible( FALSE );
+ };
+ };
+#endif // LL_LIBXUL_ENABLED
+ };
+}
+
+LLFloaterTOS::~LLFloaterTOS()
+{
+#if LL_LIBXUL_ENABLED
+ // stop obsaerving events
+ if ( mWebBrowserWindowId )
+ LLMozLib::getInstance()->addObserver( mWebBrowserWindowId, this );
+#endif // LL_LIBXUL_ENABLED
+
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ LLFloaterTOS::sInstance = NULL;
+}
+
+// virtual
+void LLFloaterTOS::draw()
+{
+ // draw children
+ LLModalDialog::draw();
+}
+
+// static
+void LLFloaterTOS::updateAgree(LLUICtrl*, void* userdata )
+{
+ LLFloaterTOS* self = (LLFloaterTOS*) userdata;
+ std::string agree = self->childGetValue("tos_agreement").asString();
+ self->childSetEnabled("Continue", (agree == "radio_agree") );
+}
+
+// static
+void LLFloaterTOS::onContinue( void* userdata )
+{
+ LLFloaterTOS* self = (LLFloaterTOS*) userdata;
+ llinfos << "User agrees with TOS." << llendl;
+ if (self->mType == TOS_TOS)
+ {
+ gAcceptTOS = TRUE;
+ }
+ else
+ {
+ gAcceptCriticalMessage = TRUE;
+ }
+ gStartupState++;
+ self->close(); // destroys this object
+}
+
+// static
+void LLFloaterTOS::onCancel( void* userdata )
+{
+ LLFloaterTOS* self = (LLFloaterTOS*) userdata;
+ llinfos << "User disagrees with TOS." << llendl;
+ gViewerWindow->alertXml("MustAgreeToLogIn", login_alert_done);
+ gStartupState = STATE_LOGIN_SHOW;
+ self->mLoadCompleteCount = 0; // reset counter for next time we come to TOS
+ self->close(); // destroys this object
+}
+
+//virtual
+void LLFloaterTOS::onNavigateComplete( const EventType& eventIn )
+{
+ // skip past the loading screen navigate complete
+ if ( ++mLoadCompleteCount == 2 )
+ {
+ llinfos << "NAVIGATE COMPLETE" << llendl;
+ // enable Agree to TOS radio button now that page has loaded
+ LLRadioGroup* tos_agreement = LLUICtrlFactory::getRadioGroupByName(this, "tos_agreement");
+ if ( tos_agreement )
+ {
+ tos_agreement->setEnabled( true );
+ };
+ };
+}
diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h
new file mode 100644
index 0000000000..1f504b4edb
--- /dev/null
+++ b/indra/newview/llfloatertos.h
@@ -0,0 +1,64 @@
+/**
+ * @file llfloatertos.h
+ * @brief Terms of Service Agreement dialog
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTOS_H
+#define LL_LLFLOATERTOS_H
+
+#include "llmodaldialog.h"
+#include "llassetstorage.h"
+#include "llmozlib.h"
+
+class LLButton;
+class LLRadioGroup;
+class LLVFS;
+class LLTextEditor;
+class LLUUID;
+
+class LLFloaterTOS :
+ public LLModalDialog,
+ public LLEmbeddedBrowserWindowObserver
+{
+public:
+ virtual ~LLFloaterTOS();
+
+ // Types of dialog.
+ enum ETOSType
+ {
+ TOS_TOS = 0,
+ TOS_CRITICAL_MESSAGE = 1
+ };
+
+ // Asset_id is overwritten with LLUUID::null when agree is clicked.
+ static LLFloaterTOS* show(ETOSType type, const std::string & message);
+
+ BOOL postBuild();
+
+ virtual void draw();
+
+ static void updateAgree( LLUICtrl *, void* userdata );
+ static void onContinue( void* userdata );
+ static void onCancel( void* userdata );
+
+ void setSiteIsAlive( bool alive );
+
+ virtual void onNavigateComplete( const EventType& eventIn );
+
+private:
+ // Asset_id is overwritten with LLUUID::null when agree is clicked.
+ LLFloaterTOS(ETOSType type, const std::string & message);
+
+private:
+ ETOSType mType;
+ LLString mMessage;
+ int mWebBrowserWindowId;
+ int mLoadCompleteCount;
+
+ static LLFloaterTOS* sInstance;
+};
+
+#endif // LL_LLFLOATERTOS_H
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
new file mode 100644
index 0000000000..3492a36353
--- /dev/null
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -0,0 +1,1579 @@
+/**
+ * @file llfloaterworldmap.cpp
+ * @author James Cook, Tom Yedwab
+ * @brief LLFloaterWorldMap class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Map of the entire world, with multiple background images,
+ * avatar tracking, teleportation by double-click, etc.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterworldmap.h"
+
+// Library includes
+#include "llfontgl.h"
+#include "llinventory.h"
+#include "lllineeditor.h"
+#include "message.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "lldraghandle.h"
+#include "lleconomy.h"
+#include "llfirstuse.h"
+#include "llfocusmgr.h"
+#include "lliconctrl.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "lllandmarklist.h"
+#include "llnetmap.h"
+#include "llpreviewlandmark.h"
+#include "llradiogroup.h"
+#include "llregionhandle.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llscrolllistctrl.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltracker.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llworldmap.h"
+#include "llworldmapview.h"
+#include "llurl.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h"
+#include "llmapimagetype.h"
+#include "llweb.h"
+
+#include "llglheaders.h"
+
+//---------------------------------------------------------------------------
+// Constants
+//---------------------------------------------------------------------------
+static const F32 MAP_ZOOM_TIME = 0.2f;
+
+enum EPanDirection
+{
+ PAN_UP,
+ PAN_DOWN,
+ PAN_LEFT,
+ PAN_RIGHT
+};
+
+// Values in pixels per region
+static const F32 ZOOM_MIN = -8.f; // initial value, updated by adjustZoomSlider
+static const F32 ZOOM_MAX = 0.f;
+static const F32 ZOOM_INC = 0.2f;
+
+static const F32 SIM_COORD_MIN = 0.f;
+static const F32 SIM_COORD_MAX = 255.f;
+static const F32 SIM_COORD_DEFAULT = 128.f;
+
+static const F64 MAX_FLY_DISTANCE = 363.f; // Diagonal size of one sim.
+static const F64 MAX_FLY_DISTANCE_SQUARED = MAX_FLY_DISTANCE * MAX_FLY_DISTANCE;
+
+//---------------------------------------------------------------------------
+// Globals
+//---------------------------------------------------------------------------
+
+LLFloaterWorldMap* gFloaterWorldMap = NULL;
+
+class LLMapInventoryObserver : public LLInventoryObserver
+{
+public:
+ LLMapInventoryObserver() {}
+ virtual ~LLMapInventoryObserver() {}
+ virtual void changed(U32 mask);
+};
+
+void LLMapInventoryObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLInventoryObserver::CALLING_CARD | LLInventoryObserver::ADD |
+ LLInventoryObserver::REMOVE)) != 0)
+ {
+ gFloaterWorldMap->inventoryChanged();
+ }
+}
+
+class LLMapFriendObserver : public LLFriendObserver
+{
+public:
+ LLMapFriendObserver() {}
+ virtual ~LLMapFriendObserver() {}
+ virtual void changed(U32 mask);
+};
+
+void LLMapFriendObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE | LLFriendObserver::POWERS)) != 0)
+ {
+ gFloaterWorldMap->friendsChanged();
+ }
+}
+
+//---------------------------------------------------------------------------
+// Statics
+//---------------------------------------------------------------------------
+
+// Used as a pretend asset and inventory id to mean "landmark at my home location."
+const LLUUID LLFloaterWorldMap::sHomeID( "10000000-0000-0000-0000-000000000001" );
+
+//---------------------------------------------------------------------------
+// Construction and destruction
+//---------------------------------------------------------------------------
+
+
+LLFloaterWorldMap::LLFloaterWorldMap()
+: LLFloater("worldmap", "FloaterWorldMapRect", "World Map",
+ TRUE, // resize
+ 410, // min-width
+ 520, // min-height
+ FALSE, // drag on left
+ TRUE, // minimize
+ TRUE), // close
+ mInventory(NULL),
+ mInventoryObserver(NULL),
+ mFriendObserver(NULL),
+ mCompletingRegionName(""),
+ mWaitingForTracker(FALSE),
+ mExactMatch(FALSE),
+ mTrackedLocation(0,0,0),
+ mTrackedStatus(LLTracker::TRACKING_NOTHING)
+{
+ LLCallbackMap::map_t factory_map;
+ factory_map["objects_mapview"] = LLCallbackMap(createWorldMapView, NULL);
+ factory_map["terrain_mapview"] = LLCallbackMap(createWorldMapView, NULL);
+ gUICtrlFactory->buildFloater(this, "floater_world_map.xml", &factory_map);
+}
+
+// static
+void* LLFloaterWorldMap::createWorldMapView(void* data)
+{
+ return new LLWorldMapView("mapview", LLRect(0,300,400,0));
+}
+
+BOOL LLFloaterWorldMap::postBuild()
+{
+ mTabs = LLUICtrlFactory::getTabContainerByName(this, "maptab");
+ if (!mTabs) return FALSE;
+
+ LLPanel *panel;
+
+ panel = LLUICtrlFactory::getPanelByName(mTabs, "objects_mapview");
+ if (panel)
+ {
+ mTabs->setTabChangeCallback(panel, onCommitBackground);
+ mTabs->setTabUserData(panel, this);
+ }
+ panel = LLUICtrlFactory::getPanelByName(mTabs, "terrain_mapview");
+ if (panel)
+ {
+ mTabs->setTabChangeCallback(panel, onCommitBackground);
+ mTabs->setTabUserData(panel, this);
+ }
+
+ onCommitBackground((void*)this, false);
+
+ childSetCommitCallback("friend combo", onAvatarComboCommit, this);
+
+ LLComboBox *avatar_combo = LLUICtrlFactory::getComboBoxByName(this, "friend combo");
+ if (avatar_combo)
+ {
+ avatar_combo->selectFirstItem();
+ avatar_combo->setPrearrangeCallback( onAvatarComboPrearrange );
+ }
+
+ childSetAction("DoSearch", onLocationCommit, this);
+
+ childSetFocusChangedCallback("location", updateSearchEnabled);
+ childSetKeystrokeCallback("location", (void (*)(LLLineEditor*,void*))updateSearchEnabled, NULL);
+
+ childSetCommitCallback("search_results", onCommitSearchResult, this);
+ childSetDoubleClickCallback("search_results", onTeleportBtn);
+ childSetCommitCallback("spin x", onCommitLocation, this);
+ childSetCommitCallback("spin y", onCommitLocation, this);
+ childSetCommitCallback("spin z", onCommitLocation, this);
+
+ childSetCommitCallback("landmark combo", onLandmarkComboCommit, this);
+
+ LLComboBox *landmark_combo = LLUICtrlFactory::getComboBoxByName(this, "landmark combo");
+ if (landmark_combo)
+ {
+ landmark_combo->selectFirstItem();
+ landmark_combo->setPrearrangeCallback( onLandmarkComboPrearrange );
+ }
+
+ childSetAction("Go Home", onGoHome, this);
+
+ childSetAction("Teleport", onTeleportBtn, this);
+
+ childSetAction("Show Destination", onShowTargetBtn, this);
+ childSetAction("Show My Location", onShowAgentBtn, this);
+ childSetAction("Clear", onClearBtn, this);
+ childSetAction("copy_slurl", onCopySLURL, this);
+
+ mCurZoomVal = log(gMapScale)/log(2.f);
+ childSetValue("zoom slider", gMapScale);
+
+ setDefaultBtn("");
+
+ if ( gAgent.mAccess <= SIM_ACCESS_PG )
+ {
+ // Hide Mature Events controls
+ childHide("events_mature_icon");
+ childHide("events_mature_label");
+ childHide("event_mature_chk");
+ }
+
+ mZoomTimer.stop();
+
+ return TRUE;
+}
+
+// virtual
+LLFloaterWorldMap::~LLFloaterWorldMap()
+{
+ // All cleaned up by LLView destructor
+ mTabs = NULL;
+
+ // Inventory deletes all observers on shutdown
+ mInventory = NULL;
+ mInventoryObserver = NULL;
+
+ // avatar tracker will delete this for us.
+ mFriendObserver = NULL;
+}
+
+
+// virtual
+void LLFloaterWorldMap::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+}
+
+// Allow us to download landmarks quickly when map is shown
+class LLLandmarkFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ virtual void done()
+ {
+ // We need to find landmarks in all folders, so get the main
+ // background download going.
+ gInventory.startBackgroundFetch();
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+// static
+void LLFloaterWorldMap::show(void*, BOOL center_on_target)
+{
+ BOOL was_visible = gFloaterWorldMap->getVisible();
+
+ gFloaterWorldMap->mIsClosing = FALSE;
+ gFloaterWorldMap->open();
+
+ LLWorldMapView* map_panel;
+ map_panel = (LLWorldMapView*)gFloaterWorldMap->mTabs->getCurrentPanel();
+ map_panel->clearLastClick();
+
+ if (!was_visible)
+ {
+ // reset pan on show, so it centers on you again
+ if (!center_on_target)
+ {
+ LLWorldMapView::setPan(0, 0, TRUE);
+ }
+ map_panel->updateVisibleBlocks();
+
+ // Reload the agent positions when we show the window
+ gWorldMap->eraseItems();
+
+ // Reload any maps that may have changed
+ gWorldMap->clearSimFlags();
+
+ const S32 panel_num = gFloaterWorldMap->mTabs->getCurrentPanelIndex();
+ const bool request_from_sim = true;
+ gWorldMap->setCurrentLayer(panel_num, request_from_sim);
+
+ // We may already have a bounding box for the regions of the world,
+ // so use that to adjust the view.
+ gFloaterWorldMap->adjustZoomSliderBounds();
+
+ // Could be first show
+ LLFirstUse::useMap();
+
+ // Start speculative download of landmarks
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
+ gInventory.startBackgroundFetch(landmark_folder_id);
+
+ gFloaterWorldMap->childSetFocus("location", TRUE);
+ gFocusMgr.triggerFocusFlash();
+
+ gFloaterWorldMap->buildAvatarIDList();
+ gFloaterWorldMap->buildLandmarkIDLists();
+ }
+
+ if (center_on_target)
+ {
+ gFloaterWorldMap->centerOnTarget(FALSE);
+ }
+}
+
+
+
+// static
+void LLFloaterWorldMap::reloadIcons(void*)
+{
+ gWorldMap->eraseItems();
+
+ gWorldMap->sendMapLayerRequest();
+}
+
+
+// static
+void LLFloaterWorldMap::toggle(void*)
+{
+ BOOL visible = gFloaterWorldMap->getVisible();
+
+ if (!visible)
+ {
+ show(NULL, FALSE);
+ }
+ else
+ {
+ gFloaterWorldMap->mIsClosing = TRUE;
+ gFloaterWorldMap->close();
+ }
+}
+
+
+// static
+void LLFloaterWorldMap::hide(void*)
+{
+ gFloaterWorldMap->mIsClosing = TRUE;
+ gFloaterWorldMap->close();
+}
+
+
+// virtual
+void LLFloaterWorldMap::setVisible( BOOL visible )
+{
+ LLFloater::setVisible( visible );
+
+ gSavedSettings.setBOOL( "ShowWorldMap", visible );
+
+ if( !visible )
+ {
+ // While we're not visible, discard the overlay images we're using
+ if (gWorldMap)
+ {
+ gWorldMap->clearImageRefs();
+ }
+ }
+}
+
+
+// virtual
+BOOL LLFloaterWorldMap::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled;
+ handled = LLFloater::handleHover(x, y, mask);
+ return handled;
+}
+
+BOOL LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (getVisible() && !isMinimized() && isFrontmost())
+ {
+ F32 slider_value = (F32)childGetValue("zoom slider").asReal();
+ slider_value += ((F32)clicks * -0.3333f);
+ childSetValue("zoom slider", LLSD(slider_value));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+// virtual
+void LLFloaterWorldMap::reshape( S32 width, S32 height, BOOL called_from_parent )
+{
+ LLFloater::reshape( width, height, called_from_parent );
+
+ // Might have changed size of world display area
+ // JC: Technically, this is correct, but it makes the slider "pop"
+ // if you resize the window, then draw the slider. Just leaving it
+ // the way it was when you opened the window seems better.
+ // adjustZoomSliderBounds();
+}
+
+
+// virtual
+void LLFloaterWorldMap::draw()
+{
+ if( !getVisible() )
+ {
+ return;
+ }
+
+ updateLocation();
+
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if (LLTracker::TRACKING_AVATAR == tracking_status)
+ {
+ childSetColor("avatar_icon", gTrackColor);
+ }
+ else
+ {
+ childSetColor("avatar_icon", gDisabledTrackColor);
+ }
+
+ if (LLTracker::TRACKING_LANDMARK == tracking_status)
+ {
+ childSetColor("landmark_icon", gTrackColor);
+ }
+ else
+ {
+ childSetColor("landmark_icon", gDisabledTrackColor);
+ }
+
+ if (LLTracker::TRACKING_LOCATION == tracking_status)
+ {
+ childSetColor("location_icon", gTrackColor);
+ }
+ else
+ {
+ if (mCompletingRegionName != "")
+ {
+ F64 seconds = LLTimer::getElapsedSeconds();
+ double value = fmod(seconds, 2);
+ value = 0.5 + 0.5*cos(value * 3.14159f);
+ LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
+ childSetColor("location_icon", loading_color);
+ }
+ else
+ {
+ childSetColor("location_icon", gDisabledTrackColor);
+ }
+ }
+
+ // check for completion of tracking data
+ if (mWaitingForTracker)
+ {
+ centerOnTarget(TRUE);
+ }
+
+ childSetEnabled("Teleport", (BOOL)tracking_status);
+// childSetEnabled("Clear", (BOOL)tracking_status);
+ childSetEnabled("Show Destination", (BOOL)tracking_status || gWorldMap->mIsTrackingUnknownLocation);
+ childSetEnabled("copy_slurl", (BOOL)tracking_status);
+
+ setMouseOpaque(TRUE);
+ mDragHandle->setMouseOpaque(TRUE);
+
+ //RN: snaps to zoom value because interpolation caused jitter in the text rendering
+ if (!mZoomTimer.getStarted() && mCurZoomVal != (F32)childGetValue("zoom slider").asReal())
+ {
+ mZoomTimer.start();
+ }
+ F32 interp = mZoomTimer.getElapsedTimeF32() / MAP_ZOOM_TIME;
+ if (interp > 1.f)
+ {
+ interp = 1.f;
+ mZoomTimer.stop();
+ }
+ mCurZoomVal = lerp(mCurZoomVal, (F32)childGetValue("zoom slider").asReal(), interp);
+ F32 map_scale = 256.f*pow(2.f, mCurZoomVal);
+ LLWorldMapView::setScale( map_scale );
+
+ LLFloater::draw();
+}
+
+
+//-------------------------------------------------------------------------
+// Internal utility functions
+//-------------------------------------------------------------------------
+
+
+void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const LLString& name )
+{
+ LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
+ if (!iface) return;
+
+ buildAvatarIDList();
+ if(iface->setCurrentByID(avatar_id) || gAgent.isGodlike())
+ {
+ // *HACK: Adjust Z values automatically for liaisons & gods so
+ // we swoop down when they click on the map.
+ if(gAgent.isGodlike())
+ {
+ childSetValue("spin z", LLSD(200.f));
+ }
+ // Don't re-request info if we already have it or we won't have it in time to teleport
+ if (mTrackedStatus != LLTracker::TRACKING_AVATAR || name != mTrackedAvatarName)
+ {
+ mTrackedStatus = LLTracker::TRACKING_AVATAR;
+ mTrackedAvatarName = name;
+ LLTracker::trackAvatar(avatar_id, name);
+ }
+ }
+ else
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
+{
+ LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo");
+ if (!iface) return;
+
+ buildLandmarkIDLists();
+ BOOL found = FALSE;
+ S32 idx;
+ for (idx = 0; idx < mLandmarkItemIDList.count(); idx++)
+ {
+ if ( mLandmarkItemIDList.get(idx) == landmark_item_id)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found && iface->setCurrentByID( landmark_item_id ) )
+ {
+ LLUUID asset_id = mLandmarkAssetIDList.get( idx );
+ LLString name;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "landmark combo");
+ if (combo) name = combo->getSimple();
+ mTrackedStatus = LLTracker::TRACKING_LANDMARK;
+ LLTracker::trackLandmark(mLandmarkAssetIDList.get( idx ), // assetID
+ mLandmarkItemIDList.get( idx ), // itemID
+ name); // name
+
+ if( asset_id != sHomeID )
+ {
+ // start the download process
+ gLandmarkList.getAsset( asset_id);
+ }
+
+ // We have to download both region info and landmark data, so set busy. JC
+// getWindow()->incBusyCount();
+ }
+ else
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ setDefaultBtn("Teleport");
+}
+
+
+void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)
+{
+ mTrackedStatus = LLTracker::TRACKING_LOCATION;
+ LLTracker::trackLocation(event_info.mPosGlobal, event_info.mName, event_info.mToolTip, LLTracker::LOCATION_EVENT);
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
+{
+ mTrackedStatus = LLTracker::TRACKING_LOCATION;
+ LLTracker::trackLocation(item.mPosGlobal, item.mName, item.mToolTip, LLTracker::LOCATION_ITEM);
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
+{
+ LLSimInfo* sim_info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if (!sim_info)
+ {
+ gWorldMap->mIsTrackingUnknownLocation = TRUE;
+ gWorldMap->mInvalidLocation = FALSE;
+ gWorldMap->mUnknownLocation = pos_global;
+ LLTracker::stopTracking(NULL);
+ S32 world_x = S32(pos_global.mdV[0] / 256);
+ S32 world_y = S32(pos_global.mdV[1] / 256);
+ gWorldMap->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
+ setDefaultBtn("");
+ return;
+ }
+ if (sim_info->mAccess == SIM_ACCESS_DOWN)
+ {
+ // Down sim. Show the blue circle of death!
+ gWorldMap->mIsTrackingUnknownLocation = TRUE;
+ gWorldMap->mUnknownLocation = pos_global;
+ gWorldMap->mInvalidLocation = TRUE;
+ LLTracker::stopTracking(NULL);
+ setDefaultBtn("");
+ return;
+ }
+
+ LLString sim_name = gWorldMap->simNameFromPosGlobal( pos_global );
+ F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
+ F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );
+ LLString full_name = llformat("%s (%d, %d, %d)",
+ sim_name.c_str(),
+ llround(region_x),
+ llround(region_y),
+ llround((F32)pos_global.mdV[VZ]));
+
+ LLString tooltip("");
+ mTrackedStatus = LLTracker::TRACKING_LOCATION;
+ LLTracker::trackLocation(pos_global, full_name, tooltip);
+ gWorldMap->mIsTrackingUnknownLocation = FALSE;
+ gWorldMap->mIsTrackingDoubleClick = FALSE;
+ gWorldMap->mIsTrackingCommit = FALSE;
+
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::updateLocation()
+{
+ // These values may get updated by a message, so need to check them every frame
+ // The fields may be changed by the user, so only update them if the data changes
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ if (pos_global.isExactlyZero())
+ {
+ return; // invalid location
+ }
+ LLTracker::ETrackingStatus status = LLTracker::getTrackingStatus();
+ LLString sim_name = gWorldMap->simNameFromPosGlobal( pos_global );
+ if ((status != LLTracker::TRACKING_NOTHING) &&
+ (status != mTrackedStatus || pos_global != mTrackedLocation || sim_name != mTrackedSimName))
+ {
+ mTrackedStatus = status;
+ mTrackedLocation = pos_global;
+ mTrackedSimName = sim_name;
+
+ if (status == LLTracker::TRACKING_AVATAR)
+ {
+ // *HACK: Adjust Z values automatically for liaisons & gods so
+ // we swoop down when they click on the map.
+ if(gAgent.isGodlike())
+ {
+ pos_global[2] = 200;
+ }
+ }
+
+ childSetValue("location", sim_name);
+
+ F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
+ F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );
+ childSetValue("spin x", LLSD(region_x) );
+ childSetValue("spin y", LLSD(region_y) );
+ childSetValue("spin z", LLSD((F32)pos_global.mdV[VZ]) );
+
+ mSLURL = LLWeb::escapeURL(llformat("http://slurl.com/secondlife/%s/%d/%d/%d",
+ sim_name.c_str(), llround(region_x), llround(region_y), llround((F32)pos_global.mdV[VZ])));
+ }
+}
+
+void LLFloaterWorldMap::trackURL(const LLString& region_name, S32 x_coord, S32 y_coord, S32 z_coord)
+{
+ LLSimInfo* sim_info = gWorldMap->simInfoFromName(region_name);
+ z_coord = llclamp(z_coord, 0, 1000);
+ if (sim_info)
+ {
+ LLVector3 local_pos;
+ local_pos.mV[VX] = (F32)x_coord;
+ local_pos.mV[VY] = (F32)y_coord;
+ local_pos.mV[VZ] = (F32)z_coord;
+ LLVector3d global_pos = sim_info->getGlobalPos(local_pos);
+ trackLocation(global_pos);
+ setDefaultBtn("Teleport");
+ }
+ else
+ {
+ // fill in UI based on URL
+ gFloaterWorldMap->childSetValue("location", region_name);
+ childSetValue("spin x", LLSD((F32)x_coord));
+ childSetValue("spin y", LLSD((F32)y_coord));
+ childSetValue("spin z", LLSD((F32)z_coord));
+
+ // pass sim name to combo box
+ gFloaterWorldMap->mCompletingRegionName = region_name;
+ gWorldMap->sendNamedRegionRequest(region_name);
+ LLString::toLower(gFloaterWorldMap->mCompletingRegionName);
+ gWorldMap->mIsTrackingCommit = TRUE;
+ }
+}
+
+void LLFloaterWorldMap::observeInventory(LLInventoryModel* model)
+{
+ if(mInventory)
+ {
+ mInventory->removeObserver(mInventoryObserver);
+ delete mInventoryObserver;
+ mInventory = NULL;
+ mInventoryObserver = NULL;
+ }
+ if(model)
+ {
+ mInventory = model;
+ mInventoryObserver = new LLMapInventoryObserver;
+ // Inventory deletes all observers on shutdown
+ mInventory->addObserver(mInventoryObserver);
+ inventoryChanged();
+ }
+}
+
+void LLFloaterWorldMap::inventoryChanged()
+{
+ if(!LLTracker::getTrackedLandmarkItemID().isNull())
+ {
+ LLUUID item_id = LLTracker::getTrackedLandmarkItemID();
+ buildLandmarkIDLists();
+ trackLandmark(item_id);
+ }
+}
+
+void LLFloaterWorldMap::observeFriends()
+{
+ if(!mFriendObserver)
+ {
+ mFriendObserver = new LLMapFriendObserver;
+ LLAvatarTracker::instance().addObserver(mFriendObserver);
+ friendsChanged();
+ }
+}
+
+void LLFloaterWorldMap::friendsChanged()
+{
+ LLAvatarTracker& t = LLAvatarTracker::instance();
+ const LLUUID& avatar_id = t.getAvatarID();
+ buildAvatarIDList();
+ if(avatar_id.notNull())
+ {
+ LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
+ if(!iface || !iface->setCurrentByID(avatar_id) ||
+ !t.getBuddyInfo(avatar_id)->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION) || gAgent.isGodlike())
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ }
+}
+
+// No longer really builds a list. Instead, just updates mAvatarCombo.
+void LLFloaterWorldMap::buildAvatarIDList()
+{
+ LLCtrlListInterface *list = childGetListInterface("friend combo");
+ if (!list) return;
+
+ // Delete all but the "None" entry
+ S32 list_size = list->getItemCount();
+ while (list_size > 1)
+ {
+ list->selectNthItem(1);
+ list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
+ --list_size;
+ }
+
+ LLSD default_column;
+ default_column["name"] = "friend name";
+ default_column["label"] = "Friend Name";
+ default_column["width"] = 500;
+ list->addColumn(default_column);
+
+ // Get all of the calling cards for avatar that are currently online
+ LLCollectMappableBuddies collector;
+ LLAvatarTracker::instance().applyFunctor(collector);
+ LLCollectMappableBuddies::buddy_map_t::iterator it;
+ LLCollectMappableBuddies::buddy_map_t::iterator end;
+ it = collector.mMappable.begin();
+ end = collector.mMappable.end();
+ for( ; it != end; ++it)
+ {
+ list->addSimpleElement((*it).first, ADD_BOTTOM, (*it).second);
+ }
+
+ list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() );
+ list->selectFirstItem();
+}
+
+
+void LLFloaterWorldMap::buildLandmarkIDLists()
+{
+ LLCtrlListInterface *list = childGetListInterface("landmark combo");
+ if (!list) return;
+
+ // Delete all but the "None" entry
+ S32 list_size = list->getItemCount();
+ while (list_size > 1)
+ {
+ list->selectNthItem(1);
+ list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
+ --list_size;
+ }
+
+ mLandmarkItemIDList.reset();
+ mLandmarkAssetIDList.reset();
+
+ // Get all of the current landmarks
+ mLandmarkAssetIDList.put( LLUUID::null );
+ mLandmarkItemIDList.put( LLUUID::null );
+
+ mLandmarkAssetIDList.put( sHomeID );
+ mLandmarkItemIDList.put( sHomeID );
+
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLIsType is_landmark(LLAssetType::AT_LANDMARK);
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_landmark);
+ std::sort(items.begin(), items.end(), LLViewerInventoryItem::comparePointers());
+
+ S32 count = items.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLInventoryItem* item = items.get(i);
+
+ list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
+
+ mLandmarkAssetIDList.put( item->getAssetUUID() );
+ mLandmarkItemIDList.put( item->getUUID() );
+ }
+ list->sortByColumn("landmark name", TRUE);
+ list->selectFirstItem();
+}
+
+
+F32 LLFloaterWorldMap::getDistanceToDestination(const LLVector3d &destination,
+ F32 z_attenuation) const
+{
+ LLVector3d delta = destination - gAgent.getPositionGlobal();
+ // by attenuating the z-component we effectively
+ // give more weight to the x-y plane
+ delta.mdV[VZ] *= z_attenuation;
+ F32 distance = (F32)delta.magVec();
+ return distance;
+}
+
+
+void LLFloaterWorldMap::clearLocationSelection(BOOL clear_ui)
+{
+ LLCtrlListInterface *list = childGetListInterface("search_results");
+ if (list)
+ {
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ }
+ if (!childHasKeyboardFocus("spin x"))
+ {
+ childSetValue("spin x", SIM_COORD_DEFAULT);
+ }
+ if (!childHasKeyboardFocus("spin y"))
+ {
+ childSetValue("spin y", SIM_COORD_DEFAULT);
+ }
+ if (!childHasKeyboardFocus("spin z"))
+ {
+ childSetValue("spin z", 0);
+ }
+ gWorldMap->mIsTrackingCommit = FALSE;
+ mCompletingRegionName = "";
+ mExactMatch = FALSE;
+}
+
+
+void LLFloaterWorldMap::clearLandmarkSelection(BOOL clear_ui)
+{
+ if (clear_ui || !childHasKeyboardFocus("landmark combo"))
+ {
+ LLCtrlListInterface *list = childGetListInterface("landmark combo");
+ if (list)
+ {
+ list->selectByValue( "None" );
+ }
+ }
+}
+
+
+void LLFloaterWorldMap::clearAvatarSelection(BOOL clear_ui)
+{
+ if (clear_ui || !childHasKeyboardFocus("friend combo"))
+ {
+ mTrackedStatus = LLTracker::TRACKING_NOTHING;
+ LLCtrlListInterface *list = childGetListInterface("friend combo");
+ if (list)
+ {
+ list->selectByValue( "None" );
+ }
+ }
+}
+
+
+// Adjust the maximally zoomed out limit of the zoom slider so you
+// can see the whole world, plus a little.
+void LLFloaterWorldMap::adjustZoomSliderBounds()
+{
+ // World size in regions
+ S32 world_width_regions = gWorldMap->getWorldWidth() / REGION_WIDTH_UNITS;
+ S32 world_height_regions = gWorldMap->getWorldHeight() / REGION_WIDTH_UNITS;
+
+ // Pad the world size a little bit, so we have a nice border on
+ // the edge
+ world_width_regions++;
+ world_height_regions++;
+
+ // Find how much space we have to display the world
+ LLWorldMapView* map_panel;
+ map_panel = (LLWorldMapView*)mTabs->getCurrentPanel();
+ LLRect view_rect = map_panel->getRect();
+
+ // View size in pixels
+ S32 view_width = view_rect.getWidth();
+ S32 view_height = view_rect.getHeight();
+
+ // Pixels per region to display entire width/height
+ F32 width_pixels_per_region = (F32) view_width / (F32) world_width_regions;
+ F32 height_pixels_per_region = (F32) view_height / (F32) world_height_regions;
+
+ F32 pixels_per_region = llmin(width_pixels_per_region,
+ height_pixels_per_region);
+
+ // Round pixels per region to an even number of slider increments
+ S32 slider_units = llfloor(pixels_per_region / 0.2f);
+ pixels_per_region = slider_units * 0.2f;
+
+ // Make sure the zoom slider can be moved at least a little bit.
+ // Likewise, less than the increment pixels per region is just silly.
+ pixels_per_region = llclamp(pixels_per_region, 1.f, (F32)(pow(2.f, ZOOM_MAX) * 128.f));
+
+ F32 min_power = log(pixels_per_region/256.f)/log(2.f);
+ childSetMinValue("zoom slider", min_power);
+}
+
+
+//-------------------------------------------------------------------------
+// User interface widget callbacks
+//-------------------------------------------------------------------------
+
+// static
+void LLFloaterWorldMap::onPanBtn( void* userdata )
+{
+ if( !gFloaterWorldMap ) return;
+
+ EPanDirection direction = (EPanDirection)(intptr_t)userdata;
+
+ S32 pan_x = 0;
+ S32 pan_y = 0;
+ switch( direction )
+ {
+ case PAN_UP: pan_y = -1; break;
+ case PAN_DOWN: pan_y = 1; break;
+ case PAN_LEFT: pan_x = 1; break;
+ case PAN_RIGHT: pan_x = -1; break;
+ default: llassert(0); return;
+ }
+
+ LLWorldMapView* map_panel;
+ map_panel = (LLWorldMapView*)gFloaterWorldMap->mTabs->getCurrentPanel();
+ map_panel->translatePan( pan_x, pan_y );
+}
+
+// static
+void LLFloaterWorldMap::onGoHome(void*)
+{
+ gAgent.teleportHome();
+ gFloaterWorldMap->close();
+}
+
+
+// static
+void LLFloaterWorldMap::onLandmarkComboPrearrange( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = self->childGetListInterface("landmark combo");
+ if (!list) return;
+
+ LLUUID current_choice = list->getCurrentID();
+
+ gFloaterWorldMap->buildLandmarkIDLists();
+
+ if( current_choice.isNull() || !list->setCurrentByID( current_choice ) )
+ {
+ LLTracker::stopTracking(NULL);
+ }
+
+}
+
+// static
+void LLFloaterWorldMap::onLandmarkComboCommit( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = gFloaterWorldMap->childGetListInterface("landmark combo");
+ if (!list) return;
+
+ LLUUID asset_id;
+ LLUUID item_id = list->getCurrentID();
+
+ LLTracker::stopTracking(NULL);
+
+ //RN: stopTracking() clears current combobox selection, need to reassert it here
+ list->setCurrentByID(item_id);
+
+ if( item_id.isNull() )
+ {
+ }
+ else if( item_id == sHomeID )
+ {
+ asset_id = sHomeID;
+ }
+ else
+ {
+ LLInventoryItem* item = gInventory.getItem( item_id );
+ if( item )
+ {
+ asset_id = item->getAssetUUID();
+ }
+ else
+ {
+ // Something went wrong, so revert to a safe value.
+ item_id.setNull();
+ }
+ }
+
+ self->trackLandmark( item_id);
+ onShowTargetBtn(self);
+}
+
+// static
+void LLFloaterWorldMap::onAvatarComboPrearrange( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = self->childGetListInterface("friend combo");
+ if (!list) return;
+
+ LLUUID current_choice;
+
+ if( LLAvatarTracker::instance().haveTrackingInfo() )
+ {
+ current_choice = LLAvatarTracker::instance().getAvatarID();
+ }
+
+ self->buildAvatarIDList();
+
+ if( !list->setCurrentByID( current_choice ) || current_choice.isNull() )
+ {
+ LLTracker::stopTracking(NULL);
+ }
+}
+
+
+// static
+void LLFloaterWorldMap::onAvatarComboCommit( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = gFloaterWorldMap->childGetListInterface("friend combo");
+ if (!list) return;
+
+ const LLUUID& new_avatar_id = list->getCurrentID();
+ if (new_avatar_id.notNull())
+ {
+ LLString name;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(gFloaterWorldMap, "friend combo");
+ if (combo) name = combo->getSimple();
+ self->trackAvatar(new_avatar_id, name);
+ onShowTargetBtn(self);
+ }
+}
+
+// static
+void LLFloaterWorldMap::updateSearchEnabled( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap *self = gFloaterWorldMap;
+ if (self->childHasKeyboardFocus("location") &&
+ self->childGetValue("location").asString().length() > 0)
+ {
+ self->setDefaultBtn("DoSearch");
+ }
+ else
+ {
+ self->setDefaultBtn("");
+ }
+}
+
+// static
+void LLFloaterWorldMap::onLocationCommit( void* userdata )
+{
+ LLFloaterWorldMap *self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ self->clearLocationSelection(FALSE);
+ self->mCompletingRegionName = "";
+ self->mLastRegionName = "";
+
+ LLString str = self->childGetValue("location").asString();
+
+ LLString::toLower(str);
+ gFloaterWorldMap->mCompletingRegionName = str;
+ gWorldMap->mIsTrackingCommit = TRUE;
+ self->mExactMatch = FALSE;
+ if (str.length() >= 3)
+ {
+ gWorldMap->sendNamedRegionRequest(str);
+ }
+ else
+ {
+ str += "#";
+ gWorldMap->sendNamedRegionRequest(str);
+ }
+}
+
+
+// static
+void LLFloaterWorldMap::onClearBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->mTrackedStatus = LLTracker::TRACKING_NOTHING;
+ LLTracker::stopTracking((void *)(intptr_t)TRUE);
+ gWorldMap->mIsTrackingUnknownLocation = FALSE;
+}
+
+// static
+void LLFloaterWorldMap::onFlyBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->fly();
+}
+
+void LLFloaterWorldMap::onShowTargetBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->centerOnTarget(TRUE);
+}
+
+void LLFloaterWorldMap::onShowAgentBtn(void* data)
+{
+ LLWorldMapView::setPan( 0, 0, FALSE); // FALSE == animate
+}
+
+// static
+void LLFloaterWorldMap::onTeleportBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->teleport();
+}
+
+// static
+void LLFloaterWorldMap::onCopySLURL(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(self->mSLURL));
+
+ LLString::format_map_t args;
+ args["[SLURL]"] = self->mSLURL;
+
+ LLAlertDialog::showXml("CopySLURL", args);
+}
+
+void LLFloaterWorldMap::onCheckEvents(LLUICtrl*, void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ if(!self) return;
+ self->childSetEnabled("event_mature_chk", self->childGetValue("event_chk"));
+}
+
+// protected
+void LLFloaterWorldMap::centerOnTarget(BOOL animate)
+{
+ LLVector3d pos_global;
+ if(LLTracker::getTrackingStatus() != LLTracker::TRACKING_NOTHING)
+ {
+ LLVector3d tracked_position = LLTracker::getTrackedPositionGlobal();
+ //RN: tracker doesn't allow us to query completion, so we check for a tracking position of
+ // absolute zero, and keep trying in the draw loop
+ if (tracked_position.isExactlyZero())
+ {
+ mWaitingForTracker = TRUE;
+ return;
+ }
+ else
+ {
+ // We've got the position finally, so we're no longer busy. JC
+// getWindow()->decBusyCount();
+ pos_global = LLTracker::getTrackedPositionGlobal() - gAgent.getCameraPositionGlobal();
+ }
+ }
+ else if(gWorldMap->mIsTrackingUnknownLocation)
+ {
+ pos_global = gWorldMap->mUnknownLocation - gAgent.getCameraPositionGlobal();;
+ }
+ else
+ {
+ // default behavior = center on agent
+ pos_global.clearVec();
+ }
+
+ LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sPixelsPerMeter)),
+ -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sPixelsPerMeter)),
+ !animate);
+ mWaitingForTracker = FALSE;
+}
+
+// protected
+void LLFloaterWorldMap::fly()
+{
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+
+ // Start the autopilot and close the floater,
+ // so we can see where we're flying
+ if (!pos_global.isExactlyZero())
+ {
+ gAgent.startAutoPilotGlobal( pos_global );
+ close();
+ }
+ else
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+}
+
+
+// protected
+void LLFloaterWorldMap::teleport()
+{
+ BOOL teleport_home = FALSE;
+ LLVector3d pos_global;
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if (LLTracker::TRACKING_AVATAR == tracking_status
+ && av_tracker.haveTrackingInfo() )
+ {
+ pos_global = av_tracker.getGlobalPos();
+ pos_global.mdV[VZ] = childGetValue("spin z");
+ }
+ else if ( LLTracker::TRACKING_LANDMARK == tracking_status)
+ {
+ if( LLTracker::getTrackedLandmarkAssetID() == sHomeID )
+ {
+ teleport_home = TRUE;
+ }
+ else
+ {
+ LLLandmark* landmark = gLandmarkList.getAsset( LLTracker::getTrackedLandmarkAssetID() );
+ LLUUID region_id;
+ if(landmark
+ && !landmark->getGlobalPos(pos_global)
+ && landmark->getRegionID(region_id))
+ {
+ LLLandmark::requestRegionHandle(
+ gMessageSystem,
+ gAgent.getRegionHost(),
+ region_id,
+ NULL);
+ }
+ }
+ }
+ else if ( LLTracker::TRACKING_LOCATION == tracking_status)
+ {
+ pos_global = LLTracker::getTrackedPositionGlobal();
+ }
+ else
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+
+ // Do the teleport, which will also close the floater
+ if (teleport_home)
+ {
+ gAgent.teleportHome();
+ }
+ else if (!pos_global.isExactlyZero())
+ {
+ if(LLTracker::TRACKING_LANDMARK == tracking_status)
+ {
+ gAgent.teleportViaLandmark(LLTracker::getTrackedLandmarkAssetID());
+ }
+ else
+ {
+ gAgent.teleportViaLocation( pos_global );
+ }
+ }
+}
+
+// static
+void LLFloaterWorldMap::onGoToLandmarkDialog( S32 option, void* userdata )
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+ switch( option )
+ {
+ case 0:
+ self->teleportToLandmark();
+ break;
+ case 1:
+ self->flyToLandmark();
+ break;
+ default:
+ // nothing
+ break;
+ }
+}
+
+void LLFloaterWorldMap::flyToLandmark()
+{
+ LLVector3d destination_pos_global;
+ if( !LLTracker::getTrackedLandmarkAssetID().isNull() )
+ {
+ if (LLTracker::hasLandmarkPosition())
+ {
+ gAgent.startAutoPilotGlobal( LLTracker::getTrackedPositionGlobal() );
+ }
+ }
+}
+
+void LLFloaterWorldMap::teleportToLandmark()
+{
+ BOOL has_destination = FALSE;
+ LLUUID destination_id; // Null means "home"
+
+ if( LLTracker::getTrackedLandmarkAssetID() == sHomeID )
+ {
+ has_destination = TRUE;
+ }
+ else
+ {
+ LLLandmark* landmark = gLandmarkList.getAsset( LLTracker::getTrackedLandmarkAssetID() );
+ LLVector3d global_pos;
+ if(landmark && landmark->getGlobalPos(global_pos))
+ {
+ destination_id = LLTracker::getTrackedLandmarkAssetID();
+ has_destination = TRUE;
+ }
+ else if(landmark)
+ {
+ // pop up an anonymous request request.
+ LLUUID region_id;
+ if(landmark->getRegionID(region_id))
+ {
+ LLLandmark::requestRegionHandle(
+ gMessageSystem,
+ gAgent.getRegionHost(),
+ region_id,
+ NULL);
+ }
+ }
+ }
+
+ if( has_destination )
+ {
+ gAgent.teleportViaLandmark( destination_id );
+ }
+}
+
+
+void LLFloaterWorldMap::teleportToAvatar()
+{
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if(av_tracker.haveTrackingInfo())
+ {
+ LLVector3d pos_global = av_tracker.getGlobalPos();
+ gAgent.teleportViaLocation( pos_global );
+ }
+}
+
+
+void LLFloaterWorldMap::flyToAvatar()
+{
+ if( LLAvatarTracker::instance().haveTrackingInfo() )
+ {
+ gAgent.startAutoPilotGlobal( LLAvatarTracker::instance().getGlobalPos() );
+ }
+}
+
+// static
+void LLFloaterWorldMap::onCommitBackground(void* userdata, bool from_click)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+
+ // Find my index
+ S32 index = self->mTabs->getCurrentPanelIndex();
+
+ if (gWorldMap)
+ {
+ gWorldMap->setCurrentLayer(index);
+ }
+}
+
+void LLFloaterWorldMap::updateSims(bool found_null_sim)
+{
+ if (mCompletingRegionName == "")
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = childGetListInterface("search_results");
+ if (!list) return;
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ LLSD selected_value = list->getSimpleSelectedValue();
+
+ S32 name_length = mCompletingRegionName.length();
+
+ BOOL match_found = FALSE;
+ S32 num_results = 0;
+ std::map<U64, LLSimInfo*>::const_iterator it;
+ for (it = gWorldMap->mSimInfoMap.begin(); it != gWorldMap->mSimInfoMap.end(); ++it)
+ {
+ LLSimInfo* info = (*it).second;
+ LLString sim_name = info->mName;
+ LLString sim_name_lower = sim_name;
+ LLString::toLower(sim_name_lower);
+
+ if (sim_name_lower.substr(0, name_length) == mCompletingRegionName)
+ {
+ if (gWorldMap->mIsTrackingCommit)
+ {
+ if (sim_name_lower == mCompletingRegionName)
+ {
+ selected_value = sim_name;
+ match_found = TRUE;
+ }
+ }
+
+ LLSD value;
+ value["id"] = sim_name;
+ value["columns"][0]["column"] = "sim_name";
+ value["columns"][0]["value"] = sim_name;
+ list->addElement(value);
+ num_results++;
+ }
+ }
+
+ list->selectByValue(selected_value);
+
+ if (found_null_sim)
+ {
+ mCompletingRegionName = "";
+ }
+
+ if (match_found)
+ {
+ mExactMatch = TRUE;
+ childSetFocus("search_results");
+ onCommitSearchResult(NULL, this);
+ }
+ else if (!mExactMatch && num_results > 0)
+ {
+ list->selectFirstItem(); // select first item by default
+ childSetFocus("search_results");
+ onCommitSearchResult(NULL, this);
+ }
+ else
+ {
+ list->addSimpleElement("None found.");
+ list->operateOnAll(LLCtrlListInterface::OP_DESELECT);
+ }
+}
+
+// static
+void LLFloaterWorldMap::onCommitLocation(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if ( LLTracker::TRACKING_LOCATION == tracking_status)
+ {
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ F64 local_x = self->childGetValue("spin x");
+ F64 local_y = self->childGetValue("spin y");
+ F64 local_z = self->childGetValue("spin z");
+ pos_global.mdV[VX] += -fmod(pos_global.mdV[VX], 256.0) + local_x;
+ pos_global.mdV[VY] += -fmod(pos_global.mdV[VY], 256.0) + local_y;
+ pos_global.mdV[VZ] = local_z;
+ self->trackLocation(pos_global);
+ }
+}
+
+// static
+void LLFloaterWorldMap::onCommitSearchResult(LLUICtrl*, void* userdata)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+
+ LLCtrlListInterface *list = self->childGetListInterface("search_results");
+ if (!list) return;
+
+ LLSD selected_value = list->getSimpleSelectedValue();
+ LLString sim_name = selected_value.asString();
+ if (sim_name.empty())
+ {
+ return;
+ }
+ LLString::toLower(sim_name);
+
+ std::map<U64, LLSimInfo*>::const_iterator it;
+ for (it = gWorldMap->mSimInfoMap.begin(); it != gWorldMap->mSimInfoMap.end(); ++it)
+ {
+ LLSimInfo* info = (*it).second;
+ LLString info_sim_name = info->mName;
+ LLString::toLower(info_sim_name);
+
+ if (sim_name == info_sim_name)
+ {
+ LLVector3d pos_global = from_region_handle( info->mHandle );
+ F64 local_x = self->childGetValue("spin x");
+ F64 local_y = self->childGetValue("spin y");
+ F64 local_z = self->childGetValue("spin z");
+ pos_global.mdV[VX] += local_x;
+ pos_global.mdV[VY] += local_y;
+ pos_global.mdV[VZ] = local_z;
+
+ self->childSetValue("location", sim_name);
+ self->trackLocation(pos_global);
+ self->setDefaultBtn("Teleport");
+ break;
+ }
+ }
+
+ onShowTargetBtn(self);
+}
diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h
new file mode 100644
index 0000000000..32112a995f
--- /dev/null
+++ b/indra/newview/llfloaterworldmap.h
@@ -0,0 +1,170 @@
+/**
+ * @file llfloaterworldmap.h
+ * @brief LLFloaterWorldMap class definition
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Map of the entire world, with multiple background images,
+ * avatar tracking, teleportation by double-click, etc.
+ */
+
+#ifndef LL_LLFLOATERWORLDMAP_H
+#define LL_LLFLOATERWORLDMAP_H
+
+#include "lldarray.h"
+#include "llfloater.h"
+#include "llhudtext.h"
+#include "llmapimagetype.h"
+#include "lltracker.h"
+
+class LLEventInfo;
+class LLFriendObserver;
+class LLInventoryModel;
+class LLInventoryObserver;
+class LLItemInfo;
+class LLTabContainer;
+class LLWorldMapView;
+
+class LLFloaterWorldMap : public LLFloater
+{
+public:
+ LLFloaterWorldMap();
+ virtual ~LLFloaterWorldMap();
+
+ static void *createWorldMapView(void* data);
+ BOOL postBuild();
+
+ virtual void onClose(bool app_quitting);
+
+ static void show(void*, BOOL center_on_target );
+ static void reloadIcons(void*);
+ static void toggle(void*);
+ static void hide(void*);
+
+ virtual void reshape( S32 width, S32 height, BOOL called_from_parent = TRUE );
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void setVisible(BOOL visible);
+ virtual void draw();
+
+ // methods for dealing with inventory. The observe() method is
+ // called during program startup. inventoryUpdated() will be
+ // called by a helper object when an interesting change has
+ // occurred.
+ void observeInventory(LLInventoryModel* inventory);
+ void inventoryChanged();
+
+ // Calls for dealing with changes in friendship
+ void observeFriends();
+ void friendsChanged();
+
+ // tracking methods
+ void trackAvatar( const LLUUID& avatar_id, const LLString& name );
+ void trackLandmark( const LLUUID& landmark_item_id );
+ void trackLocation(const LLVector3d& pos);
+ void trackEvent(const LLItemInfo &event_info);
+ void trackGenericItem(const LLItemInfo &item);
+ void trackURL(const LLString& region_name, S32 x_coord, S32 y_coord, S32 z_coord);
+
+ static const LLUUID& getHomeID() { return sHomeID; }
+
+ // A z_attenuation of 0.0f collapses the distance into the X-Y plane
+ F32 getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const;
+
+ void clearLocationSelection(BOOL clear_ui = FALSE);
+ void clearAvatarSelection(BOOL clear_ui = FALSE);
+ void clearLandmarkSelection(BOOL clear_ui = FALSE);
+
+ // Adjust the maximally zoomed out limit of the zoom slider so you can
+ // see the whole world, plus a little.
+ void adjustZoomSliderBounds();
+
+ // Catch changes in the sim list
+ void updateSims(bool found_null_sim);
+
+protected:
+ static void onPanBtn( void* userdata );
+
+ static void onGoHome(void* data);
+
+ static void onLandmarkComboPrearrange( LLUICtrl* ctrl, void* data );
+ static void onLandmarkComboCommit( LLUICtrl* ctrl, void* data );
+
+ static void onAvatarComboPrearrange( LLUICtrl* ctrl, void* data );
+ static void onAvatarComboCommit( LLUICtrl* ctrl, void* data );
+
+ static void onCommitBackground(void* data, bool from_click);
+
+ static void onClearBtn(void*);
+ static void onFlyBtn(void*);
+ static void onTeleportBtn(void*);
+ static void onShowTargetBtn(void*);
+ static void onShowAgentBtn(void*);
+ static void onCopySLURL(void*);
+
+ static void onCheckEvents(LLUICtrl* ctrl, void*);
+
+ void centerOnTarget(BOOL animate);
+ void updateLocation();
+
+ // fly to the tracked item, if there is one
+ void fly();
+
+ // teleport to the tracked item, if there is one
+ void teleport();
+
+ void buildLandmarkIDLists();
+ static void onGoToLandmarkDialog(S32 option,void* userdata);
+ void flyToLandmark();
+ void teleportToLandmark();
+ void setLandmarkVisited();
+
+ void buildAvatarIDList();
+ void flyToAvatar();
+ void teleportToAvatar();
+
+ static void updateSearchEnabled( LLUICtrl* ctrl, void* userdata );
+ static void onLocationCommit( void* userdata );
+ static void onCommitLocation( LLUICtrl* ctrl, void* userdata );
+ static void onCommitSearchResult( LLUICtrl* ctrl, void* userdata );
+
+ void cacheLandmarkPosition();
+
+protected:
+ LLTabContainerCommon* mTabs;
+
+ // Sets gMapScale, in pixels per region
+ F32 mCurZoomVal;
+ LLFrameTimer mZoomTimer;
+
+ LLDynamicArray<LLUUID> mLandmarkAssetIDList;
+ LLDynamicArray<LLUUID> mLandmarkItemIDList;
+ BOOL mHasLandmarkPosition;
+
+ static const LLUUID sHomeID;
+
+ LLInventoryModel* mInventory;
+ LLInventoryObserver* mInventoryObserver;
+ LLFriendObserver* mFriendObserver;
+
+ LLString mCompletingRegionName;
+ LLString mLastRegionName;
+ BOOL mWaitingForTracker;
+ BOOL mExactMatch;
+
+ BOOL mIsClosing;
+
+ LLVector3d mTrackedLocation;
+ LLTracker::ETrackingStatus mTrackedStatus;
+ LLString mTrackedSimName;
+ LLString mTrackedAvatarName;
+ LLString mSLURL;
+};
+
+extern LLFloaterWorldMap* gFloaterWorldMap;
+
+#endif
+
diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp
new file mode 100644
index 0000000000..e9d7808fd2
--- /dev/null
+++ b/indra/newview/llfolderview.cpp
@@ -0,0 +1,4899 @@
+/**
+ * @file llfolderview.cpp
+ * @brief Implementation of the folder view collection of classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfolderview.h"
+
+#include <algorithm>
+
+#include "llviewercontrol.h"
+#include "lldbstrings.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llinventory.h"
+
+#include "llcallbacklist.h"
+#include "llinventoryclipboard.h" // *FIX: remove this!!!!
+#include "llinventoryview.h"// hacked in for the bonus context menu items.
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llresmgr.h"
+#include "llpreview.h"
+#include "llscrollcontainer.h" // hack to allow scrolling
+#include "lltooldraganddrop.h"
+#include "llui.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerjointattachment.h"
+#include "llviewermenu.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llfloaterproperties.h"
+
+//RN: HACK
+#include "llagent.h"
+#include "viewer.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const S32 LEFT_PAD = 5;
+const S32 LEFT_INDENTATION = 13;
+const S32 ICON_PAD = 2;
+const S32 ICON_WIDTH = 16;
+const S32 TEXT_PAD = 1;
+const S32 ARROW_SIZE = 12;
+const S32 RENAME_WIDTH_PAD = 4;
+const S32 RENAME_HEIGHT_PAD = 6;
+const S32 AUTO_OPEN_STACK_DEPTH = 16;
+const S32 MIN_ITEM_WIDTH_VISIBLE = ICON_WIDTH + ICON_PAD + ARROW_SIZE + TEXT_PAD + /*first few characters*/ 40;
+const S32 MINIMUM_RENAMER_WIDTH = 80;
+const F32 FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
+const F32 FOLDER_OPEN_TIME_CONSTANT = 0.03f;
+const S32 MAX_FOLDER_ITEM_OVERLAP = 2;
+
+F32 LLFolderView::sAutoOpenTime = 1.f;
+
+void delete_selected_item(void* user_data);
+void copy_selected_item(void* user_data);
+void open_selected_items(void* user_data);
+void properties_selected_items(void* user_data);
+void paste_items(void* user_data);
+void top_view_lost( LLView* handler );
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewItem
+///----------------------------------------------------------------------------
+
+// statics
+const LLFontGL* LLFolderViewItem::sFont = NULL;
+const LLFontGL* LLFolderViewItem::sSmallFont = NULL;
+LLColor4 LLFolderViewItem::sFgColor;
+LLColor4 LLFolderViewItem::sHighlightBgColor;
+LLColor4 LLFolderViewItem::sHighlightFgColor;
+LLColor4 LLFolderViewItem::sFilterBGColor;
+LLColor4 LLFolderViewItem::sFilterTextColor;
+
+// Default constructor
+LLFolderViewItem::LLFolderViewItem( const LLString& name, LLViewerImage* icon,
+ S32 creation_date,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener ) :
+ LLUICtrl( name, LLRect(0, 0, 0, 0), TRUE, NULL, NULL, FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT),
+ mLabel( name ),
+ mLabelWidth(0),
+ mCreationDate(creation_date),
+ mParentFolder( NULL ),
+ mListener( listener ),
+ mIsSelected( FALSE ),
+ mIsCurSelection( FALSE ),
+ mSelectPending(FALSE),
+ mLabelStyle( LLFontGL::NORMAL ),
+ mHasVisibleChildren(FALSE),
+ mIndentation(0),
+ mNumDescendantsSelected(0),
+ mFiltered(FALSE),
+ mLastFilterGeneration(-1),
+ mStringMatchOffset(LLString::npos),
+ mControlLabelRotation(0.f),
+ mRoot( root ),
+ mDragAndDropTarget(FALSE)
+{
+ setIcon(icon);
+ if( !LLFolderViewItem::sFont )
+ {
+ LLFolderViewItem::sFont = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ }
+
+ if (!LLFolderViewItem::sSmallFont)
+ {
+ LLFolderViewItem::sSmallFont = gResMgr->getRes( LLFONT_SMALL );
+ }
+
+ // HACK: Can't be set above because gSavedSettings might not be constructed.
+ LLFolderViewItem::sFgColor = gColors.getColor( "MenuItemEnabledColor" );
+ LLFolderViewItem::sHighlightBgColor = gColors.getColor( "MenuItemHighlightBgColor" );
+ LLFolderViewItem::sHighlightFgColor = gColors.getColor( "MenuItemHighlightFgColor" );
+ LLFolderViewItem::sFilterBGColor = gColors.getColor( "FilterBackgroundColor" );
+ LLFolderViewItem::sFilterTextColor = gColors.getColor( "FilterTextColor" );
+
+ mArrowImage = gImageList.getImage(LLUUID(gViewerArt.getString("folder_arrow.tga")), MIPMAP_FALSE, TRUE);
+ mBoxImage = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
+
+ refresh();
+ setTabStop(FALSE);
+}
+
+// Destroys the object
+LLFolderViewItem::~LLFolderViewItem( void )
+{
+ delete mListener;
+ mListener = NULL;
+ mArrowImage = NULL;
+ mBoxImage = NULL;
+}
+
+LLFolderView* LLFolderViewItem::getRoot()
+{
+ return mRoot;
+}
+
+// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor.
+BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor )
+{
+ LLFolderViewItem* root = this;
+ while( root->mParentFolder )
+ {
+ if( root->mParentFolder == potential_ancestor )
+ {
+ return TRUE;
+ }
+ root = root->mParentFolder;
+ }
+ return FALSE;
+}
+
+LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children)
+{
+ if (!mParentFolder)
+ {
+ return NULL;
+ }
+
+ LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children );
+ while(itemp && !itemp->getVisible())
+ {
+ LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children );
+ if (itemp == next_itemp)
+ {
+ // hit last item
+ return itemp->getVisible() ? itemp : this;
+ }
+ itemp = next_itemp;
+ }
+
+ return itemp;
+}
+
+LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children)
+{
+ if (!mParentFolder)
+ {
+ return NULL;
+ }
+
+ LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children );
+ while(itemp && !itemp->getVisible())
+ {
+ LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children );
+ if (itemp == next_itemp)
+ {
+ // hit first item
+ return itemp->getVisible() ? itemp : this;
+ }
+ itemp = next_itemp;
+ }
+
+ return itemp;
+}
+
+BOOL LLFolderViewItem::getFiltered()
+{
+ return mFiltered && mLastFilterGeneration >= mRoot->getFilter()->getMinRequiredGeneration();
+}
+
+BOOL LLFolderViewItem::getFiltered(S32 filter_generation)
+{
+ return mFiltered && mLastFilterGeneration >= filter_generation;
+}
+
+void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation)
+{
+ mFiltered = filtered;
+ mLastFilterGeneration = filter_generation;
+}
+
+void LLFolderViewItem::setIcon(LLViewerImage* icon)
+{
+ mIcon = icon;
+ if (mIcon)
+ {
+ mIcon->setBoostLevel(LLViewerImage::BOOST_UI);
+ }
+}
+
+// refresh information from the listener
+void LLFolderViewItem::refresh()
+{
+ if(mListener)
+ {
+ const char* label = mListener->getDisplayName().c_str();
+ mLabel = label ? label : "";
+ setIcon(mListener->getIcon());
+ U32 creation_date = mListener->getCreationDate();
+ if (mCreationDate != creation_date)
+ {
+ mCreationDate = mListener->getCreationDate();
+ dirtyFilter();
+ }
+ mLabelStyle = mListener->getLabelStyle();
+ mLabelSuffix = mListener->getLabelSuffix();
+
+ LLString searchable_label(mLabel);
+ searchable_label.append(mLabelSuffix);
+ LLString::toUpper(searchable_label);
+
+ if (mSearchableLabel.compare(searchable_label))
+ {
+ mSearchableLabel.assign(searchable_label);
+ dirtyFilter();
+ // some part of label has changed, so overall width has potentially changed
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+ }
+
+ S32 label_width = sFont->getWidth(mLabel);
+ if( mLabelSuffix.size() )
+ {
+ label_width += sFont->getWidth( mLabelSuffix );
+ }
+
+ mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + label_width;
+ }
+}
+
+void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
+{
+ functor(mListener);
+}
+
+// This function is called when items are added or view filters change. It's
+// implemented here but called by derived classes when folding the
+// views.
+void LLFolderViewItem::filterFromRoot( void )
+{
+ LLFolderViewItem* root = getRoot();
+
+ root->filter(*((LLFolderView*)root)->getFilter());
+}
+
+// This function is called when the folder view is dirty. It's
+// implemented here but called by derived classes when folding the
+// views.
+void LLFolderViewItem::arrangeFromRoot()
+{
+ LLFolderViewItem* root = getRoot();
+
+ S32 height = 0;
+ S32 width = 0;
+ root->arrange( &width, &height, 0 );
+}
+
+// This function clears the currently selected item, and records the
+// specified selected item appropriately for display and use in the
+// UI. If open is TRUE, then folders are opened up along the way to
+// the selection.
+void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection,
+ BOOL open,
+ BOOL take_keyboard_focus)
+{
+ getRoot()->setSelection(selection, open, take_keyboard_focus);
+}
+
+// helper function to change the selection from the root.
+void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ getRoot()->changeSelection(selection, selected);
+}
+
+void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection)
+{
+ LLDynamicArray<LLFolderViewItem*> selected_items;
+
+ getRoot()->extendSelection(selection, NULL, selected_items);
+}
+
+EWidgetType LLFolderViewItem::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER_ITEM;
+}
+
+LLString LLFolderViewItem::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_ITEM_TAG;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
+{
+ if (!folder)
+ {
+ return FALSE;
+ }
+ mParentFolder = folder;
+ root->addItemID(getListener()->getUUID(), this);
+ return folder->addItem(this);
+}
+
+
+// Finds width and height of this object and it's children. Also
+// makes sure that this view and it's children are the right size.
+S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation)
+{
+ mIndentation = mParentFolder ? mParentFolder->getIndentation() + LEFT_INDENTATION : 0;
+ *width = llmax(*width, mLabelWidth + mIndentation);
+ *height = getItemHeight();
+ return *height;
+}
+
+S32 LLFolderViewItem::getItemHeight()
+{
+ S32 icon_height = mIcon->getHeight();
+ S32 label_height = llround(sFont->getLineHeight());
+ return llmax( icon_height, label_height ) + ICON_PAD;
+}
+
+void LLFolderViewItem::filter( LLInventoryFilter& filter)
+{
+ BOOL filtered = mListener && filter.check(this);
+
+ // if our visibility will change as a result of this filter, then
+ // we need to be rearranged in our parent folder
+ if (getVisible() != filtered)
+ {
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+ }
+
+ setFiltered(filtered, filter.getCurrentGeneration());
+ mStringMatchOffset = filter.getStringMatchOffset();
+ filter.decrementFilterCount();
+
+ if (getRoot()->getDebugFilters())
+ {
+ mStatusText = llformat("%d", mLastFilterGeneration);
+ }
+}
+
+void LLFolderViewItem::dirtyFilter()
+{
+ mLastFilterGeneration = -1;
+ // bubble up dirty flag all the way to root
+ if (getParentFolder())
+ {
+ getParentFolder()->setCompletedFilterGeneration(-1, TRUE);
+ }
+}
+
+// *FIX: This can be optimized a lot by simply recording that it is selected in
+// the appropriate places, and assuming that set selection means 'deselect' for a
+// leaf item. Do this optimization after multiple selection is implemented to
+// make sure it all plays nice together.
+BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus)
+{
+ if( selection == this )
+ {
+ mIsSelected = TRUE;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ }
+ else
+ {
+ mIsSelected = FALSE;
+ }
+ return mIsSelected;
+}
+
+BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ if(selection == this && mIsSelected != selected)
+ {
+ mIsSelected = selected;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderViewItem::recursiveDeselect(BOOL deselect_self)
+{
+ if (mIsSelected && deselect_self)
+ {
+ mIsSelected = FALSE;
+
+ // update ancestors' count of selected descendents
+ LLFolderViewFolder* parent_folder = getParentFolder();
+ while(parent_folder)
+ {
+ parent_folder->mNumDescendantsSelected--;
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+}
+
+
+BOOL LLFolderViewItem::isMovable()
+{
+ if( mListener )
+ {
+ return mListener->isItemMovable();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+BOOL LLFolderViewItem::isRemovable()
+{
+ if( mListener )
+ {
+ return mListener->isItemRemovable();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+void LLFolderViewItem::destroyView()
+{
+ if (mParentFolder)
+ {
+ // removeView deletes me
+ mParentFolder->removeView(this);
+ }
+}
+
+// Call through to the viewed object and return true if it can be
+// removed.
+//BOOL LLFolderViewItem::removeRecursively(BOOL single_item)
+BOOL LLFolderViewItem::remove()
+{
+ if(!isRemovable())
+ {
+ return FALSE;
+ }
+ if(mListener)
+ {
+ return mListener->removeItem();
+ }
+ return TRUE;
+}
+
+// Build an appropriate context menu for the item.
+void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ if(mListener)
+ {
+ mListener->buildContextMenu(menu, flags);
+ }
+}
+
+void LLFolderViewItem::open( void )
+{
+ if( mListener )
+ {
+ mListener->openItem();
+ }
+}
+
+void LLFolderViewItem::preview( void )
+{
+ if (mListener)
+ {
+ mListener->previewItem();
+ }
+}
+
+void LLFolderViewItem::rename(const LLString& new_name)
+{
+ if( !new_name.empty() )
+ {
+ mLabel = new_name.c_str();
+ BOOL is_renamed = TRUE;
+ if( mListener )
+ {
+ is_renamed = mListener->renameItem(new_name);
+ }
+ if(mParentFolder && is_renamed)
+ {
+ mParentFolder->resort(this);
+ }
+ //refresh();
+ }
+}
+
+const LLString& LLFolderViewItem::getSearchableLabel() const
+{
+ return mSearchableLabel;
+}
+
+const LLString& LLFolderViewItem::getName( void ) const
+{
+ if(mListener)
+ {
+ return mListener->getName();
+ }
+ return mLabel;
+}
+
+LLFolderViewFolder* LLFolderViewItem::getParentFolder( void )
+{
+ return mParentFolder;
+}
+
+LLFolderViewEventListener* LLFolderViewItem::getListener( void )
+{
+ return mListener;
+}
+
+// LLView functionality
+BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ if(!mIsSelected)
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ // No handler needed for focus lost since this class has no
+ // state that depends on it.
+ gViewerWindow->setMouseCapture( this, NULL );
+
+ if (!mIsSelected)
+ {
+ if(mask & MASK_CONTROL)
+ {
+ changeSelectionFromRoot(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ extendSelectionFromRoot(this);
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ }
+ else
+ {
+ mSelectPending = TRUE;
+ }
+
+ if( isMovable() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ gToolDragAndDrop->setDragStart( screen_x, screen_y );
+ }
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
+{
+ if( gViewerWindow->hasMouseCapture( this ) && isMovable() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ BOOL can_drag = TRUE;
+ if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) )
+ {
+ LLFolderView* root = getRoot();
+
+ if(root->getCurSelectedItem())
+ {
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD;
+
+ //FIXME: push this into listener and remove dependency on llagent
+ if(mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gAgent.getInventoryRootID()))
+ {
+ src = LLToolDragAndDrop::SOURCE_AGENT;
+ }
+ else if (mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventoryLibraryRoot))
+ {
+ src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ }
+
+ can_drag = root->startDrag(src);
+ if (can_drag)
+ {
+ // if (mListener) mListener->startDrag();
+ // RN: when starting drag and drop, clear out last auto-open
+ root->autoOpenTest(NULL);
+ root->setShowSelectionContext(TRUE);
+
+ // Release keyboard focus, so that if stuff is dropped into the
+ // world, pressing the delete key won't blow away the inventory
+ // item.
+ gViewerWindow->setKeyboardFocus(NULL, NULL);
+
+ return gToolDragAndDrop->handleHover( x, y, mask );
+ }
+ }
+ }
+
+ if (can_drag)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
+ }
+ return TRUE;
+ }
+ else
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ // let parent handle this then...
+ return FALSE;
+ }
+}
+
+
+BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ preview();
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (getParent())
+ {
+ return getParent()->handleScrollWheel(x, y, clicks);
+ }
+ return FALSE;
+}
+
+BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ // if mouse hasn't moved since mouse down...
+ if ( pointInView(x, y) && mSelectPending )
+ {
+ //...then select
+ if(mask & MASK_CONTROL)
+ {
+ changeSelectionFromRoot(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ extendSelectionFromRoot(this);
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ }
+
+ mSelectPending = FALSE;
+
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ gViewerWindow->setMouseCapture( NULL, NULL );
+ }
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL accepted = FALSE;
+ BOOL handled = FALSE;
+ if(mListener)
+ {
+ accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data);
+ handled = accepted;
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ }
+ if(mParentFolder && !handled)
+ {
+ handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
+ }
+ if (handled)
+ {
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl;
+ }
+
+ return handled;
+}
+
+
+void LLFolderViewItem::draw()
+{
+ if( getVisible() )
+ {
+ bool possibly_has_children = false;
+ bool up_to_date = mListener && mListener->isUpToDate();
+ if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter...
+ (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter)
+ {
+ possibly_has_children = true;
+ }
+ if(/*mControlLabel[0] != '\0' && */possibly_has_children)
+ {
+ LLGLSTexture gls_texture;
+ if (mArrowImage)
+ {
+ gl_draw_scaled_rotated_image(mIndentation, mRect.getHeight() - ARROW_SIZE - TEXT_PAD,
+ ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, mArrowImage, sFgColor);
+ }
+ }
+
+ F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
+
+ // If we have keyboard focus, draw selection filled
+ BOOL show_context = getRoot()->getShowSelectionContext();
+ BOOL filled = show_context || (gFocusMgr.getKeyboardFocus() == getRoot());
+
+ // always render "current" item, only render other selected items if
+ // mShowSingleSelection is FALSE
+ if( mIsSelected )
+ {
+ LLGLSNoTexture gls_no_texture;
+ LLColor4 bg_color = sHighlightBgColor;
+ //const S32 TRAILING_PAD = 5; // It just looks better with this.
+ if (!mIsCurSelection)
+ {
+ // do time-based fade of extra objects
+ F32 fade_time = getRoot()->getSelectionFadeElapsedTime();
+ if (getRoot()->getShowSingleSelection())
+ {
+ // fading out
+ bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f);
+ }
+ else
+ {
+ // fading in
+ bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
+ }
+ }
+
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ bg_color, filled);
+ if (mIsCurSelection)
+ {
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ sHighlightFgColor, FALSE);
+ }
+ if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightFgColor, FALSE);
+ if (show_context)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightBgColor, TRUE);
+ }
+ }
+ }
+ if (mDragAndDropTarget)
+ {
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ sHighlightBgColor, FALSE);
+
+ if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightBgColor, FALSE);
+ }
+ mDragAndDropTarget = FALSE;
+ }
+
+
+ if(mIcon)
+ {
+ gl_draw_image(mIndentation + ARROW_SIZE + TEXT_PAD, mRect.getHeight() - mIcon->getHeight(), mIcon);
+ mIcon->addTextureStats( (F32)(mIcon->getWidth() * mIcon->getHeight()));
+ }
+
+ if (!mLabel.empty())
+ {
+ // highlight filtered text
+ BOOL debug_filters = getRoot()->getDebugFilters();
+ LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor );
+ F32 right_x;
+ F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
+
+ if (debug_filters)
+ {
+ if (!getFiltered() && !possibly_has_children)
+ {
+ color.mV[VALPHA] *= 0.5f;
+ }
+
+ LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f);
+ sSmallFont->renderUTF8(mStatusText, 0, text_left, y, filter_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ text_left = right_x;
+ }
+
+ sFont->renderUTF8( mLabel, 0, text_left, y, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ if (!mLabelSuffix.empty())
+ {
+ sFont->renderUTF8( mLabelSuffix, 0, right_x, y, LLColor4(0.75f, 0.85f, 0.85f, 1.f),
+ LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ }
+
+ if (mBoxImage.notNull() && mStringMatchOffset != LLString::npos)
+ {
+ // don't draw backgrounds for zero-length strings
+ S32 filter_string_length = mRoot->getFilterSubString().size();
+ if (filter_string_length > 0)
+ {
+ LLString combined_string = mLabel + mLabelSuffix;
+ S32 left = llround(text_left) + sFont->getWidth(combined_string, 0, mStringMatchOffset) - 1;
+ S32 right = left + sFont->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2;
+ S32 bottom = llfloor(mRect.getHeight() - sFont->getLineHeight() - 3);
+ S32 top = mRect.getHeight();
+
+ LLViewerImage::bindTexture(mBoxImage);
+ glColor4fv(sFilterBGColor.mV);
+ gl_segmented_rect_2d_tex(left, top, right, bottom, mBoxImage->getWidth(), mBoxImage->getHeight(), 16);
+ F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset);
+ F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
+ sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ filter_string_length, S32_MAX, &right_x, FALSE );
+ }
+ }
+ }
+
+ if( sDebugRects )
+ {
+ drawDebugRect();
+ }
+ }
+ else if (mStatusText.size())
+ {
+ // just draw status text
+ sFont->renderUTF8( mStatusText, 0, 0, 1, sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewFolder
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFolderViewFolder::LLFolderViewFolder( const LLString& name, LLViewerImage* icon,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener ):
+ LLFolderViewItem( name, icon, 0, root, listener ), // 0 = no create time
+ mSortFunction(sort_item_name),
+ mIsOpen(FALSE),
+ mExpanderHighlighted(FALSE),
+ mCurHeight(0.f),
+ mTargetHeight(0.f),
+ mAutoOpenCountdown(0.f),
+ mSubtreeCreationDate(0),
+ mAmTrash(LLFolderViewFolder::UNKNOWN),
+ mLastArrangeGeneration( -1 ),
+ mLastCalculatedWidth(0),
+ mCompletedFilterGeneration(-1),
+ mMostFilteredDescendantGeneration(-1)
+{
+ mType = "(folder)";
+
+ //mItems.setInsertBefore( &sort_item_name );
+ //mFolders.setInsertBefore( &folder_insert_before );
+}
+
+// Destroys the object
+LLFolderViewFolder::~LLFolderViewFolder( void )
+{
+ // The LLView base class takes care of object destruction. make sure that we
+ // don't have mouse or keyboard focus
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+
+ //mItems.reset();
+ //mItems.removeAllNodes();
+ //mFolders.removeAllNodes();
+}
+
+EWidgetType LLFolderViewFolder::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER;
+}
+
+LLString LLFolderViewFolder::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_FOLDER_TAG;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
+{
+ if (!folder)
+ {
+ return FALSE;
+ }
+ mParentFolder = folder;
+ root->addItemID(getListener()->getUUID(), this);
+ return folder->addFolder(this);
+}
+
+// Finds width and height of this object and it's children. Also
+// makes sure that this view and it's children are the right size.
+S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation)
+{
+ mHasVisibleChildren = hasFilteredDescendants(filter_generation);
+
+ LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState();
+
+ // calculate height as a single item (without any children), and reshapes rectangle to match
+ LLFolderViewItem::arrange( width, height, filter_generation );
+
+ // clamp existing animated height so as to never get smaller than a single item
+ mCurHeight = llmax((F32)*height, mCurHeight);
+
+ // initialize running height value as height of single item in case we have no children
+ *height = getItemHeight();
+ F32 running_height = (F32)*height;
+ F32 target_height = (F32)*height;
+
+ // are my children visible?
+ if (needsArrange())
+ {
+ // set last arrange generation first, in case children are animating
+ // and need to be arranged again
+ mLastArrangeGeneration = mRoot->getArrangeGeneration();
+ if (mIsOpen)
+ {
+ // Add sizes of children
+ S32 parent_item_height = mRect.getHeight();
+
+ folders_t::iterator fit = mFolders.begin();
+ folders_t::iterator fend = mFolders.end();
+ for(; fit < fend; ++fit)
+ {
+ LLFolderViewFolder* folderp = (*fit);
+ if (getRoot()->getDebugFilters())
+ {
+ folderp->setVisible(TRUE);
+ }
+ else
+ {
+ folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
+ (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
+ }
+
+ if (folderp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - llround(running_height);
+
+ target_height += folderp->arrange( &child_width, &child_height, filter_generation );
+
+ running_height += (F32)child_height;
+ *width = llmax(*width, child_width);
+ folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() );
+ }
+ }
+ items_t::iterator iit = mItems.begin();
+ items_t::iterator iend = mItems.end();
+ for(;iit < iend; ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ if (getRoot()->getDebugFilters())
+ {
+ itemp->setVisible(TRUE);
+ }
+ else
+ {
+ itemp->setVisible(itemp->getFiltered(filter_generation));
+ }
+
+ if (itemp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - llround(running_height);
+
+ target_height += itemp->arrange( &child_width, &child_height, filter_generation );
+ // don't change width, as this item is as wide as its parent folder by construction
+ itemp->reshape( itemp->getRect().getWidth(), child_height);
+
+ running_height += (F32)child_height;
+ *width = llmax(*width, child_width);
+ itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() );
+ }
+ }
+ }
+
+ mTargetHeight = target_height;
+ // cache this width so next time we can just return it
+ mLastCalculatedWidth = *width;
+ }
+ else
+ {
+ // just use existing width
+ *width = mLastCalculatedWidth;
+ }
+
+ // animate current height towards target height
+ if (llabs(mCurHeight - mTargetHeight) > 1.f)
+ {
+ mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT));
+
+ requestArrange();
+
+ // hide child elements that fall out of current animated height
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ // number of pixels that bottom of folder label is from top of parent folder
+ if (mRect.getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight()
+ > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
+ {
+ // hide if beyond current folder height
+ (*fit)->setVisible(FALSE);
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ // number of pixels that bottom of item label is from top of parent folder
+ if (mRect.getHeight() - (*iit)->getRect().mBottom
+ > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
+ {
+ (*iit)->setVisible(FALSE);
+ }
+ }
+ }
+ else
+ {
+ mCurHeight = mTargetHeight;
+ }
+
+ // don't change width as this item is already as wide as its parent folder
+ reshape(mRect.getWidth(),llround(mCurHeight));
+
+ // pass current height value back to parent
+ *height = llround(mCurHeight);
+
+ return llround(mTargetHeight);
+}
+
+BOOL LLFolderViewFolder::needsArrange()
+{
+ return mLastArrangeGeneration < mRoot->getArrangeGeneration();
+}
+
+void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up)
+{
+ mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation);
+ mCompletedFilterGeneration = generation;
+ // only aggregate up if we are a lower (older) value
+ if (recurse_up && mParentFolder && generation < mParentFolder->getCompletedFilterGeneration())
+ {
+ mParentFolder->setCompletedFilterGeneration(generation, TRUE);
+ }
+}
+
+void LLFolderViewFolder::filter( LLInventoryFilter& filter)
+{
+ S32 filter_generation = filter.getCurrentGeneration();
+ // if failed to pass filter newer than must_pass_generation
+ // you will automatically fail this time, so we only
+ // check against items that have passed the filter
+ S32 must_pass_generation = filter.getMustPassGeneration();
+
+ // if we have already been filtered against this generation, skip out
+ if (getCompletedFilterGeneration() >= filter_generation)
+ {
+ return;
+ }
+
+ // filter folder itself
+ if (getLastFilterGeneration() < filter_generation)
+ {
+ if (getLastFilterGeneration() >= must_pass_generation && // folder has been compared to a valid precursor filter
+ !mFiltered) // and did not pass the filter
+ {
+ // go ahead and flag this folder as done
+ mLastFilterGeneration = filter_generation;
+ }
+ else
+ {
+ // filter self only on first pass through
+ LLFolderViewItem::filter( filter );
+ }
+ }
+
+ if (getRoot()->getDebugFilters())
+ {
+ mStatusText = llformat("%d", mLastFilterGeneration);
+ mStatusText += llformat("(%d)", mCompletedFilterGeneration);
+ mStatusText += llformat("+%d", mMostFilteredDescendantGeneration);
+ }
+
+ // all descendants have been filtered later than must pass generation
+ // but none passed
+ if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation))
+ {
+ // don't traverse children if we've already filtered them since must_pass_generation
+ // and came back with nothing
+ return;
+ }
+
+ // we entered here with at least one filter iteration left
+ // check to see if we have any more before continuing on to children
+ if (filter.getFilterCount() < 0)
+ {
+ return;
+ }
+
+ // when applying a filter, matching folders get their contents downloaded first
+ if (getRoot()->isFilterActive() && getFiltered(filter.getMinRequiredGeneration()) && !gInventory.isCategoryComplete(mListener->getUUID()))
+ {
+ gInventory.startBackgroundFetch(mListener->getUUID());
+ }
+
+ // now query children
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ // have we run out of iterations this frame?
+ if (filter.getFilterCount() < 0)
+ {
+ break;
+ }
+
+ // mMostFilteredDescendantGeneration might have been reset
+ // in which case we need to update it even for folders that
+ // don't need to be filtered anymore
+ if ((*fit)->getCompletedFilterGeneration() >= filter_generation)
+ {
+ // track latest generation to pass any child items
+ if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration()))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ if (mRoot->needsAutoSelect())
+ {
+ (*fit)->setOpenArrangeRecursively(TRUE);
+ }
+ }
+ // just skip it, it has already been filtered
+ continue;
+ }
+
+ // update this folders filter status (and children)
+ (*fit)->filter( filter );
+
+ // track latest generation to pass any child items
+ if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter_generation))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ if (mRoot->needsAutoSelect())
+ {
+ (*fit)->setOpenArrangeRecursively(TRUE);
+ }
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if (filter.getFilterCount() < 0)
+ {
+ break;
+ }
+ if ((*iit)->getLastFilterGeneration() >= filter_generation)
+ {
+ if ((*iit)->getFiltered())
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ }
+ continue;
+ }
+
+ if ((*iit)->getLastFilterGeneration() >= must_pass_generation &&
+ !(*iit)->getFiltered(must_pass_generation))
+ {
+ // failed to pass an earlier filter that was a subset of the current one
+ // go ahead and flag this item as done
+ (*iit)->setFiltered(FALSE, filter_generation);
+ continue;
+ }
+
+ (*iit)->filter( filter );
+
+ if ((*iit)->getFiltered(filter.getMinRequiredGeneration()))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ }
+ }
+
+ // if we didn't use all filter iterations
+ // that means we filtered all of our descendants
+ // instead of exhausting the filter count for this frame
+ if (filter.getFilterCount() > 0)
+ {
+ // flag this folder as having completed filter pass for all descendants
+ setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/);
+ }
+}
+
+void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation)
+{
+ // if this folder is now filtered, but wasn't before
+ // (it just passed)
+ if (filtered && !mFiltered)
+ {
+ // reset current height, because last time we drew it
+ // it might have had more visible items than now
+ mCurHeight = 0.f;
+ }
+
+ LLFolderViewItem::setFiltered(filtered, filter_generation);
+}
+
+void LLFolderViewFolder::dirtyFilter()
+{
+ // we're a folder, so invalidate our completed generation
+ setCompletedFilterGeneration(-1, FALSE);
+ LLFolderViewItem::dirtyFilter();
+}
+
+BOOL LLFolderViewFolder::hasFilteredDescendants()
+{
+ return mMostFilteredDescendantGeneration >= mRoot->getFilter()->getCurrentGeneration();
+}
+
+// Passes selection information on to children and record selection
+// information if necessary.
+BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus)
+{
+ BOOL rv = FALSE;
+ if( selection == this )
+ {
+ mIsSelected = TRUE;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ rv = TRUE;
+ }
+ else
+ {
+ mIsSelected = FALSE;
+ rv = FALSE;
+ }
+ BOOL child_selected = FALSE;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if((*fit)->setSelection(selection, open, take_keyboard_focus))
+ {
+ rv = TRUE;
+ child_selected = TRUE;
+ mNumDescendantsSelected++;
+ }
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if((*iit)->setSelection(selection, open, take_keyboard_focus))
+ {
+ rv = TRUE;
+ child_selected = TRUE;
+ mNumDescendantsSelected++;
+ }
+ }
+ if(open && child_selected)
+ {
+ setOpenArrangeRecursively(TRUE);
+ }
+ return rv;
+}
+
+// This method is used to change the selection of an item. If
+// selection is 'this', then note selection as true. Returns TRUE
+// if this or a child is now selected.
+BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ BOOL rv = FALSE;
+ if(selection == this)
+ {
+ mIsSelected = selected;
+ if(mListener && selected)
+ {
+ mListener->selectItem();
+ }
+ rv = TRUE;
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if((*fit)->changeSelection(selection, selected))
+ {
+ if (selected)
+ {
+ mNumDescendantsSelected++;
+ }
+ else
+ {
+ mNumDescendantsSelected--;
+ }
+ rv = TRUE;
+ }
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if((*iit)->changeSelection(selection, selected))
+ {
+ if (selected)
+ {
+ mNumDescendantsSelected++;
+ }
+ else
+ {
+ mNumDescendantsSelected--;
+ }
+ rv = TRUE;
+ }
+ }
+ return rv;
+}
+
+S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& selected_items)
+{
+ S32 num_selected = 0;
+
+ // pass on to child folders first
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ num_selected += (*fit)->extendSelection(selection, last_selected, selected_items);
+ mNumDescendantsSelected += num_selected;
+ }
+
+ // handle selection of our immediate children...
+ BOOL reverse_select = FALSE;
+ BOOL found_last_selected = FALSE;
+ BOOL found_selection = FALSE;
+ LLDynamicArray<LLFolderViewItem*> items_to_select;
+ LLFolderViewItem* item;
+
+ //...folders first...
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ item = (*fit);
+ if(item == selection)
+ {
+ found_selection = TRUE;
+ }
+ else if (item == last_selected)
+ {
+ found_last_selected = TRUE;
+ if (found_selection)
+ {
+ reverse_select = TRUE;
+ }
+ }
+
+ if (found_selection || found_last_selected)
+ {
+ // deselect currently selected items so they can be pushed back on queue
+ if (item->isSelected())
+ {
+ item->changeSelection(item, FALSE);
+ }
+ items_to_select.put(item);
+ }
+
+ if (found_selection && found_last_selected)
+ {
+ break;
+ }
+ }
+
+ if (!(found_selection && found_last_selected))
+ {
+ //,,,then items
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ item = (*iit);
+ if(item == selection)
+ {
+ found_selection = TRUE;
+ }
+ else if (item == last_selected)
+ {
+ found_last_selected = TRUE;
+ if (found_selection)
+ {
+ reverse_select = TRUE;
+ }
+ }
+
+ if (found_selection || found_last_selected)
+ {
+ // deselect currently selected items so they can be pushed back on queue
+ if (item->isSelected())
+ {
+ item->changeSelection(item, FALSE);
+ }
+ items_to_select.put(item);
+ }
+
+ if (found_selection && found_last_selected)
+ {
+ break;
+ }
+ }
+ }
+
+ if (found_last_selected && found_selection)
+ {
+ // we have a complete selection inside this folder
+ for (S32 index = reverse_select ? items_to_select.getLength() - 1 : 0;
+ reverse_select ? index >= 0 : index < items_to_select.getLength(); reverse_select ? index-- : index++)
+ {
+ LLFolderViewItem* item = items_to_select[index];
+ if (item->changeSelection(item, TRUE))
+ {
+ selected_items.put(item);
+ mNumDescendantsSelected++;
+ num_selected++;
+ }
+ }
+ }
+ else if (found_selection)
+ {
+ // last selection was not in this folder....go ahead and select just the new item
+ if (selection->changeSelection(selection, TRUE))
+ {
+ selected_items.put(selection);
+ mNumDescendantsSelected++;
+ num_selected++;
+ }
+ }
+
+ return num_selected;
+}
+
+void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self)
+{
+ // make sure we don't have negative values
+ llassert(mNumDescendantsSelected >= 0);
+
+ if (mIsSelected && deselect_self)
+ {
+ mIsSelected = FALSE;
+
+ // update ancestors' count of selected descendents
+ LLFolderViewFolder* parent_folder = getParentFolder();
+ while(parent_folder)
+ {
+ parent_folder->mNumDescendantsSelected--;
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+
+ if (0 == mNumDescendantsSelected)
+ {
+ return;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ LLFolderViewItem* item = (*iit);
+ item->recursiveDeselect(TRUE);
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ LLFolderViewFolder* folder = (*fit);
+ folder->recursiveDeselect(TRUE);
+ }
+
+}
+
+void LLFolderViewFolder::destroyView()
+{
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ LLFolderViewItem* item = (*iit);
+ getRoot()->removeItemID(item->getListener()->getUUID());
+ }
+
+ std::for_each(mItems.begin(), mItems.end(), DeletePointer());
+ mItems.clear();
+
+ while (!mFolders.empty())
+ {
+ LLFolderViewFolder *folderp = mFolders.back();
+ folderp->destroyView();
+ }
+
+ mFolders.clear();
+
+ deleteAllChildren();
+
+ if (mParentFolder)
+ {
+ mParentFolder->removeView(this);
+ }
+}
+
+// remove the specified item (and any children) if possible. Return
+// TRUE if the item was deleted.
+BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item)
+{
+ if(item->remove())
+ {
+ removeView(item);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// simply remove the view (and any children) Don't bother telling the
+// listeners.
+void LLFolderViewFolder::removeView(LLFolderViewItem* item)
+{
+ if (!item)
+ {
+ return;
+ }
+ // deselect without traversing hierarchy
+ item->recursiveDeselect(TRUE);
+ getRoot()->removeFromSelectionList(item);
+ extractItem(item);
+ delete item;
+}
+
+// extractItem() removes the specified item from the folder, but
+// doesn't delete it.
+void LLFolderViewFolder::extractItem( LLFolderViewItem* item )
+{
+ items_t::iterator it = std::find(mItems.begin(), mItems.end(), item);
+ if(it == mItems.end())
+ {
+ // This is an evil downcast. However, it's only doing
+ // pointer comparison to find if (which it should be ) the
+ // item is in the container, so it's pretty safe.
+ LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item);
+ folders_t::iterator ft;
+ ft = std::find(mFolders.begin(), mFolders.end(), f);
+ if(ft != mFolders.end())
+ {
+ mFolders.erase(ft);
+ }
+ }
+ else
+ {
+ mItems.erase(it);
+ }
+ //item has been removed, need to update filter
+ dirtyFilter();
+ //because an item is going away regardless of filter status, force rearrange
+ requestArrange();
+ getRoot()->removeItemID(item->getListener()->getUUID());
+ removeChild(item);
+}
+
+// This function is called by a child that needs to be resorted.
+// This is only called for renaming an object because it won't work for date
+void LLFolderViewFolder::resort(LLFolderViewItem* item)
+{
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+ //if(mItems.removeData(item))
+ //{
+ // mItems.addDataSorted(item);
+ //}
+ //else
+ //{
+ // // This is an evil downcast. However, it's only doing
+ // // pointer comparison to find if (which it should be ) the
+ // // item is in the container, so it's pretty safe.
+ // LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item);
+ // if(mFolders.removeData(f))
+ // {
+ // mFolders.addDataSorted(f);
+ // }
+ //}
+}
+
+bool LLFolderViewFolder::isTrash()
+{
+ if (mAmTrash == LLFolderViewFolder::UNKNOWN)
+ {
+ mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH;
+ }
+ return mAmTrash == LLFolderViewFolder::TRASH;
+}
+
+void LLFolderViewFolder::sortBy(U32 order)
+{
+ BOOL sort_order_changed = FALSE;
+ if (!(order & LLInventoryFilter::SO_DATE))
+ {
+ if (mSortFunction != sort_item_name)
+ {
+ mSortFunction = sort_item_name;
+ sort_order_changed = TRUE;
+ }
+ }
+ else
+ {
+ if (mSortFunction != sort_item_date)
+ {
+ mSortFunction = sort_item_date;
+ sort_order_changed = TRUE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->sortBy(order);
+ }
+ if (order & LLInventoryFilter::SO_FOLDERS_BY_NAME)
+ {
+ // sort folders by name if always by name
+ std::sort(mFolders.begin(), mFolders.end(), sort_item_name);
+ }
+ else
+ {
+ // sort folders by the default sort ordering
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+
+ // however, if we are at the root of the inventory and we are sorting by date
+ if (mListener->getUUID() == gAgent.getInventoryRootID() && order & LLInventoryFilter::SO_DATE)
+ {
+ // pull the trash folder and stick it on the end of the list
+ LLFolderViewFolder *t = NULL;
+ for (folders_t::iterator fit = mFolders.begin();
+ fit != mFolders.end(); ++fit)
+ {
+ if ((*fit)->isTrash())
+ {
+ t = *fit;
+ mFolders.erase(fit);
+ break;
+ }
+ }
+ if (t)
+ {
+ mFolders.push_back(t);
+ }
+ }
+ }
+ if (sort_order_changed)
+ {
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+ }
+
+ if (order & LLInventoryFilter::SO_DATE)
+ {
+ U32 latest = 0;
+
+ if (!mItems.empty())
+ {
+ LLFolderViewItem* item = *(mItems.begin());
+ latest = item->getCreationDate();
+ }
+
+ if (!mFolders.empty())
+ {
+ LLFolderViewFolder* folder = *(mFolders.begin());
+ if (folder->getCreationDate() > latest)
+ {
+ latest = folder->getCreationDate();
+ }
+ }
+ mSubtreeCreationDate = latest;
+ }
+}
+
+void LLFolderViewFolder::setItemSortFunction(sort_order_f ordering)
+{
+ mSortFunction = ordering;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setItemSortFunction(ordering);
+ }
+
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+}
+
+BOOL LLFolderViewFolder::isMovable()
+{
+ if( mListener )
+ {
+ if( !(mListener->isItemMovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLFolderViewFolder::isRemovable()
+{
+ if( mListener )
+ {
+ if( !(mListener->isItemRemovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// this is an internal method used for adding items to folders.
+BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item)
+{
+ items_t::iterator it = std::lower_bound(
+ mItems.begin(),
+ mItems.end(),
+ item,
+ mSortFunction);
+ mItems.insert(it,item);
+ item->setRect(LLRect(0, 0, mRect.getWidth(), 0));
+ item->setVisible(FALSE);
+ addChild( item );
+ item->dirtyFilter();
+ requestArrange();
+ return TRUE;
+}
+
+// this is an internal method used for adding items to folders.
+BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
+{
+ folders_t::iterator it = std::lower_bound(
+ mFolders.begin(),
+ mFolders.end(),
+ folder,
+ mSortFunction);
+ mFolders.insert(it,folder);
+ folder->setOrigin(0, 0);
+ folder->reshape(mRect.getWidth(), 0);
+ folder->setVisible(FALSE);
+ addChild( folder );
+ folder->dirtyFilter();
+ requestArrange();
+ return TRUE;
+}
+
+void LLFolderViewFolder::requestArrange()
+{
+ mLastArrangeGeneration = -1;
+ // flag all items up to root
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+}
+
+void LLFolderViewFolder::toggleOpen()
+{
+ setOpen(!mIsOpen);
+}
+
+// Force a folder open or closed
+void LLFolderViewFolder::setOpen(BOOL open)
+{
+ setOpenArrangeRecursively(open);
+}
+
+void LLFolderViewFolder::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)
+{
+ BOOL was_open = mIsOpen;
+ mIsOpen = open;
+ if(!was_open && open)
+ {
+ if(mListener)
+ {
+ mListener->openItem();
+ }
+ }
+ if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
+ {
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setOpenArrangeRecursively(open, RECURSE_DOWN);
+ }
+ }
+ if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN))
+ {
+ mParentFolder->setOpenArrangeRecursively(open, RECURSE_UP);
+ }
+
+ if (was_open != mIsOpen)
+ {
+ requestArrange();
+ }
+}
+
+BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask,
+ BOOL drop,
+ EDragAndDropType c_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data);
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ // drag and drop to child item, so clear pending auto-opens
+ getRoot()->autoOpenTest(NULL);
+
+ return TRUE;
+}
+
+void LLFolderViewFolder::open( void )
+{
+ toggleOpen();
+}
+
+void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor)
+{
+ functor.doFolder(this);
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->applyFunctorRecursively(functor);
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ functor.doItem((*iit));
+ }
+}
+
+void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
+{
+ functor(mListener);
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->applyListenerFunctorRecursively(functor);
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ (*iit)->applyListenerFunctorRecursively(functor);
+ }
+}
+
+// LLView functionality
+BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ LLFolderView* root_view = getRoot();
+
+ BOOL handled = FALSE;
+ if(mIsOpen)
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+
+ if (!handled)
+ {
+ BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data);
+
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ if (!drop && accepted)
+ {
+ root_view->autoOpenTest(this);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ if( getVisible() )
+ {
+ // fetch contents of this folder, as context menu can depend on contents
+ // still, user would have to open context menu again to see the changes
+ gInventory.fetchDescendentsOf(mListener->getUUID());
+
+ if( mIsOpen )
+ {
+ handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
+ }
+ if (!handled)
+ {
+ handled = LLFolderViewItem::handleRightMouseDown( x, y, mask );
+ }
+ }
+ return handled;
+}
+
+
+BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleHover(x, y, mask);
+
+ if (!handled)
+ {
+ // this doesn't do child processing
+ handled = LLFolderViewItem::handleHover(x, y, mask);
+ }
+
+ //if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD && y > mRect.getHeight() - )
+ //{
+ // gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ // mExpanderHighlighted = TRUE;
+ // handled = TRUE;
+ //}
+ return handled;
+}
+
+BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ if( mIsOpen )
+ {
+ handled = childrenHandleMouseDown(x,y,mask) != NULL;
+ }
+ if( !handled )
+ {
+ if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
+ {
+ toggleOpen();
+ handled = TRUE;
+ }
+ else
+ {
+ // do normal selection logic
+ handled = LLFolderViewItem::handleMouseDown(x, y, mask);
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ if (!getVisible())
+ {
+ return FALSE;
+ }
+ BOOL rv = false;
+ if( mIsOpen )
+ {
+ rv = childrenHandleDoubleClick( x, y, mask ) != NULL;
+ }
+ if( !rv )
+ {
+ if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
+ {
+ // don't select when user double-clicks plus sign
+ // so as not to contradict single-click behavior
+ toggleOpen();
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ toggleOpen();
+ }
+ return TRUE;
+ }
+ return rv;
+}
+
+void LLFolderViewFolder::draw()
+{
+ if (mAutoOpenCountdown != 0.f)
+ {
+ mControlLabelRotation = mAutoOpenCountdown * -90.f;
+ }
+ else if (mIsOpen)
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f));
+ }
+ else
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f));
+ }
+
+ LLFolderViewItem::draw();
+ if( mIsOpen )
+ {
+ LLView::draw();
+ }
+
+// if (mExpanderHighlighted)
+// {
+// gl_rect_2d(mIndentation - TEXT_PAD, llfloor(mRect.getHeight() - TEXT_PAD), mIndentation + sFont->getWidth(mControlLabel) + TEXT_PAD, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, FALSE);
+// //sFont->renderUTF8( mControlLabel, 0, mIndentation, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE );
+// }
+ mExpanderHighlighted = FALSE;
+}
+
+U32 LLFolderViewFolder::getCreationDate() const
+{
+ return llmax<U32>(mCreationDate, mSubtreeCreationDate);
+}
+
+
+// this does prefix traversal, as folders are listed above their contents
+LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children )
+{
+ BOOL found_item = FALSE;
+
+ LLFolderViewItem* result = NULL;
+ // when not starting from a given item, start at beginning
+ if(item == NULL)
+ {
+ found_item = TRUE;
+ }
+
+ // find current item among children
+ folders_t::iterator fit = mFolders.begin();
+ folders_t::iterator fend = mFolders.end();
+
+ items_t::iterator iit = mItems.begin();
+ items_t::iterator iend = mItems.end();
+
+ // if not trivially starting at the beginning, we have to find the current item
+ if (!found_item)
+ {
+ // first, look among folders, since they are always above items
+ for(; fit != fend; ++fit)
+ {
+ if(item == (*fit))
+ {
+ found_item = TRUE;
+ // if we are on downwards traversal
+ if (include_children && (*fit)->isOpen())
+ {
+ // look for first descendant
+ return (*fit)->getNextFromChild(NULL, TRUE);
+ }
+ // otherwise advance to next folder
+ ++fit;
+ include_children = TRUE;
+ break;
+ }
+ }
+
+ // didn't find in folders? Check items...
+ if (!found_item)
+ {
+ for(; iit != iend; ++iit)
+ {
+ if(item == (*iit))
+ {
+ found_item = TRUE;
+ // point to next item
+ ++iit;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found_item)
+ {
+ // you should never call this method with an item that isn't a child
+ // so we should always find something
+ llassert(FALSE);
+ return NULL;
+ }
+
+ // at this point, either iit or fit point to a candidate "next" item
+ // if both are out of range, we need to punt up to our parent
+
+ // now, starting from found folder, continue through folders
+ // searching for next visible folder
+ while(fit != fend && !(*fit)->getVisible())
+ {
+ // turn on downwards traversal for next folder
+ ++fit;
+ }
+
+ if (fit != fend)
+ {
+ result = (*fit);
+ }
+ else
+ {
+ // otherwise, scan for next visible item
+ while(iit != iend && !(*iit)->getVisible())
+ {
+ ++iit;
+ }
+
+ // check to see if we have a valid item
+ if (iit != iend)
+ {
+ result = (*iit);
+ }
+ }
+
+ if( !result && mParentFolder )
+ {
+ // If there are no siblings or children to go to, recurse up one level in the tree
+ // and skip children for this folder, as we've already discounted them
+ result = mParentFolder->getNextFromChild(this, FALSE);
+ }
+
+ return result;
+}
+
+// this does postfix traversal, as folders are listed above their contents
+LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children )
+{
+ BOOL found_item = FALSE;
+
+ LLFolderViewItem* result = NULL;
+ // when not starting from a given item, start at end
+ if(item == NULL)
+ {
+ found_item = TRUE;
+ }
+
+ // find current item among children
+ folders_t::reverse_iterator fit = mFolders.rbegin();
+ folders_t::reverse_iterator fend = mFolders.rend();
+
+ items_t::reverse_iterator iit = mItems.rbegin();
+ items_t::reverse_iterator iend = mItems.rend();
+
+ // if not trivially starting at the end, we have to find the current item
+ if (!found_item)
+ {
+ // first, look among items, since they are always below the folders
+ for(; iit != iend; ++iit)
+ {
+ if(item == (*iit))
+ {
+ found_item = TRUE;
+ // point to next item
+ ++iit;
+ break;
+ }
+ }
+
+ // didn't find in items? Check folders...
+ if (!found_item)
+ {
+ for(; fit != fend; ++fit)
+ {
+ if(item == (*fit))
+ {
+ found_item = TRUE;
+ // point to next folder
+ ++fit;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found_item)
+ {
+ // you should never call this method with an item that isn't a child
+ // so we should always find something
+ llassert(FALSE);
+ return NULL;
+ }
+
+ // at this point, either iit or fit point to a candidate "next" item
+ // if both are out of range, we need to punt up to our parent
+
+ // now, starting from found item, continue through items
+ // searching for next visible item
+ while(iit != iend && !(*iit)->getVisible())
+ {
+ ++iit;
+ }
+
+ if (iit != iend)
+ {
+ // we found an appropriate item
+ result = (*iit);
+ }
+ else
+ {
+ // otherwise, scan for next visible folder
+ while(fit != fend && !(*fit)->getVisible())
+ {
+ ++fit;
+ }
+
+ // check to see if we have a valid folder
+ if (fit != fend)
+ {
+ // try selecting child element of this folder
+ if ((*fit)->isOpen())
+ {
+ result = (*fit)->getPreviousFromChild(NULL);
+ }
+ else
+ {
+ result = (*fit);
+ }
+ }
+ }
+
+ if( !result )
+ {
+ // If there are no siblings or children to go to, recurse up one level in the tree
+ // which gets back to this folder, which will only be visited if it is a valid, visible item
+ result = this;
+ }
+
+ return result;
+}
+
+
+//---------------------------------------------------------------------------
+
+// Tells all folders in a folderview to sort their items
+// (and only their items, not folders) by a certain function.
+class LLSetItemSortFunction : public LLFolderViewFunctor
+{
+public:
+ LLSetItemSortFunction(sort_order_f ordering)
+ : mSortFunction(ordering) {}
+ virtual ~LLSetItemSortFunction() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+
+ sort_order_f mSortFunction;
+};
+
+
+// Set the sort order.
+void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder)
+{
+ folder->setItemSortFunction(mSortFunction);
+}
+
+// Do nothing.
+void LLSetItemSortFunction::doItem(LLFolderViewItem* item)
+{
+ return;
+}
+
+//---------------------------------------------------------------------------
+
+// Tells all folders in a folderview to close themselves
+// For efficiency, calls setOpenArrangeRecursively().
+// The calling function must then call:
+// LLFolderView* root = getRoot();
+// if( root )
+// {
+// root->arrange( NULL, NULL );
+// root->scrollToShowSelection();
+// }
+// to patch things up.
+class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
+{
+public:
+ LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; }
+ virtual ~LLCloseAllFoldersFunctor() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+
+ BOOL mOpen;
+};
+
+
+// Set the sort order.
+void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
+{
+ folder->setOpenArrangeRecursively(mOpen);
+}
+
+// Do nothing.
+void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
+{ }
+
+///----------------------------------------------------------------------------
+/// Class LLFolderView
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFolderView::LLFolderView( const LLString& name, LLViewerImage* root_folder_icon,
+ const LLRect& rect, const LLUUID& source_id, LLView *parent_view ) :
+#if LL_WINDOWS
+#pragma warning( push )
+#pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list
+#endif
+ LLFolderViewFolder( name, root_folder_icon, this, NULL ),
+#if LL_WINDOWS
+#pragma warning( pop )
+#endif
+ mScrollContainer( NULL ),
+ mPopupMenuHandle( LLViewHandle::sDeadHandle ),
+ mAllowMultiSelect(TRUE),
+ mShowFolderHierarchy(FALSE),
+ mSourceID(source_id),
+ mRenameItem( NULL ),
+ mNeedsScroll( FALSE ),
+ mLastScrollItem( NULL ),
+ mNeedsAutoSelect( FALSE ),
+ mAutoSelectOverride(FALSE),
+ mDebugFilters(FALSE),
+ mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately
+ mFilter(name),
+ mShowSelectionContext(FALSE),
+ mShowSingleSelection(FALSE),
+ mArrangeGeneration(0),
+ mSelectCallback(NULL),
+ mMinWidth(0),
+ mDragAndDropThisFrame(FALSE)
+{
+ LLRect new_rect(rect.mLeft, rect.mBottom + mRect.getHeight(), rect.mLeft + mRect.getWidth(), rect.mBottom);
+ setRect( rect );
+ reshape(rect.getWidth(), rect.getHeight());
+ mIsOpen = TRUE; // this view is always open.
+ mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
+ mAutoOpenCandidate = NULL;
+ mAutoOpenTimer.stop();
+ mKeyboardSelection = FALSE;
+ mIndentation = -LEFT_INDENTATION; // children start at indentation 0
+ gIdleCallbacks.addFunction(idle, this);
+
+ //clear label
+ // go ahead and render root folder as usual
+ // just make sure the label ("Inventory Folder") never shows up
+ mLabel = LLString::null;
+
+ mRenamer = new LLLineEditor("ren", mRect, "", sFont,
+ DB_INV_ITEM_NAME_STR_LEN,
+ &LLFolderView::commitRename,
+ NULL,
+ NULL,
+ this,
+ &LLLineEditor::prevalidatePrintableNotPipe,
+ LLViewBorder::BEVEL_NONE,
+ LLViewBorder::STYLE_LINE,
+ 2);
+ mRenamer->setWriteableBgColor(LLColor4::white);
+ // Escape is handled by reverting the rename, not commiting it (default behavior)
+ mRenamer->setCommitOnFocusLost(TRUE);
+ mRenamer->setVisible(FALSE);
+ addChild(mRenamer);
+
+ // make the popup menu available
+ LLMenuGL* menu = gUICtrlFactory->buildMenu("menu_inventory.xml", parent_view);
+ if (!menu)
+ {
+ menu = new LLMenuGL("");
+ }
+ menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor"));
+ menu->setVisible(FALSE);
+ mPopupMenuHandle = menu->mViewHandle;
+
+ setTabStop(TRUE);
+}
+
+// Destroys the object
+LLFolderView::~LLFolderView( void )
+{
+ // The release focus call can potentially call the
+ // scrollcontainer, which can potentially be called with a partly
+ // destroyed scollcontainer. Just null it out here, and no worries
+ // about calling into the invalid scroll container.
+ // Same with the renamer.
+ mScrollContainer = NULL;
+ mRenameItem = NULL;
+ mRenamer = NULL;
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ mAutoOpenItems.removeAllNodes();
+ gIdleCallbacks.deleteFunction(idle, this);
+
+ LLView::deleteViewByHandle(mPopupMenuHandle);
+
+ if(gViewerWindow->hasTopView(mRenamer))
+ {
+ gViewerWindow->setTopView(NULL, NULL);
+ }
+
+ mAutoOpenItems.removeAllNodes();
+ clearSelection();
+ mItems.clear();
+ mFolders.clear();
+
+ mItemMap.clear();
+}
+
+EWidgetType LLFolderView::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER_VIEW;
+}
+
+LLString LLFolderView::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_TAG;
+}
+
+BOOL LLFolderView::canFocusChildren() const
+{
+ return FALSE;
+}
+
+void LLFolderView::checkTreeResortForModelChanged()
+{
+ if (mSortOrder & LLInventoryFilter::SO_DATE && !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME))
+ {
+ // This is the case where something got added or removed. If we are date sorting
+ // everything including folders, then we need to rebuild the whole tree.
+ // Just set to something not SO_DATE to force the folder most resent date resort.
+ mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE;
+ setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE);
+ }
+}
+
+void LLFolderView::setSortOrder(U32 order)
+{
+ if (order != mSortOrder)
+ {
+ LLFastTimer t(LLFastTimer::FTM_SORT);
+ mSortOrder = order;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->sortBy(order);
+ }
+
+ arrangeAll();
+ }
+}
+
+
+U32 LLFolderView::getSortOrder() const
+{
+ return mSortOrder;
+}
+
+BOOL LLFolderView::addFolder( LLFolderViewFolder* folder)
+{
+ // enforce sort order of My Inventory followed by Library
+ if (folder->getListener()->getUUID() == gInventoryLibraryRoot)
+ {
+ mFolders.push_back(folder);
+ }
+ else
+ {
+ mFolders.insert(mFolders.begin(), folder);
+ }
+ folder->setOrigin(0, 0);
+ folder->reshape(mRect.getWidth(), 0);
+ folder->setVisible(FALSE);
+ addChild( folder );
+ folder->dirtyFilter();
+ return TRUE;
+}
+
+void LLFolderView::closeAllFolders()
+{
+ // Close all the folders
+ setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
+}
+
+void LLFolderView::openFolder(const LLString& foldername)
+{
+ LLFolderViewFolder* inv = (LLFolderViewFolder*)getChildByName(foldername);
+ if (inv)
+ {
+ setSelection(inv, FALSE, FALSE);
+ inv->setOpen(TRUE);
+ }
+}
+
+void LLFolderView::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)
+{
+ // call base class to do proper recursion
+ LLFolderViewFolder::setOpenArrangeRecursively(open, recurse);
+ // make sure root folder is always open
+ mIsOpen = TRUE;
+}
+
+// This view grows and shinks to enclose all of its children items and folders.
+S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation )
+{
+ LLFastTimer t2(LLFastTimer::FTM_ARRANGE);
+
+ filter_generation = mFilter.getMinRequiredGeneration();
+ mMinWidth = 0;
+
+ mHasVisibleChildren = hasFilteredDescendants(filter_generation);
+ // arrange always finishes, so optimistically set the arrange generation to the most current
+ mLastArrangeGeneration = mRoot->getArrangeGeneration();
+
+ LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState();
+
+ S32 total_width = LEFT_PAD;
+ S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0;
+ S32 target_height = running_height;
+ S32 parent_item_height = mRect.getHeight();
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ LLFolderViewFolder* folderp = (*fit);
+ if (getDebugFilters())
+ {
+ folderp->setVisible(TRUE);
+ }
+ else
+ {
+ folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
+ (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
+ }
+ if (folderp->getVisible())
+ {
+ S32 child_height = 0;
+ S32 child_width = 0;
+ S32 child_top = parent_item_height - running_height;
+
+ target_height += folderp->arrange( &child_width, &child_height, filter_generation );
+
+ mMinWidth = llmax(mMinWidth, child_width);
+ total_width = llmax( total_width, child_width );
+ running_height += child_height;
+ folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() );
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ LLFolderViewItem* itemp = (*iit);
+ itemp->setVisible(itemp->getFiltered(filter_generation));
+
+ if (itemp->getVisible())
+ {
+ S32 child_width = 0;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - running_height;
+
+ target_height += itemp->arrange( &child_width, &child_height, filter_generation );
+ itemp->reshape(itemp->getRect().getWidth(), child_height);
+
+ mMinWidth = llmax(mMinWidth, child_width);
+ total_width = llmax( total_width, child_width );
+ running_height += child_height;
+ itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() );
+ }
+ }
+
+ S32 dummy_s32;
+ BOOL dummy_bool;
+ S32 min_width;
+ mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool);
+ reshape( llmax(min_width, total_width), running_height );
+
+ S32 new_min_width;
+ mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool);
+ if (new_min_width != min_width)
+ {
+ reshape( llmax(min_width, total_width), running_height );
+ }
+
+ mTargetHeight = (F32)target_height;
+ return llround(mTargetHeight);
+}
+
+const LLString LLFolderView::getFilterSubString(BOOL trim)
+{
+ return mFilter.getFilterSubString(trim);
+}
+
+void LLFolderView::filter( LLInventoryFilter& filter )
+{
+ LLFastTimer t2(LLFastTimer::FTM_FILTER);
+ filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000));
+
+ if (getCompletedFilterGeneration() < filter.getCurrentGeneration())
+ {
+ mFiltered = FALSE;
+ mMinWidth = 0;
+ LLFolderViewFolder::filter(filter);
+ }
+}
+
+void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ S32 min_width = 0;
+ S32 dummy_height;
+ BOOL dummy_bool;
+ if (mScrollContainer)
+ {
+ mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool);
+ }
+ width = llmax(mMinWidth, min_width);
+ LLView::reshape(width, height, called_from_parent);
+}
+
+void LLFolderView::addToSelectionList(LLFolderViewItem* item)
+{
+ if (item->isSelected())
+ {
+ removeFromSelectionList(item);
+ }
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(FALSE);
+ }
+ item->setIsCurSelection(TRUE);
+ mSelectedItems.push_back(item);
+}
+
+void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
+{
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(FALSE);
+ }
+
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();)
+ {
+ if (*item_iter == item)
+ {
+ item_iter = mSelectedItems.erase(item_iter);
+ }
+ else
+ {
+ ++item_iter;
+ }
+ }
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(TRUE);
+ }
+}
+
+LLFolderViewItem* LLFolderView::getCurSelectedItem( void )
+{
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* itemp = mSelectedItems.back();
+ llassert(itemp->getIsCurSelection());
+ return itemp;
+ }
+ return NULL;
+}
+
+
+// Record the selected item and pass it down the hierachy.
+BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus)
+{
+ if( selection == this )
+ {
+ return FALSE;
+ }
+
+ if( selection && take_keyboard_focus)
+ {
+ setFocus(TRUE);
+ }
+
+ // clear selection down here because change of keyboard focus can potentially
+ // affect selection
+ clearSelection();
+
+ if(selection)
+ {
+ addToSelectionList(selection);
+ }
+
+ BOOL rv = LLFolderViewFolder::setSelection(selection, open, take_keyboard_focus);
+ if(open)
+ {
+ selection->getParentFolder()->requestArrange();
+ }
+
+ llassert(mSelectedItems.size() <= 1);
+
+ mSelectionChanged = TRUE;
+
+ return rv;
+}
+
+BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected)
+{
+ BOOL rv = FALSE;
+
+ // can't select root folder
+ if(!selection || selection == this)
+ {
+ return FALSE;
+ }
+
+ if (!mAllowMultiSelect)
+ {
+ clearSelection();
+ }
+
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ if (*item_iter == selection)
+ {
+ break;
+ }
+ }
+
+ BOOL on_list = (item_iter != mSelectedItems.end());
+ if (on_list && mSelectedItems.size() == 1)
+ {
+ // we are trying to select/deselect the only selected item
+ return FALSE;
+ }
+
+ if(selected && !on_list)
+ {
+ mNumDescendantsSelected++;
+ addToSelectionList(selection);
+ }
+ if(!selected && on_list)
+ {
+ mNumDescendantsSelected--;
+ removeFromSelectionList(selection);
+ }
+
+ rv = LLFolderViewFolder::changeSelection(selection, selected);
+
+ mSelectionChanged = TRUE;
+
+ return rv;
+}
+
+S32 LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items)
+{
+ S32 rv = 0;
+
+ // now store resulting selection
+ if (mAllowMultiSelect)
+ {
+ LLFolderViewItem *cur_selection = getCurSelectedItem();
+ rv = LLFolderViewFolder::extendSelection(selection, cur_selection, items);
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ addToSelectionList(items[i]);
+ rv++;
+ }
+ }
+ else
+ {
+ setSelection(selection, FALSE, FALSE);
+ rv++;
+ }
+
+ mSelectionChanged = TRUE;
+ return rv;
+}
+
+void LLFolderView::sanitizeSelection()
+{
+ std::vector<LLFolderViewItem*> items_to_remove;
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ LLFolderViewItem* item = *item_iter;
+
+ BOOL visible = item->getVisible();
+ LLFolderViewFolder* parent_folder = item->getParentFolder();
+ while(visible && parent_folder)
+ {
+ visible = visible && parent_folder->isOpen() && parent_folder->getVisible();
+ parent_folder = parent_folder->getParentFolder();
+ }
+ if (!visible || item->getNumSelectedDescendants() > 0)
+ {
+ // only deselect self if not visible
+ // check to see if item failed the filter but was checked against most recent generation
+ if ((!item->getFiltered() && item->getLastFilterGeneration() >= getFilter()->getMinRequiredGeneration())
+ || (item->getParentFolder() && !item->getParentFolder()->isOpen()))
+ {
+ item->recursiveDeselect(TRUE);
+ items_to_remove.push_back(item);
+ }
+ else
+ {
+ item->recursiveDeselect(FALSE);
+ }
+
+ selected_items_t::iterator other_item_iter;
+ for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter)
+ {
+ LLFolderViewItem* other_item = *other_item_iter;
+ LLFolderViewFolder* parent_folder = other_item->getParentFolder();
+ while (parent_folder)
+ {
+ if (parent_folder == item)
+ {
+ // this is a descendent of the current folder, remove from list
+ items_to_remove.push_back(other_item);
+ break;
+ }
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+ }
+ }
+
+ std::vector<LLFolderViewItem*>::iterator item_it;
+ for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
+ {
+ removeFromSelectionList(*item_it);
+ }
+}
+
+void LLFolderView::clearSelection()
+{
+ if (mSelectedItems.size() > 0)
+ {
+ recursiveDeselect(FALSE);
+ mSelectedItems.clear();
+ }
+}
+
+BOOL LLFolderView::getSelectionList(std::set<LLUUID> &selection)
+{
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ selection.insert((*item_it)->getListener()->getUUID());
+ }
+
+ return (selection.size() != 0);
+}
+
+BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source)
+{
+ std::vector<EDragAndDropType> types;
+ std::vector<LLUUID> cargo_ids;
+ selected_items_t::iterator item_it;
+ BOOL can_drag = TRUE;
+ if (!mSelectedItems.empty())
+ {
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ EDragAndDropType type = DAD_NONE;
+ LLUUID id = LLUUID::null;
+ can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id);
+
+ types.push_back(type);
+ cargo_ids.push_back(id);
+ }
+
+ gToolDragAndDrop->beginMultiDrag(types, cargo_ids, source, mSourceID);
+ }
+ return can_drag;
+}
+
+void LLFolderView::commitRename( LLUICtrl* renamer, void* user_data )
+{
+ LLFolderView* root = reinterpret_cast<LLFolderView*>(user_data);
+ if( root )
+ {
+ root->finishRenamingItem();
+ }
+}
+
+void LLFolderView::draw()
+{
+ if (mDebugFilters)
+ {
+ LLString current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d",
+ mFilter.getCurrentGeneration(), mFilter.getMinRequiredGeneration(), mFilter.getMustPassGeneration());
+ sSmallFont->renderUTF8(current_filter_string, 0, 2,
+ mRect.getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f),
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+
+ // if cursor has moved off of me during drag and drop
+ // close all auto opened folders
+ if (!mDragAndDropThisFrame)
+ {
+ closeAutoOpenedFolders();
+ }
+ if(gViewerWindow->hasKeyboardFocus(this) && !getVisible())
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ }
+
+ // while dragging, update selection rendering to reflect single/multi drag status
+ if (gToolDragAndDrop->hasMouseCapture())
+ {
+ EAcceptance last_accept = gToolDragAndDrop->getLastAccept();
+ if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE)
+ {
+ setShowSingleSelection(TRUE);
+ }
+ else
+ {
+ setShowSingleSelection(FALSE);
+ }
+ }
+ else
+ {
+ setShowSingleSelection(FALSE);
+ }
+
+
+ if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size())
+ {
+ mSearchString.clear();
+ }
+
+ if (hasVisibleChildren() || getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS)
+ {
+ setStatusText("");
+ }
+ else
+ {
+ if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration())
+ {
+ setStatusText("Searching...");
+ sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ else
+ {
+ setStatusText("No matching items found in inventory.");
+ sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ }
+
+ LLFolderViewFolder::draw();
+
+ mDragAndDropThisFrame = FALSE;
+}
+
+void LLFolderView::finishRenamingItem( void )
+{
+ if(!mRenamer)
+ {
+ return;
+ }
+ if( mRenameItem )
+ {
+ mRenameItem->rename( mRenamer->getText().c_str() );
+ }
+
+ mRenamer->setCommitOnFocusLost( FALSE );
+ mRenamer->setFocus( FALSE );
+ mRenamer->setVisible( FALSE );
+ mRenamer->setCommitOnFocusLost( TRUE );
+ gViewerWindow->setTopView( NULL, NULL );
+
+ if( mRenameItem )
+ {
+ setSelectionFromRoot( mRenameItem, TRUE );
+ mRenameItem = NULL;
+ }
+
+ // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible.
+ scrollToShowSelection();
+}
+
+void LLFolderView::revertRenamingItem( void )
+{
+ mRenamer->setCommitOnFocusLost( FALSE );
+ mRenamer->setFocus( FALSE );
+ mRenamer->setVisible( FALSE );
+ mRenamer->setCommitOnFocusLost( TRUE );
+ gViewerWindow->setTopView( NULL, NULL );
+
+ if( mRenameItem )
+ {
+ setSelectionFromRoot( mRenameItem, TRUE );
+ mRenameItem = NULL;
+ }
+}
+
+void LLFolderView::removeSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ // just in case we're removing the renaming item.
+ mRenameItem = NULL;
+
+ // create a temporary structure which we will use to remove
+ // items, since the removal will futz with internal data
+ // structures.
+ LLDynamicArray<LLFolderViewItem*> items;
+ S32 count = mSelectedItems.size();
+ if(count == 0) return;
+ LLFolderViewItem* item = NULL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ item = *item_it;
+ if(item->isRemovable())
+ {
+ items.put(item);
+ }
+ else
+ {
+ llinfos << "Cannot delete " << item->getName() << llendl;
+ return;
+ }
+ }
+
+ // iterate through the new container.
+ count = items.count();
+ LLUUID new_selection_id;
+ if(count == 1)
+ {
+ LLFolderViewItem* item_to_delete = items.get(0);
+ LLFolderViewFolder* parent = item_to_delete->getParentFolder();
+ LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE);
+ if (!new_selection)
+ {
+ new_selection = item_to_delete->getPreviousOpenNode(FALSE);
+ }
+ if (new_selection)
+ {
+ setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this));
+ }
+ else
+ {
+ setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
+ }
+
+ if(parent)
+ {
+ parent->removeItem(item_to_delete);
+ }
+ arrangeAll();
+ }
+ else if (count > 1)
+ {
+ LLDynamicArray<LLFolderViewEventListener*> listeners;
+ LLFolderViewEventListener* listener;
+ LLFolderViewItem* last_item = items.get(count - 1);
+ LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE);
+ while(new_selection && new_selection->isSelected())
+ {
+ new_selection = new_selection->getNextOpenNode(FALSE);
+ }
+ if (!new_selection)
+ {
+ new_selection = last_item->getPreviousOpenNode(FALSE);
+ while (new_selection && new_selection->isSelected())
+ {
+ new_selection = new_selection->getPreviousOpenNode(FALSE);
+ }
+ }
+ if (new_selection)
+ {
+ setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this));
+ }
+ else
+ {
+ setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
+ }
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ listener = items.get(i)->getListener();
+ if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL))
+ {
+ listeners.put(listener);
+ }
+ }
+ listener = listeners.get(0);
+ if(listener)
+ {
+ listener->removeBatch(listeners);
+ }
+ }
+ arrangeAll();
+ scrollToShowSelection();
+ }
+}
+
+// open the selected item.
+void LLFolderView::openSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ if (mSelectedItems.size() == 1)
+ {
+ mSelectedItems.front()->open();
+ }
+ else
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+
+ LLMultiPreview* multi_previewp = new LLMultiPreview(LLRect(left, top, left + 300, top - 100));
+
+ LLFloater::setFloaterHost(multi_previewp);
+
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ (*item_it)->open();
+ }
+
+ LLFloater::setFloaterHost(NULL);
+ multi_previewp->open();
+ }
+ }
+}
+
+void LLFolderView::propertiesSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ if (mSelectedItems.size() == 1)
+ {
+ LLFolderViewItem* folder_item = mSelectedItems.front();
+ if(!folder_item) return;
+ folder_item->getListener()->showProperties();
+ }
+ else
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+
+ LLMultiProperties* multi_propertiesp = new LLMultiProperties(LLRect(left, top, left + 100, top - 100));
+
+ LLFloater::setFloaterHost(multi_propertiesp);
+
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ (*item_it)->getListener()->showProperties();
+ }
+
+ LLFloater::setFloaterHost(NULL);
+ multi_propertiesp->open();
+ }
+ }
+}
+
+void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
+{
+ if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH)
+ {
+ return;
+ }
+
+ // close auto-opened folders
+ LLFolderViewFolder* close_item = mAutoOpenItems.check();
+ while (close_item && close_item != item->getParentFolder())
+ {
+ mAutoOpenItems.pop();
+ close_item->setOpenArrangeRecursively(FALSE);
+ close_item = mAutoOpenItems.check();
+ }
+
+ item->requestArrange();
+
+ mAutoOpenItems.push(item);
+
+ item->setOpen(TRUE);
+ scrollToShowItem(item);
+}
+
+void LLFolderView::closeAutoOpenedFolders()
+{
+ while (mAutoOpenItems.check())
+ {
+ LLFolderViewFolder* close_item = mAutoOpenItems.pop();
+ close_item->setOpen(FALSE);
+ }
+
+ if (mAutoOpenCandidate)
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(0.f);
+ }
+ mAutoOpenCandidate = NULL;
+ mAutoOpenTimer.stop();
+}
+
+BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
+{
+ if (folder && mAutoOpenCandidate == folder)
+ {
+ if (mAutoOpenTimer.getStarted())
+ {
+ if (!mAutoOpenCandidate->isOpen())
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f));
+ }
+ if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
+ {
+ autoOpenItem(folder);
+ mAutoOpenTimer.stop();
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ // otherwise new candidate, restart timer
+ if (mAutoOpenCandidate)
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(0.f);
+ }
+ mAutoOpenCandidate = folder;
+ mAutoOpenTimer.start();
+ return FALSE;
+}
+
+BOOL LLFolderView::canCopy()
+{
+ if (!(getVisible() && mEnabled && (mSelectedItems.size() > 0)))
+ {
+ return FALSE;
+ }
+
+ selected_items_t::iterator selected_it;
+ for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ LLFolderViewItem* item = *selected_it;
+ if (!item->getListener()->isItemCopyable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// copy selected item
+void LLFolderView::copy()
+{
+ // *FIX: total hack to clear the inventory clipboard
+ LLInventoryClipboard::instance().reset();
+ S32 count = mSelectedItems.size();
+ if(getVisible() && mEnabled && (count > 0))
+ {
+ LLFolderViewEventListener* listener = NULL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ listener = (*item_it)->getListener();
+ if(listener)
+ {
+ listener->copyToClipboard();
+ }
+ }
+ }
+ mSearchString.clear();
+}
+
+BOOL LLFolderView::canCut()
+{
+ return FALSE;
+}
+
+void LLFolderView::cut()
+{
+ // implement Windows-style cut-and-leave
+}
+
+BOOL LLFolderView::canPaste()
+{
+ if (mSelectedItems.empty())
+ {
+ return FALSE;
+ }
+
+ if(getVisible() && mEnabled)
+ {
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ //FIXME: only check folders and parent folders of items
+ LLFolderViewItem* item = (*item_it);
+ LLFolderViewEventListener* listener = item->getListener();
+ if(!listener || !listener->isClipboardPasteable())
+ {
+ LLFolderViewFolder* folderp = item->getParentFolder();
+ listener = folderp->getListener();
+ if (!listener || !listener->isClipboardPasteable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// paste selected item
+void LLFolderView::paste()
+{
+ if(getVisible() && mEnabled)
+ {
+ // find set of unique folders to paste into
+ std::set<LLFolderViewItem*> folder_set;
+
+ selected_items_t::iterator selected_it;
+ for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ LLFolderViewItem* item = *selected_it;
+ LLFolderViewEventListener* listener = item->getListener();
+ if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY)
+ {
+ item = item->getParentFolder();
+ }
+ folder_set.insert(item);
+ }
+
+ std::set<LLFolderViewItem*>::iterator set_iter;
+ for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter)
+ {
+ LLFolderViewEventListener* listener = (*set_iter)->getListener();
+ if(listener && listener->isClipboardPasteable())
+ {
+ listener->pasteFromClipboard();
+ }
+ }
+ }
+ mSearchString.clear();
+}
+
+// public rename functionality - can only start the process
+void LLFolderView::startRenamingSelectedItem( void )
+{
+ // make sure selection is visible
+ scrollToShowSelection();
+
+ S32 count = mSelectedItems.size();
+ LLFolderViewItem* item = NULL;
+ if(count > 0)
+ {
+ item = mSelectedItems.front();
+ }
+ if(getVisible() && mEnabled && (count == 1) && item && item->getListener() &&
+ item->getListener()->isItemRenameable())
+ {
+ mRenameItem = item;
+
+ S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + item->getIndentation();
+ S32 y = llfloor(item->getRect().getHeight()-sFont->getLineHeight()-2);
+ item->localPointToScreen( x, y, &x, &y );
+ screenPointToLocal( x, y, &x, &y );
+ mRenamer->setOrigin( x, y );
+
+ S32 scroller_height = 0;
+ S32 scroller_width = gViewerWindow->getWindowWidth();
+ BOOL dummy_bool;
+ if (mScrollContainer)
+ {
+ mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool);
+ }
+
+ S32 width = llmax(llmin(item->getRect().getWidth() - x, scroller_width - x - mRect.mLeft), MINIMUM_RENAMER_WIDTH);
+ S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD);
+ mRenamer->reshape( width, height, TRUE );
+
+ mRenamer->setText(item->getName());
+ mRenamer->selectAll();
+ mRenamer->setVisible( TRUE );
+ // set focus will fail unless item is visible
+ mRenamer->setFocus( TRUE );
+ gViewerWindow->setTopView( mRenamer, top_view_lost );
+ }
+}
+
+void LLFolderView::setFocus(BOOL focus)
+{
+ if (focus)
+ {
+ // select "My Inventory" if nothing selected
+ if (!getCurSelectedItem())
+ {
+ LLFolderViewItem* itemp = getItemByID(gAgent.getInventoryRootID());
+ if (itemp)
+ {
+ setSelection(itemp, FALSE, FALSE);
+ }
+ }
+
+ if (mRenamer->getVisible())
+ {
+ //RN: commit rename changes when focus is moved, only revert on ESC
+ finishRenamingItem();
+ }
+ if(!hasFocus())
+ {
+ gEditMenuHandler = this;
+ }
+ }
+
+ LLFolderViewFolder::setFocus(focus);
+}
+
+BOOL LLFolderView::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ LLView *item = NULL;
+ if (getChildCount() > 0)
+ {
+ item = *(getChildList()->begin());
+ }
+
+ if( getVisible() && mEnabled && !called_from_parent )
+ {
+ switch( key )
+ {
+ case KEY_F2:
+ mSearchString.clear();
+ startRenamingSelectedItem();
+ handled = TRUE;
+ break;
+
+ case KEY_RETURN:
+ if (mask == MASK_NONE)
+ {
+ if( mRenameItem && mRenamer->getVisible() )
+ {
+ finishRenamingItem();
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ else
+ {
+ LLFolderView::openSelectedItems();
+ handled = TRUE;
+ }
+ }
+ break;
+
+ case KEY_ESCAPE:
+ // mark flag don't commit
+ if( mRenameItem && mRenamer->getVisible() )
+ {
+ revertRenamingItem();
+ handled = TRUE;
+ }
+ else
+ {
+ if( gViewerWindow->childHasKeyboardFocus( this ) )
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ }
+ }
+ mSearchString.clear();
+ break;
+
+ case KEY_PAGE_UP:
+ mSearchString.clear();
+ mScrollContainer->pageUp(30);
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_DOWN:
+ mSearchString.clear();
+ mScrollContainer->pageDown(30);
+ handled = TRUE;
+ break;
+
+ case KEY_HOME:
+ mSearchString.clear();
+ mScrollContainer->goToTop();
+ handled = TRUE;
+ break;
+
+ case KEY_END:
+ mSearchString.clear();
+ mScrollContainer->goToBottom();
+ break;
+
+ case KEY_DOWN:
+ if((mSelectedItems.size() > 0) && mScrollContainer)
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+
+ if (!mKeyboardSelection)
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ LLFolderViewItem* next = NULL;
+ if (mask & MASK_SHIFT)
+ {
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ next = last_selected->getNextOpenNode(FALSE);
+ if (next)
+ {
+ if (next->isSelected())
+ {
+ // shrink selection
+ changeSelectionFromRoot(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == next->getParentFolder())
+ {
+ // grow selection
+ changeSelectionFromRoot(next, TRUE);
+ }
+ }
+ }
+ else
+ {
+ next = last_selected->getNextOpenNode();
+ if( next )
+ {
+ if (next == last_selected)
+ {
+ return FALSE;
+ }
+ setSelection( next, FALSE, TRUE );
+ }
+ }
+ scrollToShowSelection();
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_UP:
+ if((mSelectedItems.size() > 0) && mScrollContainer)
+ {
+ LLFolderViewItem* last_selected = mSelectedItems.back();
+
+ if (!mKeyboardSelection)
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ LLFolderViewItem* prev = NULL;
+ if (mask & MASK_SHIFT)
+ {
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ prev = last_selected->getPreviousOpenNode(FALSE);
+ if (prev)
+ {
+ if (prev->isSelected())
+ {
+ // shrink selection
+ changeSelectionFromRoot(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == prev->getParentFolder())
+ {
+ // grow selection
+ changeSelectionFromRoot(prev, TRUE);
+ }
+ }
+ }
+ else
+ {
+ prev = last_selected->getPreviousOpenNode();
+ if( prev )
+ {
+ if (prev == this)
+ {
+ return FALSE;
+ }
+ setSelection( prev, FALSE, TRUE );
+ }
+ }
+ scrollToShowSelection();
+ mSearchString.clear();
+
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+ last_selected->setOpen( TRUE );
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_LEFT:
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+ LLFolderViewItem* parent_folder = last_selected->getParentFolder();
+ if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())
+ {
+ setSelection(parent_folder, FALSE, TRUE);
+ }
+ else
+ {
+ last_selected->setOpen( FALSE );
+ }
+ mSearchString.clear();
+ scrollToShowSelection();
+ handled = TRUE;
+ }
+ break;
+ }
+ }
+
+ if (!handled && gFocusMgr.childHasKeyboardFocus(getRoot()))
+ {
+ if (key == KEY_BACKSPACE)
+ {
+ mSearchTimer.reset();
+ if (mSearchString.size())
+ {
+ mSearchString.erase(mSearchString.size() - 1, 1);
+ }
+ search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
+ handled = TRUE;
+ }
+ else if (mask & MASK_CONTROL && key == 'N')
+ {
+ LLFolderViewItem* selection = getCurSelectedItem();
+ if (selection)
+ {
+ selection = selection->getNextOpenNode();
+ }
+ search(selection, mSearchString.c_str(), FALSE);
+ mSearchTimer.reset();
+ handled = TRUE;
+ }
+ else if (mask & MASK_CONTROL && key == 'P')
+ {
+ LLFolderViewItem* selection = getCurSelectedItem();
+ if (selection)
+ {
+ selection = selection->getPreviousOpenNode();
+ }
+ search(selection, mSearchString.c_str(), TRUE);
+ mSearchTimer.reset();
+ handled = TRUE;
+ }
+ }
+
+ if (handled)
+ {
+ gViewerWindow->requestFastFrame(this);
+ }
+ return handled;
+}
+
+
+BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return FALSE;
+ }
+
+ if (uni_char > 0x7f)
+ {
+ llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl;
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+ if (gFocusMgr.childHasKeyboardFocus(getRoot()))
+ {
+ //do text search
+ if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout"))
+ {
+ mSearchString.clear();
+ }
+ mSearchTimer.reset();
+ if (mSearchString.size() < 128)
+ {
+ mSearchString += uni_char;
+ }
+ search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
+
+ handled = TRUE;
+ }
+
+ if (handled)
+ {
+ gViewerWindow->requestFastFrame(this);
+ }
+
+ return handled;
+}
+
+
+BOOL LLFolderView::canDoDelete()
+{
+ if (mSelectedItems.size() == 0) return FALSE;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ if (!(*item_it)->getListener()->isItemRemovable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLFolderView::doDelete()
+{
+ if(mSelectedItems.size() > 0)
+ {
+ removeSelectedItems();
+ }
+}
+
+
+BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ mKeyboardSelection = FALSE;
+ mSearchString.clear();
+
+ setFocus(TRUE);
+
+ return LLView::handleMouseDown( x, y, mask );
+}
+
+void LLFolderView::onFocusLost( )
+{
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+BOOL LLFolderView::search(LLFolderViewItem* first_item, const LLString &search_string, BOOL backward)
+{
+ // get first selected item
+ LLFolderViewItem* search_item = first_item;
+
+ // make sure search string is upper case
+ LLString upper_case_string = search_string;
+ LLString::toUpper(upper_case_string);
+
+ // if nothing selected, select first item in folder
+ if (!first_item)
+ {
+ // start from first item
+ first_item = getNextFromChild(NULL);
+ }
+
+ // search over all open nodes for first substring match (with wrapping)
+ BOOL found = FALSE;
+ LLFolderViewItem* original_search_item = search_item;
+ do
+ {
+ // wrap at end
+ if (!search_item)
+ {
+ if (backward)
+ {
+ search_item = getPreviousFromChild(NULL);
+ }
+ else
+ {
+ search_item = getNextFromChild(NULL);
+ }
+ if (!search_item || search_item == original_search_item)
+ {
+ break;
+ }
+ }
+
+ const LLString current_item_label(search_item->getSearchableLabel());
+ S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size());
+ if (!current_item_label.compare(0, search_string_length, upper_case_string))
+ {
+ found = TRUE;
+ break;
+ }
+ if (backward)
+ {
+ search_item = search_item->getPreviousOpenNode();
+ }
+ else
+ {
+ search_item = search_item->getNextOpenNode();
+ }
+
+ } while(search_item != original_search_item);
+
+
+ if (found)
+ {
+ setSelection(search_item, FALSE, TRUE);
+ scrollToShowSelection();
+ }
+
+ return found;
+}
+
+BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ if (!getVisible())
+ {
+ return FALSE;
+ }
+
+ return LLView::handleDoubleClick( x, y, mask );
+}
+
+BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ // all user operations move keyboard focus to inventory
+ // this way, we know when to stop auto-updating a search
+ setFocus(TRUE);
+
+ BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
+ S32 count = mSelectedItems.size();
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ if(handled && (count > 0) && menu)
+ {
+ //menu->empty();
+ const LLView::child_list_t *list = menu->getChildList();
+
+ LLView::child_list_t::const_iterator menu_itor;
+ for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor)
+ {
+ (*menu_itor)->setVisible(TRUE);
+ (*menu_itor)->setEnabled(TRUE);
+ }
+
+ // Successively filter out invalid options
+ selected_items_t::iterator item_itor;
+ U32 flags = FIRST_SELECTED_ITEM;
+ for (item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor)
+ {
+ (*item_itor)->buildContextMenu(*menu, flags);
+ flags = 0x0;
+ }
+
+ menu->arrange();
+ menu->updateParent(gMenuHolder);
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+ else
+ {
+ if(menu && menu->getVisible())
+ {
+ menu->setVisible(FALSE);
+ }
+ setSelection(NULL, FALSE, TRUE);
+ }
+ return handled;
+}
+
+BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
+{
+ return LLView::handleHover( x, y, mask );
+}
+
+BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ mDragAndDropThisFrame = TRUE;
+ BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data,
+ accept, tooltip_msg);
+
+ if (handled)
+ {
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl;
+ }
+
+ return handled;
+}
+
+BOOL LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (mScrollContainer)
+ {
+ return mScrollContainer->handleScrollWheel(x, y, clicks);
+ }
+ return FALSE;
+}
+
+void LLFolderView::deleteAllChildren()
+{
+ if(gViewerWindow->hasTopView(mRenamer))
+ {
+ gViewerWindow->setTopView(NULL, NULL);
+ }
+ LLView::deleteViewByHandle(mPopupMenuHandle);
+ mPopupMenuHandle = LLViewHandle::sDeadHandle;
+ mRenamer = NULL;
+ mRenameItem = NULL;
+ clearSelection();
+ LLView::deleteAllChildren();
+}
+
+void LLFolderView::scrollToShowSelection()
+{
+ if (mSelectedItems.size())
+ {
+ mNeedsScroll = TRUE;
+ }
+}
+
+// If the parent is scroll containter, scroll it to make the selection
+// is maximally visible.
+void LLFolderView::scrollToShowItem(LLFolderViewItem* item)
+{
+ // don't scroll to items when mouse is being used to scroll/drag and drop
+ if (gFocusMgr.childHasMouseCapture(mScrollContainer))
+ {
+ mNeedsScroll = FALSE;
+ return;
+ }
+ if(item && mScrollContainer)
+ {
+ LLRect local_rect = item->getRect();
+ LLRect item_scrolled_rect; // item position relative to display area of scroller
+
+ S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight();
+ S32 label_height = llround(sFont->getLineHeight());
+ // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder
+ S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight();
+ item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer);
+ item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer);
+
+ item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight);
+ LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft,
+ mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1);
+
+ S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight();
+ if (item != mLastScrollItem || // if we're scrolling to focus on a new item
+ // or the item has just appeared on screen and it wasn't onscreen before
+ (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset &&
+ (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset)))
+ {
+ // we now have a position on screen that we want to keep stable
+ // offset of selection relative to top of visible area
+ mLastScrollOffset = scroll_offset;
+ mLastScrollItem = item;
+ }
+
+ mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset );
+
+ // after scrolling, store new offset
+ // in case we don't have room to maintain the original position
+ LLCoordGL new_item_left_top;
+ item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer);
+ mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1);
+ }
+}
+
+LLRect LLFolderView::getVisibleRect()
+{
+ S32 visible_height = mScrollContainer->getRect().getHeight();
+ S32 visible_width = mScrollContainer->getRect().getWidth();
+ LLRect visible_rect;
+ visible_rect.setLeftTopAndSize(-mRect.mLeft, visible_height - mRect.mBottom, visible_width, visible_height);
+ return visible_rect;
+}
+
+BOOL LLFolderView::getShowSelectionContext()
+{
+ if (mShowSelectionContext)
+ {
+ return TRUE;
+ }
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ if (menu && menu->getVisible())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderView::setShowSingleSelection(BOOL show)
+{
+ if (show != mShowSingleSelection)
+ {
+ mMultiSelectionFadeTimer.reset();
+ mShowSingleSelection = show;
+ }
+}
+
+void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp)
+{
+ mItemMap[id] = itemp;
+}
+
+void LLFolderView::removeItemID(const LLUUID& id)
+{
+ mItemMap.erase(id);
+}
+
+LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id)
+{
+ if (id.isNull())
+ {
+ return this;
+ }
+
+ std::map<LLUUID, LLFolderViewItem*>::iterator map_it;
+ map_it = mItemMap.find(id);
+ if (map_it != mItemMap.end())
+ {
+ return map_it->second;
+ }
+
+ return NULL;
+}
+
+//static
+void LLFolderView::idle(void* user_data)
+{
+ LLFastTimer t2(LLFastTimer::FTM_INVENTORY);
+ LLFolderView* self = (LLFolderView*)user_data;
+
+ BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters");
+ if (debug_filters != self->getDebugFilters())
+ {
+ self->mDebugFilters = debug_filters;
+ self->arrangeAll();
+ }
+
+ self->mFilter.clearModified();
+ BOOL filter_modified_and_active = self->mCompletedFilterGeneration < self->mFilter.getCurrentGeneration() &&
+ self->mFilter.isActive();
+ self->mNeedsAutoSelect = filter_modified_and_active &&
+ !(gFocusMgr.childHasKeyboardFocus(self) || gFocusMgr.getMouseCapture());
+
+ // filter to determine visiblity before arranging
+ self->filterFromRoot();
+
+ self->sanitizeSelection();
+
+ // automatically show matching items, and select first one
+ // do this every frame until user puts keyboard focus into the inventory window
+ // signaling the end of the automatic update
+ // only do this when mNeedsFilter is set, meaning filtered items have
+ // potentially changed
+ if (self->mNeedsAutoSelect)
+ {
+ LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT);
+ // select new item only if a filtered item not currently selected
+ LLFolderViewItem* selected_itemp = self->mSelectedItems.empty() ? NULL : self->mSelectedItems.back();
+ if ((!selected_itemp || !selected_itemp->getFiltered()) && !self->mAutoSelectOverride)
+ {
+ // select first filtered item
+ LLSelectFirstFilteredItem filter;
+ self->applyFunctorRecursively(filter);
+ }
+ self->scrollToShowSelection();
+ }
+
+ if( self->needsArrange() && self->isInVisibleChain())
+ {
+ self->arrangeFromRoot();
+ }
+
+ if (self->mSelectedItems.size() && self->mNeedsScroll)
+ {
+ self->scrollToShowItem(self->mSelectedItems.back());
+ // continue scrolling until animated layout change is done
+ if (!self->needsArrange() || !self->isInVisibleChain())
+ {
+ self->mNeedsScroll = FALSE;
+ }
+ }
+
+ if (self->mSelectionChanged && self->mSelectCallback)
+ {
+ //RN: we use keyboard focus as a proxy for user-explicit actions
+ self->mSelectCallback(self->mSelectedItems, gFocusMgr.childHasKeyboardFocus(self), self->mUserData);
+ }
+ self->mSelectionChanged = FALSE;
+}
+
+void LLFolderView::dumpSelectionInformation()
+{
+ llinfos << "LLFolderView::dumpSelectionInformation()" << llendl;
+ llinfos << "****************************************" << llendl;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ llinfos << " " << (*item_it)->getName() << llendl;
+ }
+ llinfos << "****************************************" << llendl;
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b)
+{
+ S32 compare = LLString::compareDict(a->getLabel(), b->getLabel());
+ if (0 == compare)
+ {
+ return (a->getCreationDate() > b->getCreationDate());
+ }
+ else
+ {
+ return (compare < 0);
+ }
+}
+
+// BUG: This is very very slow. The getCreationDate() is log n in number
+// of inventory items.
+bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b)
+{
+ U32 first_create = a->getCreationDate();
+ U32 second_create = b->getCreationDate();
+ if (first_create == second_create)
+ {
+ return (LLString::compareDict(a->getLabel(), b->getLabel()) < 0);
+ }
+ else
+ {
+ return (first_create > second_create);
+ }
+}
+
+void top_view_lost( LLView* view )
+{
+ if( view ) view->setVisible( FALSE );
+}
+
+void delete_selected_item(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->removeSelectedItems();
+ }
+}
+
+void copy_selected_item(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->copy();
+ }
+}
+
+void paste_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->paste();
+ }
+}
+
+void open_selected_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->openSelectedItems();
+ }
+}
+
+void properties_selected_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->propertiesSelectedItems();
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewEventListener
+///----------------------------------------------------------------------------
+
+void LLFolderViewEventListener::arrangeAndSet(LLFolderViewItem* focus,
+ BOOL set_selection,
+ BOOL take_keyboard_focus)
+{
+ if(!focus) return;
+ LLFolderView* root = focus->getRoot();
+ focus->getParentFolder()->requestArrange();
+ if(set_selection)
+ {
+ focus->setSelectionFromRoot(focus, TRUE, take_keyboard_focus);
+ if(root)
+ {
+ root->scrollToShowSelection();
+ }
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryFilter
+///----------------------------------------------------------------------------
+LLInventoryFilter::LLInventoryFilter(const LLString& name) :
+ mName(name),
+ mModified(FALSE),
+ mNeedTextRebuild(TRUE)
+{
+ mFilterOps.mFilterTypes = 0xffffffff;
+ mFilterOps.mMinDate = 0;
+ mFilterOps.mMaxDate = U32_MAX;
+ mFilterOps.mHoursAgo = 0;
+ mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS;
+ mFilterOps.mPermissions = PERM_NONE;
+
+ mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately
+
+ mSubStringMatchOffset = 0;
+ mFilterSubString = "";
+ mFilterGeneration = 0;
+ mMustPassGeneration = S32_MAX;
+ mMinRequiredGeneration = 0;
+ mNextFilterGeneration = mFilterGeneration + 1;
+
+ mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff");
+}
+
+LLInventoryFilter::~LLInventoryFilter()
+{
+}
+
+BOOL LLInventoryFilter::check(LLFolderViewItem* item)
+{
+ U32 earliest;
+
+ earliest = time_corrected() - mFilterOps.mHoursAgo * 3600;
+ if (mFilterOps.mMinDate && mFilterOps.mMinDate < earliest)
+ {
+ earliest = mFilterOps.mMinDate;
+ }
+ else if (!mFilterOps.mHoursAgo)
+ {
+ earliest = 0;
+ }
+ LLFolderViewEventListener* listener = item->getListener();
+ mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : LLString::npos;
+ BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE)
+ && (mFilterSubString.size() == 0 || mSubStringMatchOffset != LLString::npos)
+ && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions)
+ && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate);
+ return passed;
+}
+
+const LLString LLInventoryFilter::getFilterSubString(BOOL trim)
+{
+ return mFilterSubString;
+}
+
+std::string::size_type LLInventoryFilter::getStringMatchOffset() const
+{
+ return mSubStringMatchOffset;
+}
+
+BOOL LLInventoryFilter::isActive()
+{
+ return mFilterOps.mFilterTypes != 0xffffffff
+ || mFilterSubString.size()
+ || mFilterOps.mPermissions != PERM_NONE
+ || mFilterOps.mMinDate != 0
+ || mFilterOps.mMaxDate != U32_MAX
+ || mFilterOps.mHoursAgo != 0;
+}
+
+BOOL LLInventoryFilter::isModified()
+{
+ return mModified;
+}
+
+BOOL LLInventoryFilter::isModifiedAndClear()
+{
+ BOOL ret = mModified;
+ mModified = FALSE;
+ return ret;
+}
+
+void LLInventoryFilter::setFilterTypes(U32 types)
+{
+ if (mFilterOps.mFilterTypes != types)
+ {
+ // keep current items only if no type bits getting turned off
+ BOOL fewer_bits_set = (mFilterOps.mFilterTypes & ~types);
+ BOOL more_bits_set = (~mFilterOps.mFilterTypes & types);
+
+ mFilterOps.mFilterTypes = types;
+ if (more_bits_set && fewer_bits_set)
+ {
+ // neither less or more restrive, both simultaneously
+ // so we need to filter from scratch
+ setModified(FILTER_RESTART);
+ }
+ else if (more_bits_set)
+ {
+ // target is only one of all requested types so more type bits == less restrictive
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (fewer_bits_set)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+
+ }
+}
+
+void LLInventoryFilter::setFilterSubString(const LLString& string)
+{
+ if (mFilterSubString != string)
+ {
+ // hitting BACKSPACE, for example
+ BOOL less_restrictive = mFilterSubString.size() >= string.size() && !mFilterSubString.substr(0, string.size()).compare(string);
+ // appending new characters
+ BOOL more_restrictive = mFilterSubString.size() < string.size() && !string.substr(0, mFilterSubString.size()).compare(mFilterSubString);
+ mFilterSubString = string;
+ LLString::toUpper(mFilterSubString);
+ LLString::trimHead(mFilterSubString);
+
+ if (less_restrictive)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (more_restrictive)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else
+ {
+ setModified(FILTER_RESTART);
+ }
+ }
+}
+
+void LLInventoryFilter::setFilterPermissions(PermissionMask perms)
+{
+ if (mFilterOps.mPermissions != perms)
+ {
+ // keep current items only if no perm bits getting turned off
+ BOOL fewer_bits_set = (mFilterOps.mPermissions & ~perms);
+ BOOL more_bits_set = (~mFilterOps.mPermissions & perms);
+ mFilterOps.mPermissions = perms;
+
+ if (more_bits_set && fewer_bits_set)
+ {
+ setModified(FILTER_RESTART);
+ }
+ else if (more_bits_set)
+ {
+ // target must have all requested permission bits, so more bits == more restrictive
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else if (fewer_bits_set)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ }
+}
+
+void LLInventoryFilter::setDateRange(U32 min_date, U32 max_date)
+{
+ mFilterOps.mHoursAgo = 0;
+ if (mFilterOps.mMinDate != min_date)
+ {
+ mFilterOps.mMinDate = min_date;
+ setModified();
+ }
+ if (mFilterOps.mMaxDate != llmax(mFilterOps.mMinDate, max_date))
+ {
+ mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date);
+ setModified();
+ }
+}
+
+void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl)
+{
+ if (sl && !isSinceLogoff())
+ {
+ setDateRange(mLastLogoff, U32_MAX);
+ setModified();
+ }
+ if (!sl && isSinceLogoff())
+ {
+ setDateRange(0, U32_MAX);
+ setModified();
+ }
+}
+
+BOOL LLInventoryFilter::isSinceLogoff()
+{
+ return mFilterOps.mMinDate == mLastLogoff && mFilterOps.mMaxDate == U32_MAX;
+}
+
+void LLInventoryFilter::setHoursAgo(U32 hours)
+{
+ if (mFilterOps.mHoursAgo != hours)
+ {
+ //FIXME: need to cache last filter time, in case filter goes stale
+ BOOL less_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours > mFilterOps.mHoursAgo);
+ BOOL more_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours <= mFilterOps.mHoursAgo);
+ mFilterOps.mHoursAgo = hours;
+ mFilterOps.mMinDate = 0;
+ mFilterOps.mMaxDate = U32_MAX;
+ if (less_restrictive)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (more_restrictive)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else
+ {
+ setModified(FILTER_RESTART);
+ }
+ }
+}
+void LLInventoryFilter::setShowFolderState(EFolderShow state)
+{
+ if (mFilterOps.mShowFolderState != state)
+ {
+ mFilterOps.mShowFolderState = state;
+ if (state == SHOW_NON_EMPTY_FOLDERS)
+ {
+ // showing fewer folders than before
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else if (state == SHOW_ALL_FOLDERS)
+ {
+ // showing same folders as before and then some
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else
+ {
+ setModified();
+ }
+ }
+}
+
+void LLInventoryFilter::setSortOrder(U32 order)
+{
+ if (mOrder != order)
+ {
+ mOrder = order;
+ setModified();
+ }
+}
+
+void LLInventoryFilter::markDefault()
+{
+ mDefaultFilterOps = mFilterOps;
+}
+
+void LLInventoryFilter::resetDefault()
+{
+ mFilterOps = mDefaultFilterOps;
+ setModified();
+}
+
+void LLInventoryFilter::setModified(EFilterBehavior behavior)
+{
+ mModified = TRUE;
+ mNeedTextRebuild = TRUE;
+ mFilterGeneration = mNextFilterGeneration++;
+
+ if (mFilterBehavior == FILTER_NONE)
+ {
+ mFilterBehavior = behavior;
+ }
+ else if (mFilterBehavior != behavior)
+ {
+ // trying to do both less restrictive and more restrictive filter
+ // basically means restart from scratch
+ mFilterBehavior = FILTER_RESTART;
+ }
+
+ if (isActive())
+ {
+ // if not keeping current filter results, update last valid as well
+ switch(mFilterBehavior)
+ {
+ case FILTER_RESTART:
+ mMustPassGeneration = mFilterGeneration;
+ mMinRequiredGeneration = mFilterGeneration;
+ break;
+ case FILTER_LESS_RESTRICTIVE:
+ mMustPassGeneration = mFilterGeneration;
+ break;
+ case FILTER_MORE_RESTRICTIVE:
+ mMinRequiredGeneration = mFilterGeneration;
+ // must have passed either current filter generation (meaningless, as it hasn't been run yet)
+ // or some older generation, so keep the value
+ mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration);
+ break;
+ default:
+ llerrs << "Bad filter behavior specified" << llendl;
+ }
+ }
+ else
+ {
+ // shortcut disabled filters to show everything immediately
+ mMinRequiredGeneration = 0;
+ mMustPassGeneration = S32_MAX;
+ }
+}
+
+BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t)
+{
+ return mFilterOps.mFilterTypes & (0x01 << t);
+}
+
+LLString LLInventoryFilter::getFilterText()
+{
+ if (!mNeedTextRebuild)
+ {
+ return mFilterText;
+ }
+
+ mNeedTextRebuild = FALSE;
+ LLString filtered_types;
+ LLString not_filtered_types;
+ BOOL filtered_by_type = FALSE;
+ BOOL filtered_by_all_types = TRUE;
+ S32 num_filter_types = 0;
+ mFilterText = "";
+
+ if (isFilterWith(LLInventoryType::IT_ANIMATION))
+ {
+ filtered_types += " Animations,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Animations,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_CALLINGCARD))
+ {
+ filtered_types += " Calling Cards,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Calling Cards,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_WEARABLE))
+ {
+ filtered_types += " Clothing,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Clothing,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_GESTURE))
+ {
+ filtered_types += " Gestures,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Gestures,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_LANDMARK))
+ {
+ filtered_types += " Landmarks,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Landmarks,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_NOTECARD))
+ {
+ filtered_types += " Notecards,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Notecards,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT))
+ {
+ filtered_types += " Objects,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Objects,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_LSL))
+ {
+ filtered_types += " Scripts,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Scripts,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_SOUND))
+ {
+ filtered_types += " Sounds,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Sounds,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_TEXTURE))
+ {
+ filtered_types += " Textures,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Textures,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_SNAPSHOT))
+ {
+ filtered_types += " Snapshots,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Snapshots,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!gInventory.backgroundFetchActive() && filtered_by_type && !filtered_by_all_types)
+ {
+ mFilterText += " - ";
+ if (num_filter_types < 5)
+ {
+ mFilterText += filtered_types;
+ }
+ else
+ {
+ mFilterText += "No ";
+ mFilterText += not_filtered_types;
+ }
+ // remove the ',' at the end
+ mFilterText.erase(mFilterText.size() - 1, 1);
+ }
+
+ if (isSinceLogoff())
+ {
+ mFilterText += " - Since Logoff";
+ }
+ return mFilterText;
+}
diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h
new file mode 100644
index 0000000000..08185e24fb
--- /dev/null
+++ b/indra/newview/llfolderview.h
@@ -0,0 +1,864 @@
+/**
+ * @file llfolderview.h
+ * @brief Definition of the folder view collection of classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * The folder view collection of classes provides an interface for
+ * making a 'folder view' similar to the way the a single pane file
+ * folder interface works.
+ *
+ */
+
+#ifndef LL_LLFOLDERVIEW_H
+#define LL_LLFOLDERVIEW_H
+
+#include <vector>
+#include <map>
+#include <deque>
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "lldarray.h"
+//#include "llviewermenu.h"
+#include "stdenums.h"
+#include "llfontgl.h"
+#include "lleditmenuhandler.h"
+#include "llviewerimage.h"
+#include "lldepthstack.h"
+#include "lltooldraganddrop.h"
+
+class LLMenuGL;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewEventListener
+//
+// This is an abstract base class that users of the folderview classes
+// would use to catch the useful events emitted from the folder
+// views.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class LLFolderViewItem;
+class LLFolderView;
+class LLInventoryModel;
+class LLScrollableContainerView;
+typedef BOOL (*LLFolderSearchFunction)(LLFolderViewItem* first_item, const char *find_text, BOOL backward);
+
+class LLFolderViewEventListener
+{
+public:
+ virtual ~LLFolderViewEventListener( void ) {}
+ virtual const LLString& getName() const = 0;
+ virtual const LLString& getDisplayName() const = 0;
+ virtual const LLUUID& getUUID() const = 0;
+ virtual U32 getCreationDate() const = 0; // UTC seconds
+ virtual PermissionMask getPermissionMask() const = 0;
+ virtual LLViewerImage* getIcon() const = 0;
+ virtual LLFontGL::StyleFlags getLabelStyle() const = 0;
+ virtual LLString getLabelSuffix() const = 0;
+ virtual void openItem( void ) = 0;
+ virtual void previewItem( void ) = 0;
+ virtual void selectItem(void) = 0;
+ virtual void showProperties(void) = 0;
+ virtual BOOL isItemRenameable() const = 0;
+ virtual BOOL renameItem(const LLString& new_name) = 0;
+ virtual BOOL isItemMovable( void ) = 0; // Can be moved to another folder
+ virtual BOOL isItemRemovable( void ) = 0; // Can be destroyed
+ virtual BOOL removeItem() = 0;
+ virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) = 0;
+ virtual void move( LLFolderViewEventListener* parent_listener ) = 0;
+ virtual BOOL isItemCopyable() const = 0;
+ virtual BOOL copyToClipboard() const = 0;
+ virtual void cutToClipboard() = 0;
+ virtual BOOL isClipboardPasteable() const = 0;
+ virtual void pasteFromClipboard() = 0;
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0;
+ virtual BOOL isUpToDate() const = 0;
+ virtual BOOL hasChildren() const = 0;
+ virtual LLInventoryType::EType getInventoryType() const = 0;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) {}
+
+ // This method should be called when a drag begins. returns TRUE
+ // if the drag can begin, otherwise FALSE.
+ virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) = 0;
+
+ // This method will be called to determine if a drop can be
+ // performed, and will set drop to TRUE if a drop is
+ // requested. Returns TRUE if a drop is possible/happened,
+ // otherwise FALSE.
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data) = 0;
+
+ // This method is called when the object being referenced by the
+ // bridge is actually dropped. This allows for cleanup of the old
+ // view, reference counting, etc.
+// virtual void dropped() = 0;
+
+ // this method accesses the parent and arranges and sets it as
+ // specified.
+ void arrangeAndSet(LLFolderViewItem* focus, BOOL set_selection,
+ BOOL take_keyboard_focus = TRUE);
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewListenerFunctor
+//
+// This simple abstract base class can be used to applied to all
+// listeners in a hierarchy.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewListenerFunctor
+{
+public:
+ virtual ~LLFolderViewListenerFunctor() {}
+ virtual void operator()(LLFolderViewEventListener* listener) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewFunctor
+//
+// Simple abstract base class for applying a functor to folders and
+// items in a folder view hierarchy. This is suboptimal for algorithms
+// that only work folders or only work on items, but I'll worry about
+// that later when it's determined to be too slow.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewItem;
+class LLFolderViewFolder;
+
+class LLFolderViewFunctor
+{
+public:
+ virtual ~LLFolderViewFunctor() {}
+ virtual void doFolder(LLFolderViewFolder* folder) = 0;
+ virtual void doItem(LLFolderViewItem* item) = 0;
+};
+
+class LLInventoryFilter
+{
+public:
+ typedef enum e_folder_show
+ {
+ SHOW_ALL_FOLDERS,
+ SHOW_NON_EMPTY_FOLDERS,
+ SHOW_NO_FOLDERS
+ } EFolderShow;
+
+ typedef enum e_filter_behavior
+ {
+ FILTER_NONE, // nothing to do, already filtered
+ FILTER_RESTART, // restart filtering from scratch
+ FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter
+ FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one
+ } EFilterBehavior;
+
+ static const U32 SO_DATE = 1;
+ static const U32 SO_FOLDERS_BY_NAME = 2;
+
+ LLInventoryFilter(const LLString& name);
+ virtual ~LLInventoryFilter();
+
+ void setFilterTypes(U32 types);
+ U32 getFilterTypes() const { return mFilterOps.mFilterTypes; }
+
+ void setFilterSubString(const LLString& string);
+ const LLString getFilterSubString(BOOL trim = FALSE);
+
+ void setFilterPermissions(PermissionMask perms);
+ PermissionMask getFilterPermissions() const { return mFilterOps.mPermissions; }
+
+ void setDateRange(U32 min_date, U32 max_date);
+ void setDateRangeLastLogoff(BOOL sl);
+ U32 getMinDate() const { return mFilterOps.mMinDate; }
+ U32 getMaxDate() const { return mFilterOps.mMaxDate; }
+
+ void setHoursAgo(U32 hours);
+ U32 getHoursAgo() const { return mFilterOps.mHoursAgo; }
+
+ void setShowFolderState( EFolderShow state);
+ EFolderShow getShowFolderState() { return mFilterOps.mShowFolderState; }
+
+ void setSortOrder(U32 order);
+ U32 getSortOrder() { return mOrder; }
+
+ BOOL check(LLFolderViewItem* item);
+ std::string::size_type getStringMatchOffset() const;
+ BOOL isActive();
+ BOOL isModified();
+ BOOL isModifiedAndClear();
+ BOOL isSinceLogoff();
+ void clearModified() { mModified = FALSE; mFilterBehavior = FILTER_NONE; }
+ const LLString getName() const { return mName; }
+ LLString getFilterText();
+
+ void setFilterCount(S32 count) { mFilterCount = count; }
+ S32 getFilterCount() { return mFilterCount; }
+ void decrementFilterCount() { mFilterCount--; }
+
+ void markDefault();
+ void resetDefault();
+
+ BOOL isFilterWith(LLInventoryType::EType t);
+
+ S32 getCurrentGeneration() const { return mFilterGeneration; }
+ S32 getMinRequiredGeneration() const { return mMinRequiredGeneration; }
+ S32 getMustPassGeneration() const { return mMustPassGeneration; }
+
+ //RN: this is public to allow system to externally force a global refilter
+ void setModified(EFilterBehavior behavior = FILTER_RESTART);
+
+protected:
+ struct filter_ops
+ {
+ U32 mFilterTypes;
+ U32 mMinDate;
+ U32 mMaxDate;
+ U32 mHoursAgo;
+ EFolderShow mShowFolderState;
+ PermissionMask mPermissions;
+ };
+ filter_ops mFilterOps;
+ filter_ops mDefaultFilterOps;
+ std::string::size_type mSubStringMatchOffset;
+ LLString mFilterSubString;
+ U32 mOrder;
+ const LLString mName;
+ S32 mFilterGeneration;
+ S32 mMustPassGeneration;
+ S32 mMinRequiredGeneration;
+ S32 mFilterCount;
+ S32 mNextFilterGeneration;
+ EFilterBehavior mFilterBehavior;
+
+private:
+ U32 mLastLogoff;
+ BOOL mModified;
+ BOOL mNeedTextRebuild;
+ LLString mFilterText;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewItem
+//
+// An instance of this class represents a single item in a folder view
+// such as an inventory item or a file.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFontGL;
+class LLFolderViewFolder;
+class LLFolderView;
+
+class LLFolderViewItem : public LLUICtrl
+{
+protected:
+ friend class LLFolderViewEventListener;
+
+ static const LLFontGL* sFont;
+ static const LLFontGL* sSmallFont;
+ static LLColor4 sFgColor;
+ static LLColor4 sHighlightBgColor;
+ static LLColor4 sHighlightFgColor;
+ static LLColor4 sFilterBGColor;
+ static LLColor4 sFilterTextColor;
+
+ LLString mLabel;
+ LLString mSearchableLabel;
+ LLString mType;
+ S32 mLabelWidth;
+ U32 mCreationDate;
+ LLFolderViewFolder* mParentFolder;
+ LLFolderViewEventListener* mListener;
+ BOOL mIsSelected;
+ BOOL mIsCurSelection;
+ BOOL mSelectPending;
+ LLFontGL::StyleFlags mLabelStyle;
+ LLString mLabelSuffix;
+ LLPointer<LLViewerImage> mIcon;
+ LLString mStatusText;
+ BOOL mHasVisibleChildren;
+ S32 mIndentation;
+ S32 mNumDescendantsSelected;
+ BOOL mFiltered;
+ S32 mLastFilterGeneration;
+ std::string::size_type mStringMatchOffset;
+ F32 mControlLabelRotation;
+ LLFolderView* mRoot;
+ BOOL mDragAndDropTarget;
+ LLPointer<LLViewerImage> mArrowImage;
+ LLPointer<LLViewerImage> mBoxImage;
+
+ // This function clears the currently selected item, and records
+ // the specified selected item appropriately for display and use
+ // in the UI. If open is TRUE, then folders are opened up along
+ // the way to the selection.
+ void setSelectionFromRoot(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus = TRUE);
+
+ // helper function to change the selection from the root.
+ void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected);
+
+ // helper function to change the selection from the root.
+ void extendSelectionFromRoot(LLFolderViewItem* selection);
+
+ // this is an internal method used for adding items to folders. A
+ // no-op at this leve, but reimplemented in derived classes.
+ virtual BOOL addItem(LLFolderViewItem*) { return FALSE; }
+ virtual BOOL addFolder(LLFolderViewFolder*) { return FALSE; }
+
+public:
+ // This function is called when the folder view is dirty. It's
+ // implemented here but called by derived classes when folding the
+ // views.
+ void arrangeFromRoot();
+ void filterFromRoot( void );
+
+ // creation_date is in UTC seconds
+ LLFolderViewItem( const LLString& name, LLViewerImage* icon, S32 creation_date, LLFolderView* root, LLFolderViewEventListener* listener );
+ virtual ~LLFolderViewItem( void );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // addToFolder() returns TRUE if it succeeds. FALSE otherwise
+ enum { ARRANGE = TRUE, DO_NOT_ARRANGE = FALSE };
+ virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
+ virtual S32 getItemHeight();
+
+ // applies filters to control visibility of inventory items
+ virtual void filter( LLInventoryFilter& filter);
+
+ // updates filter serial number and optionally propagated value up to root
+ S32 getLastFilterGeneration() { return mLastFilterGeneration; }
+
+ virtual void dirtyFilter();
+
+ // If the selection is 'this' then note that otherwise
+ // ignore. Returns TRUE if this object was affected. If open is
+ // TRUE, then folders are opened up along the way to the
+ // selection.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus);
+
+ // This method is used to toggle the selection of an item. If
+ // selection is 'this', then note selection, and return TRUE.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ // this method is used to group select items
+ virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items){ return FALSE; }
+
+ // this method is used to group select items
+ virtual void recursiveDeselect(BOOL deselect_self);
+
+ // gets multiple-element selection
+ virtual BOOL getSelectionList(std::set<LLUUID> &selection){return TRUE;}
+
+ // Returns true is this object and all of its children can be removed (deleted by user)
+ virtual BOOL isRemovable();
+
+ // Returns true is this object and all of its children can be moved
+ virtual BOOL isMovable();
+
+ // destroys this item recursively
+ virtual void destroyView();
+
+ S32 getNumSelectedDescendants() { return mNumDescendantsSelected; }
+
+ BOOL isSelected() { return mIsSelected; }
+
+ void setIsCurSelection(BOOL select) { mIsCurSelection = select; }
+
+ BOOL getIsCurSelection() { return mIsCurSelection; }
+
+ BOOL hasVisibleChildren() { return mHasVisibleChildren; }
+
+ // Call through to the viewed object and return true if it can be
+ // removed. Returns true if it's removed.
+ //virtual BOOL removeRecursively(BOOL single_item);
+ BOOL remove();
+
+ // Build an appropriate context menu for the item. Flags unused.
+ void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+ // This method returns the actual name of the thing being
+ // viewed. This method will ask the viewed object itself.
+ const LLString& getName( void ) const;
+
+ const LLString& getSearchableLabel( void ) const;
+
+ // This method returns the label displayed on the view. This
+ // method was primarily added to allow sorting on the folder
+ // contents possible before the entire view has been constructed.
+ const char* getLabel() const { return mLabel.c_str(); }
+
+ // Used for sorting, like getLabel() above.
+ virtual U32 getCreationDate() const { return mCreationDate; }
+
+ LLFolderViewFolder* getParentFolder( void );
+ LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE );
+ LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE );
+
+ LLFolderViewEventListener* getListener( void );
+
+ // just rename the object.
+ void rename(const LLString& new_name);
+
+ // open
+ virtual void open( void );
+ virtual void preview(void);
+
+ // Show children (unfortunate that this is called "open")
+ virtual void setOpen(BOOL open = TRUE) {};
+
+ virtual BOOL isOpen() { return FALSE; }
+
+ LLFolderView* getRoot();
+ BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor );
+ S32 getIndentation() { return mIndentation; }
+
+ virtual void setStatusText(const LLString& text) { mStatusText = text; }
+
+ BOOL getFiltered();
+ BOOL getFiltered(S32 filter_generation);
+ virtual void setFiltered(BOOL filtered, S32 filter_generation);
+
+ // change the icon
+ void setIcon(LLViewerImage* icon);
+
+ // refresh information from the object being viewed.
+ virtual void refresh();
+
+ virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor);
+
+ // LLView functionality
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
+ // virtual void handleDropped();
+ virtual void draw();
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+};
+
+
+// function used for sorting.
+typedef bool (*sort_order_f)(LLFolderViewItem* a, LLFolderViewItem* b);
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewFolder
+//
+// An instance of an LLFolderViewFolder represents a collection of
+// more folders and items. This is used to build the hierarchy of
+// items found in the folder view. :)
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewFolder : public LLFolderViewItem
+{
+public:
+ typedef enum e_trash
+ {
+ UNKNOWN, TRASH, NOT_TRASH
+ } ETrash;
+
+protected:
+ typedef std::vector<LLFolderViewItem*> items_t;
+ typedef std::vector<LLFolderViewFolder*> folders_t;
+ items_t mItems;
+ folders_t mFolders;
+ sort_order_f mSortFunction;
+
+ BOOL mIsOpen;
+ BOOL mExpanderHighlighted;
+ F32 mCurHeight;
+ F32 mTargetHeight;
+ F32 mAutoOpenCountdown;
+ U32 mSubtreeCreationDate;
+ ETrash mAmTrash;
+ S32 mLastArrangeGeneration;
+ S32 mLastCalculatedWidth;
+ S32 mCompletedFilterGeneration;
+ S32 mMostFilteredDescendantGeneration;
+public:
+ typedef enum e_recurse_type
+ {
+ RECURSE_NO,
+ RECURSE_UP,
+ RECURSE_DOWN,
+ RECURSE_UP_DOWN
+ } ERecurseType;
+
+ LLFolderViewFolder( const LLString& name, LLViewerImage* icon,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener );
+ virtual ~LLFolderViewFolder( void );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ LLFolderViewItem* getNextFromChild( LLFolderViewItem*, BOOL include_children = TRUE );
+ LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, BOOL include_children = TRUE );
+
+ // addToFolder() returns TRUE if it succeeds. FALSE otherwise
+ virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
+
+ BOOL needsArrange();
+
+ virtual void setCompletedFilterGeneration(S32 generation, BOOL recurse_up);
+ virtual S32 getCompletedFilterGeneration() { return mCompletedFilterGeneration; }
+
+ BOOL hasFilteredDescendants(S32 filter_generation) { return mMostFilteredDescendantGeneration >= filter_generation; }
+ BOOL hasFilteredDescendants();
+
+ // applies filters to control visibility of inventory items
+ virtual void filter( LLInventoryFilter& filter);
+ virtual void setFiltered(BOOL filtered, S32 filter_generation);
+ virtual void dirtyFilter();
+
+ // Passes selection information on to children and record
+ // selection information if necessary. Returns TRUE if this object
+ // (or a child) was affected.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus);
+
+ // This method is used to change the selection of an item. If
+ // selection is 'this', then note selection as true. Returns TRUE
+ // if this or a child is now selected.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ // this method is used to group select items
+ virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items);
+
+ virtual void recursiveDeselect(BOOL deselect_self);
+
+ // Returns true is this object and all of its children can be removed.
+ virtual BOOL isRemovable();
+
+ // Returns true is this object and all of its children can be moved
+ virtual BOOL isMovable();
+
+ // destroys this folder, and all children
+ virtual void destroyView();
+
+ // If this folder can be removed, remove all children that can be
+ // removed, return TRUE if this is empty after the operation and
+ // it's viewed folder object can be removed.
+ //virtual BOOL removeRecursively(BOOL single_item);
+ //virtual BOOL remove();
+
+ // remove the specified item (and any children) if
+ // possible. Return TRUE if the item was deleted.
+ BOOL removeItem(LLFolderViewItem* item);
+
+ // simply remove the view (and any children) Don't bother telling
+ // the listeners.
+ void removeView(LLFolderViewItem* item);
+
+ // extractItem() removes the specified item from the folder, but
+ // doesn't delete it.
+ void extractItem( LLFolderViewItem* item );
+
+ // This function is called by a child that needs to be resorted.
+ void resort(LLFolderViewItem* item);
+
+ void setItemSortFunction(sort_order_f ordering);
+ void sortBy(U32);
+ //BOOL (*func)(LLFolderViewItem* a, LLFolderViewItem* b));
+
+ void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; }
+
+ // folders can be opened. This will usually be called by internal
+ // methods.
+ virtual void toggleOpen();
+
+ // Force a folder open or closed
+ virtual void setOpen(BOOL open = TRUE);
+
+ // Called when a child is refreshed.
+ virtual void requestArrange();
+
+ // internal method which doesn't update the entire view. This
+ // method was written because the list iterators destroy the state
+ // of other iterations, thus, we can't arrange while iterating
+ // through the children (such as when setting which is selected.
+ virtual void setOpenArrangeRecursively(BOOL open, ERecurseType recurse = RECURSE_NO);
+
+ // Get the current state of the folder.
+ virtual BOOL isOpen() { return mIsOpen; }
+
+ // special case if an object is dropped on the child.
+ BOOL handleDragAndDropFromChild(MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ void applyFunctorRecursively(LLFolderViewFunctor& functor);
+ virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor);
+
+ virtual void open( void );
+ virtual BOOL addItem(LLFolderViewItem* item);
+ virtual BOOL addFolder( LLFolderViewFolder* folder);
+
+ // LLView functionality
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+ virtual void draw();
+
+ U32 getCreationDate() const;
+ bool isTrash();
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderView
+//
+// Th LLFolderView represents the root level folder view object. It
+// manages the screen region of the folder view.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLUICtrl;
+class LLLineEditor;
+
+class LLFolderView : public LLFolderViewFolder, LLEditMenuHandler
+{
+public:
+ typedef void (*SelectCallback)(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data);
+
+ static F32 sAutoOpenTime;
+
+ LLFolderView( const LLString& name, LLViewerImage* root_folder_icon, const LLRect& rect,
+ const LLUUID& source_id, LLView *parent_view );
+ virtual ~LLFolderView( void );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ virtual BOOL canFocusChildren() const;
+
+ // FolderViews default to sort by name. This will change that,
+ // and resort the items if necessary.
+ void setSortOrder(U32 order);
+ void checkTreeResortForModelChanged();
+ void setFilterPermMask(PermissionMask filter_perm_mask) { mFilter.setFilterPermissions(filter_perm_mask); }
+ void setSelectCallback(SelectCallback callback, void* user_data) { mSelectCallback = callback, mUserData = user_data; }
+ void setAllowMultiSelect(BOOL allow) { mAllowMultiSelect = allow; }
+
+ LLInventoryFilter* getFilter() { return &mFilter; }
+ const LLString getFilterSubString(BOOL trim = FALSE);
+ U32 getFilterTypes() const { return mFilter.getFilterTypes(); }
+ PermissionMask getFilterPermissions() const { return mFilter.getFilterPermissions(); }
+ LLInventoryFilter::EFolderShow getShowFolderState() { return mFilter.getShowFolderState(); }
+ U32 getSortOrder() const;
+ BOOL isFilterActive() { return mFilter.isActive(); }
+ BOOL getAllowMultiSelect() { return mAllowMultiSelect; }
+
+ // Close all folders in the view
+ void closeAllFolders();
+ void openFolder(const LLString& foldername);
+
+ virtual void toggleOpen() {};
+ virtual void setOpenArrangeRecursively(BOOL open, ERecurseType recurse);
+ virtual BOOL addFolder( LLFolderViewFolder* folder);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
+
+ void arrangeAll() { mArrangeGeneration++; }
+ S32 getArrangeGeneration() { return mArrangeGeneration; }
+
+ // applies filters to control visibility of inventory items
+ virtual void filter( LLInventoryFilter& filter);
+
+ // get the last selected item
+ virtual LLFolderViewItem* getCurSelectedItem( void );
+
+ // Record the selected item and pass it down the hierachy.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus);
+
+ // This method is used to toggle the selection of an item. Walks
+ // children, and keeps track of selected objects.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items);
+
+ virtual BOOL getSelectionList(std::set<LLUUID> &selection);
+
+ // make sure if ancestor is selected, descendents are not
+ void sanitizeSelection();
+ void clearSelection();
+ void addToSelectionList(LLFolderViewItem* item);
+ void removeFromSelectionList(LLFolderViewItem* item);
+
+ BOOL startDrag(LLToolDragAndDrop::ESource source);
+ void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; }
+
+ // deletion functionality
+ void removeSelectedItems();
+
+ // open the selected item.
+ void openSelectedItems( void );
+ void propertiesSelectedItems( void );
+
+ void autoOpenItem(LLFolderViewFolder* item);
+ void closeAutoOpenedFolders();
+ BOOL autoOpenTest(LLFolderViewFolder* item);
+
+ // copy & paste
+ virtual void copy();
+ virtual BOOL canCopy();
+
+ virtual void cut();
+ virtual BOOL canCut();
+
+ virtual void paste();
+ virtual BOOL canPaste();
+
+ virtual void doDelete();
+ virtual BOOL canDoDelete();
+
+ // public rename functionality - can only start the process
+ void startRenamingSelectedItem( void );
+
+ // These functions were used when there was only one folderview,
+ // and relied on that concept. This functionality is now handled
+ // by the listeners and the lldraganddroptool.
+ //LLFolderViewItem* getMovingItem() { return mMovingItem; }
+ //void setMovingItem( LLFolderViewItem* item ) { mMovingItem = item; }
+ //void dragItemIntoFolder( LLFolderViewItem* moving_item, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept );
+ //void dragFolderIntoFolder( LLFolderViewFolder* moving_folder, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept );
+
+ // LLUICtrl Functionality
+ /*virtual*/ void setFocus(BOOL focus);
+
+ // LLView functionality
+ ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent );
+ /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask, BOOL called_from_parent );
+ /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+ /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ /*virtual*/ void onFocusLost();
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void draw();
+ virtual void deleteAllChildren();
+
+ void scrollToShowSelection();
+ void scrollToShowItem(LLFolderViewItem* item);
+ void setScrollContainer( LLScrollableContainerView* parent ) { mScrollContainer = parent; }
+ LLRect getVisibleRect();
+
+ BOOL search(LLFolderViewItem* first_item, const LLString &search_string, BOOL backward);
+ void setShowSelectionContext(BOOL show) { mShowSelectionContext = show; }
+ BOOL getShowSelectionContext();
+ void setShowSingleSelection(BOOL show);
+ BOOL getShowSingleSelection() { return mShowSingleSelection; }
+ F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); }
+
+ void addItemID(const LLUUID& id, LLFolderViewItem* itemp);
+ void removeItemID(const LLUUID& id);
+ LLFolderViewItem* getItemByID(const LLUUID& id);
+
+ static void idle(void* user_data);
+
+ void setAutoSelectOverride(bool override) { mAutoSelectOverride = override; }
+ BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; }
+ BOOL getDebugFilters() { return mDebugFilters; }
+
+ // DEBUG only
+ void dumpSelectionInformation();
+
+protected:
+ LLScrollableContainerView* mScrollContainer; // NULL if this is not a child of a scroll container.
+
+ static void commitRename( LLUICtrl* renamer, void* user_data );
+ void finishRenamingItem( void );
+ void revertRenamingItem( void );
+
+protected:
+ LLViewHandle mPopupMenuHandle;
+
+ typedef std::deque<LLFolderViewItem*> selected_items_t;
+ selected_items_t mSelectedItems;
+ BOOL mKeyboardSelection;
+ BOOL mAllowMultiSelect;
+ BOOL mShowFolderHierarchy;
+ LLUUID mSourceID;
+
+ // Renaming variables and methods
+ LLFolderViewItem* mRenameItem; // The item currently being renamed
+ LLLineEditor* mRenamer;
+
+ BOOL mNeedsScroll;
+ LLFolderViewItem* mLastScrollItem;
+ LLCoordGL mLastScrollOffset;
+ BOOL mNeedsAutoSelect;
+ BOOL mAutoSelectOverride;
+
+ BOOL mDebugFilters;
+ U32 mSortOrder;
+ LLDepthStack<LLFolderViewFolder> mAutoOpenItems;
+ LLFolderViewFolder* mAutoOpenCandidate;
+ LLFrameTimer mAutoOpenTimer;
+ LLFrameTimer mSearchTimer;
+ LLString mSearchString;
+ LLInventoryFilter mFilter;
+ BOOL mShowSelectionContext;
+ BOOL mShowSingleSelection;
+ LLFrameTimer mMultiSelectionFadeTimer;
+ S32 mArrangeGeneration;
+
+ void* mUserData;
+ SelectCallback mSelectCallback;
+ BOOL mSelectionChanged;
+ S32 mMinWidth;
+ std::map<LLUUID, LLFolderViewItem*> mItemMap;
+ BOOL mDragAndDropThisFrame;
+
+};
+
+bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b);
+bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b);
+
+// Flags for buildContextMenu()
+const U32 SUPPRESS_OPEN_ITEM = 0x1;
+const U32 FIRST_SELECTED_ITEM = 0x2;
+
+#endif // LL_LLFOLDERVIEW_H
diff --git a/indra/newview/llfollowcam.cpp b/indra/newview/llfollowcam.cpp
new file mode 100644
index 0000000000..6f1addcebb
--- /dev/null
+++ b/indra/newview/llfollowcam.cpp
@@ -0,0 +1,882 @@
+/**
+ * @file llfollowcam.cpp
+ * @author Jeffrey Ventrella
+ * @brief LLFollowCam class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfollowcam.h"
+#include "llagent.h"
+
+//-------------------------------------------------------
+// class statics
+//-------------------------------------------------------
+std::map<LLUUID, LLFollowCamParams*> LLFollowCamMgr::sParamMap;
+std::vector<LLFollowCamParams*> LLFollowCamMgr::sParamStack;
+
+//-------------------------------------------------------
+// constants
+//-------------------------------------------------------
+const F32 ONE_HALF = 0.5;
+const F32 FOLLOW_CAM_ZOOM_FACTOR = 0.1f;
+const F32 FOLLOW_CAM_MIN_ZOOM_AMOUNT = 0.1f;
+const F32 DISTANCE_EPSILON = 0.0001f;
+const F32 DEFAULT_MAX_DISTANCE_FROM_SUBJECT = 1000.0; // this will be correctly set on me by my caller
+
+//----------------------------------------------------------------------------------------
+// This is how slowly the camera position moves to its ideal position
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_POSITION_LAG = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_POSITION_LAG = 0.1f;
+const F32 FOLLOW_CAM_MAX_POSITION_LAG = 3.0f;
+
+//----------------------------------------------------------------------------------------
+// This is how slowly the camera focus moves to its subject
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_FOCUS_LAG = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_FOCUS_LAG = 0.1f;
+const F32 FOLLOW_CAM_MAX_FOCUS_LAG = 3.0f;
+
+//----------------------------------------------------------------------------------------
+// This is far the position can get from its IDEAL POSITION before it starts getting pulled
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_POSITION_THRESHOLD = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD = 1.0f;
+const F32 FOLLOW_CAM_MAX_POSITION_THRESHOLD = 4.0f;
+
+//----------------------------------------------------------------------------------------
+// This is far the focus can get from the subject before it starts getting pulled
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_FOCUS_THRESHOLD = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD = 1.0f;
+const F32 FOLLOW_CAM_MAX_FOCUS_THRESHOLD = 4.0f;
+
+//----------------------------------------------------------------------------------------
+// This is the distance the camera wants to be from the subject
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_DISTANCE = 0.5f;
+const F32 FOLLOW_CAM_DEFAULT_DISTANCE = 3.0f;
+//const F32 FOLLOW_CAM_MAX_DISTANCE = 10.0f; // from now on I am using mMaxCameraDistantFromSubject
+
+//----------------------------------------------------------------------------------------
+// this is an angluar value
+// It affects the angle that the camera rises (pitches) in relation
+// to the horizontal plane
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_PITCH = -45.0f;
+const F32 FOLLOW_CAM_DEFAULT_PITCH = 0.0f;
+const F32 FOLLOW_CAM_MAX_PITCH = 80.0f; // keep under 90 degrees - avoid gimble lock!
+
+
+//----------------------------------------------------------------------------------------
+// how high or low the camera considers its ideal focus to be (relative to its subject)
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_FOCUS_OFFSET = -10.0f;
+const LLVector3 FOLLOW_CAM_DEFAULT_FOCUS_OFFSET = LLVector3(1.0f, 0.f, 0.f);
+const F32 FOLLOW_CAM_MAX_FOCUS_OFFSET = 10.0f;
+
+//----------------------------------------------------------------------------------------
+// This affects the rate at which the camera adjusts to stay behind the subject
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_BEHINDNESS_LAG = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_BEHINDNESS_LAG = 0.f;
+const F32 FOLLOW_CAM_MAX_BEHINDNESS_LAG = 3.0f;
+
+//---------------------------------------------------------------------------------------------------------------------
+// in degrees: This is the size of the pie slice behind the subject matter within which the camera is free to move
+//---------------------------------------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_BEHINDNESS_ANGLE = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_BEHINDNESS_ANGLE = 10.0f;
+const F32 FOLLOW_CAM_MAX_BEHINDNESS_ANGLE = 180.0f;
+const F32 FOLLOW_CAM_BEHINDNESS_EPSILON = 1.0f;
+
+//------------------------------------
+// Constructor
+//------------------------------------
+LLFollowCamParams::LLFollowCamParams()
+{
+ mMaxCameraDistantFromSubject = DEFAULT_MAX_DISTANCE_FROM_SUBJECT;
+ mPositionLocked = false;
+ mFocusLocked = false;
+ mUsePosition = false;
+ mUseFocus = false;
+
+ //------------------------------------------------------
+ // setting the attributes to their defaults
+ //------------------------------------------------------
+ setPositionLag ( FOLLOW_CAM_DEFAULT_POSITION_LAG );
+ setFocusLag ( FOLLOW_CAM_DEFAULT_FOCUS_LAG );
+ setPositionThreshold( FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD );
+ setFocusThreshold ( FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD );
+ setBehindnessLag ( FOLLOW_CAM_DEFAULT_BEHINDNESS_LAG );
+ setDistance ( FOLLOW_CAM_DEFAULT_DISTANCE );
+ setPitch ( FOLLOW_CAM_DEFAULT_PITCH );
+ setFocusOffset ( FOLLOW_CAM_DEFAULT_FOCUS_OFFSET );
+ setBehindnessAngle ( FOLLOW_CAM_DEFAULT_BEHINDNESS_ANGLE );
+ setPositionThreshold( FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD );
+ setFocusThreshold ( FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD );
+
+}
+
+LLFollowCamParams::~LLFollowCamParams() { }
+
+//---------------------------------------------------------
+// buncho set methods
+//---------------------------------------------------------
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPositionLag( F32 p )
+{
+ mPositionLag = llclamp(p, FOLLOW_CAM_MIN_POSITION_LAG, FOLLOW_CAM_MAX_POSITION_LAG);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusLag( F32 f )
+{
+ mFocusLag = llclamp(f, FOLLOW_CAM_MIN_FOCUS_LAG, FOLLOW_CAM_MAX_FOCUS_LAG);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPositionThreshold( F32 p )
+{
+ mPositionThreshold = llclamp(p, FOLLOW_CAM_MIN_POSITION_THRESHOLD, FOLLOW_CAM_MAX_POSITION_THRESHOLD);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusThreshold( F32 f )
+{
+ mFocusThreshold = llclamp(f, FOLLOW_CAM_MIN_FOCUS_THRESHOLD, FOLLOW_CAM_MAX_FOCUS_THRESHOLD);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPitch( F32 p )
+{
+ mPitch = llclamp(p, FOLLOW_CAM_MIN_PITCH, FOLLOW_CAM_MAX_PITCH);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setBehindnessLag( F32 b )
+{
+ mBehindnessLag = llclamp(b, FOLLOW_CAM_MIN_BEHINDNESS_LAG, FOLLOW_CAM_MAX_BEHINDNESS_LAG);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setBehindnessAngle( F32 b )
+{
+ mBehindnessMaxAngle = llclamp(b, FOLLOW_CAM_MIN_BEHINDNESS_ANGLE, FOLLOW_CAM_MAX_BEHINDNESS_ANGLE);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setDistance( F32 d )
+{
+ mDistance = llclamp(d, FOLLOW_CAM_MIN_DISTANCE, mMaxCameraDistantFromSubject);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPositionLocked( bool l )
+{
+ mPositionLocked = l;
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusLocked( bool l )
+{
+ mFocusLocked = l;
+
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusOffset( const LLVector3& v )
+{
+ mFocusOffset = v;
+ mFocusOffset.clamp(FOLLOW_CAM_MIN_FOCUS_OFFSET, FOLLOW_CAM_MAX_FOCUS_OFFSET);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPosition( const LLVector3& p )
+{
+ mUsePosition = true;
+ mPosition = p;
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocus( const LLVector3& f )
+{
+ mUseFocus = true;
+ mFocus = f;
+}
+
+//---------------------------------------------------------
+// buncho get methods
+//---------------------------------------------------------
+F32 LLFollowCamParams::getPositionLag () const { return mPositionLag; }
+F32 LLFollowCamParams::getFocusLag () const { return mFocusLag; }
+F32 LLFollowCamParams::getPositionThreshold () const { return mPositionThreshold; }
+F32 LLFollowCamParams::getFocusThreshold () const { return mFocusThreshold; }
+F32 LLFollowCamParams::getDistance () const { return mDistance; }
+F32 LLFollowCamParams::getPitch () const { return mPitch; }
+LLVector3 LLFollowCamParams::getFocusOffset () const { return mFocusOffset; }
+F32 LLFollowCamParams::getBehindnessAngle () const { return mBehindnessMaxAngle; }
+F32 LLFollowCamParams::getBehindnessLag () const { return mBehindnessLag; }
+LLVector3 LLFollowCamParams::getPosition () const { return mPosition; }
+LLVector3 LLFollowCamParams::getFocus () const { return mFocus; }
+bool LLFollowCamParams::getPositionLocked () const { return mPositionLocked; }
+bool LLFollowCamParams::getFocusLocked () const { return mFocusLocked; }
+
+//------------------------------------
+// Constructor
+//------------------------------------
+LLFollowCam::LLFollowCam() : LLFollowCamParams()
+{
+ mUpVector = LLVector3::z_axis;
+ mSubjectPosition = LLVector3::zero;
+ mSubjectRotation = LLQuaternion::DEFAULT;
+
+ mZoomedToMinimumDistance = false;
+ mPitchSineAndCosineNeedToBeUpdated = true;
+
+ mSimulatedDistance = mDistance;
+}
+
+void LLFollowCam::copyParams(LLFollowCamParams& params)
+{
+ setPositionLag(params.getPositionLag());
+ setFocusLag(params.getFocusLag());
+ setFocusThreshold( params.getFocusThreshold());
+ setPositionThreshold(params.getPositionThreshold());
+ setPitch(params.getPitch());
+ setFocusOffset(params.getFocusOffset());
+ setBehindnessAngle(params.getBehindnessAngle());
+ setBehindnessLag(params.getBehindnessLag());
+
+ setPositionLocked(params.getPositionLocked());
+ setFocusLocked(params.getFocusLocked());
+
+ setDistance(params.getDistance());
+ if (params.getUsePosition())
+ {
+ setPosition(params.getPosition());
+ }
+ if (params.getUseFocus())
+ {
+ setFocus(params.getFocus());
+ }
+}
+
+//---------------------------------------------------------------------------------------------------------
+void LLFollowCam::update()
+{
+ //####################################################################################
+ // update Focus
+ //####################################################################################
+ LLVector3 offsetSubjectPosition = mSubjectPosition + (mFocusOffset * mSubjectRotation);
+
+ LLVector3 simulated_pos_agent = gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal);
+ LLVector3 vectorFromCameraToSubject = offsetSubjectPosition - simulated_pos_agent;
+ F32 distanceFromCameraToSubject = vectorFromCameraToSubject.magVec();
+
+ LLVector3 whereFocusWantsToBe = mFocus;
+ LLVector3 focus_pt_agent = gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal);
+ if ( mFocusLocked ) // if focus is locked, only relative focus needs to be updated
+ {
+ mRelativeFocus = (focus_pt_agent - mSubjectPosition) * ~mSubjectRotation;
+ }
+ else
+ {
+ LLVector3 focusOffset = offsetSubjectPosition - focus_pt_agent;
+ F32 focusOffsetDistance = focusOffset.magVec();
+
+ LLVector3 focusOffsetDirection = focusOffset / focusOffsetDistance;
+ whereFocusWantsToBe = focus_pt_agent +
+ (focusOffsetDirection * (focusOffsetDistance - mFocusThreshold));
+ if ( focusOffsetDistance > mFocusThreshold )
+ {
+ // this version normalizes focus threshold by distance
+ // so that the effect is not changed with distance
+ /*
+ F32 focusThresholdNormalizedByDistance = distanceFromCameraToSubject * mFocusThreshold;
+ if ( focusOffsetDistance > focusThresholdNormalizedByDistance )
+ {
+ LLVector3 focusOffsetDirection = focusOffset / focusOffsetDistance;
+ F32 force = focusOffsetDistance - focusThresholdNormalizedByDistance;
+ */
+
+ F32 focusLagLerp = LLCriticalDamp::getInterpolant( mFocusLag );
+ focus_pt_agent = lerp( focus_pt_agent, whereFocusWantsToBe, focusLagLerp );
+ mSimulatedFocusGlobal = gAgent.getPosGlobalFromAgent(focus_pt_agent);
+ }
+ mRelativeFocus = lerp(mRelativeFocus, (focus_pt_agent - mSubjectPosition) * ~mSubjectRotation, LLCriticalDamp::getInterpolant(0.05f));
+ }// if focus is not locked ---------------------------------------------
+
+
+ LLVector3 whereCameraPositionWantsToBe = gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal);
+ if ( mPositionLocked )
+ {
+ mRelativePos = (whereCameraPositionWantsToBe - mSubjectPosition) * ~mSubjectRotation;
+ }
+ else
+ {
+ //####################################################################################
+ // update Position
+ //####################################################################################
+ //-------------------------------------------------------------------------
+ // I determine the horizontal vector from the camera to the subject
+ //-------------------------------------------------------------------------
+ LLVector3 horizontalVectorFromCameraToSubject = vectorFromCameraToSubject;
+ horizontalVectorFromCameraToSubject.mV[VZ] = 0.0f;
+
+ //---------------------------------------------------------
+ // Now I determine the horizontal distance
+ //---------------------------------------------------------
+ F32 horizontalDistanceFromCameraToSubject = horizontalVectorFromCameraToSubject.magVec();
+
+ //---------------------------------------------------------
+ // Then I get the (normalized) horizontal direction...
+ //---------------------------------------------------------
+ LLVector3 horizontalDirectionFromCameraToSubject;
+ if ( horizontalDistanceFromCameraToSubject < DISTANCE_EPSILON )
+ {
+ // make sure we still have a normalized vector if distance is really small
+ // (this case is rare and fleeting)
+ horizontalDirectionFromCameraToSubject = LLVector3::z_axis;
+ }
+ else
+ {
+ // I'm not using the "normalize" method, because I can just divide by horizontalDistanceFromCameraToSubject
+ horizontalDirectionFromCameraToSubject = horizontalVectorFromCameraToSubject / horizontalDistanceFromCameraToSubject;
+ }
+
+ //------------------------------------------------------------------------------------------------------------
+ // Here is where I determine an offset relative to subject position in oder to set the ideal position.
+ //------------------------------------------------------------------------------------------------------------
+ if ( mPitchSineAndCosineNeedToBeUpdated )
+ {
+ calculatePitchSineAndCosine();
+ mPitchSineAndCosineNeedToBeUpdated = false;
+ }
+
+ LLVector3 positionOffsetFromSubject;
+ positionOffsetFromSubject.setVec
+ (
+ horizontalDirectionFromCameraToSubject.mV[ VX ] * mPitchCos,
+ horizontalDirectionFromCameraToSubject.mV[ VY ] * mPitchCos,
+ -mPitchSin
+ );
+
+ positionOffsetFromSubject *= mSimulatedDistance;
+
+ //----------------------------------------------------------------------
+ // Finally, ideal position is set by taking the subject position and
+ // extending the positionOffsetFromSubject from that
+ //----------------------------------------------------------------------
+ LLVector3 idealCameraPosition = offsetSubjectPosition - positionOffsetFromSubject;
+
+ //--------------------------------------------------------------------------------
+ // Now I prepare to move the current camera position towards its ideal position...
+ //--------------------------------------------------------------------------------
+ LLVector3 vectorFromPositionToIdealPosition = idealCameraPosition - simulated_pos_agent;
+ F32 distanceFromPositionToIdealPosition = vectorFromPositionToIdealPosition.magVec();
+
+ //put this inside of the block?
+ LLVector3 normalFromPositionToIdealPosition = vectorFromPositionToIdealPosition / distanceFromPositionToIdealPosition;
+
+ whereCameraPositionWantsToBe = simulated_pos_agent +
+ (normalFromPositionToIdealPosition * (distanceFromPositionToIdealPosition - mPositionThreshold));
+ //-------------------------------------------------------------------------------------------------
+ // The following method takes the target camera position and resets it so that it stays "behind" the subject,
+ // using behindness angle and behindness force as parameters affecting the exact behavior
+ //-------------------------------------------------------------------------------------------------
+ if ( distanceFromPositionToIdealPosition > mPositionThreshold )
+ {
+ F32 positionPullLerp = LLCriticalDamp::getInterpolant( mPositionLag );
+ simulated_pos_agent = lerp( simulated_pos_agent, whereCameraPositionWantsToBe, positionPullLerp );
+ }
+
+ //--------------------------------------------------------------------
+ // don't let the camera get farther than its official max distance
+ //--------------------------------------------------------------------
+ if ( distanceFromCameraToSubject > mMaxCameraDistantFromSubject )
+ {
+ LLVector3 directionFromCameraToSubject = vectorFromCameraToSubject / distanceFromCameraToSubject;
+ simulated_pos_agent = offsetSubjectPosition - directionFromCameraToSubject * mMaxCameraDistantFromSubject;
+ }
+
+ ////-------------------------------------------------------------------------------------------------
+ //// The following method takes mSimulatedPositionGlobal and resets it so that it stays "behind" the subject,
+ //// using behindness angle and behindness force as parameters affecting the exact behavior
+ ////-------------------------------------------------------------------------------------------------
+ updateBehindnessConstraint(gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal), simulated_pos_agent);
+ mSimulatedPositionGlobal = gAgent.getPosGlobalFromAgent(simulated_pos_agent);
+
+ mRelativePos = lerp(mRelativePos, (simulated_pos_agent - mSubjectPosition) * ~mSubjectRotation, LLCriticalDamp::getInterpolant(0.05f));
+ } // if position is not locked -----------------------------------------------------------
+
+
+ //####################################################################################
+ // update UpVector
+ //####################################################################################
+ // this just points upward for now, but I anticipate future effects requiring
+ // some rolling ("banking" effects for fun, swoopy vehicles, etc.)
+ mUpVector = LLVector3::z_axis;
+}
+
+
+
+//-------------------------------------------------------------------------------------
+BOOL LLFollowCam::updateBehindnessConstraint(LLVector3 focus, LLVector3& cam_position)
+{
+ BOOL constraint_active = FALSE;
+ // only apply this stuff if the behindness angle is something other than opened up all the way
+ if ( mBehindnessMaxAngle < FOLLOW_CAM_MAX_BEHINDNESS_ANGLE - FOLLOW_CAM_BEHINDNESS_EPSILON )
+ {
+ //--------------------------------------------------------------
+ // horizontalized vector from focus to camera
+ //--------------------------------------------------------------
+ LLVector3 horizontalVectorFromFocusToCamera;
+ horizontalVectorFromFocusToCamera.setVec(cam_position - focus);
+ horizontalVectorFromFocusToCamera.mV[ VZ ] = 0.0f;
+ F32 cameraZ = cam_position.mV[ VZ ];
+
+ //--------------------------------------------------------------
+ // distance of horizontalized vector
+ //--------------------------------------------------------------
+ F32 horizontalDistance = horizontalVectorFromFocusToCamera.magVec();
+
+ //--------------------------------------------------------------------------------------------------
+ // calculate horizontalized back vector of the subject and scale by horizontalDistance
+ //--------------------------------------------------------------------------------------------------
+ LLVector3 horizontalSubjectBack( -1.0f, 0.0f, 0.0f );
+ horizontalSubjectBack *= mSubjectRotation;
+ horizontalSubjectBack.mV[ VZ ] = 0.0f;
+ horizontalSubjectBack.normVec(); // because horizontalizing might make it shorter than 1
+ horizontalSubjectBack *= horizontalDistance;
+
+ //--------------------------------------------------------------------------------------------------
+ // find the angle (in degrees) between these vectors
+ //--------------------------------------------------------------------------------------------------
+ F32 cameraOffsetAngle = 0.f;
+ LLVector3 cameraOffsetRotationAxis;
+ LLQuaternion camera_offset_rotation;
+ camera_offset_rotation.shortestArc(horizontalSubjectBack, horizontalVectorFromFocusToCamera);
+ camera_offset_rotation.getAngleAxis(&cameraOffsetAngle, cameraOffsetRotationAxis);
+ cameraOffsetAngle *= RAD_TO_DEG;
+
+ if ( cameraOffsetAngle > mBehindnessMaxAngle )
+ {
+ F32 fraction = ((cameraOffsetAngle - mBehindnessMaxAngle) / cameraOffsetAngle) * LLCriticalDamp::getInterpolant(mBehindnessLag);
+ cam_position = focus + horizontalSubjectBack * (slerp(fraction, camera_offset_rotation, LLQuaternion::DEFAULT));
+ cam_position.mV[VZ] = cameraZ; // clamp z value back to what it was before we started messing with it
+ constraint_active = TRUE;
+ }
+ }
+ return constraint_active;
+}
+
+
+//---------------------------------------------------------
+void LLFollowCam::calculatePitchSineAndCosine()
+{
+ F32 radian = mPitch * DEG_TO_RAD;
+ mPitchCos = cos( radian );
+ mPitchSin = sin( radian );
+}
+
+//---------------------------------------------------------
+void LLFollowCam::setSubjectPositionAndRotation( const LLVector3 p, const LLQuaternion r )
+{
+ mSubjectPosition = p;
+ mSubjectRotation = r;
+}
+
+
+//---------------------------------------------------------
+void LLFollowCam::zoom( S32 z )
+{
+ F32 zoomAmount = z * mSimulatedDistance * FOLLOW_CAM_ZOOM_FACTOR;
+
+ if (( zoomAmount < FOLLOW_CAM_MIN_ZOOM_AMOUNT )
+ && ( zoomAmount > -FOLLOW_CAM_MIN_ZOOM_AMOUNT ))
+ {
+ if ( zoomAmount < 0.0f )
+ {
+ zoomAmount = -FOLLOW_CAM_MIN_ZOOM_AMOUNT;
+ }
+ else
+ {
+ zoomAmount = FOLLOW_CAM_MIN_ZOOM_AMOUNT;
+ }
+ }
+
+ mSimulatedDistance += zoomAmount;
+
+ mZoomedToMinimumDistance = false;
+ if ( mSimulatedDistance < FOLLOW_CAM_MIN_DISTANCE )
+ {
+ mSimulatedDistance = FOLLOW_CAM_MIN_DISTANCE;
+
+ // if zoomAmount is negative (i.e., getting closer), then
+ // this signifies having hit the minimum:
+ if ( zoomAmount < 0.0f )
+ {
+ mZoomedToMinimumDistance = true;
+ }
+ }
+ else if ( mSimulatedDistance > mMaxCameraDistantFromSubject )
+ {
+ mSimulatedDistance = mMaxCameraDistantFromSubject;
+ }
+}
+
+
+//---------------------------------------------------------
+bool LLFollowCam::isZoomedToMinimumDistance()
+{
+ return mZoomedToMinimumDistance;
+}
+
+
+//---------------------------------------------------------
+void LLFollowCam::reset( const LLVector3 p, const LLVector3 f , const LLVector3 u )
+{
+ setPosition(p);
+ setFocus(f);
+ mUpVector = u;
+}
+
+//---------------------------------------------------------
+void LLFollowCam::setMaxCameraDistantFromSubject( F32 m )
+{
+ mMaxCameraDistantFromSubject = m;
+}
+
+
+void LLFollowCam::setPitch( F32 p )
+{
+ LLFollowCamParams::setPitch(p);
+ mPitchSineAndCosineNeedToBeUpdated = true; // important
+}
+
+void LLFollowCam::setDistance( F32 d )
+{
+ if (d != mDistance)
+ {
+ LLFollowCamParams::setDistance(d);
+ mSimulatedDistance = d;
+ mZoomedToMinimumDistance = false;
+ }
+}
+
+void LLFollowCam::setPosition( const LLVector3& p )
+{
+ if (p != mPosition)
+ {
+ LLFollowCamParams::setPosition(p);
+ mSimulatedPositionGlobal = gAgent.getPosGlobalFromAgent(mPosition);
+ if (mPositionLocked)
+ {
+ mRelativePos = (mPosition - mSubjectPosition) * ~mSubjectRotation;
+ }
+ }
+}
+
+void LLFollowCam::setFocus( const LLVector3& f )
+{
+ if (f != mFocus)
+ {
+ LLFollowCamParams::setFocus(f);
+ mSimulatedFocusGlobal = gAgent.getPosGlobalFromAgent(f);
+ if (mFocusLocked)
+ {
+ mRelativeFocus = (mFocus - mSubjectPosition) * ~mSubjectRotation;
+ }
+ }
+}
+
+void LLFollowCam::setPositionLocked( bool locked )
+{
+ LLFollowCamParams::setPositionLocked(locked);
+ if (locked)
+ {
+ // propagate set position to relative position
+ mRelativePos = (gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal) - mSubjectPosition) * ~mSubjectRotation;
+ }
+}
+
+void LLFollowCam::setFocusLocked( bool locked )
+{
+ LLFollowCamParams::setFocusLocked(locked);
+ if (locked)
+ {
+ // propagate set position to relative position
+ mRelativeFocus = (gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal) - mSubjectPosition) * ~mSubjectRotation;
+ }
+}
+
+
+LLVector3 LLFollowCam::getSimulatedPosition() const
+{
+ // return simulated position
+ return mSubjectPosition + (mRelativePos * mSubjectRotation);
+}
+
+LLVector3 LLFollowCam::getSimulatedFocus() const
+{
+ // return simulated focus point
+ return mSubjectPosition + (mRelativeFocus * mSubjectRotation);
+}
+
+LLVector3 LLFollowCam::getUpVector()
+{
+ return mUpVector;
+}
+
+
+//------------------------------------
+// Destructor
+//------------------------------------
+LLFollowCam::~LLFollowCam()
+{
+}
+
+
+//-------------------------------------------------------
+// LLFollowCamMgr
+//-------------------------------------------------------
+//static
+void LLFollowCamMgr::cleanupClass()
+{
+ for (param_map_t::iterator iter = sParamMap.begin(); iter != sParamMap.end(); ++iter)
+ {
+ LLFollowCamParams* params = iter->second;
+ delete params;
+ }
+ sParamMap.clear();
+}
+
+//static
+void LLFollowCamMgr::setPositionLag( const LLUUID& source, F32 lag)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPositionLag(lag);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusLag( const LLUUID& source, F32 lag)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusLag(lag);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusThreshold( const LLUUID& source, F32 threshold)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusThreshold(threshold);
+ }
+
+}
+
+//static
+void LLFollowCamMgr::setPositionThreshold( const LLUUID& source, F32 threshold)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPositionThreshold(threshold);
+ }
+}
+
+//static
+void LLFollowCamMgr::setDistance( const LLUUID& source, F32 distance)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setDistance(distance);
+ }
+}
+
+//static
+void LLFollowCamMgr::setPitch( const LLUUID& source, F32 pitch)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPitch(pitch);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusOffset( const LLUUID& source, const LLVector3& offset)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusOffset(offset);
+ }
+}
+
+//static
+void LLFollowCamMgr::setBehindnessAngle( const LLUUID& source, F32 angle)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setBehindnessAngle(angle);
+ }
+}
+
+//static
+void LLFollowCamMgr::setBehindnessLag( const LLUUID& source, F32 force)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setBehindnessLag(force);
+ }
+}
+
+//static
+void LLFollowCamMgr::setPosition( const LLUUID& source, const LLVector3 position)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPosition(position);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocus( const LLUUID& source, const LLVector3 focus)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocus(focus);
+ }
+}
+
+//static
+void LLFollowCamMgr::setPositionLocked( const LLUUID& source, bool locked)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPositionLocked(locked);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusLocked( const LLUUID& source, bool locked )
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusLocked(locked);
+ }
+}
+
+//static
+LLFollowCamParams* LLFollowCamMgr::getParamsForID(const LLUUID& source)
+{
+ LLFollowCamParams* params = NULL;
+
+ param_map_t::iterator found_it = sParamMap.find(source);
+ if (found_it == sParamMap.end()) // didn't find it?
+ {
+ params = new LLFollowCamParams();
+ sParamMap[source] = params;
+ }
+ else
+ {
+ params = found_it->second;
+ }
+
+ return params;
+}
+
+//static
+LLFollowCamParams* LLFollowCamMgr::getActiveFollowCamParams()
+{
+ if (sParamStack.empty())
+ {
+ return NULL;
+ }
+
+ return sParamStack.back();
+}
+
+//static
+void LLFollowCamMgr::setCameraActive( const LLUUID& source, bool active )
+{
+ LLFollowCamParams* params = getParamsForID(source);
+ param_stack_t::iterator found_it = std::find(sParamStack.begin(), sParamStack.end(), params);
+ if (found_it != sParamStack.end())
+ {
+ sParamStack.erase(found_it);
+ }
+ // put on top of stack
+ if(active)
+ {
+ sParamStack.push_back(params);
+ }
+}
+
+//static
+void LLFollowCamMgr::removeFollowCamParams(const LLUUID& source)
+{
+ setCameraActive(source, FALSE);
+ LLFollowCamParams* params = getParamsForID(source);
+ sParamMap.erase(source);
+ delete params;
+}
+
+//static
+bool LLFollowCamMgr::isScriptedCameraSource(const LLUUID& source)
+{
+ param_map_t::iterator found_it = sParamMap.find(source);
+ return (found_it != sParamMap.end());
+}
+
+//static
+void LLFollowCamMgr::dump()
+{
+ S32 param_count = 0;
+ llinfos << "Scripted camera active stack" << llendl;
+ for (param_stack_t::iterator param_it = sParamStack.begin();
+ param_it != sParamStack.end();
+ ++param_it)
+ {
+ llinfos << param_count++ <<
+ " rot_limit: " << (*param_it)->getBehindnessAngle() <<
+ " rot_lag: " << (*param_it)->getBehindnessLag() <<
+ " distance: " << (*param_it)->getDistance() <<
+ " focus: " << (*param_it)->getFocus() <<
+ " foc_lag: " << (*param_it)->getFocusLag() <<
+ " foc_lock: " << ((*param_it)->getFocusLocked() ? "Y" : "N") <<
+ " foc_offset: " << (*param_it)->getFocusOffset() <<
+ " foc_thresh: " << (*param_it)->getFocusThreshold() <<
+ " pitch: " << (*param_it)->getPitch() <<
+ " pos: " << (*param_it)->getPosition() <<
+ " pos_lag: " << (*param_it)->getPositionLag() <<
+ " pos_lock: " << ((*param_it)->getPositionLocked() ? "Y" : "N") <<
+ " pos_thresh: " << (*param_it)->getPositionThreshold() << llendl;
+ }
+}
+
diff --git a/indra/newview/llfollowcam.h b/indra/newview/llfollowcam.h
new file mode 100644
index 0000000000..22450d2792
--- /dev/null
+++ b/indra/newview/llfollowcam.h
@@ -0,0 +1,216 @@
+/**
+ * @file llfollowcam.h
+ * @author Jeffrey Ventrella
+ * @brief LLFollowCam class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//--------------------------------------------------------------------
+// FollowCam
+//
+// The FollowCam controls three dynamic variables which determine
+// a camera orientation and position for a "loose" third-person view
+// (orientation being derived from a combination of focus and up
+// vector). It is good for fast-moving vehicles that change
+// acceleration a lot, but it can also be general-purpose, like for
+// avatar navigation. It has a handful of parameters allowing it to
+// be tweaked to assume different styles of tracking objects.
+//
+//--------------------------------------------------------------------
+#ifndef LL_FOLLOWCAM_H
+#define LL_FOLLOWCAM_H
+
+#include "llcoordframe.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "lltimer.h"
+#include "llquaternion.h"
+#include "llcriticaldamp.h"
+#include <map>
+#include <vector>
+
+class LLFollowCamParams
+{
+public:
+ LLFollowCamParams();
+ virtual ~LLFollowCamParams();
+
+ //--------------------------------------
+ // setty setty set set
+ //--------------------------------------
+ virtual void setPositionLag ( F32 );
+ virtual void setFocusLag ( F32 );
+ virtual void setFocusThreshold ( F32 );
+ virtual void setPositionThreshold ( F32 );
+ virtual void setDistance ( F32 );
+ virtual void setPitch ( F32 );
+ virtual void setFocusOffset ( const LLVector3& );
+ virtual void setBehindnessAngle ( F32 );
+ virtual void setBehindnessLag ( F32 );
+ virtual void setPosition ( const LLVector3& );
+ virtual void setFocus ( const LLVector3& );
+ virtual void setPositionLocked ( bool );
+ virtual void setFocusLocked ( bool );
+
+
+ //--------------------------------------
+ // getty getty get get
+ //--------------------------------------
+ virtual F32 getPositionLag() const;
+ virtual F32 getFocusLag() const;
+ virtual F32 getPositionThreshold() const;
+ virtual F32 getFocusThreshold() const;
+ virtual F32 getDistance() const;
+ virtual F32 getPitch() const;
+ virtual LLVector3 getFocusOffset() const;
+ virtual F32 getBehindnessAngle() const;
+ virtual F32 getBehindnessLag() const;
+ virtual LLVector3 getPosition() const;
+ virtual LLVector3 getFocus() const;
+ virtual bool getFocusLocked() const;
+ virtual bool getPositionLocked() const;
+ virtual bool getUseFocus() const { return mUseFocus; }
+ virtual bool getUsePosition() const { return mUsePosition; }
+
+protected:
+ F32 mPositionLag;
+ F32 mFocusLag;
+ F32 mFocusThreshold;
+ F32 mPositionThreshold;
+ F32 mDistance;
+ F32 mPitch;
+ LLVector3 mFocusOffset;
+ F32 mBehindnessMaxAngle;
+ F32 mBehindnessLag;
+ F32 mMaxCameraDistantFromSubject;
+
+ bool mPositionLocked;
+ bool mFocusLocked;
+ bool mUsePosition; // specific camera point specified by script
+ bool mUseFocus; // specific focus point specified by script
+ LLVector3 mPosition; // where the camera is (in world-space)
+ LLVector3 mFocus; // what the camera is aimed at (in world-space)
+};
+
+class LLFollowCam : public LLFollowCamParams
+{
+public:
+ //--------------------
+ // Contructor
+ //--------------------
+ LLFollowCam();
+
+ //--------------------
+ // Destructor
+ //--------------------
+ virtual ~LLFollowCam();
+
+ //---------------------------------------------------------------------------------------
+ // The following methods must be called every time step. However, if you know for
+ // sure that your subject matter (what the camera is looking at) is not moving,
+ // then you can get away with not calling "update" But keep in mind that "update"
+ // may still be needed after the subject matter has stopped moving because the
+ // camera may still need to animate itself catching up to its ideal resting place.
+ //---------------------------------------------------------------------------------------
+ void setSubjectPositionAndRotation ( const LLVector3 p, const LLQuaternion r );
+ void update();
+
+ // initialize from another instance of llfollowcamparams
+ void copyParams(LLFollowCamParams& params);
+
+ //-----------------------------------------------------------------------------------
+ // this is how to bang the followCam into a specific configuration. Keep in mind
+ // that it will immediately try to adjust these values according to its attributes.
+ //-----------------------------------------------------------------------------------
+ void reset( const LLVector3 position, const LLVector3 focus, const LLVector3 upVector );
+
+ void setMaxCameraDistantFromSubject ( F32 m ); // this should be determined by llAgent
+ bool isZoomedToMinimumDistance();
+ LLVector3 getUpVector();
+ void zoom( S32 );
+
+ // overrides for setters and getters
+ virtual void setPitch( F32 );
+ virtual void setDistance( F32 );
+ virtual void setPosition(const LLVector3& pos);
+ virtual void setFocus(const LLVector3& focus);
+ virtual void setPositionLocked ( bool );
+ virtual void setFocusLocked ( bool );
+
+ LLVector3 getSimulatedPosition() const;
+ LLVector3 getSimulatedFocus() const;
+
+ //------------------------------------------
+ // protected members of FollowCam
+ //------------------------------------------
+protected:
+ F32 mPositionLagTimeScale; // derived from mPositionLag
+ F32 mFocusLagTimeScale; // derived from mFocusLag
+ F32 mPitchCos; // derived from mPitch
+ F32 mPitchSin; // derived from mPitch
+ LLGlobalVec mSimulatedPositionGlobal; // where the camera is (global coordinates), simulated
+ LLGlobalVec mSimulatedFocusGlobal; // what the camera is aimed at (global coordinates), simulated
+ F32 mSimulatedDistance;
+
+ //---------------------
+ // dynamic variables
+ //---------------------
+ bool mZoomedToMinimumDistance;
+ LLFrameTimer mTimer;
+ LLVector3 mSubjectPosition; // this is the position of what I'm looking at
+ LLQuaternion mSubjectRotation; // this is the rotation of what I'm looking at
+ LLVector3 mUpVector; // the camera's up vector in world-space (determines roll)
+ LLVector3 mRelativeFocus;
+ LLVector3 mRelativePos;
+
+ bool mPitchSineAndCosineNeedToBeUpdated;
+
+ //------------------------------------------
+ // protected methods of FollowCam
+ //------------------------------------------
+protected:
+ void calculatePitchSineAndCosine();
+ BOOL updateBehindnessConstraint(LLVector3 focus, LLVector3& cam_position);
+
+};// end of FollowCam class
+
+
+class LLFollowCamMgr
+{
+public:
+ static void cleanupClass ( );
+
+ static void setPositionLag ( const LLUUID& source, F32 lag);
+ static void setFocusLag ( const LLUUID& source, F32 lag);
+ static void setFocusThreshold ( const LLUUID& source, F32 threshold);
+ static void setPositionThreshold ( const LLUUID& source, F32 threshold);
+ static void setDistance ( const LLUUID& source, F32 distance);
+ static void setPitch ( const LLUUID& source, F32 pitch);
+ static void setFocusOffset ( const LLUUID& source, const LLVector3& offset);
+ static void setBehindnessAngle ( const LLUUID& source, F32 angle);
+ static void setBehindnessLag ( const LLUUID& source, F32 lag);
+ static void setPosition ( const LLUUID& source, const LLVector3 position);
+ static void setFocus ( const LLUUID& source, const LLVector3 focus);
+ static void setPositionLocked ( const LLUUID& source, bool locked);
+ static void setFocusLocked ( const LLUUID& source, bool locked );
+
+ static void setCameraActive ( const LLUUID& source, bool active );
+
+ static LLFollowCamParams* getActiveFollowCamParams();
+ static LLFollowCamParams* getParamsForID(const LLUUID& source);
+ static void removeFollowCamParams(const LLUUID& source);
+ static bool isScriptedCameraSource(const LLUUID& source);
+ static void dump();
+
+protected:
+
+ typedef std::map<LLUUID, LLFollowCamParams*> param_map_t;
+ static param_map_t sParamMap;
+
+ typedef std::vector<LLFollowCamParams*> param_stack_t;
+ static param_stack_t sParamStack;
+};
+
+#endif //LL_FOLLOWCAM_H
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
new file mode 100644
index 0000000000..b9ae06963f
--- /dev/null
+++ b/indra/newview/llgesturemgr.cpp
@@ -0,0 +1,1067 @@
+/**
+ * @file llgesturemgr.cpp
+ * @brief Manager for playing gestures on the viewer
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llgesturemgr.h"
+
+// system
+#include <functional>
+#include <algorithm>
+#include <boost/tokenizer.hpp>
+
+// library
+#include "lldatapacker.h"
+#include "llinventory.h"
+#include "llmultigesture.h"
+#include "llstl.h"
+#include "llstring.h" // todo: remove
+#include "llvfile.h"
+#include "message.h"
+
+// newview
+#include "llagent.h"
+#include "llchatbar.h"
+#include "llinventorymodel.h"
+#include "llnotify.h"
+#include "llviewermessage.h"
+#include "llvoavatar.h"
+#include "llviewerstats.h"
+#include "viewer.h"
+
+LLGestureManager gGestureManager;
+
+// Longest time, in seconds, to wait for all animations to stop playing
+const F32 MAX_WAIT_ANIM_SECS = 30.f;
+
+
+// Lightweight constructor.
+// init() does the heavy lifting.
+LLGestureManager::LLGestureManager()
+: mValid(FALSE),
+ mPlaying(),
+ mActive(),
+ mLoadingCount(0)
+{ }
+
+
+// We own the data for gestures, so clean them up.
+LLGestureManager::~LLGestureManager()
+{
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+
+ delete gesture;
+ gesture = NULL;
+ }
+}
+
+
+void LLGestureManager::init()
+{
+ // TODO
+}
+
+
+// Use this version when you have the item_id but not the asset_id,
+// and you KNOW the inventory is loaded.
+void LLGestureManager::activateGesture(const LLUUID& item_id)
+{
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if (!item) return;
+
+ LLUUID asset_id = item->getAssetUUID();
+
+ mLoadingCount = 1;
+ mDeactivateSimilarNames.clear();
+
+ const BOOL inform_server = TRUE;
+ const BOOL deactivate_similar = TRUE;
+ activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar);
+}
+
+
+void LLGestureManager::activateGestures(LLViewerInventoryItem::item_array_t& items)
+{
+ // Load up the assets
+ S32 count = 0;
+ LLViewerInventoryItem::item_array_t::const_iterator it;
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+
+ if (isGestureActive(item->getUUID()))
+ {
+ continue;
+ }
+ else
+ { // Make gesture active and persistent through login sessions. -spatters 07-12-06
+ activateGesture(item->getUUID());
+ }
+
+ count++;
+ }
+
+ mLoadingCount = count;
+ mDeactivateSimilarNames.clear();
+
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+
+ if (isGestureActive(item->getUUID()))
+ {
+ continue;
+ }
+
+ // Don't inform server, we'll do that in bulk
+ const BOOL no_inform_server = FALSE;
+ const BOOL deactivate_similar = TRUE;
+ activateGestureWithAsset(item->getUUID(), item->getAssetUUID(),
+ no_inform_server,
+ deactivate_similar);
+ }
+
+ // Inform the database of this change
+ LLMessageSystem* msg = gMessageSystem;
+
+ BOOL start_message = TRUE;
+
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+
+ if (isGestureActive(item->getUUID()))
+ {
+ continue;
+ }
+
+ if (start_message)
+ {
+ msg->newMessage("ActivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+ start_message = FALSE;
+ }
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", item->getUUID());
+ msg->addUUID("AssetID", item->getAssetUUID());
+ msg->addU32("GestureFlags", 0x0);
+
+ if (msg->getCurrentSendTotal() > MTUBYTES)
+ {
+ gAgent.sendReliableMessage();
+ start_message = TRUE;
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+
+struct LLLoadInfo
+{
+ LLUUID mItemID;
+ BOOL mInformServer;
+ BOOL mDeactivateSimilar;
+};
+
+// If inform_server is true, will send a message upstream to update
+// the user_gesture_active table.
+void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id,
+ const LLUUID& asset_id,
+ BOOL inform_server,
+ BOOL deactivate_similar)
+{
+ // If gesture is already active, nothing to do.
+ if (isGestureActive(item_id))
+ {
+ llwarns << "Tried to loadGesture twice " << item_id << llendl;
+ return;
+ }
+
+// if (asset_id.isNull())
+// {
+// llwarns << "loadGesture() - gesture has no asset" << llendl;
+// return;
+// }
+
+ // For now, put NULL into the item map. We'll build a gesture
+ // class object when the asset data arrives.
+ mActive[item_id] = NULL;
+
+ // Copy the UUID
+ if (asset_id.notNull())
+ {
+ LLLoadInfo* info = new LLLoadInfo;
+ info->mItemID = item_id;
+ info->mInformServer = inform_server;
+ info->mDeactivateSimilar = deactivate_similar;
+
+ const BOOL high_priority = TRUE;
+ gAssetStorage->getAssetData(asset_id,
+ LLAssetType::AT_GESTURE,
+ onLoadComplete,
+ (void*)info,
+ high_priority);
+ }
+ else
+ {
+ notifyObservers();
+ }
+}
+
+
+void LLGestureManager::deactivateGesture(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end())
+ {
+ llwarns << "deactivateGesture for inactive gesture " << item_id << llendl;
+ return;
+ }
+
+ // mActive owns this gesture pointer, so clean up memory.
+ LLMultiGesture* gesture = (*it).second;
+
+ // Can be NULL gestures in the map
+ if (gesture)
+ {
+ stopGesture(gesture);
+
+ delete gesture;
+ gesture = NULL;
+ }
+
+ mActive.erase(it);
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+
+ // Inform the database of this change
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("DeactivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", item_id);
+ msg->addU32("GestureFlags", 0x0);
+
+ gAgent.sendReliableMessage();
+
+ notifyObservers();
+}
+
+
+void LLGestureManager::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id)
+{
+ std::vector<LLUUID> gest_item_ids;
+
+ // Deactivate all gestures that match
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); )
+ {
+ const LLUUID& item_id = (*it).first;
+ LLMultiGesture* gest = (*it).second;
+
+ // Don't deactivate the gesture we are looking for duplicates of
+ // (for replaceGesture)
+ if (!gest || item_id == in_item_id)
+ {
+ // legal, can have null pointers in list
+ ++it;
+ }
+ else if ((!gest->mTrigger.empty() && gest->mTrigger == in->mTrigger)
+ || (gest->mKey != KEY_NONE && gest->mKey == in->mKey && gest->mMask == in->mMask))
+ {
+ gest_item_ids.push_back(item_id);
+
+ stopGesture(gest);
+
+ delete gest;
+ gest = NULL;
+
+ mActive.erase(it++);
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ // Inform database of the change
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_message = TRUE;
+ std::vector<LLUUID>::const_iterator vit = gest_item_ids.begin();
+ while (vit != gest_item_ids.end())
+ {
+ if (start_message)
+ {
+ msg->newMessage("DeactivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+ start_message = FALSE;
+ }
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", *vit);
+ msg->addU32("GestureFlags", 0x0);
+
+ if (msg->getCurrentSendTotal() > MTUBYTES)
+ {
+ gAgent.sendReliableMessage();
+ start_message = TRUE;
+ }
+
+ ++vit;
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ // Add to the list of names for the user.
+ for (vit = gest_item_ids.begin(); vit != gest_item_ids.end(); ++vit)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*vit);
+ if (!item) continue;
+
+ mDeactivateSimilarNames.append(item->getName());
+ mDeactivateSimilarNames.append("\n");
+ }
+
+ notifyObservers();
+}
+
+
+BOOL LLGestureManager::isGestureActive(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ return (it != mActive.end());
+}
+
+
+BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end()) return FALSE;
+
+ LLMultiGesture* gesture = (*it).second;
+ if (!gesture) return FALSE;
+
+ return gesture->mPlaying;
+}
+
+void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end())
+ {
+ llwarns << "replaceGesture for inactive gesture " << item_id << llendl;
+ return;
+ }
+
+ LLMultiGesture* old_gesture = (*it).second;
+ stopGesture(old_gesture);
+
+ mActive.erase(item_id);
+
+ mActive[item_id] = new_gesture;
+
+ delete old_gesture;
+ old_gesture = NULL;
+
+ if (asset_id.notNull())
+ {
+ mLoadingCount = 1;
+ mDeactivateSimilarNames.clear();
+
+ LLLoadInfo* info = new LLLoadInfo;
+ info->mItemID = item_id;
+ info->mInformServer = TRUE;
+ info->mDeactivateSimilar = TRUE;
+
+ const BOOL high_priority = TRUE;
+ gAssetStorage->getAssetData(asset_id,
+ LLAssetType::AT_GESTURE,
+ onLoadComplete,
+ (void*)info,
+ high_priority);
+ }
+
+ notifyObservers();
+}
+
+
+void LLGestureManager::playGesture(LLMultiGesture* gesture)
+{
+ if (!gesture) return;
+
+ // Reset gesture to first step
+ gesture->mCurrentStep = 0;
+
+ // Add to list of playing
+ gesture->mPlaying = TRUE;
+ mPlaying.push_back(gesture);
+
+ // And get it going
+ stepGesture(gesture);
+
+ notifyObservers();
+}
+
+
+// Convenience function that looks up the item_id for you.
+void LLGestureManager::playGesture(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end()) return;
+
+ LLMultiGesture* gesture = (*it).second;
+ if (!gesture) return;
+
+ playGesture(gesture);
+}
+
+
+// Iterates through space delimited tokens in string, triggering any gestures found.
+// Generates a revised string that has the found tokens replaced by their replacement strings
+// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
+BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::string* revised_string)
+{
+ LLString tokenized = LLString(utf8str.c_str());
+
+ BOOL found_gestures = FALSE;
+ BOOL first_token = TRUE;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(tokenized, sep);
+ tokenizer::iterator token_iter;
+
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ const char* cur_token = token_iter->c_str();
+ LLMultiGesture* gesture = NULL;
+
+ // Only pay attention to the first gesture in the string.
+ if( !found_gestures )
+ {
+ LLString cur_token_lower = cur_token;
+ LLString::toLower(cur_token_lower);
+
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ gesture = (*it).second;
+
+ // Gesture asset data might not have arrived yet
+ if (!gesture) continue;
+
+ if (!stricmp(gesture->mTrigger.c_str(), cur_token_lower.c_str()))
+ {
+ playGesture(gesture);
+
+ if (!gesture->mReplaceText.empty())
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+
+ // Don't muck with the user's capitalization if we don't have to.
+ LLString output = gesture->mReplaceText.c_str();
+ LLString output_lower = output;
+ LLString::toLower(output_lower);
+ if( cur_token_lower == output_lower )
+ {
+ revised_string->append( cur_token );
+ }
+ else
+ {
+ revised_string->append( output );
+ }
+ }
+ found_gestures = TRUE;
+ break;
+ }
+ gesture = NULL;
+ }
+ }
+
+ if( !gesture )
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+ revised_string->append( cur_token );
+ }
+
+ first_token = FALSE;
+ }
+ return found_gestures;
+}
+
+
+BOOL LLGestureManager::triggerGesture(KEY key, MASK mask)
+{
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+
+ // asset data might not have arrived yet
+ if (!gesture) continue;
+
+ if (gesture->mKey == key
+ && gesture->mMask == mask)
+ {
+ playGesture(gesture);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+S32 LLGestureManager::getPlayingCount() const
+{
+ return mPlaying.size();
+}
+
+
+struct IsGesturePlaying : public std::unary_function<LLMultiGesture*, bool>
+{
+ bool operator()(const LLMultiGesture* gesture) const
+ {
+ return gesture->mPlaying ? true : false;
+ }
+};
+
+void LLGestureManager::update()
+{
+ S32 i;
+ for (i = 0; i < (S32)mPlaying.size(); ++i)
+ {
+ stepGesture(mPlaying[i]);
+ }
+
+ // Clear out gestures that are done, by moving all the
+ // ones that are still playing to the front.
+ std::vector<LLMultiGesture*>::iterator new_end;
+ new_end = std::partition(mPlaying.begin(),
+ mPlaying.end(),
+ IsGesturePlaying());
+
+ // Something finished playing
+ if (new_end != mPlaying.end())
+ {
+ // Delete the completed gestures that want deletion
+ std::vector<LLMultiGesture*>::iterator it;
+ for (it = new_end; it != mPlaying.end(); ++it)
+ {
+ LLMultiGesture* gesture = *it;
+
+ if (gesture->mDoneCallback)
+ {
+ gesture->mDoneCallback(gesture, gesture->mCallbackData);
+
+ // callback might have deleted gesture, can't
+ // rely on this pointer any more
+ gesture = NULL;
+ }
+ }
+
+ // And take done gestures out of the playing list
+ mPlaying.erase(new_end, mPlaying.end());
+
+ notifyObservers();
+ }
+}
+
+
+// Run all steps until you're either done or hit a wait.
+void LLGestureManager::stepGesture(LLMultiGesture* gesture)
+{
+ if (!gesture)
+ {
+ return;
+ }
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (!avatar) return;
+
+ // Of the ones that started playing, have any stopped?
+
+ std::set<LLUUID>::iterator gest_it;
+ for (gest_it = gesture->mPlayingAnimIDs.begin();
+ gest_it != gesture->mPlayingAnimIDs.end();
+ )
+ {
+ // look in signaled animations (simulator's view of what is
+ // currently playing.
+ LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it);
+ if (play_it != avatar->mSignaledAnimations.end())
+ {
+ ++gest_it;
+ }
+ else
+ {
+ // not found, so not currently playing or scheduled to play
+ // delete from the triggered set
+ gesture->mPlayingAnimIDs.erase(gest_it++);
+ }
+ }
+
+ // Of all the animations that we asked the sim to start for us,
+ // pick up the ones that have actually started.
+ for (gest_it = gesture->mRequestedAnimIDs.begin();
+ gest_it != gesture->mRequestedAnimIDs.end();
+ )
+ {
+ LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it);
+ if (play_it != avatar->mSignaledAnimations.end())
+ {
+ // Hooray, this animation has started playing!
+ // Copy into playing.
+ gesture->mPlayingAnimIDs.insert(*gest_it);
+ gesture->mRequestedAnimIDs.erase(gest_it++);
+ }
+ else
+ {
+ // nope, not playing yet
+ ++gest_it;
+ }
+ }
+
+ // Run the current steps
+ BOOL waiting = FALSE;
+ while (!waiting && gesture->mPlaying)
+ {
+ // Get the current step, if there is one.
+ // Otherwise enter the waiting at end state.
+ LLGestureStep* step = NULL;
+ if (gesture->mCurrentStep < (S32)gesture->mSteps.size())
+ {
+ step = gesture->mSteps[gesture->mCurrentStep];
+ llassert(step != NULL);
+ }
+ else
+ {
+ // step stays null, we're off the end
+ gesture->mWaitingAtEnd = TRUE;
+ }
+
+
+ // If we're waiting at the end, wait for all gestures to stop
+ // playing.
+ // TODO: Wait for all sounds to complete as well.
+ if (gesture->mWaitingAtEnd)
+ {
+ // Neither do we have any pending requests, nor are they
+ // still playing.
+ if ((gesture->mRequestedAnimIDs.empty()
+ && gesture->mPlayingAnimIDs.empty()))
+ {
+ // all animations are done playing
+ gesture->mWaitingAtEnd = FALSE;
+ gesture->mPlaying = FALSE;
+ }
+ else
+ {
+ waiting = TRUE;
+ }
+ continue;
+ }
+
+ // If we're waiting on our animations to stop, poll for
+ // completion.
+ if (gesture->mWaitingAnimations)
+ {
+ // Neither do we have any pending requests, nor are they
+ // still playing.
+ if ((gesture->mRequestedAnimIDs.empty()
+ && gesture->mPlayingAnimIDs.empty()))
+ {
+ // all animations are done playing
+ gesture->mWaitingAnimations = FALSE;
+ gesture->mCurrentStep++;
+ }
+ else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_ANIM_SECS)
+ {
+ // we've waited too long for an animation
+ llinfos << "Waited too long for animations to stop, continuing gesture."
+ << llendl;
+ gesture->mWaitingAnimations = FALSE;
+ gesture->mCurrentStep++;
+ }
+ else
+ {
+ waiting = TRUE;
+ }
+ continue;
+ }
+
+ // If we're waiting a fixed amount of time, check for timer
+ // expiration.
+ if (gesture->mWaitingTimer)
+ {
+ // We're waiting for a certain amount of time to pass
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+
+ F32 elapsed = gesture->mWaitTimer.getElapsedTimeF32();
+ if (elapsed > wait_step->mWaitSeconds)
+ {
+ // wait is done, continue execution
+ gesture->mWaitingTimer = FALSE;
+ gesture->mCurrentStep++;
+ }
+ else
+ {
+ // we're waiting, so execution is done for now
+ waiting = TRUE;
+ }
+ continue;
+ }
+
+ // Not waiting, do normal execution
+ runStep(gesture, step);
+ }
+}
+
+
+void LLGestureManager::runStep(LLMultiGesture* gesture, LLGestureStep* step)
+{
+ switch(step->getType())
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ if (anim_step->mAnimAssetID.isNull())
+ {
+ gesture->mCurrentStep++;
+ }
+
+ if (anim_step->mFlags & ANIM_FLAG_STOP)
+ {
+ gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_STOP);
+ // remove it from our request set in case we just requested it
+ std::set<LLUUID>::iterator set_it = gesture->mRequestedAnimIDs.find(anim_step->mAnimAssetID);
+ if (set_it != gesture->mRequestedAnimIDs.end())
+ {
+ gesture->mRequestedAnimIDs.erase(set_it);
+ }
+ }
+ else
+ {
+ gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_START);
+ // Indicate that we've requested this animation to play as
+ // part of this gesture (but it won't start playing for at
+ // least one round-trip to simulator).
+ gesture->mRequestedAnimIDs.insert(anim_step->mAnimAssetID);
+ }
+ gesture->mCurrentStep++;
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ const LLUUID& sound_id = sound_step->mSoundAssetID;
+ const F32 volume = 1.f;
+ send_sound_trigger(sound_id, volume);
+ gesture->mCurrentStep++;
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ std::string chat_text = chat_step->mChatText;
+ // Don't animate the nodding, as this might not blend with
+ // other playing animations.
+ const BOOL animate = FALSE;
+
+ gChatBar->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate);
+ gesture->mCurrentStep++;
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ if (wait_step->mFlags & WAIT_FLAG_TIME)
+ {
+ gesture->mWaitingTimer = TRUE;
+ gesture->mWaitTimer.reset();
+ }
+ else if (wait_step->mFlags & WAIT_FLAG_ALL_ANIM)
+ {
+ gesture->mWaitingAnimations = TRUE;
+ // Use the wait timer as a deadlock breaker for animation
+ // waits.
+ gesture->mWaitTimer.reset();
+ }
+ else
+ {
+ gesture->mCurrentStep++;
+ }
+ // Don't increment instruction pointer until wait is complete.
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+
+// static
+void LLGestureManager::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLLoadInfo* info = (LLLoadInfo*)user_data;
+
+ LLUUID item_id = info->mItemID;
+ BOOL inform_server = info->mInformServer;
+ BOOL deactivate_similar = info->mDeactivateSimilar;
+
+ delete info;
+ info = NULL;
+
+ gGestureManager.mLoadingCount--;
+
+ if (0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ char* buffer = new char[size+1];
+ file.read((U8*)buffer, size);
+ // ensure there's a trailing NULL so strlen will work.
+ buffer[size] = '\0';
+
+ LLMultiGesture* gesture = new LLMultiGesture();
+
+ LLDataPackerAsciiBuffer dp(buffer, size+1);
+ BOOL ok = gesture->deserialize(dp);
+
+ if (ok)
+ {
+ if (deactivate_similar)
+ {
+ gGestureManager.deactivateSimilarGestures(gesture, item_id);
+
+ // Display deactivation message if this was the last of the bunch.
+ if (gGestureManager.mLoadingCount == 0
+ && gGestureManager.mDeactivateSimilarNames.length() > 0)
+ {
+ // we're done with this set of deactivations
+ LLString::format_map_t args;
+ args["[NAMES]"] = gGestureManager.mDeactivateSimilarNames;
+ LLNotifyBox::showXml("DeactivatedGesturesTrigger", args);
+ }
+ }
+
+ // Everything has been successful. Add to the active list.
+ gGestureManager.mActive[item_id] = gesture;
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ if (inform_server)
+ {
+ // Inform the database of this change
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ActivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", item_id);
+ msg->addUUID("AssetID", asset_uuid);
+ msg->addU32("GestureFlags", 0x0);
+
+ gAgent.sendReliableMessage();
+ }
+
+ gGestureManager.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Unable to load gesture" << llendl;
+
+ gGestureManager.mActive.erase(item_id);
+
+ delete gesture;
+ gesture = NULL;
+ }
+
+ delete [] buffer;
+ buffer = NULL;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("GestureMissing");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadGesture");
+ }
+
+ llwarns << "Problem loading gesture: " << status << llendl;
+
+ gGestureManager.mActive.erase(item_id);
+ }
+}
+
+
+void LLGestureManager::stopGesture(LLMultiGesture* gesture)
+{
+ if (!gesture) return;
+
+ // Stop any animations that this gesture is currently playing
+ std::set<LLUUID>::const_iterator set_it;
+ for (set_it = gesture->mRequestedAnimIDs.begin(); set_it != gesture->mRequestedAnimIDs.end(); ++set_it)
+ {
+ const LLUUID& anim_id = *set_it;
+ gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
+ }
+ for (set_it = gesture->mPlayingAnimIDs.begin(); set_it != gesture->mPlayingAnimIDs.end(); ++set_it)
+ {
+ const LLUUID& anim_id = *set_it;
+ gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
+ }
+
+ std::vector<LLMultiGesture*>::iterator it;
+ it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
+ while (it != mPlaying.end())
+ {
+ mPlaying.erase(it);
+ it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
+ }
+
+ gesture->reset();
+
+ if (gesture->mDoneCallback)
+ {
+ gesture->mDoneCallback(gesture, gesture->mCallbackData);
+
+ // callback might have deleted gesture, can't
+ // rely on this pointer any more
+ gesture = NULL;
+ }
+
+ notifyObservers();
+}
+
+
+void LLGestureManager::stopGesture(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end()) return;
+
+ LLMultiGesture* gesture = (*it).second;
+ if (!gesture) return;
+
+ stopGesture(gesture);
+}
+
+
+void LLGestureManager::addObserver(LLGestureManagerObserver* observer)
+{
+ mObservers.push_back(observer);
+}
+
+void LLGestureManager::removeObserver(LLGestureManagerObserver* observer)
+{
+ std::vector<LLGestureManagerObserver*>::iterator it;
+ it = std::find(mObservers.begin(), mObservers.end(), observer);
+ if (it != mObservers.end())
+ {
+ mObservers.erase(it);
+ }
+}
+
+// Call this method when it's time to update everyone on a new state.
+// Copy the list because an observer could respond by removing itself
+// from the list.
+void LLGestureManager::notifyObservers()
+{
+ lldebugs << "LLGestureManager::notifyObservers" << llendl;
+
+ std::vector<LLGestureManagerObserver*> observers = mObservers;
+
+ std::vector<LLGestureManagerObserver*>::iterator it;
+ for (it = observers.begin(); it != observers.end(); ++it)
+ {
+ LLGestureManagerObserver* observer = *it;
+ observer->changed();
+ }
+}
+
+BOOL LLGestureManager::matchPrefix(const std::string& in_str, std::string* out_str)
+{
+ S32 in_len = in_str.length();
+
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+ if (gesture)
+ {
+ const std::string& trigger = gesture->getTrigger();
+
+ if (in_len > (S32)trigger.length())
+ {
+ // too short, bail out
+ continue;
+ }
+
+ LLString trigger_trunc = trigger;
+ LLString::truncate(trigger_trunc, in_len);
+ if (!LLString::compareInsensitive(in_str.c_str(), trigger_trunc.c_str()))
+ {
+ *out_str = trigger;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+void LLGestureManager::getItemIDs(std::vector<LLUUID>* ids)
+{
+ item_map_t::const_iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ ids->push_back(it->first);
+ }
+}
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
new file mode 100644
index 0000000000..b79dc0687f
--- /dev/null
+++ b/indra/newview/llgesturemgr.h
@@ -0,0 +1,138 @@
+/**
+ * @file llgesturemgr.h
+ * @brief Manager for playing gestures on the viewer
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLGESTUREMGR_H
+#define LL_LLGESTUREMGR_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "llassetstorage.h" // LLAssetType
+#include "llviewerinventory.h"
+
+class LLMultiGesture;
+class LLGestureStep;
+class LLUUID;
+class LLVFS;
+
+class LLGestureManagerObserver
+{
+public:
+ virtual ~LLGestureManagerObserver() { };
+ virtual void changed() = 0;
+};
+
+class LLGestureManager
+{
+public:
+ LLGestureManager();
+ ~LLGestureManager();
+
+ void init();
+
+ // Call once per frame to manage gestures
+ void update();
+
+ // Loads a gesture out of inventory into the in-memory active form
+ // Note that the inventory item must exist, so we can look up the
+ // asset id.
+ void activateGesture(const LLUUID& item_id);
+
+ // Activate a list of gestures
+ void activateGestures(LLViewerInventoryItem::item_array_t& items);
+
+ // If you change a gesture, you need to build a new multigesture
+ // and call this method.
+ void replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id);
+
+ // Load gesture into in-memory active form.
+ // Can be called even if the inventory item isn't loaded yet.
+ // inform_server TRUE will send message upstream to update database
+ // user_gesture_active table, which isn't necessary on login.
+ // deactivate_similar will cause other gestures with the same trigger phrase
+ // or keybinding to be deactivated.
+ void activateGestureWithAsset(const LLUUID& item_id, const LLUUID& asset_id, BOOL inform_server, BOOL deactivate_similar);
+
+ // Takes gesture out of active list and deletes it.
+ void deactivateGesture(const LLUUID& item_id);
+
+ // Deactivates all gestures that match either this trigger phrase,
+ // or this hot key.
+ void deactivateSimilarGestures(LLMultiGesture* gesture, const LLUUID& in_item_id);
+
+ BOOL isGestureActive(const LLUUID& item_id);
+
+ BOOL isGesturePlaying(const LLUUID& item_id);
+
+ // Force a gesture to be played, for example, if it is being
+ // previewed.
+ void playGesture(LLMultiGesture* gesture);
+ void playGesture(const LLUUID& item_id);
+
+ // Stop all requested or playing anims for this gesture
+ // Also remove from playing list
+ void stopGesture(LLMultiGesture* gesture);
+ void stopGesture(const LLUUID& item_id);
+
+ // Trigger the first gesture that matches this key.
+ // Returns TRUE if it finds a gesture bound to that key.
+ BOOL triggerGesture(KEY key, MASK mask);
+
+ // Trigger all gestures referenced as substrings in this string
+ BOOL triggerAndReviseString(const std::string &str, std::string *revised_string);
+
+ // Does some gesture have this key bound?
+ BOOL isKeyBound(KEY key, MASK mask);
+
+ S32 getPlayingCount() const;
+
+ void addObserver(LLGestureManagerObserver* observer);
+ void removeObserver(LLGestureManagerObserver* observer);
+ void notifyObservers();
+
+ BOOL matchPrefix(const std::string& in_str, std::string* out_str);
+
+ // Copy item ids into the vector
+ void getItemIDs(std::vector<LLUUID>* ids);
+
+protected:
+ // Handle the processing of a single gesture
+ void stepGesture(LLMultiGesture* gesture);
+
+ // Do a single step in a gesture
+ void runStep(LLMultiGesture* gesture, LLGestureStep* step);
+
+ // Used by loadGesture
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+public:
+ BOOL mValid;
+ std::vector<LLMultiGesture*> mPlaying;
+
+ // Maps inventory item_id to gesture
+ typedef std::map<LLUUID, LLMultiGesture*> item_map_t;
+
+ // Active gestures.
+ // NOTE: The gesture pointer CAN BE NULL. This means that
+ // there is a gesture with that item_id, but the asset data
+ // is still on its way down from the server.
+ item_map_t mActive;
+
+ S32 mLoadingCount;
+ std::string mDeactivateSimilarNames;
+
+ std::vector<LLGestureManagerObserver*> mObservers;
+};
+
+extern LLGestureManager gGestureManager;
+
+#endif
diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp
new file mode 100644
index 0000000000..b51bdd057d
--- /dev/null
+++ b/indra/newview/llglsandbox.cpp
@@ -0,0 +1,1222 @@
+/**
+ * @file llglsandbox.cpp
+ * @brief GL functionality access
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Contains ALL methods which directly access GL functionality
+ * except for core rendering engine functionality.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewercontrol.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llparcel.h"
+#include "llui.h"
+
+#include "lldrawable.h"
+#include "lltextureentry.h"
+#include "llviewercamera.h"
+
+#include "llvoavatar.h"
+#include "llagent.h"
+#include "lltoolmgr.h"
+#include "llselectmgr.h"
+#include "llhudmanager.h"
+#include "llsphere.h"
+#include "llviewerobjectlist.h"
+#include "lltoolselectrect.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llcompass.h"
+#include "llsurface.h"
+#include "llwind.h"
+#include "llworld.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "pipeline.h"
+
+BOOL LLAgent::setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ if(object && object->isAttachment())
+ {
+ LLViewerObject* parent = object;
+ while(parent)
+ {
+ if (parent == mAvatarObject)
+ {
+ // looking at an attachment on ourselves, which we don't want to do
+ object = mAvatarObject;
+ position.clearVec();
+ }
+ parent = (LLViewerObject*)parent->getParent();
+ }
+ }
+ if(!mLookAt || mLookAt->isDead())
+ {
+ mLookAt = (LLHUDEffectLookAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_LOOKAT);
+ mLookAt->setSourceObject(mAvatarObject);
+ }
+
+ return mLookAt->setLookAt(target_type, object, position);
+}
+
+BOOL LLAgent::setPointAt(EPointAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ // disallow pointing at attachments and avatars
+ if (object && (object->isAttachment() || object->isAvatar()))
+ {
+ return FALSE;
+ }
+
+ if(!mPointAt || mPointAt->isDead())
+ {
+ mPointAt = (LLHUDEffectPointAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINTAT);
+ mPointAt->setSourceObject(mAvatarObject);
+ }
+
+ return mPointAt->setPointAt(target_type, object, position);
+}
+
+ELookAtType LLAgent::getLookAtType()
+{
+ if (mLookAt)
+ {
+ return mLookAt->getLookAtType();
+ }
+
+ return LOOKAT_TARGET_NONE;
+}
+
+EPointAtType LLAgent::getPointAtType()
+{
+ if (mPointAt)
+ {
+ return mPointAt->getPointAtType();
+ }
+
+ return POINTAT_TARGET_NONE;
+}
+
+// Draw a representation of current autopilot target
+void LLAgent::renderAutoPilotTarget()
+{
+ if (mAutoPilot)
+ {
+ F32 height_meters;
+ LLVector3d target_global;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ // not textured
+ LLGLSNoTexture no_texture;
+
+ // lovely green
+ glColor4f(0.f, 1.f, 1.f, 1.f);
+
+ target_global = mAutoPilotTargetGlobal;
+
+ glTranslatef((F32)(target_global.mdV[VX]), (F32)(target_global.mdV[VY]), (F32)(target_global.mdV[VZ]));
+
+ /*
+ LLVector3 offset = target_global - mCamera.getOrigin();
+ F32 range = offset.magVec();
+ if (range > 0.001f)
+ {
+ // range != zero
+ F32 fraction_of_fov = height_pixels / (F32) mCamera.getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * mCamera.getView();
+ height_meters = range * tan(apparent_angle);
+ }
+ else
+ {
+ // range == zero
+ height_meters = 1.0f;
+ }
+ */
+ height_meters = 1.f;
+
+ glScalef(height_meters, height_meters, height_meters);
+
+ gSphere.render(1500.f);
+
+ glPopMatrix();
+ }
+}
+
+extern BOOL gDebugSelect;
+
+// Returns true if you got at least one object
+void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask)
+{
+ LLVector3 av_pos = gAgent.getPositionAgent();
+ F32 select_dist_squared = gSavedSettings.getF32("MaxSelectDistance");
+ select_dist_squared = select_dist_squared * select_dist_squared;
+
+ x = llround((F32)x * LLUI::sGLScaleFactor.mV[VX]);
+ y = llround((F32)y * LLUI::sGLScaleFactor.mV[VY]);
+
+ BOOL deselect = (mask == MASK_CONTROL);
+ S32 left = llmin(x, mDragStartX);
+ S32 right = llmax(x, mDragStartX);
+ S32 top = llmax(y, mDragStartY);
+ S32 bottom =llmin(y, mDragStartY);
+
+ F32 old_far_plane = gCamera->getFar();
+ F32 old_near_plane = gCamera->getNear();
+
+ S32 width = right - left + 1;
+ S32 height = top - bottom + 1;
+
+ BOOL grow_selection = FALSE;
+ BOOL shrink_selection = FALSE;
+
+ if (height > mDragLastHeight || width > mDragLastWidth)
+ {
+ grow_selection = TRUE;
+ }
+ if (height < mDragLastHeight || width < mDragLastWidth)
+ {
+ shrink_selection = TRUE;
+ }
+
+ if (!grow_selection && !shrink_selection)
+ {
+ // nothing to do
+ return;
+ }
+
+ mDragLastHeight = height;
+ mDragLastWidth = width;
+
+ S32 center_x = (left + right) / 2;
+ S32 center_y = (top + bottom) / 2;
+
+ // save drawing mode
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+
+ BOOL limit_select_distance = gSavedSettings.getBOOL("LimitSelectDistance");
+ if (limit_select_distance)
+ {
+ // ...select distance from control
+ LLVector3 relative_av_pos = av_pos;
+ relative_av_pos -= gCamera->getOrigin();
+
+ F32 new_far = relative_av_pos * gCamera->getAtAxis() + gSavedSettings.getF32("MaxSelectDistance");
+ F32 new_near = relative_av_pos * gCamera->getAtAxis() - gSavedSettings.getF32("MaxSelectDistance");
+
+ new_near = llmax(new_near, 0.1f);
+
+ gCamera->setFar(new_far);
+ gCamera->setNear(new_near);
+ }
+ gCamera->setPerspective(FOR_SELECTION,
+ center_x-width/2, center_y-height/2, width, height,
+ limit_select_distance);
+
+ if (shrink_selection)
+ {
+ for (LLViewerObject* vobjp = gSelectMgr->getFirstHighlightedObject();
+ vobjp;
+ vobjp = gSelectMgr->getNextHighlightedObject())
+ {
+ LLDrawable* drawable = vobjp->mDrawable;
+ if (!drawable || vobjp->getPCode() != LL_PCODE_VOLUME || vobjp->isAttachment())
+ {
+ continue;
+ }
+
+ S32 result = gCamera->sphereInFrustum(drawable->getWorldPosition(), drawable->getRadius());
+ switch (result)
+ {
+ case 0:
+ gSelectMgr->unhighlightObjectOnly(vobjp);
+ break;
+ case 1:
+ // check vertices
+ if (!gCamera->areVertsVisible(vobjp, LLSelectMgr::sRectSelectInclusive))
+ {
+ gSelectMgr->unhighlightObjectOnly(vobjp);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (grow_selection)
+ {
+ std::vector<LLDrawable*> potentials;
+
+ if (gPipeline.mObjectPartition)
+ {
+ gPipeline.mObjectPartition->cull(*gCamera, &potentials, TRUE);
+ }
+
+ for (std::vector<LLDrawable*>::iterator iter = potentials.begin();
+ iter != potentials.end(); iter++)
+ {
+ LLDrawable* drawable = *iter;
+ LLViewerObject* vobjp = drawable->getVObj();
+
+ if (!drawable || !vobjp ||
+ vobjp->getPCode() != LL_PCODE_VOLUME ||
+ vobjp->isAttachment() ||
+ (deselect && !vobjp->isSelected()))
+ {
+ continue;
+ }
+
+ if (limit_select_distance && dist_vec_squared(drawable->getWorldPosition(), av_pos) > select_dist_squared)
+ {
+ continue;
+ }
+
+ S32 result = gCamera->sphereInFrustum(drawable->getWorldPosition(), drawable->getRadius());
+ if (result)
+ {
+ switch (result)
+ {
+ case 1:
+ // check vertices
+ if (gCamera->areVertsVisible(vobjp, LLSelectMgr::sRectSelectInclusive))
+ {
+ gSelectMgr->highlightObjectOnly(vobjp);
+ }
+ break;
+ case 2:
+ gSelectMgr->highlightObjectOnly(vobjp);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // restore drawing mode
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ // restore camera
+ gCamera->setFar(old_far_plane);
+ gCamera->setNear(old_near_plane);
+ gViewerWindow->setup3DRender();
+}
+
+
+const F32 COMPASS_SIZE = 64;
+static const F32 COMPASS_RANGE = 0.33f;
+
+void LLCompass::draw()
+{
+// S32 left, top, right, bottom;
+
+ if (!getVisible()) return;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ S32 width = 32;
+ S32 height = 32;
+
+ LLGLSUIDefault gls_ui;
+
+ glTranslatef( COMPASS_SIZE/2.f, COMPASS_SIZE/2.f, 0.f);
+
+ if (mBkgndTexture)
+ {
+ mBkgndTexture->bind();
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ glBegin(GL_QUADS);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(-width, height);
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(-width, -height);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, -height);
+
+ glEnd();
+ }
+
+ // rotate subsequent draws to agent rotation
+ F32 rotation = atan2( gAgent.getFrameAgent().getAtAxis().mV[VX], gAgent.getFrameAgent().getAtAxis().mV[VY] );
+ glRotatef( - rotation * RAD_TO_DEG, 0.f, 0.f, -1.f);
+
+ if (mTexture)
+ {
+ mTexture->bind();
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ glBegin(GL_QUADS);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(-width, height);
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(-width, -height);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, -height);
+
+ glEnd();
+ }
+
+ glPopMatrix();
+
+}
+
+
+
+void LLHorizontalCompass::draw()
+{
+ if (!getVisible()) return;
+
+ LLGLSUIDefault gls_ui;
+
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+ S32 half_width = width / 2;
+
+ if( mTexture )
+ {
+ const LLVector3& at_axis = gCamera->getAtAxis();
+ F32 center = atan2( at_axis.mV[VX], at_axis.mV[VY] );
+
+ center += F_PI;
+ center = llclamp( center, 0.0f, F_TWO_PI ); // probably not necessary...
+ center /= F_TWO_PI;
+ F32 left = center - COMPASS_RANGE;
+ F32 right = center + COMPASS_RANGE;
+
+ mTexture->bind();
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f );
+ glBegin( GL_QUADS );
+
+ glTexCoord2f(right, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(left, 1.f);
+ glVertex2i(0, height);
+
+ glTexCoord2f(left, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(right, 0.f);
+ glVertex2i(width, 0);
+
+ glEnd();
+ }
+
+ // Draw the focus line
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( mFocusColor.mV );
+ gl_line_2d( half_width, 0, half_width, height );
+ }
+}
+
+
+const F32 WIND_ALTITUDE = 180.f;
+
+
+void LLWind::renderVectors()
+{
+ // Renders the wind as vectors (used for debug)
+ S32 i,j;
+ F32 x,y;
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+
+ LLGLSNoTexture gls_no_texture;
+ glPushMatrix();
+ LLVector3 origin_agent;
+ origin_agent = gAgent.getPosAgentFromGlobal(mOriginGlobal);
+ glTranslatef(origin_agent.mV[VX], origin_agent.mV[VY], WIND_ALTITUDE);
+ for (j = 0; j < mSize; j++)
+ {
+ for (i = 0; i < mSize; i++)
+ {
+ x = mCloudVelX[i + j*mSize] * WIND_SCALE_HACK;
+ y = mCloudVelY[i + j*mSize] * WIND_SCALE_HACK;
+ glPushMatrix();
+ glTranslatef((F32)i * region_width_meters/mSize, (F32)j * region_width_meters/mSize, 0.0);
+ glColor3f(0,1,0);
+ glBegin(GL_POINTS);
+ glVertex3f(0,0,0);
+ glEnd();
+ glColor3f(1,0,0);
+ glBegin(GL_LINES);
+ glVertex3f(x * 0.1f, y * 0.1f ,0.f);
+ glVertex3f(x, y, 0.f);
+ glEnd();
+ glPopMatrix();
+ }
+ }
+ glPopMatrix();
+}
+
+
+
+
+// Used by lltoolselectland
+void LLViewerParcelMgr::renderRect(const LLVector3d &west_south_bottom_global,
+ const LLVector3d &east_north_top_global )
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ LLVector3 west_south_bottom_agent = gAgent.getPosAgentFromGlobal(west_south_bottom_global);
+ F32 west = west_south_bottom_agent.mV[VX];
+ F32 south = west_south_bottom_agent.mV[VY];
+// F32 bottom = west_south_bottom_agent.mV[VZ] - 1.f;
+
+ LLVector3 east_north_top_agent = gAgent.getPosAgentFromGlobal(east_north_top_global);
+ F32 east = east_north_top_agent.mV[VX];
+ F32 north = east_north_top_agent.mV[VY];
+// F32 top = east_north_top_agent.mV[VZ] + 1.f;
+
+ // HACK: At edge of last region of world, we need to make sure the region
+ // resolves correctly so we can get a height value.
+ const F32 FUDGE = 0.01f;
+
+ F32 sw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, south, 0.f ) );
+ F32 se_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, south, 0.f ) );
+ F32 ne_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, north-FUDGE, 0.f ) );
+ F32 nw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, north-FUDGE, 0.f ) );
+
+ F32 sw_top = sw_bottom + PARCEL_POST_HEIGHT;
+ F32 se_top = se_bottom + PARCEL_POST_HEIGHT;
+ F32 ne_top = ne_bottom + PARCEL_POST_HEIGHT;
+ F32 nw_top = nw_bottom + PARCEL_POST_HEIGHT;
+
+ LLUI::setLineWidth(2.f);
+ glColor4f(1.f, 1.f, 0.f, 1.f);
+
+ // Cheat and give this the same pick-name as land
+ glBegin(GL_LINES);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+
+ glEnd();
+
+ glColor4f(1.f, 1.f, 0.f, 0.2f);
+ glBegin(GL_QUADS);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, north, ne_bottom);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, south, se_top);
+ glVertex3f(east, south, se_bottom);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, south, sw_bottom);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(west, north, nw_bottom);
+
+ glEnd();
+
+ LLUI::setLineWidth(1.f);
+}
+
+/*
+void LLViewerParcelMgr::renderParcel(LLParcel* parcel )
+{
+ S32 i;
+ S32 count = parcel->getBoxCount();
+ for (i = 0; i < count; i++)
+ {
+ const LLParcelBox& box = parcel->getBox(i);
+
+ F32 west = box.mMin.mV[VX];
+ F32 south = box.mMin.mV[VY];
+
+ F32 east = box.mMax.mV[VX];
+ F32 north = box.mMax.mV[VY];
+
+ // HACK: At edge of last region of world, we need to make sure the region
+ // resolves correctly so we can get a height value.
+ const F32 FUDGE = 0.01f;
+
+ F32 sw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, south, 0.f ) );
+ F32 se_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, south, 0.f ) );
+ F32 ne_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, north-FUDGE, 0.f ) );
+ F32 nw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, north-FUDGE, 0.f ) );
+
+ // little hack to make nearby lines not Z-fight
+ east -= 0.1f;
+ north -= 0.1f;
+
+ F32 sw_top = sw_bottom + POST_HEIGHT;
+ F32 se_top = se_bottom + POST_HEIGHT;
+ F32 ne_top = ne_bottom + POST_HEIGHT;
+ F32 nw_top = nw_bottom + POST_HEIGHT;
+
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ LLUI::setLineWidth(2.f);
+ glColor4f(0.f, 1.f, 1.f, 1.f);
+
+ // Cheat and give this the same pick-name as land
+ glBegin(GL_LINES);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+
+ glEnd();
+
+ glColor4f(0.f, 1.f, 1.f, 0.2f);
+ glBegin(GL_QUADS);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, north, ne_bottom);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, south, se_top);
+ glVertex3f(east, south, se_bottom);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, south, sw_bottom);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(west, north, nw_bottom);
+
+ glEnd();
+
+ LLUI::setLineWidth(1.f);
+ }
+}
+*/
+
+
+// north = a wall going north/south. Need that info to set up texture
+// coordinates correctly.
+void LLViewerParcelMgr::renderOneSegment(F32 x1, F32 y1, F32 x2, F32 y2, F32 height, U8 direction, LLViewerRegion* regionp)
+{
+ // HACK: At edge of last region of world, we need to make sure the region
+ // resolves correctly so we can get a height value.
+ const F32 BORDER = REGION_WIDTH_METERS - 0.1f;
+
+ F32 clamped_x1 = x1;
+ F32 clamped_y1 = y1;
+ F32 clamped_x2 = x2;
+ F32 clamped_y2 = y2;
+
+ if (clamped_x1 > BORDER) clamped_x1 = BORDER;
+ if (clamped_y1 > BORDER) clamped_y1 = BORDER;
+ if (clamped_x2 > BORDER) clamped_x2 = BORDER;
+ if (clamped_y2 > BORDER) clamped_y2 = BORDER;
+
+ F32 z;
+ F32 z1;
+ F32 z2;
+
+ z1 = regionp->getLand().resolveHeightRegion( LLVector3( clamped_x1, clamped_y1, 0.f ) );
+ z2 = regionp->getLand().resolveHeightRegion( LLVector3( clamped_x2, clamped_y2, 0.f ) );
+
+ // Convert x1 and x2 from region-local to agent coords.
+ LLVector3 origin = regionp->getOriginAgent();
+ x1 += origin.mV[VX];
+ x2 += origin.mV[VX];
+ y1 += origin.mV[VY];
+ y2 += origin.mV[VY];
+
+ if (height < 1.f)
+ {
+ z = z1+height;
+ glVertex3f(x1, y1, z);
+
+ glVertex3f(x1, y1, z1);
+
+ glVertex3f(x2, y2, z2);
+
+ z = z2+height;
+ glVertex3f(x2, y2, z);
+ }
+ else
+ {
+ F32 tex_coord1;
+ F32 tex_coord2;
+
+ if (WEST_MASK == direction)
+ {
+ tex_coord1 = y1;
+ tex_coord2 = y2;
+ }
+ else if (SOUTH_MASK == direction)
+ {
+ tex_coord1 = x1;
+ tex_coord2 = x2;
+ }
+ else if (EAST_MASK == direction)
+ {
+ tex_coord1 = y2;
+ tex_coord2 = y1;
+ }
+ else /* (NORTH_MASK == direction) */
+ {
+ tex_coord1 = x2;
+ tex_coord2 = x1;
+ }
+
+
+ glTexCoord2f(tex_coord1*0.5f+0.5f, z1*0.5f);
+ glVertex3f(x1, y1, z1);
+
+ glTexCoord2f(tex_coord2*0.5f+0.5f, z2*0.5f);
+ glVertex3f(x2, y2, z2);
+
+ // top edge stairsteps
+ z = llmax(z2+height, z1+height);
+ glTexCoord2f(tex_coord2*0.5f+0.5f, z*0.5f);
+ glVertex3f(x2, y2, z);
+
+ glTexCoord2f(tex_coord1*0.5f+0.5f, z*0.5f);
+ glVertex3f(x1, y1, z);
+ }
+}
+
+
+void LLViewerParcelMgr::renderHighlightSegments(const U8* segments, LLViewerRegion* regionp)
+{
+ S32 x, y;
+ F32 x1, y1; // start point
+ F32 x2, y2; // end point
+
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ glColor4f(1.f, 1.f, 0.f, 0.2f);
+
+ // Cheat and give this the same pick-name as land
+ glBegin(GL_QUADS);
+
+ const S32 STRIDE = (mParcelsPerEdge+1);
+ for (y = 0; y < STRIDE; y++)
+ {
+ for (x = 0; x < STRIDE; x++)
+ {
+ U8 segment_mask = segments[x + y*STRIDE];
+
+ if (segment_mask & SOUTH_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1 + PARCEL_GRID_STEP_METERS;
+ y2 = y1;
+
+ renderOneSegment(x1, y1, x2, y2, PARCEL_POST_HEIGHT, SOUTH_MASK, regionp);
+ }
+
+ if (segment_mask & WEST_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1;
+ y2 = y1 + PARCEL_GRID_STEP_METERS;
+
+ renderOneSegment(x1, y1, x2, y2, PARCEL_POST_HEIGHT, WEST_MASK, regionp);
+ }
+ }
+ }
+
+ glEnd();
+}
+
+
+void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLViewerRegion* regionp)
+{
+
+ S32 x, y;
+ F32 x1, y1; // start point
+ F32 x2, y2; // end point
+ F32 alpha = 0;
+ F32 dist = 0;
+ F32 dx, dy;
+ F32 collision_height;
+
+ const S32 STRIDE = (mParcelsPerEdge+1);
+
+ LLVector3 pos = gAgent.getPositionAgent();
+
+ F32 pos_x = pos.mV[VX];
+ F32 pos_y = pos.mV[VY];
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLDisable cull(GL_CULL_FACE);
+
+ if (mCollisionBanned == BA_BANNED)
+ {
+ collision_height = BAN_HEIGHT;
+ }
+ else
+ {
+ collision_height = PARCEL_HEIGHT;
+ }
+
+
+ if (use_pass && (mCollisionBanned == BA_NOT_ON_LIST))
+ {
+ LLViewerImage::bindTexture(mPassImage);
+ }
+ else
+ {
+ LLViewerImage::bindTexture(mBlockedImage);
+ }
+
+ glBegin(GL_QUADS);
+
+ for (y = 0; y < STRIDE; y++)
+ {
+ for (x = 0; x < STRIDE; x++)
+ {
+ U8 segment_mask = segments[x + y*STRIDE];
+ U8 direction;
+ const F32 MAX_ALPHA = 0.95f;
+ const S32 DIST_OFFSET = 5;
+ const S32 MIN_DIST_SQ = DIST_OFFSET*DIST_OFFSET;
+ const S32 MAX_DIST_SQ = 169;
+
+ if (segment_mask & SOUTH_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1 + PARCEL_GRID_STEP_METERS;
+ y2 = y1;
+
+ if (gRenderForSelect)
+ {
+ LLColor4U color((U8)(GL_NAME_PARCEL_WALL >> 16), (U8)(GL_NAME_PARCEL_WALL >> 8), (U8)GL_NAME_PARCEL_WALL);
+ glColor4ubv(color.mV);
+ }
+ else
+ {
+ dy = (pos_y - y1) + DIST_OFFSET;
+
+ if (pos_x < x1)
+ dx = pos_x - x1;
+ else if (pos_x > x2)
+ dx = pos_x - x2;
+ else
+ dx = 0;
+
+ dist = dx*dx+dy*dy;
+
+ if (dist < MIN_DIST_SQ)
+ alpha = MAX_ALPHA;
+ else if (dist > MAX_DIST_SQ)
+ alpha = 0.0f;
+ else
+ alpha = 30/dist;
+
+ alpha = llclamp(alpha, 0.0f, MAX_ALPHA);
+
+ glColor4f(1.f, 1.f, 1.f, alpha);
+ }
+
+ if ((pos_y - y1) < 0) direction = SOUTH_MASK;
+ else direction = NORTH_MASK;
+
+ // avoid Z fighting
+ renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp);
+
+ }
+
+ if (segment_mask & WEST_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1;
+ y2 = y1 + PARCEL_GRID_STEP_METERS;
+
+ if (gRenderForSelect)
+ {
+ LLColor4U color((U8)(GL_NAME_PARCEL_WALL >> 16), (U8)(GL_NAME_PARCEL_WALL >> 8), (U8)GL_NAME_PARCEL_WALL);
+ glColor4ubv(color.mV);
+ }
+ else
+ {
+ dx = (pos_x - x1) + DIST_OFFSET;
+
+ if (pos_y < y1)
+ dy = pos_y - y1;
+ else if (pos_y > y2)
+ dy = pos_y - y2;
+ else
+ dy = 0;
+
+ dist = dx*dx+dy*dy;
+
+ if (dist < MIN_DIST_SQ)
+ alpha = MAX_ALPHA;
+ else if (dist > MAX_DIST_SQ)
+ alpha = 0.0f;
+ else
+ alpha = 30/dist;
+
+ alpha = llclamp(alpha, 0.0f, MAX_ALPHA);
+
+ glColor4f(1.f, 1.f, 1.f, alpha);
+ }
+
+ if ((pos_x - x1) > 0) direction = WEST_MASK;
+ else direction = EAST_MASK;
+
+ // avoid Z fighting
+ renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp);
+
+ }
+ }
+ }
+
+ glEnd();
+}
+
+
+const S32 CLIENT_RECT_VPAD = 4;
+void LLPreviewTexture::draw()
+{
+ if( getVisible() )
+ {
+ updateAspectRatio();
+
+ LLPreview::draw();
+
+ if (!mMinimized)
+ {
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_notex;
+
+ const LLRect& border = mClientRect;
+ LLRect interior = mClientRect;
+ interior.stretch( -PREVIEW_BORDER_WIDTH );
+
+ // ...border
+ gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f));
+ gl_rect_2d_checkerboard( interior );
+
+ if ( mImage.notNull() )
+ {
+ LLGLSTexture gls_no_texture;
+ // Draw the texture
+ glColor3f( 1.f, 1.f, 1.f );
+ gl_draw_scaled_image(interior.mLeft,
+ interior.mBottom,
+ interior.getWidth(),
+ interior.getHeight(),
+ mImage);
+
+ // Pump the texture priority
+ F32 pixel_area = mLoadingFullImage ? (F32)MAX_IMAGE_AREA : (F32)(interior.getWidth() * interior.getHeight() );
+ mImage->addTextureStats( pixel_area );
+
+ // Don't bother decoding more than we can display, unless
+ // we're loading the full image.
+ if (!mLoadingFullImage)
+ {
+ S32 int_width = interior.getWidth();
+ S32 int_height = interior.getHeight();
+ mImage->setKnownDrawSize(int_width, int_height);
+ }
+ else
+ {
+ // Don't use this feature
+ mImage->setKnownDrawSize(0, 0);
+ }
+
+ if( mLoadingFullImage )
+ {
+ LLFontGL::sSansSerif->renderUTF8("Receiving:", 0,
+ interior.mLeft + 4,
+ interior.mBottom + 4,
+ LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::DROP_SHADOW);
+
+ F32 data_progress = 0.0f;
+ F32 decode_progress = mImage->getDecodeProgress(&data_progress);
+
+ // Draw the progress bar.
+ const S32 BAR_HEIGHT = 12;
+ const S32 BAR_LEFT_PAD = 80;
+ S32 left = interior.mLeft + 4 + BAR_LEFT_PAD;
+ S32 bar_width = mRect.getWidth() - left - RESIZE_HANDLE_WIDTH - 2;
+ S32 top = interior.mBottom + 4 + BAR_HEIGHT;
+ S32 right = left + bar_width;
+ S32 bottom = top - BAR_HEIGHT;
+
+ LLColor4 background_color(0.f, 0.f, 0.f, 0.75f);
+ LLColor4 decoded_color(0.f, 1.f, 0.f, 1.0f);
+ LLColor4 downloaded_color(0.f, 0.5f, 0.f, 1.0f);
+
+ gl_rect_2d(left, top, right, bottom, background_color);
+
+ if (data_progress > 0.0f)
+ {
+ // Decoded bytes
+ right = left + llfloor(decode_progress * (F32)bar_width);
+
+ if (left < right)
+ {
+ gl_rect_2d(left, top, right, bottom, decoded_color);
+ }
+
+ // Downloaded bytes
+ left = right;
+ right = left + llfloor((data_progress - decode_progress) * (F32)bar_width);
+
+ if (left < right)
+ {
+ gl_rect_2d(left, top, right, bottom, downloaded_color);
+ }
+ }
+ }
+ else
+ if( !mSavedFileTimer.hasExpired() )
+ {
+ LLFontGL::sSansSerif->renderUTF8("File Saved", 0,
+ interior.mLeft + 4,
+ interior.mBottom + 4,
+ LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::DROP_SHADOW);
+ }
+ }
+ }
+ }
+}
+
+
+void draw_line_cube(F32 width, const LLVector3& center)
+{
+ width = 0.5f * width;
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
+
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
+
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
+}
+
+
+void LLViewerObjectList::renderObjectBeacons()
+{
+ S32 i;
+ //const LLFontGL *font = gResMgr->getRes(LLFONT_SANSSERIF);
+
+ LLGLSUIDefault gls_ui;
+
+ S32 last_line_width = -1;
+
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ glBegin(GL_LINES);
+ for (i = 0; i < mDebugBeacons.count(); i++)
+ {
+ const LLDebugBeacon &debug_beacon = mDebugBeacons[i];
+ LLColor4 color = debug_beacon.mColor;
+ color.mV[3] *= 0.25f;
+ S32 line_width = debug_beacon.mLineWidth;
+ if (line_width != last_line_width)
+ {
+ glEnd();
+ glLineWidth( (F32)line_width );
+ last_line_width = line_width;
+ glBegin(GL_LINES);
+ }
+
+ const LLVector3 &thisline = debug_beacon.mPositionAgent;
+ glColor4fv(color.mV);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] - 50.f);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] + 50.f);
+ glVertex3f(thisline.mV[VX] - 2.f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX] + 2.f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] - 2.f,thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] + 2.f,thisline.mV[VZ]);
+
+ draw_line_cube(0.10f, thisline);
+ }
+ glEnd();
+ }
+
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ glBegin(GL_LINES);
+ last_line_width = -1;
+ for (i = 0; i < mDebugBeacons.count(); i++)
+ {
+ const LLDebugBeacon &debug_beacon = mDebugBeacons[i];
+
+ S32 line_width = debug_beacon.mLineWidth;
+ if (line_width != last_line_width)
+ {
+ glEnd();
+ glLineWidth( (F32)line_width );
+ last_line_width = line_width;
+ glBegin(GL_LINES);
+ }
+
+ const LLVector3 &thisline = debug_beacon.mPositionAgent;
+ glColor4fv(debug_beacon.mColor.mV);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] - 0.5f);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] + 0.5f);
+ glVertex3f(thisline.mV[VX] - 0.5f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX] + 0.5f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] - 0.5f,thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] + 0.5f,thisline.mV[VZ]);
+
+ draw_line_cube(0.10f, thisline);
+ }
+ glEnd();
+
+ glLineWidth(1.f);
+
+ for (i = 0; i < mDebugBeacons.count(); i++)
+ {
+ LLDebugBeacon &debug_beacon = mDebugBeacons[i];
+ if (debug_beacon.mString == "")
+ {
+ continue;
+ }
+ LLHUDText *hud_textp = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+
+ hud_textp->setZCompare(FALSE);
+ LLColor4 color;
+ color = debug_beacon.mTextColor;
+ color.mV[3] *= 1.f;
+
+ hud_textp->setString(utf8str_to_wstring(debug_beacon.mString.c_str()));
+ hud_textp->setColor(color);
+ hud_textp->setPositionAgent(debug_beacon.mPositionAgent);
+ debug_beacon.mHUDObject = hud_textp;
+ }
+ }
+}
+
+
+void pre_show_depth_buffer()
+{
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glEnable(GL_STENCIL_TEST);
+ glStencilFunc(GL_ALWAYS,0,0);
+ glStencilOp(GL_INCR,GL_INCR,GL_INCR);
+}
+
+void post_show_depth_buffer()
+{
+ int xsize =500, ysize =500;
+ U8 *buf = new U8[xsize*ysize];
+
+ glReadPixels(0,0,xsize,ysize,GL_STENCIL_INDEX,GL_UNSIGNED_BYTE, buf);
+
+ int total = 0;
+ int i;
+ for (i=0;i<xsize*ysize;i++)
+ {
+ total += buf[i];
+ buf[i] <<= 3;
+ }
+
+ float DC = (float)total/(float)(ysize*xsize);
+ int DCline = llfloor((xsize-20) * DC / 10.0f);
+ int stride = xsize / 10;
+
+ int y = 2;
+
+ for (i=0;i<DCline;i++)
+ {
+ if (i % stride == 0) i+=2;
+ if (i > xsize) y=6;
+ buf[ysize*(y+0)+i]=255;
+ buf[ysize*(y+1)+i]=255;
+ buf[ysize*(y+2)+i]=255;
+ }
+ glDrawPixels(xsize,ysize,GL_RED,GL_UNSIGNED_BYTE,buf);
+
+ delete buf;
+}
diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp
new file mode 100644
index 0000000000..106369ac01
--- /dev/null
+++ b/indra/newview/llgroupmgr.cpp
@@ -0,0 +1,1816 @@
+/**
+ * @file llgroupmgr.cpp
+ * @brief LLGroupMgr class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Manager for aggregating all client knowledge for specific groups
+ * Keeps a cache of group information.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llgroupmgr.h"
+
+#include <vector>
+#include <algorithm>
+
+#include "llagent.h"
+#include "llui.h"
+#include "message.h"
+#include "roles_constants.h"
+#include "lltransactiontypes.h"
+#include "llstatusbar.h"
+#include "viewer.h"
+#include "lleconomy.h"
+#include "llviewerwindow.h"
+#include "llfloaterdirectory.h"
+#include "llfloatergroupinfo.h"
+
+LLGroupMgr sGroupMgr; // use local instance so that it gets cleaned up on application exit
+LLGroupMgr* gGroupMgr = &sGroupMgr;
+
+const U32 MAX_CACHED_GROUPS = 10;
+
+//
+// LLRoleActionSet
+//
+LLRoleActionSet::LLRoleActionSet()
+: mActionSetData(NULL)
+{ }
+
+LLRoleActionSet::~LLRoleActionSet()
+{
+ delete mActionSetData;
+ std::for_each(mActions.begin(), mActions.end(), DeletePointer());
+}
+
+//
+// LLGroupMemberData
+//
+
+LLGroupMemberData::LLGroupMemberData(const LLUUID& id,
+ S32 contribution,
+ U64 agent_powers,
+ const std::string& title,
+ const std::string& online_status,
+ BOOL is_owner) :
+ mID(id),
+ mContribution(contribution),
+ mAgentPowers(agent_powers),
+ mTitle(title),
+ mOnlineStatus(online_status),
+ mIsOwner(is_owner)
+{
+}
+
+LLGroupMemberData::~LLGroupMemberData()
+{
+}
+
+void LLGroupMemberData::addRole(const LLUUID& role, LLGroupRoleData* rd)
+{
+ mRoles[role] = rd;
+}
+
+bool LLGroupMemberData::removeRole(const LLUUID& role)
+{
+ std::map<LLUUID,LLGroupRoleData*>::iterator it = mRoles.find(role);
+
+ if (it != mRoles.end())
+ {
+ mRoles.erase(it);
+ return true;
+ }
+
+ return false;
+}
+
+//
+// LLGroupRoleData
+//
+
+LLGroupRoleData::LLGroupRoleData(const LLUUID& role_id,
+ const std::string& role_name,
+ const std::string& role_title,
+ const std::string& role_desc,
+ const U64 role_powers,
+ const S32 member_count) :
+ mRoleID(role_id),
+ mMemberCount(member_count),
+ mMembersNeedsSort(FALSE)
+{
+ mRoleData.mRoleName = role_name;
+ mRoleData.mRoleTitle = role_title;
+ mRoleData.mRoleDescription = role_desc;
+ mRoleData.mRolePowers = role_powers;
+ mRoleData.mChangeType = RC_UPDATE_NONE;
+}
+
+LLGroupRoleData::LLGroupRoleData(const LLUUID& role_id,
+ LLRoleData role_data,
+ const S32 member_count) :
+ mRoleID(role_id),
+ mRoleData(role_data),
+ mMemberCount(member_count),
+ mMembersNeedsSort(FALSE)
+{
+
+}
+
+LLGroupRoleData::~LLGroupRoleData()
+{
+}
+
+S32 LLGroupRoleData::getMembersInRole(std::vector<LLUUID> members,
+ BOOL needs_sort)
+{
+ if (mRoleID.isNull())
+ {
+ // This is the everyone role, just return the size of members,
+ // because everyone is in the everyone role.
+ return members.size();
+ }
+
+ // Sort the members list, if needed.
+ if (mMembersNeedsSort)
+ {
+ std::sort(mMemberIDs.begin(), mMemberIDs.end());
+ mMembersNeedsSort = FALSE;
+ }
+ if (needs_sort)
+ {
+ // Sort the members parameter.
+ std::sort(members.begin(), members.end());
+ }
+
+ // Return the number of members in the intersection.
+ S32 max_size = llmin( members.size(), mMemberIDs.size() );
+ std::vector<LLUUID> in_role( max_size );
+ std::vector<LLUUID>::iterator in_role_end;
+ in_role_end = std::set_intersection(mMemberIDs.begin(), mMemberIDs.end(),
+ members.begin(), members.end(),
+ in_role.begin());
+ return in_role_end - in_role.begin();
+}
+
+void LLGroupRoleData::addMember(const LLUUID& member)
+{
+ mMembersNeedsSort = TRUE;
+ mMemberIDs.push_back(member);
+}
+
+bool LLGroupRoleData::removeMember(const LLUUID& member)
+{
+ std::vector<LLUUID>::iterator it = std::find(mMemberIDs.begin(),mMemberIDs.end(),member);
+
+ if (it != mMemberIDs.end())
+ {
+ mMembersNeedsSort = TRUE;
+ mMemberIDs.erase(it);
+ return true;
+ }
+
+ return false;
+}
+
+void LLGroupRoleData::clearMembers()
+{
+ mMembersNeedsSort = FALSE;
+ mMemberIDs.clear();
+}
+
+
+//
+// LLGroupMgrGroupData
+//
+
+LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) :
+ mID(id),
+ mShowInList(TRUE),
+ mOpenEnrollment(FALSE),
+ mMembershipFee(0),
+ mAllowPublish(FALSE),
+ mMaturePublish(FALSE),
+ mChanged(FALSE),
+ mMemberCount(0),
+ mRoleCount(0),
+ mReceivedRoleMemberPairs(0),
+ mMemberDataComplete(FALSE),
+ mRoleDataComplete(FALSE),
+ mRoleMemberDataComplete(FALSE),
+ mGroupPropertiesDataComplete(FALSE),
+ mPendingRoleMemberRequest(FALSE)
+{
+}
+
+BOOL LLGroupMgrGroupData::getRoleData(const LLUUID& role_id, LLRoleData& role_data)
+{
+ std::map<LLUUID,LLRoleData>::const_iterator it;
+
+ // Do we have changes for it?
+ it = mRoleChanges.find(role_id);
+ if (it != mRoleChanges.end())
+ {
+ if ((*it).second.mChangeType == RC_DELETE) return FALSE;
+
+ role_data = (*it).second;
+ return TRUE;
+ }
+
+ // Ok, no changes, hasn't been deleted, isn't a new role, just find the role.
+ role_list::const_iterator rit = mRoles.find(role_id);
+ if (rit != mRoles.end())
+ {
+ role_data = (*rit).second->getRoleData();
+ return TRUE;
+ }
+
+ // This role must not exist.
+ return FALSE;
+}
+
+
+void LLGroupMgrGroupData::setRoleData(const LLUUID& role_id, LLRoleData role_data)
+{
+ // If this is a newly created group, we need to change the data in the created list.
+ std::map<LLUUID,LLRoleData>::iterator it;
+ it = mRoleChanges.find(role_id);
+ if (it != mRoleChanges.end())
+ {
+ if ((*it).second.mChangeType == RC_CREATE)
+ {
+ role_data.mChangeType = RC_CREATE;
+ mRoleChanges[role_id] = role_data;
+ return;
+ }
+ else if ((*it).second.mChangeType == RC_DELETE)
+ {
+ // Don't do anything for a role being deleted.
+ return;
+ }
+ }
+
+ // Not a new role, so put it in the changes list.
+ LLRoleData old_role_data;
+ role_iter rit = mRoles.find(role_id);
+ if (rit != mRoles.end())
+ {
+ bool data_change = ( ((*rit).second->mRoleData.mRoleDescription != role_data.mRoleDescription)
+ || ((*rit).second->mRoleData.mRoleName != role_data.mRoleName)
+ || ((*rit).second->mRoleData.mRoleTitle != role_data.mRoleTitle) );
+ bool powers_change = ((*rit).second->mRoleData.mRolePowers != role_data.mRolePowers);
+
+ if (!data_change && !powers_change)
+ {
+ // We are back to the original state, the changes have been 'undone' so take out the change.
+ mRoleChanges.erase(role_id);
+ return;
+ }
+
+ if (data_change && powers_change)
+ {
+ role_data.mChangeType = RC_UPDATE_ALL;
+ }
+ else if (data_change)
+ {
+ role_data.mChangeType = RC_UPDATE_DATA;
+ }
+ else
+ {
+ role_data.mChangeType = RC_UPDATE_POWERS;
+ }
+
+ mRoleChanges[role_id] = role_data;
+ }
+ else
+ {
+ llwarns << "Change being made to non-existant role " << role_id << llendl;
+ }
+}
+
+BOOL LLGroupMgrGroupData::pendingRoleChanges()
+{
+ return (!mRoleChanges.empty());
+}
+
+// This is a no-op if the role has already been created.
+void LLGroupMgrGroupData::createRole(const LLUUID& role_id, LLRoleData role_data)
+{
+ if (mRoleChanges.find(role_id) != mRoleChanges.end())
+ {
+ llwarns << "create role for existing role! " << role_id << llendl;
+ }
+ else
+ {
+ role_data.mChangeType = RC_CREATE;
+ mRoleChanges[role_id] = role_data;
+ }
+}
+
+void LLGroupMgrGroupData::deleteRole(const LLUUID& role_id)
+{
+ std::map<LLUUID,LLRoleData>::iterator it;
+
+ // If this was a new role, just discard it.
+ it = mRoleChanges.find(role_id);
+ if (it != mRoleChanges.end()
+ && (*it).second.mChangeType == RC_CREATE)
+ {
+ mRoleChanges.erase(it);
+ return;
+ }
+
+ LLRoleData rd;
+ rd.mChangeType = RC_DELETE;
+ mRoleChanges[role_id] = rd;
+}
+
+void LLGroupMgrGroupData::addRolePower(const LLUUID &role_id, U64 power)
+{
+ LLRoleData rd;
+ if (getRoleData(role_id,rd))
+ {
+ rd.mRolePowers |= power;
+ setRoleData(role_id,rd);
+ }
+ else
+ {
+ llwarns << "addRolePower: no role data found for " << role_id << llendl;
+ }
+}
+
+void LLGroupMgrGroupData::removeRolePower(const LLUUID &role_id, U64 power)
+{
+ LLRoleData rd;
+ if (getRoleData(role_id,rd))
+ {
+ rd.mRolePowers &= ~power;
+ setRoleData(role_id,rd);
+ }
+ else
+ {
+ llwarns << "removeRolePower: no role data found for " << role_id << llendl;
+ }
+}
+
+U64 LLGroupMgrGroupData::getRolePowers(const LLUUID& role_id)
+{
+ LLRoleData rd;
+ if (getRoleData(role_id,rd))
+ {
+ return rd.mRolePowers;
+ }
+ else
+ {
+ llwarns << "getRolePowers: no role data found for " << role_id << llendl;
+ return GP_NO_POWERS;
+ }
+}
+
+void LLGroupMgrGroupData::removeData()
+{
+ // Remove member data first, because removeRoleData will walk the member list
+ removeMemberData();
+ removeRoleData();
+}
+
+void LLGroupMgrGroupData::removeMemberData()
+{
+ for (member_iter mi = mMembers.begin(); mi != mMembers.end(); ++mi)
+ {
+ delete mi->second;
+ }
+ mMembers.clear();
+ mMemberDataComplete = FALSE;
+}
+
+void LLGroupMgrGroupData::removeRoleData()
+{
+ for (member_iter mi = mMembers.begin(); mi != mMembers.end(); ++mi)
+ {
+ LLGroupMemberData* data = mi->second;
+ if (data)
+ {
+ data->clearRoles();
+ }
+ }
+
+ for (role_iter ri = mRoles.begin(); ri != mRoles.end(); ++ri)
+ {
+ LLGroupRoleData* data = ri->second;
+ delete data;
+ }
+ mRoles.clear();
+ mReceivedRoleMemberPairs = 0;
+ mRoleDataComplete = FALSE;
+ mRoleMemberDataComplete = FALSE;
+}
+
+void LLGroupMgrGroupData::removeRoleMemberData()
+{
+ for (member_iter mi = mMembers.begin(); mi != mMembers.end(); ++mi)
+ {
+ LLGroupMemberData* data = mi->second;
+ if (data)
+ {
+ data->clearRoles();
+ }
+ }
+
+ for (role_iter ri = mRoles.begin(); ri != mRoles.end(); ++ri)
+ {
+ LLGroupRoleData* data = ri->second;
+ if (data)
+ {
+ data->clearMembers();
+ }
+ }
+
+ mReceivedRoleMemberPairs = 0;
+ mRoleMemberDataComplete = FALSE;
+}
+
+LLGroupMgrGroupData::~LLGroupMgrGroupData()
+{
+ removeData();
+}
+
+bool LLGroupMgrGroupData::changeRoleMember(const LLUUID& role_id,
+ const LLUUID& member_id,
+ LLRoleMemberChangeType rmc)
+{
+ role_iter ri = mRoles.find(role_id);
+ member_iter mi = mMembers.find(member_id);
+
+ if (ri == mRoles.end()
+ || mi == mMembers.end() )
+ {
+ if (ri == mRoles.end()) llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't find role " << role_id << llendl;
+ if (mi == mMembers.end()) llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't find member " << member_id << llendl;
+ return false;
+ }
+
+ LLGroupRoleData* grd = ri->second;
+ LLGroupMemberData* gmd = mi->second;
+
+ if (!grd || !gmd)
+ {
+ llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't get member or role data." << llendl;
+ return false;
+ }
+
+ if (RMC_ADD == rmc)
+ {
+ llinfos << " adding member to role." << llendl;
+ grd->addMember(member_id);
+ gmd->addRole(role_id,grd);
+
+ //TODO move this into addrole function
+ //see if they added someone to the owner role and update isOwner
+ gmd->mIsOwner = (role_id == mOwnerRole) ? TRUE : gmd->mIsOwner;
+ }
+ else if (RMC_REMOVE == rmc)
+ {
+ llinfos << " removing member from role." << llendl;
+ grd->removeMember(member_id);
+ gmd->removeRole(role_id);
+
+ //see if they removed someone from the owner role and update isOwner
+ gmd->mIsOwner = (role_id == mOwnerRole) ? FALSE : gmd->mIsOwner;
+ }
+
+ lluuid_pair role_member;
+ role_member.first = role_id;
+ role_member.second = member_id;
+
+ change_map::iterator it = mRoleMemberChanges.find(role_member);
+ if (it != mRoleMemberChanges.end())
+ {
+ // There was already a role change for this role_member
+ if (it->second.mChange == rmc)
+ {
+ // Already recorded this change? Weird.
+ llinfos << "Received duplicate change for "
+ << " role: " << role_id << " member " << member_id
+ << " change " << (rmc == RMC_ADD ? "ADD" : "REMOVE") << llendl;
+ }
+ else
+ {
+ // The only two operations (add and remove) currently cancel each other out
+ // If that changes this will need more logic
+ if (rmc == RMC_NONE)
+ {
+ llwarns << "changeRoleMember: existing entry with 'RMC_NONE' change! This shouldn't happen." << llendl;
+ LLRoleMemberChange rc(role_id,member_id,rmc);
+ mRoleMemberChanges[role_member] = rc;
+ }
+ else
+ {
+ mRoleMemberChanges.erase(it);
+ }
+ }
+ }
+ else
+ {
+ LLRoleMemberChange rc(role_id,member_id,rmc);
+ mRoleMemberChanges[role_member] = rc;
+ }
+
+ recalcAgentPowers(member_id);
+
+ mChanged = TRUE;
+ return true;
+}
+
+void LLGroupMgrGroupData::recalcAllAgentPowers()
+{
+ LLGroupMemberData* gmd;
+
+ member_iter mit = mMembers.begin();
+ member_iter mend = mMembers.end();
+ for( ; mit != mend; ++mit)
+ {
+ gmd = mit->second;
+ if (!gmd) continue;
+
+ std::map<LLUUID,LLGroupRoleData*>::iterator it = gmd->mRoles.begin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator end = gmd->mRoles.end();
+
+ gmd->mAgentPowers = 0;
+ for ( ; it != end; ++it)
+ {
+ LLGroupRoleData* grd = (*it).second;
+ if (!grd) continue;
+
+ gmd->mAgentPowers |= grd->mRoleData.mRolePowers;
+ }
+ }
+}
+
+void LLGroupMgrGroupData::recalcAgentPowers(const LLUUID& agent_id)
+{
+ member_iter mi = mMembers.find(agent_id);
+ if (mi == mMembers.end()) return;
+
+ LLGroupMemberData* gmd = mi->second;
+
+ if (!gmd) return;
+
+ std::map<LLUUID,LLGroupRoleData*>::iterator it = gmd->mRoles.begin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator end = gmd->mRoles.end();
+
+ gmd->mAgentPowers = 0;
+ for ( ; it != end; ++it)
+ {
+ LLGroupRoleData* grd = (*it).second;
+ if (!grd) continue;
+
+ gmd->mAgentPowers |= grd->mRoleData.mRolePowers;
+ }
+}
+
+bool packRoleUpdateMessageBlock(LLMessageSystem* msg,
+ const LLUUID& group_id,
+ const LLUUID& role_id,
+ const LLRoleData& role_data,
+ bool start_message)
+{
+ if (start_message)
+ {
+ msg->newMessage("GroupRoleUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->addUUID("GroupID",group_id);
+ start_message = false;
+ }
+
+ msg->nextBlock("RoleData");
+ msg->addUUID("RoleID",role_id);
+ msg->addString("Name", role_data.mRoleName);
+ msg->addString("Description", role_data.mRoleDescription);
+ msg->addString("Title", role_data.mRoleTitle);
+ msg->addU64("Powers", role_data.mRolePowers);
+ msg->addU8("UpdateType", (U8)role_data.mChangeType);
+
+ if (msg->isSendFullFast())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+
+ return start_message;
+}
+
+void LLGroupMgrGroupData::sendRoleChanges()
+{
+ // Commit changes locally
+ std::map<LLUUID,LLRoleData>::iterator it;
+ std::map<LLUUID,LLRoleData>::iterator end;
+ LLGroupRoleData* grd;
+ role_iter role_it;
+ LLMessageSystem* msg = gMessageSystem;
+ bool start_message = true;
+
+ bool need_role_cleanup = false;
+ bool need_role_data = false;
+ bool need_power_recalc = false;
+
+ // Apply all changes
+ it = mRoleChanges.begin();
+ end = mRoleChanges.end();
+ for ( ; it != end; ++it)
+ {
+ const LLUUID& role_id = (*it).first;
+ const LLRoleData& role_data = (*it).second;
+
+ // Commit to local data set
+ role_it = mRoles.find((*it).first);
+ LLGroupRoleData* group_role_data = (*role_it).second;
+ if ( (mRoles.end() == role_it
+ && RC_CREATE != role_data.mChangeType)
+ || (mRoles.end() != role_it
+ && RC_CREATE == role_data.mChangeType))
+ {
+ continue;
+ }
+
+ switch (role_data.mChangeType)
+ {
+ case RC_CREATE:
+ {
+ grd = new LLGroupRoleData(role_id, role_data, 0);
+ mRoles[role_id] = grd;
+ need_role_data = true;
+ break;
+ }
+ case RC_DELETE:
+ {
+ delete group_role_data;
+ mRoles.erase(role_it);
+ need_role_cleanup = true;
+ need_power_recalc = true;
+ break;
+ }
+ case RC_UPDATE_ALL:
+ case RC_UPDATE_POWERS:
+ need_power_recalc = true;
+ case RC_UPDATE_DATA:
+ default:
+ {
+ group_role_data->setRoleData(role_data);
+ break;
+ }
+ }
+
+ // Update dataserver
+ start_message = packRoleUpdateMessageBlock(msg,getID(),role_id,role_data,start_message);
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ // If we delete a role then all the role-member pairs are invalid!
+ if (need_role_cleanup)
+ {
+ removeRoleMemberData();
+ }
+
+ // If we create a new role, then we need to re-fetch all the role data.
+ if (need_role_data)
+ {
+ gGroupMgr->sendGroupRoleDataRequest(getID());
+ }
+
+ // Clean up change lists
+ mRoleChanges.clear();
+
+ // Recalculate all the agent powers because role powers have now changed.
+ if (need_power_recalc)
+ {
+ recalcAllAgentPowers();
+ }
+}
+
+void LLGroupMgrGroupData::cancelRoleChanges()
+{
+ // Clear out all changes!
+ mRoleChanges.clear();
+}
+//
+// LLGroupMgr
+//
+
+LLGroupMgr::LLGroupMgr()
+{
+}
+
+LLGroupMgr::~LLGroupMgr()
+{
+ clearGroups();
+}
+
+void LLGroupMgr::clearGroups()
+{
+ std::for_each(mRoleActionSets.begin(), mRoleActionSets.end(), DeletePointer());
+ mRoleActionSets.clear();
+ std::for_each(mGroups.begin(), mGroups.end(), DeletePairedPointer());
+ mGroups.clear();
+ mObservers.clear();
+}
+
+void LLGroupMgr::clearGroupData(const LLUUID& group_id)
+{
+ std::map<LLUUID, LLGroupMgrGroupData*>::iterator iter = mGroups.find(group_id);
+ if (iter != mGroups.end())
+ {
+ delete (*iter).second;
+ mGroups.erase(iter);
+ }
+}
+
+void LLGroupMgr::addObserver(LLGroupMgrObserver* observer)
+{
+ mObservers.insert(std::pair<LLUUID, LLGroupMgrObserver*>(observer->getID(), observer));
+}
+
+void LLGroupMgr::removeObserver(LLGroupMgrObserver* observer)
+{
+ if (!observer)
+ {
+ return;
+ }
+ observer_iter it;
+ it = mObservers.find(observer->getID());
+ while (it != mObservers.end())
+ {
+ if (it->second == observer)
+ {
+ mObservers.erase(it);
+ break;
+ }
+ ++it;
+ }
+}
+
+LLGroupMgrGroupData* LLGroupMgr::getGroupData(const LLUUID& id)
+{
+ group_iter gi = mGroups.find(id);
+
+ if (gi != mGroups.end())
+ {
+ return gi->second;
+ }
+ return NULL;
+}
+
+// static
+void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupMembersReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_RequestID, request_id);
+
+ LLGroupMgrGroupData* group_datap = gGroupMgr->createGroupData(group_id);
+ if (group_datap->mMemberRequestID != request_id)
+ {
+ llwarns << "processGroupMembersReply: Received incorrect (stale?) request id" << llendl;
+ return;
+ }
+
+ msg->getS32(_PREHASH_GroupData, "MemberCount", group_datap->mMemberCount );
+
+ if (group_datap->mMemberCount > 0)
+ {
+ S32 contribution = 0;
+ char online_status[DB_DATETIME_BUF_SIZE];
+ char title[DB_GROUP_TITLE_BUF_SIZE];
+ U64 agent_powers = 0;
+ BOOL is_owner = FALSE;
+
+ S32 num_members = msg->getNumberOfBlocksFast(_PREHASH_MemberData);
+ for (S32 i = 0; i < num_members; i++)
+ {
+ LLUUID member_id;
+
+ msg->getUUIDFast(_PREHASH_MemberData, _PREHASH_AgentID, member_id, i );
+ msg->getS32(_PREHASH_MemberData, _PREHASH_Contribution, contribution, i);
+ msg->getU64(_PREHASH_MemberData, "AgentPowers", agent_powers, i);
+ msg->getStringFast(_PREHASH_MemberData, _PREHASH_OnlineStatus, DB_DATETIME_BUF_SIZE, online_status, i);
+ msg->getString(_PREHASH_MemberData, "Title", DB_GROUP_TITLE_BUF_SIZE, title, i);
+ msg->getBOOL(_PREHASH_MemberData,"IsOwner",is_owner,i);
+
+ if (member_id.notNull())
+ {
+ //llinfos << "Member " << member_id << " has powers " << std::hex << agent_powers << std::dec << llendl;
+ LLGroupMemberData* newdata = new LLGroupMemberData(member_id,
+ contribution,
+ agent_powers,
+ std::string(title),
+ std::string(online_status),
+ is_owner);
+#if LL_DEBUG
+ LLGroupMgrGroupData::member_iter mit = group_datap->mMembers.find(member_id);
+ if (mit != group_datap->mMembers.end())
+ {
+ llinfos << " *** Received duplicate member data for agent " << member_id << llendl;
+ }
+#endif
+ group_datap->mMembers[member_id] = newdata;
+ }
+ else
+ {
+ llinfos << "Received null group member data." << llendl;
+ }
+ }
+ }
+
+ if (group_datap->mMembers.size() == group_datap->mMemberCount)
+ {
+ group_datap->mMemberDataComplete = TRUE;
+ group_datap->mMemberRequestID.setNull();
+ // We don't want to make role-member data requests until we have all the members
+ if (group_datap->mPendingRoleMemberRequest)
+ {
+ group_datap->mPendingRoleMemberRequest = FALSE;
+ gGroupMgr->sendGroupRoleMembersRequest(group_datap->mID);
+ }
+ }
+
+ group_datap->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_MEMBER_DATA);
+}
+
+//static
+void LLGroupMgr::processGroupPropertiesReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupPropertiesReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ char name[DB_GROUP_NAME_BUF_SIZE];
+ char charter[DB_GROUP_CHARTER_BUF_SIZE];
+ BOOL show_in_list = FALSE;
+ LLUUID founder_id;
+ U64 powers_mask = GP_NO_POWERS;
+ S32 money = 0;
+ char member_title[DB_GROUP_TITLE_BUF_SIZE];
+ LLUUID insignia_id;
+ LLUUID owner_role;
+ U32 membership_fee = 0;
+ BOOL open_enrollment = FALSE;
+ S32 num_group_members = 0;
+ S32 num_group_roles = 0;
+ BOOL allow_publish = FALSE;
+ BOOL mature = FALSE;
+
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_FounderID, founder_id);
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_Name, DB_GROUP_NAME_BUF_SIZE, name );
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_Charter, DB_GROUP_CHARTER_BUF_SIZE, charter );
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_ShowInList, show_in_list );
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_MemberTitle, DB_GROUP_TITLE_BUF_SIZE, member_title );
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_InsigniaID, insignia_id );
+ msg->getU64Fast(_PREHASH_GroupData, _PREHASH_PowersMask, powers_mask );
+ msg->getU32Fast(_PREHASH_GroupData, _PREHASH_MembershipFee, membership_fee );
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_OpenEnrollment, open_enrollment );
+ msg->getS32Fast(_PREHASH_GroupData, _PREHASH_GroupMembershipCount, num_group_members);
+ msg->getS32(_PREHASH_GroupData, "GroupRolesCount", num_group_roles);
+ msg->getS32Fast(_PREHASH_GroupData, _PREHASH_Money, money);
+ msg->getBOOL("GroupData", "AllowPublish", allow_publish);
+ msg->getBOOL("GroupData", "MaturePublish", mature);
+ msg->getUUID(_PREHASH_GroupData, "OwnerRole", owner_role);
+
+ LLGroupMgrGroupData* group_datap = gGroupMgr->createGroupData(group_id);
+
+ group_datap->mName = name;
+ group_datap->mCharter = charter;
+ group_datap->mShowInList = show_in_list;
+ group_datap->mInsigniaID = insignia_id;
+ group_datap->mFounderID = founder_id;
+ group_datap->mMembershipFee = membership_fee;
+ group_datap->mOpenEnrollment = open_enrollment;
+ group_datap->mAllowPublish = allow_publish;
+ group_datap->mMaturePublish = mature;
+ group_datap->mOwnerRole = owner_role;
+ group_datap->mMemberCount = num_group_members;
+ group_datap->mRoleCount = num_group_roles + 1; // Add the everyone role.
+
+ group_datap->mGroupPropertiesDataComplete = TRUE;
+ group_datap->mChanged = TRUE;
+
+ gGroupMgr->notifyObservers(GC_PROPERTIES);
+}
+
+// static
+void LLGroupMgr::processGroupRoleDataReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupRoleDataReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_RequestID, request_id);
+
+ LLGroupMgrGroupData* group_data = gGroupMgr->createGroupData(group_id);
+ if (group_data->mRoleDataRequestID != request_id)
+ {
+ llwarns << "processGroupRoleDataReply: Received incorrect (stale?) request id" << llendl;
+ return;
+ }
+
+ msg->getS32(_PREHASH_GroupData, "RoleCount", group_data->mRoleCount );
+
+ char name[DB_GROUP_NAME_BUF_SIZE];
+ char title[DB_GROUP_TITLE_BUF_SIZE];
+ char desc[DB_GROUP_CHARTER_BUF_SIZE];
+ U64 powers = 0;
+ U32 member_count = 0;
+ LLUUID role_id;
+
+ U32 num_blocks = msg->getNumberOfBlocks("RoleData");
+ U32 i = 0;
+ for (i=0; i< num_blocks; ++i)
+ {
+ msg->getUUID("RoleData", "RoleID", role_id, i );
+
+ msg->getString("RoleData","Name",DB_GROUP_NAME_BUF_SIZE,name,i);
+ msg->getString("RoleData","Title",DB_GROUP_TITLE_BUF_SIZE,title,i);
+ msg->getString("RoleData","Description",DB_GROUP_CHARTER_BUF_SIZE,desc,i);
+ msg->getU64("RoleData","Powers",powers,i);
+ msg->getU32("RoleData","Members",member_count,i);
+
+ lldebugs << "Adding role data: " << name << " {" << role_id << "}" << llendl;
+ LLGroupRoleData* rd = new LLGroupRoleData(role_id,name,title,desc,powers,member_count);
+ group_data->mRoles[role_id] = rd;
+ }
+
+ if (group_data->mRoles.size() == group_data->mRoleCount)
+ {
+ group_data->mRoleDataComplete = TRUE;
+ group_data->mRoleDataRequestID.setNull();
+ // We don't want to make role-member data requests until we have all the role data
+ if (group_data->mPendingRoleMemberRequest)
+ {
+ group_data->mPendingRoleMemberRequest = FALSE;
+ gGroupMgr->sendGroupRoleMembersRequest(group_data->mID);
+ }
+ }
+
+ group_data->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_ROLE_DATA);
+}
+
+// static
+void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupRoleMembersReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_RequestID, request_id);
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+
+ U32 total_pairs;
+ msg->getU32(_PREHASH_AgentData, "TotalPairs", total_pairs);
+
+ LLGroupMgrGroupData* group_data = gGroupMgr->createGroupData(group_id);
+
+ if (group_data->mRoleMembersRequestID != request_id)
+ {
+ llwarns << "processGroupRoleMembersReply: Received incorrect (stale?) role member request id" << llendl;
+ return;
+ }
+
+ U32 num_blocks = msg->getNumberOfBlocks("MemberData");
+ U32 i;
+ LLUUID member_id;
+ LLUUID role_id;
+ LLGroupRoleData* rd = NULL;
+ LLGroupMemberData* md = NULL;
+
+ LLGroupMgrGroupData::role_iter ri;
+ LLGroupMgrGroupData::member_iter mi;
+
+ // If total_pairs == 0, there are no members in any custom roles.
+ if (total_pairs > 0)
+ {
+ for (i = 0;i < num_blocks; ++i)
+ {
+ msg->getUUID("MemberData","RoleID",role_id,i);
+ msg->getUUID("MemberData","MemberID",member_id,i);
+
+ if (role_id.notNull() && member_id.notNull() )
+ {
+ rd = NULL;
+ ri = group_data->mRoles.find(role_id);
+ if (ri != group_data->mRoles.end())
+ {
+ rd = ri->second;
+ }
+
+ md = NULL;
+ mi = group_data->mMembers.find(member_id);
+ if (mi != group_data->mMembers.end())
+ {
+ md = mi->second;
+ }
+
+ if (rd && md)
+ {
+ lldebugs << "Adding role-member pair: " << role_id << ", " << member_id << llendl;
+ rd->addMember(member_id);
+ md->addRole(role_id,rd);
+ }
+ else
+ {
+ if (!rd) llwarns << "Received role data for unkown role " << role_id << " in group " << group_id << llendl;
+ if (!md) llwarns << "Received role data for unkown member " << member_id << " in group " << group_id << llendl;
+ }
+ }
+ }
+
+ group_data->mReceivedRoleMemberPairs += num_blocks;
+ }
+
+ if (group_data->mReceivedRoleMemberPairs == total_pairs)
+ {
+ // Add role data for the 'everyone' role to all members
+ LLGroupRoleData* everyone = group_data->mRoles[LLUUID::null];
+ if (!everyone)
+ {
+ llwarns << "Everyone role not found!" << llendl;
+ }
+ else
+ {
+ LLGroupMgrGroupData::member_iter mi = group_data->mMembers.begin();
+ LLGroupMgrGroupData::member_iter end = group_data->mMembers.end();
+ for ( ; mi != end; ++mi)
+ {
+ LLGroupMemberData* data = mi->second;
+ if (data)
+ {
+ data->addRole(LLUUID::null,everyone);
+ }
+ }
+ }
+
+ group_data->mRoleMemberDataComplete = TRUE;
+ group_data->mRoleMembersRequestID.setNull();
+ }
+
+ group_data->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_ROLE_MEMBER_DATA);
+}
+
+// static
+void LLGroupMgr::processGroupTitlesReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupTitlesReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+
+ LLGroupMgrGroupData* group_data = gGroupMgr->createGroupData(group_id);
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_RequestID, request_id);
+
+ if (group_data->mTitlesRequestID != request_id)
+ {
+ llwarns << "processGroupTitlesReply: Received incorrect (stale?) title request id" << llendl;
+ return;
+ }
+
+ char title_buf[DB_GROUP_TITLE_BUF_SIZE];
+
+ LLGroupTitle title;
+
+ S32 i = 0;
+ S32 blocks = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
+ for (i=0; i<blocks; ++i)
+ {
+ msg->getString("GroupData","Title",DB_GROUP_TITLE_BUF_SIZE,title_buf,i);
+ title.mTitle = title_buf;
+ msg->getUUID("GroupData","RoleID",title.mRoleID,i);
+ msg->getBOOL("GroupData","Selected",title.mSelected,i);
+
+ if (title_buf[0] != '\0')
+ {
+ lldebugs << "LLGroupMgr adding title: " << title.mTitle << ", " << title.mRoleID << ", " << (title.mSelected ? 'Y' : 'N') << llendl;
+ group_data->mTitles.push_back(title);
+ }
+ }
+
+ group_data->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_TITLES);
+}
+
+// static
+void LLGroupMgr::processEjectGroupMemberReply(LLMessageSystem* msg, void ** data)
+{
+ llinfos << "processEjectGroupMemberReply" << llendl;
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
+ BOOL success;
+ msg->getBOOLFast(_PREHASH_EjectData, _PREHASH_Success, success);
+
+ // If we had a failure, the group panel needs to be updated.
+ if (!success)
+ {
+ LLFloaterGroupInfo::refreshGroup(group_id);
+ }
+}
+
+// static
+void LLGroupMgr::processJoinGroupReply(LLMessageSystem* msg, void ** data)
+{
+ llinfos << "processJoinGroupReply" << llendl;
+ LLUUID group_id;
+ BOOL success;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_Success, success);
+
+ if (success)
+ {
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ gGroupMgr->clearGroupData(group_id);
+ // refresh the floater for this group, if any.
+ LLFloaterGroupInfo::refreshGroup(group_id);
+ // refresh the group panel of the search window, if necessary.
+ LLFloaterDirectory::refreshGroup(group_id);
+ }
+}
+
+// static
+void LLGroupMgr::processLeaveGroupReply(LLMessageSystem* msg, void ** data)
+{
+ llinfos << "processLeaveGroupReply" << llendl;
+ LLUUID group_id;
+ BOOL success;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_Success, success);
+
+ if (success)
+ {
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ gGroupMgr->clearGroupData(group_id);
+ // close the floater for this group, if any.
+ LLFloaterGroupInfo::closeGroup(group_id);
+ // refresh the group panel of the search window, if necessary.
+ LLFloaterDirectory::refreshGroup(group_id);
+ }
+}
+
+// static
+void LLGroupMgr::processCreateGroupReply(LLMessageSystem* msg, void ** data)
+{
+ LLUUID group_id;
+ BOOL success;
+ char message[MAX_STRING];
+
+ msg->getUUIDFast(_PREHASH_ReplyData, _PREHASH_GroupID, group_id );
+
+ msg->getBOOLFast(_PREHASH_ReplyData, _PREHASH_Success, success );
+ msg->getStringFast(_PREHASH_ReplyData, _PREHASH_Message, MAX_STRING, message );
+
+ if (success)
+ {
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ // HACK! We haven't gotten the agent group update yet, so ... um ... fake it.
+ // This is so when we go to modify the group we will be able to do so.
+ // This isn't actually too bad because real data will come down in 2 or 3 miliseconds and replace this.
+ LLGroupData gd;
+ gd.mAcceptNotices = TRUE;
+ gd.mContribution = 0;
+ gd.mID = group_id;
+ gd.mName = "new group";
+ gd.mPowers = GP_ALL_POWERS;
+
+ gAgent.mGroups.push_back(gd);
+
+ LLFloaterGroupInfo::closeCreateGroup();
+ LLFloaterGroupInfo::showFromUUID(group_id,"roles_tab");
+ }
+ else
+ {
+ // XUI:translate
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = message;
+ gViewerWindow->alertXml("UnableToCreateGroup", args);
+ }
+}
+
+LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id)
+{
+ LLGroupMgrGroupData* group_datap;
+ group_iter existing_group = gGroupMgr->mGroups.find(id);
+ if (existing_group == gGroupMgr->mGroups.end())
+ {
+ group_datap = new LLGroupMgrGroupData(id);
+ gGroupMgr->addGroup(group_datap);
+ }
+ else
+ {
+ group_datap = existing_group->second;
+ }
+
+ return group_datap;
+}
+
+void LLGroupMgr::notifyObservers(LLGroupChange gc)
+{
+ for (group_iter gi = mGroups.begin(); gi != mGroups.end(); ++gi)
+ {
+ if (gi->second->mChanged)
+ {
+ // find all observers for this group id
+ observer_iter oi = mObservers.find(gi->first);
+ for (; oi != mObservers.end(); ++oi)
+ {
+ oi->second->changed(gc);
+ }
+ gi->second->mChanged = FALSE;
+ }
+ }
+}
+
+void LLGroupMgr::addGroup(LLGroupMgrGroupData* group_datap)
+{
+ mGroups[group_datap->getID()] = group_datap;
+ if (mGroups.size() > MAX_CACHED_GROUPS)
+ {
+ // get rid of groups that aren't observed
+ for (group_iter gi = mGroups.begin(); gi != mGroups.end() && mGroups.size() > MAX_CACHED_GROUPS / 2; )
+ {
+ observer_iter oi = mObservers.find(gi->first);
+ if (oi == mObservers.end())
+ {
+ // not observed
+ LLGroupMgrGroupData* group_datap = gi->second;
+ delete group_datap;
+ mGroups.erase(gi++);
+ }
+ else
+ {
+ ++gi;
+ }
+ }
+ }
+}
+
+
+void LLGroupMgr::sendGroupPropertiesRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupPropertiesRequest" << llendl;
+ // This will happen when we get the reply
+ //LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupProfileRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ gAgent.sendReliableMessage();
+}
+
+void LLGroupMgr::sendGroupMembersRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupMembersRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+ if (group_datap->mMemberRequestID.isNull())
+ {
+ group_datap->removeMemberData();
+ group_datap->mMemberRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupMembersRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mMemberRequestID);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupRoleDataRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleDataRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+ if (group_datap->mRoleDataRequestID.isNull())
+ {
+ group_datap->removeRoleData();
+ group_datap->mRoleDataRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupRoleDataRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mRoleDataRequestID);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleMembersRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ if (group_datap->mRoleMembersRequestID.isNull())
+ {
+ // Don't send the request if we don't have all the member or role data
+ if (!group_datap->isMemberDataComplete()
+ || !group_datap->isRoleDataComplete())
+ {
+ // TODO: KLW FIXME: Should we start a member or role data request?
+ llinfos << " Pending: " << (group_datap->mPendingRoleMemberRequest ? "Y" : "N")
+ << " MemberDataComplete: " << (group_datap->mMemberDataComplete ? "Y" : "N")
+ << " RoleDataComplete: " << (group_datap->mRoleDataComplete ? "Y" : "N") << llendl;
+ group_datap->mPendingRoleMemberRequest = TRUE;
+ return;
+ }
+
+ group_datap->removeRoleMemberData();
+ group_datap->mRoleMembersRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupRoleMembersRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mRoleMembersRequestID);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupTitlesRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupTitlesRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ group_datap->mTitles.clear();
+ group_datap->mTitlesRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupTitlesRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mTitlesRequestID);
+
+ gAgent.sendReliableMessage();
+}
+
+void LLGroupMgr::sendGroupTitleUpdate(const LLUUID& group_id, const LLUUID& title_role_id)
+{
+ llinfos << "LLGroupMgr::sendGroupTitleUpdate" << llendl;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupTitleUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("TitleRoleID",title_role_id);
+
+ gAgent.sendReliableMessage();
+
+ // Save the change locally
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+ std::vector<LLGroupTitle>::iterator iter = group_datap->mTitles.begin();
+ std::vector<LLGroupTitle>::iterator end = group_datap->mTitles.end();
+
+ for ( ; iter != end; ++iter)
+ {
+ if (iter->mRoleID == title_role_id)
+ {
+ iter->mSelected = TRUE;
+ }
+ else if (iter->mSelected)
+ {
+ iter->mSelected = FALSE;
+ }
+ }
+}
+
+// static
+void LLGroupMgr::sendCreateGroupRequest(const std::string& name,
+ const std::string& charter,
+ U8 show_in_list,
+ const LLUUID& insignia,
+ S32 membership_fee,
+ BOOL open_enrollment,
+ BOOL allow_publish,
+ BOOL mature_publish)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("CreateGroupRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+
+ msg->nextBlock("GroupData");
+ msg->addString("Name",name);
+ msg->addString("Charter",charter);
+ msg->addBOOL("ShowInList",show_in_list);
+ msg->addUUID("InsigniaID",insignia);
+ msg->addS32("MembershipFee",membership_fee);
+ msg->addBOOL("OpenEnrollment",open_enrollment);
+ msg->addBOOL("AllowPublish",allow_publish);
+ msg->addBOOL("MaturePublish",mature_publish);
+
+ gAgent.sendReliableMessage();
+}
+
+void LLGroupMgr::sendUpdateGroupInfo(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendUpdateGroupInfo" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_UpdateGroupInfo);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID,gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID,group_datap->getID());
+ msg->addStringFast(_PREHASH_Charter,group_datap->mCharter);
+ msg->addBOOLFast(_PREHASH_ShowInList,group_datap->mShowInList);
+ msg->addUUIDFast(_PREHASH_InsigniaID,group_datap->mInsigniaID);
+ msg->addS32Fast(_PREHASH_MembershipFee,group_datap->mMembershipFee);
+ msg->addBOOLFast(_PREHASH_OpenEnrollment,group_datap->mOpenEnrollment);
+ msg->addBOOLFast(_PREHASH_AllowPublish,group_datap->mAllowPublish);
+ msg->addBOOLFast(_PREHASH_MaturePublish,group_datap->mMaturePublish);
+
+ gAgent.sendReliableMessage();
+
+ // Not expecting a response, so let anyone else watching know the data has changed.
+ group_datap->mChanged = TRUE;
+ notifyObservers(GC_PROPERTIES);
+}
+
+void LLGroupMgr::sendGroupRoleMemberChanges(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleMemberChanges" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ if (group_datap->mRoleMemberChanges.empty()) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ change_map::const_iterator citer = group_datap->mRoleMemberChanges.begin();
+ change_map::const_iterator end = group_datap->mRoleMemberChanges.end();
+ bool start_message = true;
+
+ for ( ; citer != end; ++citer)
+ {
+ if (start_message)
+ {
+ msg->newMessage("GroupRoleChanges");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID,gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID,group_id);
+ start_message = false;
+ }
+ msg->nextBlock("RoleChange");
+ msg->addUUID("RoleID",citer->second.mRole);
+ msg->addUUID("MemberID",citer->second.mMember);
+ msg->addU32("Change",(U32)citer->second.mChange);
+
+ if (msg->isSendFullFast())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ group_datap->mRoleMemberChanges.clear();
+
+ // Not expecting a response, so let anyone else watching know the data has changed.
+ group_datap->mChanged = TRUE;
+ notifyObservers(GC_ROLE_MEMBER_DATA);
+}
+
+//static
+void LLGroupMgr::sendGroupMemberJoin(const LLUUID& group_id)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_JoinGroupRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID, group_id);
+
+ gAgent.sendReliableMessage();
+}
+
+// member_role_pairs is <member_id,role_id>
+// static
+void LLGroupMgr::sendGroupMemberInvites(const LLUUID& group_id, std::map<LLUUID,LLUUID>& member_role_pairs)
+{
+ bool start_message = true;
+ LLMessageSystem* msg = gMessageSystem;
+
+ std::map<LLUUID,LLUUID>::iterator it = member_role_pairs.begin();
+ std::map<LLUUID,LLUUID>::iterator end = member_role_pairs.end();
+ for ( ; it != end; ++it)
+ {
+ if (start_message)
+ {
+ msg->newMessage("InviteGroupRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ start_message = false;
+ }
+
+ msg->nextBlock("InviteData");
+ msg->addUUID("InviteeID",(*it).first);
+ msg->addUUID("RoleID",(*it).second);
+
+ if (msg->isSendFull())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+//static
+void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id,
+ std::vector<LLUUID>& member_ids)
+{
+ bool start_message = true;
+ LLMessageSystem* msg = gMessageSystem;
+
+ LLGroupMgrGroupData* group_datap = gGroupMgr->getGroupData(group_id);
+ if (!group_datap) return;
+
+ std::vector<LLUUID>::iterator it = member_ids.begin();
+ std::vector<LLUUID>::iterator end = member_ids.end();
+ for ( ; it != end; ++it)
+ {
+ // Can't use 'eject' to leave a group.
+ if ((*it) == gAgent.getID()) continue;
+
+ // Make sure they are in the group, and we need the member data
+ LLGroupMgrGroupData::member_iter mit = group_datap->mMembers.find(*it);
+ if (mit != group_datap->mMembers.end())
+ {
+ // Add them to the message
+ if (start_message)
+ {
+ msg->newMessage("EjectGroupMemberRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ start_message = false;
+ }
+
+ msg->nextBlock("EjectData");
+ msg->addUUID("EjecteeID",(*it));
+
+ if (msg->isSendFull())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+
+ // Clean up groupmgr
+ std::map<LLUUID,LLGroupRoleData*>::iterator rit = (*mit).second->roleBegin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator rend = (*mit).second->roleEnd();
+ for ( ; rit != rend; ++rit)
+ {
+ if ((*rit).first.notNull())
+ {
+ (*rit).second->removeMember(*it);
+ }
+ }
+ delete (*mit).second;
+ group_datap->mMembers.erase(*it);
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupRoleChanges(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleChanges" << llendl;
+ LLGroupMgrGroupData* group_datap = getGroupData(group_id);
+
+ if (group_datap && group_datap->pendingRoleChanges())
+ {
+ group_datap->sendRoleChanges();
+
+ // Not expecting a response, so let anyone else watching know the data has changed.
+ group_datap->mChanged = TRUE;
+ notifyObservers(GC_ROLE_DATA);
+ }
+}
+
+void LLGroupMgr::cancelGroupRoleChanges(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::cancelGroupRoleChanges" << llendl;
+ LLGroupMgrGroupData* group_datap = getGroupData(group_id);
+
+ if (group_datap) group_datap->cancelRoleChanges();
+}
+
+//static
+bool LLGroupMgr::parseRoleActions(const LLString& xml_filename)
+{
+ LLXmlTree xml_tree;
+ LLString xml_file = LLUI::locateSkin(xml_filename);
+ BOOL success = xml_tree.parseFile(xml_file, TRUE );
+ LLXmlTreeNode* root = xml_tree.getRoot();
+ if (!success || !root || !root->hasName( "role_actions" ))
+ {
+ llerrs << "Problem reading UI role_actions file: " << xml_filename << llendl;
+ return false;
+ }
+
+ for (LLXmlTreeNode* action_set = root->getChildByName("action_set");
+ action_set != NULL; action_set = root->getNextNamedChild())
+ {
+ LLRoleActionSet* role_action_set = new LLRoleActionSet();
+ LLRoleAction* role_action_data = new LLRoleAction();
+ // name=
+ LLString action_set_name;
+ if (action_set->getAttributeString("name", action_set_name))
+ {
+ lldebugs << "Loading action set " << action_set_name << llendl;
+ role_action_data->mName = action_set_name;
+ }
+ else
+ {
+ llwarns << "Unable to parse action set with no name" << llendl;
+ delete role_action_set;
+ delete role_action_data;
+ continue;
+ }
+ // description=
+ LLString set_description;
+ if (action_set->getAttributeString("description", set_description))
+ {
+ role_action_data->mDescription = set_description;
+ }
+ // long description=
+ LLString set_longdescription;
+ if (action_set->getAttributeString("longdescription", set_longdescription))
+ {
+ role_action_data->mLongDescription = set_longdescription;
+ }
+
+ // power mask=
+ U64 set_power_mask = 0;
+
+ for (LLXmlTreeNode* action = action_set->getChildByName("action");
+ action != NULL; action = action_set->getNextNamedChild())
+ {
+ LLRoleAction* role_action = new LLRoleAction();
+
+ // name=
+ LLString action_name;
+ if (action->getAttributeString("name", action_name))
+ {
+ lldebugs << "Loading action " << action_name << llendl;
+ role_action->mName = action_name;
+ }
+ else
+ {
+ llwarns << "Unable to parse action with no name" << llendl;
+ delete role_action;
+ continue;
+ }
+ // description=
+ LLString description;
+ if (action->getAttributeString("description", description))
+ {
+ role_action->mDescription = description;
+ }
+ // long description=
+ LLString longdescription;
+ if (action->getAttributeString("longdescription", longdescription))
+ {
+ role_action->mLongDescription = longdescription;
+ }
+ // description=
+ S32 power_bit = 0;
+ if (action->getAttributeS32("value", power_bit))
+ {
+ if (0 <= power_bit && power_bit < 64)
+ {
+ role_action->mPowerBit = 0x1LL << power_bit;
+ }
+ }
+
+ set_power_mask |= role_action->mPowerBit;
+
+ role_action_set->mActions.push_back(role_action);
+ }
+
+ role_action_data->mPowerBit = set_power_mask;
+ role_action_set->mActionSetData = role_action_data;
+
+ gGroupMgr->mRoleActionSets.push_back(role_action_set);
+ }
+ return true;
+}
+
+// static
+void LLGroupMgr::debugClearAllGroups(void*)
+{
+ gGroupMgr->clearGroups();
+ LLGroupMgr::parseRoleActions("role_actions.xml");
+}
+
diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h
new file mode 100644
index 0000000000..7c06acbc1f
--- /dev/null
+++ b/indra/newview/llgroupmgr.h
@@ -0,0 +1,343 @@
+/**
+ * @file llgroupmgr.h
+ * @brief Manager for aggregating all client knowledge for specific groups
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLGROUPMGR_H
+#define LL_LLGROUPMGR_H
+
+#include "lluuid.h"
+#include "roles_constants.h"
+#include <vector>
+#include <string>
+#include <map>
+
+class LLMessageSystem;
+
+class LLGroupMgrObserver
+{
+public:
+ LLGroupMgrObserver(const LLUUID& id) : mID(id){};
+ virtual ~LLGroupMgrObserver(){};
+ virtual void changed(LLGroupChange gc) = 0;
+ const LLUUID& getID() { return mID; }
+protected:
+ LLUUID mID;
+};
+
+class LLGroupRoleData;
+
+class LLGroupMemberData
+{
+friend class LLGroupMgrGroupData;
+
+public:
+ LLGroupMemberData(const LLUUID& id,
+ S32 contribution,
+ U64 agent_powers,
+ const std::string& title,
+ const std::string& online_status,
+ BOOL is_owner);
+
+ ~LLGroupMemberData();
+
+ const LLUUID& getID() const { return mID; }
+ S32 getContribution() const { return mContribution; }
+ U64 getAgentPowers() const { return mAgentPowers; }
+ BOOL isOwner() const { return mIsOwner; }
+ const std::string& getTitle() const { return mTitle; }
+ const std::string& getOnlineStatus() const { return mOnlineStatus; }
+ void addRole(const LLUUID& role, LLGroupRoleData* rd);
+ bool removeRole(const LLUUID& role);
+ void clearRoles() { mRoles.clear(); };
+ std::map<LLUUID,LLGroupRoleData*>::iterator roleBegin() { return mRoles.begin(); }
+ std::map<LLUUID,LLGroupRoleData*>::iterator roleEnd() { return mRoles.end(); }
+
+ BOOL isInRole(const LLUUID& role_id) { return (mRoles.find(role_id) != mRoles.end()); }
+
+protected:
+ LLUUID mID;
+ S32 mContribution;
+ U64 mAgentPowers;
+ std::string mTitle;
+ std::string mOnlineStatus;
+ BOOL mIsOwner;
+ std::map<LLUUID,LLGroupRoleData*> mRoles;
+};
+
+struct LLRoleData
+{
+ LLRoleData() : mRolePowers(0), mChangeType(RC_UPDATE_NONE) { }
+ LLRoleData(const LLRoleData& rd)
+ : mRoleName(rd.mRoleName),
+ mRoleTitle(rd.mRoleTitle),
+ mRoleDescription(rd.mRoleDescription),
+ mRolePowers(rd.mRolePowers),
+ mChangeType(rd.mChangeType) { }
+
+ std::string mRoleName;
+ std::string mRoleTitle;
+ std::string mRoleDescription;
+ U64 mRolePowers;
+ LLRoleChangeType mChangeType;
+};
+
+class LLGroupRoleData
+{
+friend class LLGroupMgrGroupData;
+
+public:
+ LLGroupRoleData(const LLUUID& role_id,
+ const std::string& role_name,
+ const std::string& role_title,
+ const std::string& role_desc,
+ const U64 role_powers,
+ const S32 member_count);
+
+ LLGroupRoleData(const LLUUID& role_id,
+ LLRoleData role_data,
+ const S32 member_count);
+
+ ~LLGroupRoleData();
+
+ const LLUUID& getID() const { return mRoleID; }
+
+ const std::vector<LLUUID>& getRoleMembers() const { return mMemberIDs; }
+ S32 getMembersInRole(std::vector<LLUUID> members, BOOL needs_sort = TRUE);
+ S32 getTotalMembersInRole() { return mMemberIDs.size(); }
+
+ LLRoleData getRoleData() const { return mRoleData; }
+ void setRoleData(LLRoleData data) { mRoleData = data; }
+
+ void addMember(const LLUUID& member);
+ bool removeMember(const LLUUID& member);
+ void clearMembers();
+
+ const std::vector<LLUUID>::const_iterator getMembersBegin() const
+ { return mMemberIDs.begin(); }
+
+ const std::vector<LLUUID>::const_iterator getMembersEnd() const
+ { return mMemberIDs.end(); }
+
+
+protected:
+ LLGroupRoleData()
+ : mMemberCount(0), mMembersNeedsSort(FALSE) {}
+
+ LLUUID mRoleID;
+ LLRoleData mRoleData;
+
+ std::vector<LLUUID> mMemberIDs;
+ S32 mMemberCount;
+
+private:
+ BOOL mMembersNeedsSort;
+};
+
+struct LLRoleMemberChange
+{
+ LLRoleMemberChange() : mChange(RMC_NONE) { }
+ LLRoleMemberChange(const LLUUID& role, const LLUUID& member, LLRoleMemberChangeType change)
+ : mRole(role), mMember(member), mChange(change) { }
+ LLRoleMemberChange(const LLRoleMemberChange& rc)
+ : mRole(rc.mRole), mMember(rc.mMember), mChange(rc.mChange) { }
+ LLUUID mRole;
+ LLUUID mMember;
+ LLRoleMemberChangeType mChange;
+};
+
+typedef std::pair<LLUUID,LLUUID> lluuid_pair;
+
+struct lluuid_pair_less
+{
+ bool operator()(const lluuid_pair& lhs, const lluuid_pair& rhs) const
+ {
+ if (lhs.first == rhs.first)
+ return lhs.second < rhs.second;
+ else
+ return lhs.first < rhs.first;
+ }
+};
+
+typedef std::map<lluuid_pair,LLRoleMemberChange,lluuid_pair_less> change_map;
+
+struct LLGroupTitle
+{
+ std::string mTitle;
+ LLUUID mRoleID;
+ BOOL mSelected;
+};
+
+class LLGroupMgr;
+
+class LLGroupMgrGroupData
+{
+friend class LLGroupMgr;
+
+public:
+ LLGroupMgrGroupData(const LLUUID& id);
+ ~LLGroupMgrGroupData();
+
+ const LLUUID& getID() { return mID; }
+
+ BOOL getRoleData(const LLUUID& role_id, LLRoleData& role_data);
+ void setRoleData(const LLUUID& role_id, LLRoleData role_data);
+ void createRole(const LLUUID& role_id, LLRoleData role_data);
+ void deleteRole(const LLUUID& role_id);
+ BOOL pendingRoleChanges();
+
+ void addRolePower(const LLUUID& role_id, U64 power);
+ void removeRolePower(const LLUUID& role_id, U64 power);
+ U64 getRolePowers(const LLUUID& role_id);
+
+ void removeData();
+ void removeRoleData();
+ void removeMemberData();
+ void removeRoleMemberData();
+
+ bool changeRoleMember(const LLUUID& role_id, const LLUUID& member_id, LLRoleMemberChangeType rmc);
+ void recalcAllAgentPowers();
+ void recalcAgentPowers(const LLUUID& agent_id);
+
+ BOOL isMemberDataComplete() { return mMemberDataComplete; }
+ BOOL isRoleDataComplete() { return mRoleDataComplete; }
+ BOOL isRoleMemberDataComplete() { return mRoleMemberDataComplete; }
+ BOOL isGroupPropertiesDataComplete() { return mGroupPropertiesDataComplete; }
+
+public:
+ typedef std::map<LLUUID,LLGroupMemberData*> member_list;
+ typedef member_list::iterator member_iter;
+ typedef std::map<LLUUID,LLGroupRoleData*> role_list;
+ typedef role_list::iterator role_iter;
+
+ member_list mMembers;
+ role_list mRoles;
+
+
+ change_map mRoleMemberChanges;
+ std::map<LLUUID,LLRoleData> mRoleChanges;
+
+ std::vector<LLGroupTitle> mTitles;
+
+ LLUUID mID;
+ LLUUID mOwnerRole;
+ std::string mName;
+ std::string mCharter;
+ BOOL mShowInList;
+ LLUUID mInsigniaID;
+ LLUUID mFounderID;
+ BOOL mOpenEnrollment;
+ S32 mMembershipFee;
+ BOOL mAllowPublish;
+ BOOL mMaturePublish;
+ BOOL mChanged;
+ S32 mMemberCount;
+ S32 mRoleCount;
+
+protected:
+ void sendRoleChanges();
+ void cancelRoleChanges();
+
+private:
+ LLUUID mMemberRequestID;
+ LLUUID mRoleDataRequestID;
+ LLUUID mRoleMembersRequestID;
+ LLUUID mTitlesRequestID;
+ U32 mReceivedRoleMemberPairs;
+
+ BOOL mMemberDataComplete;
+ BOOL mRoleDataComplete;
+ BOOL mRoleMemberDataComplete;
+ BOOL mGroupPropertiesDataComplete;
+
+ BOOL mPendingRoleMemberRequest;
+};
+
+struct LLRoleAction
+{
+ std::string mName;
+ std::string mDescription;
+ std::string mLongDescription;
+ U64 mPowerBit;
+};
+
+struct LLRoleActionSet
+{
+ LLRoleActionSet();
+ ~LLRoleActionSet();
+ LLRoleAction* mActionSetData;
+ std::vector<LLRoleAction*> mActions;
+};
+
+class LLGroupMgr
+{
+public:
+ LLGroupMgr();
+ ~LLGroupMgr();
+
+ void addObserver(LLGroupMgrObserver* observer);
+ void removeObserver(LLGroupMgrObserver* observer);
+ LLGroupMgrGroupData* getGroupData(const LLUUID& id);
+
+ void sendGroupPropertiesRequest(const LLUUID& group_id);
+ void sendGroupRoleDataRequest(const LLUUID& group_id);
+ void sendGroupRoleMembersRequest(const LLUUID& group_id);
+ void sendGroupMembersRequest(const LLUUID& group_id);
+ void sendGroupTitlesRequest(const LLUUID& group_id);
+ void sendGroupTitleUpdate(const LLUUID& group_id, const LLUUID& title_role_id);
+ void sendUpdateGroupInfo(const LLUUID& group_id);
+ void sendGroupRoleMemberChanges(const LLUUID& group_id);
+ void sendGroupRoleChanges(const LLUUID& group_id);
+
+ static void sendCreateGroupRequest(const std::string& name,
+ const std::string& charter,
+ U8 show_in_list,
+ const LLUUID& insignia,
+ S32 membership_fee,
+ BOOL open_enrollment,
+ BOOL allow_publish,
+ BOOL mature_publish);
+
+ static void sendGroupMemberJoin(const LLUUID& group_id);
+ static void sendGroupMemberInvites(const LLUUID& group_id, std::map<LLUUID,LLUUID>& role_member_pairs);
+ static void sendGroupMemberEjects(const LLUUID& group_id,
+ std::vector<LLUUID>& member_ids);
+
+ void cancelGroupRoleChanges(const LLUUID& group_id);
+
+ static void processGroupPropertiesReply(LLMessageSystem* msg, void** data);
+ static void processGroupMembersReply(LLMessageSystem* msg, void** data);
+ static void processGroupRoleDataReply(LLMessageSystem* msg, void** data);
+ static void processGroupRoleMembersReply(LLMessageSystem* msg, void** data);
+ static void processGroupTitlesReply(LLMessageSystem* msg, void** data);
+ static void processCreateGroupReply(LLMessageSystem* msg, void** data);
+ static void processJoinGroupReply(LLMessageSystem* msg, void ** data);
+ static void processEjectGroupMemberReply(LLMessageSystem* msg, void ** data);
+ static void processLeaveGroupReply(LLMessageSystem* msg, void ** data);
+
+ static bool parseRoleActions(const LLString& xml_filename);
+
+ std::vector<LLRoleActionSet*> mRoleActionSets;
+
+ static void debugClearAllGroups(void*);
+ void clearGroups();
+ void clearGroupData(const LLUUID& group_id);
+protected:
+ void notifyObservers(LLGroupChange gc);
+ void addGroup(LLGroupMgrGroupData* group_datap);
+ LLGroupMgrGroupData* createGroupData(const LLUUID &id);
+
+protected:
+ typedef std::multimap<LLUUID,LLGroupMgrObserver*>::iterator observer_iter;
+ std::multimap<LLUUID,LLGroupMgrObserver*> mObservers;
+ typedef std::map<LLUUID, LLGroupMgrGroupData*>::iterator group_iter;
+ std::map<LLUUID, LLGroupMgrGroupData*> mGroups;
+};
+
+extern LLGroupMgr* gGroupMgr;
+
+#endif
+
diff --git a/indra/newview/llhudeffect.cpp b/indra/newview/llhudeffect.cpp
new file mode 100644
index 0000000000..4f80fffb01
--- /dev/null
+++ b/indra/newview/llhudeffect.cpp
@@ -0,0 +1,115 @@
+/**
+ * @file llhudeffect.cpp
+ * @brief LLHUDEffect class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffect.h"
+
+#include "message.h"
+#include "llgl.h"
+#include "llagent.h"
+#include "llsphere.h"
+#include "llimagegl.h"
+
+#include "llviewerobjectlist.h"
+#include "lldrawable.h"
+
+LLHUDEffect::LLHUDEffect(const U8 type)
+: LLHUDObject(type),
+ mID(),
+ mDuration(1.f),
+ mColor()
+{
+ mNeedsSendToSim = FALSE;
+ mOriginatedHere = FALSE;
+ mDead = FALSE;
+}
+
+LLHUDEffect::~LLHUDEffect()
+{
+}
+
+
+void LLHUDEffect::packData(LLMessageSystem *mesgsys)
+{
+ mesgsys->addUUIDFast(_PREHASH_ID, mID);
+ mesgsys->addU8Fast(_PREHASH_Type, mType);
+ mesgsys->addF32Fast(_PREHASH_Duration, mDuration);
+ mesgsys->addBinaryData(_PREHASH_Color, mColor.mV, 4);
+}
+
+void LLHUDEffect::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, mID, blocknum);
+ mesgsys->getU8Fast(_PREHASH_Effect, _PREHASH_Type, mType, blocknum);
+ mesgsys->getF32Fast(_PREHASH_Effect, _PREHASH_Duration, mDuration, blocknum);
+ mesgsys->getBinaryDataFast(_PREHASH_Effect,_PREHASH_Color, mColor.mV, 4, blocknum);
+}
+
+void LLHUDEffect::render()
+{
+ llerrs << "Never call this!" << llendl;
+}
+
+void LLHUDEffect::setID(const LLUUID &id)
+{
+ mID = id;
+}
+
+const LLUUID &LLHUDEffect::getID() const
+{
+ return mID;
+}
+
+void LLHUDEffect::setColor(const LLColor4U &color)
+{
+ mColor = color;
+}
+
+void LLHUDEffect::setDuration(const F32 duration)
+{
+ mDuration = duration;
+}
+
+void LLHUDEffect::setNeedsSendToSim(const BOOL send_to_sim)
+{
+ mNeedsSendToSim = send_to_sim;
+}
+
+BOOL LLHUDEffect::getNeedsSendToSim() const
+{
+ return mNeedsSendToSim;
+}
+
+
+void LLHUDEffect::setOriginatedHere(const BOOL orig_here)
+{
+ mOriginatedHere = orig_here;
+}
+
+BOOL LLHUDEffect::getOriginatedHere() const
+{
+ return mOriginatedHere;
+}
+
+//static
+void LLHUDEffect::getIDType(LLMessageSystem *mesgsys, S32 blocknum, LLUUID &id, U8 &type)
+{
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, id, blocknum);
+ mesgsys->getU8Fast(_PREHASH_Effect, _PREHASH_Type, type, blocknum);
+}
+
+BOOL LLHUDEffect::isDead() const
+{
+ return mDead;
+}
+
+void LLHUDEffect::update()
+{
+ // do nothing
+}
diff --git a/indra/newview/llhudeffect.h b/indra/newview/llhudeffect.h
new file mode 100644
index 0000000000..948670ceed
--- /dev/null
+++ b/indra/newview/llhudeffect.h
@@ -0,0 +1,62 @@
+/**
+ * @file llhudeffect.h
+ * @brief LLHUDEffect class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECT_H
+#define LL_LLHUDEFFECT_H
+
+#include "llhudobject.h"
+
+#include "lluuid.h"
+#include "v4coloru.h"
+#include "llinterp.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+
+const F32 LL_HUD_DUR_SHORT = 1.f;
+
+class LLMessageSystem;
+
+
+class LLHUDEffect : public LLHUDObject
+{
+public:
+ void setNeedsSendToSim(const BOOL send_to_sim);
+ BOOL getNeedsSendToSim() const;
+ void setOriginatedHere(const BOOL orig_here);
+ BOOL getOriginatedHere() const;
+
+ void setDuration(const F32 duration);
+ void setColor(const LLColor4U &color);
+ void setID(const LLUUID &id);
+ const LLUUID &getID() const;
+
+ BOOL isDead() const;
+
+ friend class LLHUDManager;
+protected:
+ LLHUDEffect(const U8 type);
+ ~LLHUDEffect();
+
+ /*virtual*/ void render();
+
+ virtual void packData(LLMessageSystem *mesgsys);
+ virtual void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+ virtual void update();
+
+ static void getIDType(LLMessageSystem *mesgsys, S32 blocknum, LLUUID &uuid, U8 &type);
+
+protected:
+ LLUUID mID;
+ F32 mDuration;
+ LLColor4U mColor;
+
+ BOOL mNeedsSendToSim;
+ BOOL mOriginatedHere;
+};
+
+#endif // LL_LLHUDEFFECT_H
diff --git a/indra/newview/llhudeffectbeam.cpp b/indra/newview/llhudeffectbeam.cpp
new file mode 100644
index 0000000000..069986eb77
--- /dev/null
+++ b/indra/newview/llhudeffectbeam.cpp
@@ -0,0 +1,371 @@
+/**
+ * @file llhudeffectbeam.cpp
+ * @brief LLHUDEffectBeam class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffectbeam.h"
+#include "message.h"
+
+#include "llviewerobjectlist.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llhudrender.h"
+#include "llimagegl.h"
+#include "llsphere.h"
+#include "llviewercamera.h"
+#include "llvoavatar.h"
+#include "llviewercontrol.h"
+
+const F32 BEAM_SPACING = 0.075f;
+
+LLHUDEffectBeam::LLHUDEffectBeam(const U8 type) : LLHUDEffect(type)
+{
+ mKillTime = mDuration;
+
+ // Initialize all of these to defaults
+ S32 i;
+ for (i = 0; i < NUM_POINTS; i++)
+ {
+ mInterp[i].setStartTime(BEAM_SPACING*i);
+ mInterp[i].setEndTime(BEAM_SPACING*NUM_POINTS + BEAM_SPACING*i);
+ mInterp[i].start();
+ mInterpFade[i].setStartTime(BEAM_SPACING*NUM_POINTS + BEAM_SPACING*i - 0.5f*NUM_POINTS*BEAM_SPACING);
+ mInterpFade[i].setEndTime(BEAM_SPACING*NUM_POINTS + BEAM_SPACING*i);
+ mInterpFade[i].setStartVal(1.f);
+ mInterpFade[i].setEndVal(0.f);
+ }
+
+ // Setup default timeouts and fade animations.
+ F32 fade_length;
+ fade_length = llmin(0.5f, mDuration);
+ mFadeInterp.setStartTime(mKillTime - fade_length);
+ mFadeInterp.setEndTime(mKillTime);
+ mFadeInterp.setStartVal(1.f);
+ mFadeInterp.setEndVal(0.f);
+}
+
+LLHUDEffectBeam::~LLHUDEffectBeam()
+{
+}
+
+void LLHUDEffectBeam::packData(LLMessageSystem *mesgsys)
+{
+ if (!mSourceObject)
+ {
+ llwarns << "Missing source object!" << llendl;
+ }
+
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // Pack the type-specific data. Uses a fun packed binary format. Whee!
+ // 16 + 24 + 1 = 41
+ U8 packed_data[41];
+ memset(packed_data, 0, 41);
+ if (mSourceObject)
+ {
+ htonmemcpy(packed_data, mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+
+ if (mTargetObject)
+ {
+ packed_data[16] = 1;
+ }
+ else
+ {
+ packed_data[16] = 0;
+ }
+
+ if (mTargetObject)
+ {
+ htonmemcpy(&(packed_data[17]), mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[17]), mTargetPos.mdV, MVT_LLVector3d, 24);
+ }
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 41);
+}
+
+void LLHUDEffectBeam::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ llerrs << "Got beam!" << llendl;
+ BOOL use_target_object;
+ LLVector3d new_target;
+ U8 packed_data[41];
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != 41)
+ {
+ llwarns << "Beam effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 41, blocknum);
+
+ htonmemcpy(source_id.mData, packed_data, MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(source_id);
+ if (objp)
+ {
+ setSourceObject(objp);
+ }
+
+ use_target_object = packed_data[16];
+
+ if (use_target_object)
+ {
+ htonmemcpy(target_id.mData, &packed_data[17], MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(target_id);
+ if (objp)
+ {
+ setTargetObject(objp);
+ }
+ }
+ else
+ {
+ htonmemcpy(new_target.mdV, &(packed_data[17]), MVT_LLVector3d, 24);
+ setTargetPos(new_target);
+ }
+
+ // We've received an update for the effect, update the various timeouts
+ // and fade animations.
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+ F32 fade_length;
+ fade_length = llmin(0.5f, mDuration);
+ mFadeInterp.setStartTime(mKillTime - fade_length);
+ mFadeInterp.setEndTime(mKillTime);
+ mFadeInterp.setStartVal(1.f);
+ mFadeInterp.setEndVal(0.f);
+}
+
+void LLHUDEffectBeam::setSourceObject(LLViewerObject *objp)
+{
+ if (objp->isDead())
+ {
+ llwarns << "HUDEffectBeam: Source object is dead!" << llendl;
+ mSourceObject = NULL;
+ return;
+ }
+
+ if (mSourceObject == objp)
+ {
+ return;
+ }
+
+ mSourceObject = objp;
+ if (mSourceObject)
+ {
+ S32 i;
+ for (i = 0; i < NUM_POINTS; i++)
+ {
+ if (mSourceObject->isAvatar())
+ {
+ LLViewerObject *objp = mSourceObject;
+ LLVOAvatar *avatarp = (LLVOAvatar *)objp;
+ LLVector3d hand_pos_global = gAgent.getPosGlobalFromAgent(avatarp->mWristLeftp->getWorldPosition());
+ mInterp[i].setStartVal(hand_pos_global);
+ mInterp[i].start();
+ }
+ else
+ {
+ mInterp[i].setStartVal(mSourceObject->getPositionGlobal());
+ mInterp[i].start();
+ }
+ }
+ }
+}
+
+
+void LLHUDEffectBeam::setTargetObject(LLViewerObject *objp)
+{
+ if (mTargetObject->isDead())
+ {
+ llwarns << "HUDEffectBeam: Target object is dead!" << llendl;
+ }
+
+ mTargetObject = objp;
+}
+
+void LLHUDEffectBeam::setTargetPos(const LLVector3d &pos_global)
+{
+ mTargetPos = pos_global;
+ mTargetObject = NULL;
+}
+
+void LLHUDEffectBeam::render()
+{
+ if (!mSourceObject)
+ {
+ markDead();
+ return;
+ }
+ if (mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+
+ // Kill us if our time is over...
+ if (mKillTime < time)
+ {
+ markDead();
+ return;
+ }
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+
+ // Interpolate the global fade alpha
+ mFadeInterp.update(time);
+
+ if (mTargetObject.notNull() && mTargetObject->mDrawable.notNull())
+ {
+ // use viewer object position on freshly created objects
+ if (mTargetObject->mDrawable->getGeneration() == -1)
+ {
+ mTargetPos = mTargetObject->getPositionGlobal();
+ }
+ // otherwise use drawable
+ else
+ {
+ mTargetPos = gAgent.getPosGlobalFromAgent(mTargetObject->mDrawable->getPositionAgent());
+ }
+ }
+
+
+ // Init the color of the particles
+ LLColor4U coloru = mColor;
+
+ /*
+ // This is disabled for now - DJS
+
+ // Fade the alpha
+ coloru.mV[3] = mFadeInterp.getCurVal()*mColor.mV[3];
+
+ // Draw a regular "beam" that connects the source and target
+
+ // First, figure out start and end positions relative to the camera
+ LLVector3 start_pos_agent;
+ if (mSourceObject->getPCode() == LL_PCODE_LEGACY_AVATAR)
+ {
+ LLViewerObject *objp = mSourceObject;
+ LLVOAvatar *avatarp = (LLVOAvatar *)objp;
+ LLVector3d hand_pos_global = gAgent.getPosGlobalFromAgent(avatarp->mWristLeftp->getWorldPosition());
+ start_pos_agent = gAgent.getPosAgentFromGlobal(hand_pos_global);
+ }
+ else
+ {
+ start_pos_agent = mSourceObject->getPositionAgent();
+ }
+ LLVector3 start_pos_camera = (start_pos_agent - gAgent.getCameraPositionAgent());
+ LLVector3 target_pos_agent = gAgent.getPosAgentFromGlobal(mTargetPos);
+ LLVector3 target_pos_camera = target_pos_agent - gAgent.getCameraPositionAgent();
+
+ // Generate the right "up" vector which is perpendicular to the beam, make it 1/10 meter wide, going to a point.
+ LLVector3 camera_up = gCamera->getUpAxis();
+ LLVector3 camera_at = gCamera->getAtAxis();
+ LLVector3 up = target_pos_camera % start_pos_camera;
+ up.normVec();
+ up *= 0.1f;
+
+ // Draw the triangle for the beam.
+ LLVector3 vertex;
+ glColor4ubv(coloru.mV);
+ glBegin(GL_TRIANGLE_STRIP);
+ vertex = start_pos_agent + up;
+ glVertex3fv(vertex.mV);
+ vertex = start_pos_agent - up;
+ glVertex3fv(vertex.mV);
+ vertex = target_pos_agent;
+ glVertex3fv(vertex.mV);
+ glEnd();
+ */
+
+ // Draw the particles
+ S32 i;
+ for (i = 0; i < NUM_POINTS; i++)
+ {
+ mInterp[i].update(time);
+ if (!mInterp[i].isActive())
+ {
+ continue;
+ }
+ mInterpFade[i].update(time);
+
+ if (mInterp[i].isDone())
+ {
+ // Reinitialize the particle when the particle has finished its animation.
+ setupParticle(i);
+ }
+
+ F32 frac = mInterp[i].getCurFrac();
+ F32 scale = 0.025f + fabs(0.05f*sin(2.f*F_PI*(frac - time)));
+ scale *= mInterpFade[i].getCurVal();
+
+ LLVector3 pos_agent = gAgent.getPosAgentFromGlobal(mInterp[i].getCurVal());
+
+ F32 alpha = mFadeInterp.getCurVal()*mColor.mV[3];
+ alpha *= mInterpFade[i].getCurVal();
+ coloru.mV[3] = (U8)alpha;
+ glColor4ubv(coloru.mV);
+
+ glPushMatrix();
+ glTranslatef(pos_agent.mV[0], pos_agent.mV[1], pos_agent.mV[2]);
+ glScalef(scale, scale, scale);
+ gSphere.render(0);
+ glPopMatrix();
+ }
+}
+
+void LLHUDEffectBeam::setupParticle(const S32 i)
+{
+ LLVector3d start_pos_global;
+ if (mSourceObject->getPCode() == LL_PCODE_LEGACY_AVATAR)
+ {
+ LLViewerObject *objp = mSourceObject;
+ LLVOAvatar *avatarp = (LLVOAvatar *)objp;
+ start_pos_global = gAgent.getPosGlobalFromAgent(avatarp->mWristLeftp->getWorldPosition());
+ }
+ else
+ {
+ start_pos_global = mSourceObject->getPositionGlobal();
+ }
+
+ // Generate a random offset for the target point.
+ const F32 SCALE = 0.5f;
+ F32 x, y, z;
+ x = frand(SCALE) - 0.5f*SCALE;
+ y = frand(SCALE) - 0.5f*SCALE;
+ z = frand(SCALE) - 0.5f*SCALE;
+
+ LLVector3d target_pos_global(mTargetPos);
+ target_pos_global += LLVector3d(x, y, z);
+
+ mInterp[i].setStartTime(mInterp[i].getEndTime());
+ mInterp[i].setEndTime(mInterp[i].getStartTime() + BEAM_SPACING*NUM_POINTS);
+ mInterp[i].setStartVal(start_pos_global);
+ mInterp[i].setEndVal(target_pos_global);
+ mInterp[i].start();
+
+
+ // Setup the interpolator that fades out the alpha.
+ mInterpFade[i].setStartTime(mInterp[i].getStartTime() + BEAM_SPACING*NUM_POINTS - 0.5f*NUM_POINTS*BEAM_SPACING);
+ mInterpFade[i].setEndTime(mInterp[i].getStartTime() + BEAM_SPACING*NUM_POINTS - 0.05f);
+ mInterpFade[i].start();
+}
diff --git a/indra/newview/llhudeffectbeam.h b/indra/newview/llhudeffectbeam.h
new file mode 100644
index 0000000000..9cb28ac1dc
--- /dev/null
+++ b/indra/newview/llhudeffectbeam.h
@@ -0,0 +1,51 @@
+/**
+ * @file llhudeffectbeam.h
+ * @brief LLHUDEffectBeam class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTBEAM_H
+#define LL_LLHUDEFFECTBEAM_H
+
+#include "llhudeffect.h"
+
+#include "llframetimer.h"
+
+#include "llinterp.h"
+
+class LLViewerObject;
+
+const S32 NUM_POINTS = 5;
+
+class LLHUDEffectBeam : public LLHUDEffect
+{
+public:
+ /*virtual*/ void setSourceObject(LLViewerObject *objp);
+
+ // A beam can have either a target object or a target position
+ void setTargetObject(LLViewerObject *objp);
+ void setTargetPos(const LLVector3d &target_pos_global);
+
+ friend class LLHUDObject;
+protected:
+ LLHUDEffectBeam(const U8 type);
+ ~LLHUDEffectBeam();
+
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+private:
+ void setupParticle(const S32 i);
+
+
+ F32 mKillTime;
+ LLFrameTimer mTimer;
+ LLInterpLinear<LLVector3d> mInterp[NUM_POINTS];
+ LLInterpLinear<F32> mInterpFade[NUM_POINTS];
+ LLInterpLinear<F32> mFadeInterp;
+ LLVector3d mTargetPos;
+};
+
+#endif // LL_LLHUDEFFECTBEAM_H
diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp
new file mode 100644
index 0000000000..9f1b80e1ba
--- /dev/null
+++ b/indra/newview/llhudeffectlookat.cpp
@@ -0,0 +1,501 @@
+/**
+ * @file llhudeffectlookat.cpp
+ * @brief LLHUDEffectLookAt class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffectlookat.h"
+
+#include "message.h"
+#include "llagent.h"
+#include "llvoavatar.h"
+#include "lldrawable.h"
+#include "llviewerobjectlist.h"
+#include "llsphere.h"
+#include "llselectmgr.h"
+#include "llglheaders.h"
+
+BOOL LLHUDEffectLookAt::sDebugLookAt = FALSE;
+
+// packet layout
+const S32 SOURCE_AVATAR = 0;
+const S32 TARGET_OBJECT = 16;
+const S32 TARGET_POS = 32;
+const S32 LOOKAT_TYPE = 56;
+const S32 PKT_SIZE = 57;
+
+// throttle
+const F32 MAX_SENDS_PER_SEC = 4.f;
+
+const F32 MIN_DELTAPOS_FOR_UPDATE = 0.05f;
+const F32 MIN_TARGET_OFFSET_SQUARED = 0.0001f;
+
+// timeouts
+// can't use actual F32_MAX, because we add this to the current frametime
+const F32 MAX_TIMEOUT = F32_MAX / 2.f;
+
+const F32 LOOKAT_TIMEOUTS[LOOKAT_NUM_TARGETS] =
+{
+ MAX_TIMEOUT, //LOOKAT_TARGET_NONE
+ 3.f, //LOOKAT_TARGET_IDLE
+ 4.f, //LOOKAT_TARGET_AUTO_LISTEN
+ 2.f, //LOOKAT_TARGET_FREELOOK
+ 4.f, //LOOKAT_TARGET_RESPOND
+ 1.f, //LOOKAT_TARGET_HOVER
+ MAX_TIMEOUT, //LOOKAT_TARGET_CONVERSATION
+ MAX_TIMEOUT, //LOOKAT_TARGET_SELECT
+ MAX_TIMEOUT, //LOOKAT_TARGET_FOCUS
+ MAX_TIMEOUT, //LOOKAT_TARGET_MOUSELOOK
+ 0.f, //LOOKAT_TARGET_CLEAR
+};
+
+const S32 LOOKAT_PRIORITIES[LOOKAT_NUM_TARGETS] =
+{
+ 0, //LOOKAT_TARGET_NONE
+ 1, //LOOKAT_TARGET_IDLE
+ 3, //LOOKAT_TARGET_AUTO_LISTEN
+ 2, //LOOKAT_TARGET_FREELOOK
+ 3, //LOOKAT_TARGET_RESPOND
+ 4, //LOOKAT_TARGET_HOVER
+ 5, //LOOKAT_TARGET_CONVERSATION
+ 6, //LOOKAT_TARGET_SELECT
+ 6, //LOOKAT_TARGET_FOCUS
+ 7, //LOOKAT_TARGET_MOUSELOOK
+ 8, //LOOKAT_TARGET_CLEAR
+};
+
+const char *LOOKAT_STRINGS[] =
+{
+ "None", //LOOKAT_TARGET_NONE
+ "Idle", //LOOKAT_TARGET_IDLE
+ "AutoListen", //LOOKAT_TARGET_AUTO_LISTEN
+ "FreeLook", //LOOKAT_TARGET_FREELOOK
+ "Respond", //LOOKAT_TARGET_RESPOND
+ "Hover", //LOOKAT_TARGET_HOVER
+ "Conversation", //LOOKAT_TARGET_CONVERSATION
+ "Select", //LOOKAT_TARGET_SELECT
+ "Focus", //LOOKAT_TARGET_FOCUS
+ "Mouselook", //LOOKAT_TARGET_MOUSELOOK
+ "Clear", //LOOKAT_TARGET_CLEAR
+};
+
+const LLColor3 LOOKAT_COLORS[LOOKAT_NUM_TARGETS] =
+{
+ LLColor3(0.3f, 0.3f, 0.3f), //LOOKAT_TARGET_NONE
+ LLColor3(0.5f, 0.5f, 0.5f), //LOOKAT_TARGET_IDLE
+ LLColor3(0.5f, 0.5f, 0.5f), //LOOKAT_TARGET_AUTO_LISTEN
+ LLColor3(0.5f, 0.5f, 0.9f), //LOOKAT_TARGET_FREELOOK
+ LLColor3(0.f, 0.f, 0.f), //LOOKAT_TARGET_RESPOND
+ LLColor3(0.5f, 0.9f, 0.5f), //LOOKAT_TARGET_HOVER
+ LLColor3(0.1f, 0.1f, 0.5f), //LOOKAT_TARGET_CONVERSATION
+ LLColor3(0.9f, 0.5f, 0.5f), //LOOKAT_TARGET_SELECT
+ LLColor3(0.9f, 0.5f, 0.9f), //LOOKAT_TARGET_FOCUS
+ LLColor3(0.9f, 0.9f, 0.5f), //LOOKAT_TARGET_MOUSELOOK
+ LLColor3(1.f, 1.f, 1.f), //LOOKAT_TARGET_CLEAR
+};
+
+//-----------------------------------------------------------------------------
+// LLHUDEffectLookAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectLookAt::LLHUDEffectLookAt(const U8 type) :
+ LLHUDEffect(type),
+ mKillTime(0.f),
+ mLastSendTime(0.f)
+{
+ clearLookAtTarget();
+}
+
+//-----------------------------------------------------------------------------
+// ~LLHUDEffectLookAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectLookAt::~LLHUDEffectLookAt()
+{
+}
+
+//-----------------------------------------------------------------------------
+// packData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::packData(LLMessageSystem *mesgsys)
+{
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // Pack the type-specific data. Uses a fun packed binary format. Whee!
+ U8 packed_data[PKT_SIZE];
+ memset(packed_data, 0, PKT_SIZE);
+
+ if (mSourceObject)
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ // pack both target object and position
+ // position interpreted as offset if target object is non-null
+ if (mTargetObject)
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ htonmemcpy(&(packed_data[TARGET_POS]), mTargetOffsetGlobal.mdV, MVT_LLVector3d, 24);
+
+ U8 lookAtTypePacked = (U8)mTargetType;
+
+ htonmemcpy(&(packed_data[LOOKAT_TYPE]), &lookAtTypePacked, MVT_U8, 1);
+
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE);
+
+ mLastSendTime = mTimer.getElapsedTimeF32();
+}
+
+//-----------------------------------------------------------------------------
+// unpackData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ LLVector3d new_target;
+ U8 packed_data[PKT_SIZE];
+
+ LLUUID dataId;
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, dataId, blocknum);
+
+ if (!gAgent.mLookAt.isNull() && dataId == gAgent.mLookAt->getID())
+ {
+ return;
+ }
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != PKT_SIZE)
+ {
+ llwarns << "LookAt effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum);
+
+ htonmemcpy(source_id.mData, &(packed_data[SOURCE_AVATAR]), MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(source_id);
+ if (objp && objp->isAvatar())
+ {
+ setSourceObject(objp);
+ }
+ else
+ {
+ //llwarns << "Could not find source avatar for lookat effect" << llendl;
+ return;
+ }
+
+ htonmemcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16);
+
+ objp = gObjectList.findObject(target_id);
+
+ htonmemcpy(new_target.mdV, &(packed_data[TARGET_POS]), MVT_LLVector3d, 24);
+
+ if (objp)
+ {
+ setTargetObjectAndOffset(objp, new_target);
+ }
+ else if (target_id.isNull())
+ {
+ setTargetPosGlobal(new_target);
+ }
+ else
+ {
+ //llwarns << "Could not find target object for lookat effect" << llendl;
+ }
+
+ U8 lookAtTypeUnpacked = 0;
+ htonmemcpy(&lookAtTypeUnpacked, &(packed_data[LOOKAT_TYPE]), MVT_U8, 1);
+ mTargetType = (ELookAtType)lookAtTypeUnpacked;
+
+ if (mTargetType == LOOKAT_TARGET_NONE)
+ {
+ clearLookAtTarget();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setTargetObjectAndOffset()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset)
+{
+ mTargetObject = objp;
+ mTargetOffsetGlobal = offset;
+}
+
+//-----------------------------------------------------------------------------
+// setTargetPosGlobal()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::setTargetPosGlobal(const LLVector3d &target_pos_global)
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal = target_pos_global;
+}
+
+//-----------------------------------------------------------------------------
+// setLookAt()
+// called by agent logic to set look at behavior locally, and propagate to sim
+//-----------------------------------------------------------------------------
+BOOL LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ if (!mSourceObject)
+ {
+ return FALSE;
+ }
+
+ llassert(target_type < LOOKAT_NUM_TARGETS);
+
+ // must be same or higher priority than existing effect
+ if (LOOKAT_PRIORITIES[target_type] < LOOKAT_PRIORITIES[mTargetType])
+ {
+ return FALSE;
+ }
+
+ F32 current_time = mTimer.getElapsedTimeF32();
+
+ // type of lookat behavior or target object has changed
+ BOOL lookAtChanged = (target_type != mTargetType) ||
+ (object != mTargetObject);
+
+ // lookat position has moved a certain amount and we haven't just sent an update
+ lookAtChanged = lookAtChanged || (dist_vec(position, mLastSentOffsetGlobal) > MIN_DELTAPOS_FOR_UPDATE) &&
+ ((current_time - mLastSendTime) > (1.f / MAX_SENDS_PER_SEC));
+
+ if (lookAtChanged)
+ {
+ mLastSentOffsetGlobal = position;
+ setDuration(LOOKAT_TIMEOUTS[target_type]);
+ setNeedsSendToSim(TRUE);
+ }
+
+ if (target_type == LOOKAT_TARGET_CLEAR)
+ {
+ clearLookAtTarget();
+ }
+ else
+ {
+ mTargetType = target_type;
+ mTargetObject = object;
+ if (object)
+ {
+ mTargetOffsetGlobal.setVec(position);
+ }
+ else
+ {
+ mTargetOffsetGlobal = gAgent.getPosGlobalFromAgent(position);
+ }
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+
+ update();
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// clearLookAtTarget()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::clearLookAtTarget()
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal.clearVec();
+ mTargetType = LOOKAT_TARGET_NONE;
+ if (mSourceObject.notNull())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->stopMotion(ANIM_AGENT_HEAD_ROT);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// markDead()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::markDead()
+{
+ if (mSourceObject.notNull())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("LookAtPoint");
+ }
+
+ mSourceObject = NULL;
+ clearLookAtTarget();
+ LLHUDEffect::markDead();
+}
+
+void LLHUDEffectLookAt::setSourceObject(LLViewerObject* objectp)
+{
+ // restrict source objects to avatars
+ if (objectp && objectp->isAvatar())
+ {
+ LLHUDEffect::setSourceObject(objectp);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::render()
+{
+ if (sDebugLookAt && mSourceObject.notNull())
+ {
+ LLGLSNoTexture gls_no_texture;
+
+ LLVector3 target = mTargetPos + ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->mHeadp->getWorldPosition();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef(target.mV[VX], target.mV[VY], target.mV[VZ]);
+ glScalef(0.3f, 0.3f, 0.3f);
+ glBegin(GL_LINES);
+ {
+ LLColor3 color = LOOKAT_COLORS[mTargetType];
+ glColor3f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]);
+ glVertex3f(-1.f, 0.f, 0.f);
+ glVertex3f(1.f, 0.f, 0.f);
+
+ glVertex3f(0.f, -1.f, 0.f);
+ glVertex3f(0.f, 1.f, 0.f);
+
+ glVertex3f(0.f, 0.f, -1.f);
+ glVertex3f(0.f, 0.f, 1.f);
+ } glEnd();
+ glPopMatrix();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::update()
+{
+ // If the target object is dead, set the target object to NULL
+ if (!mTargetObject.isNull() && mTargetObject->isDead())
+ {
+ clearLookAtTarget();
+ }
+
+ // if source avatar is null or dead, mark self as dead and return
+ if (mSourceObject.isNull() || mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+
+ // clear out the effect if time is up
+ if (mKillTime != 0.f && time > mKillTime)
+ {
+ if (mTargetType != LOOKAT_TARGET_NONE)
+ {
+ clearLookAtTarget();
+ // look at timed out (only happens on own avatar), so tell everyone
+ setNeedsSendToSim(TRUE);
+ }
+ }
+
+ if (mTargetType != LOOKAT_TARGET_NONE)
+ {
+ calcTargetPosition();
+
+ LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT);
+ if (!head_motion || head_motion->isStopped())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_HEAD_ROT);
+ }
+ }
+
+ if (sDebugLookAt)
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->addDebugText(LOOKAT_STRINGS[mTargetType]);
+ }
+}
+
+void LLHUDEffectLookAt::calcTargetPosition()
+{
+ LLViewerObject *targetObject = (LLViewerObject *)mTargetObject;
+ LLVector3 local_offset;
+
+ if (targetObject)
+ {
+ local_offset.setVec(mTargetOffsetGlobal);
+ }
+ else
+ {
+ local_offset = gAgent.getPosAgentFromGlobal(mTargetOffsetGlobal);
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+ LLVector3 head_position = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->mHeadp->getWorldPosition();
+
+ if (targetObject && targetObject->mDrawable.notNull())
+ {
+ LLQuaternion objRot;
+ if (targetObject->isAvatar())
+ {
+ LLVOAvatar *avatarp = (LLVOAvatar *)targetObject;
+
+ BOOL looking_at_self = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->isSelf() && avatarp->isSelf();
+
+ // if selecting self, stare forward
+ if (looking_at_self && mTargetOffsetGlobal.magVecSquared() < MIN_TARGET_OFFSET_SQUARED)
+ {
+ //sets the lookat point in front of the avatar
+ mTargetOffsetGlobal.setVec(5.0, 0.0, 0.0);
+ }
+
+ mTargetPos = avatarp->mHeadp->getWorldPosition();
+ if (mTargetType == LOOKAT_TARGET_MOUSELOOK || mTargetType == LOOKAT_TARGET_FREELOOK)
+ {
+ // mouselook and freelook target offsets are absolute
+ objRot = LLQuaternion::DEFAULT;
+ }
+ else if (looking_at_self && gAgent.cameraCustomizeAvatar())
+ {
+ //FIXME: have animation overrides for lookat behavior and then we don't need to do this
+ objRot = avatarp->mPelvisp->getWorldRotation();
+ }
+ else
+ {
+ objRot = avatarp->mRoot.getWorldRotation();
+ }
+ }
+ else
+ {
+ if (targetObject->mDrawable->getGeneration() == -1)
+ {
+ mTargetPos = targetObject->getPositionAgent();
+ objRot = targetObject->getWorldRotation();
+ }
+ else
+ {
+ mTargetPos = targetObject->getRenderPosition();
+ objRot = targetObject->getRenderRotation();
+ }
+ }
+
+ mTargetPos += (local_offset * objRot);
+ }
+ else
+ {
+ mTargetPos = local_offset;
+ }
+
+ mTargetPos -= head_position;
+
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->setAnimationData("LookAtPoint", (void *)&mTargetPos);
+}
diff --git a/indra/newview/llhudeffectlookat.h b/indra/newview/llhudeffectlookat.h
new file mode 100644
index 0000000000..1b50b7d566
--- /dev/null
+++ b/indra/newview/llhudeffectlookat.h
@@ -0,0 +1,76 @@
+/**
+ * @file llhudeffectlookat.h
+ * @brief LLHUDEffectLookAt class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTLOOKAT_H
+#define LL_LLHUDEFFECTLOOKAT_H
+
+#include "llhudeffect.h"
+#include "llskiplist.h"
+
+class LLViewerObject;
+class LLVOAvatar;
+
+typedef enum e_lookat_type
+{
+ LOOKAT_TARGET_NONE,
+ LOOKAT_TARGET_IDLE,
+ LOOKAT_TARGET_AUTO_LISTEN,
+ LOOKAT_TARGET_FREELOOK,
+ LOOKAT_TARGET_RESPOND,
+ LOOKAT_TARGET_HOVER,
+ LOOKAT_TARGET_CONVERSATION, // conversation mode deprecated
+ LOOKAT_TARGET_SELECT,
+ LOOKAT_TARGET_FOCUS,
+ LOOKAT_TARGET_MOUSELOOK,
+ LOOKAT_TARGET_CLEAR,
+ LOOKAT_NUM_TARGETS
+} ELookAtType;
+
+class LLHUDEffectLookAt : public LLHUDEffect
+{
+public:
+ friend class LLHUDObject;
+
+ /*virtual*/ void markDead();
+ /*virtual*/ void setSourceObject(LLViewerObject* objectp);
+
+ BOOL setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position);
+ void clearLookAtTarget();
+
+ ELookAtType getLookAtType() { return mTargetType; }
+ const LLVector3& getTargetPos() { return mTargetPos; }
+ const LLVector3d& getTargetOffset() { return mTargetOffsetGlobal; }
+ void calcTargetPosition();
+
+protected:
+ LLHUDEffectLookAt(const U8 type);
+ ~LLHUDEffectLookAt();
+
+ /*virtual*/ void update();
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+
+ // lookat behavior has either target position or target object with offset
+ void setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset);
+ void setTargetPosGlobal(const LLVector3d &target_pos_global);
+
+public:
+ static BOOL sDebugLookAt;
+
+private:
+ ELookAtType mTargetType;
+ LLVector3d mTargetOffsetGlobal;
+ LLVector3 mLastSentOffsetGlobal;
+ F32 mKillTime;
+ LLFrameTimer mTimer;
+ LLVector3 mTargetPos;
+ F32 mLastSendTime;
+};
+
+#endif // LL_LLHUDEFFECTLOOKAT_H
diff --git a/indra/newview/llhudeffectpointat.cpp b/indra/newview/llhudeffectpointat.cpp
new file mode 100644
index 0000000000..f72c5b06de
--- /dev/null
+++ b/indra/newview/llhudeffectpointat.cpp
@@ -0,0 +1,432 @@
+/**
+ * @file llhudeffectpointat.cpp
+ * @brief LLHUDEffectPointAt class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffectpointat.h"
+
+#include "llgl.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+#include "message.h"
+
+// packet layout
+const S32 SOURCE_AVATAR = 0;
+const S32 TARGET_OBJECT = 16;
+const S32 TARGET_POS = 32;
+const S32 POINTAT_TYPE = 56;
+const S32 PKT_SIZE = 57;
+
+// throttle
+const F32 MAX_SENDS_PER_SEC = 4.f;
+
+const F32 MIN_DELTAPOS_FOR_UPDATE = 0.05f;
+
+// timeouts
+// can't use actual F32_MAX, because we add this to the current frametime
+const F32 MAX_TIMEOUT = F32_MAX / 4.f;
+
+const F32 POINTAT_TIMEOUTS[POINTAT_NUM_TARGETS] =
+{
+ MAX_TIMEOUT, //POINTAT_TARGET_NONE
+ MAX_TIMEOUT, //POINTAT_TARGET_SELECT
+ MAX_TIMEOUT, //POINTAT_TARGET_GRAB
+ 0.f, //POINTAT_TARGET_CLEAR
+};
+
+const S32 POINTAT_PRIORITIES[POINTAT_NUM_TARGETS] =
+{
+ 0, //POINTAT_TARGET_NONE
+ 1, //POINTAT_TARGET_SELECT
+ 2, //POINTAT_TARGET_GRAB
+ 3, //POINTAT_TARGET_CLEAR
+};
+
+// statics
+
+BOOL LLHUDEffectPointAt::sDebugPointAt;
+
+
+//-----------------------------------------------------------------------------
+// LLHUDEffectPointAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectPointAt::LLHUDEffectPointAt(const U8 type) :
+ LLHUDEffect(type),
+ mKillTime(0.f),
+ mLastSendTime(0.f)
+{
+ clearPointAtTarget();
+}
+
+//-----------------------------------------------------------------------------
+// ~LLHUDEffectPointAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectPointAt::~LLHUDEffectPointAt()
+{
+}
+
+//-----------------------------------------------------------------------------
+// packData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::packData(LLMessageSystem *mesgsys)
+{
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // Pack the type-specific data. Uses a fun packed binary format. Whee!
+ U8 packed_data[PKT_SIZE];
+ memset(packed_data, 0, PKT_SIZE);
+
+ if (mSourceObject)
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ // pack both target object and position
+ // position interpreted as offset if target object is non-null
+ if (mTargetObject)
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ htonmemcpy(&(packed_data[TARGET_POS]), mTargetOffsetGlobal.mdV, MVT_LLVector3d, 24);
+
+ U8 pointAtTypePacked = (U8)mTargetType;
+ htonmemcpy(&(packed_data[POINTAT_TYPE]), &pointAtTypePacked, MVT_U8, 1);
+
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE);
+
+ mLastSendTime = mTimer.getElapsedTimeF32();
+}
+
+//FIXME: avatar selection does a weird double take
+
+//-----------------------------------------------------------------------------
+// unpackData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ LLVector3d new_target;
+ U8 packed_data[PKT_SIZE];
+
+ LLUUID dataId;
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, dataId, blocknum);
+
+ // ignore messages from ourselves
+ if (!gAgent.mPointAt.isNull() && dataId == gAgent.mPointAt->getID())
+ {
+ return;
+ }
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != PKT_SIZE)
+ {
+ llwarns << "PointAt effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum);
+
+ htonmemcpy(source_id.mData, &(packed_data[SOURCE_AVATAR]), MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(source_id);
+ if (objp && objp->isAvatar())
+ {
+ setSourceObject(objp);
+ }
+ else
+ {
+ //llwarns << "Could not find source avatar for pointat effect" << llendl;
+ return;
+ }
+
+ htonmemcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16);
+
+ objp = gObjectList.findObject(target_id);
+
+ htonmemcpy(new_target.mdV, &(packed_data[TARGET_POS]), MVT_LLVector3d, 24);
+
+ if (objp)
+ {
+ setTargetObjectAndOffset(objp, new_target);
+ }
+ else if (target_id.isNull())
+ {
+ setTargetPosGlobal(new_target);
+ }
+
+ U8 pointAtTypeUnpacked = 0;
+ htonmemcpy(&pointAtTypeUnpacked, &(packed_data[POINTAT_TYPE]), MVT_U8, 1);
+ mTargetType = (EPointAtType)pointAtTypeUnpacked;
+
+// mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+ update();
+}
+
+//-----------------------------------------------------------------------------
+// setTargetObjectAndOffset()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset)
+{
+ mTargetObject = objp;
+ mTargetOffsetGlobal = offset;
+}
+
+//-----------------------------------------------------------------------------
+// setTargetPosGlobal()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::setTargetPosGlobal(const LLVector3d &target_pos_global)
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal = target_pos_global;
+}
+
+//-----------------------------------------------------------------------------
+// setPointAt()
+// called by agent logic to set look at behavior locally, and propagate to sim
+//-----------------------------------------------------------------------------
+BOOL LLHUDEffectPointAt::setPointAt(EPointAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ if (!mSourceObject)
+ {
+ return FALSE;
+ }
+
+ llassert(target_type < POINTAT_NUM_TARGETS);
+
+ // must be same or higher priority than existing effect
+ if (POINTAT_PRIORITIES[target_type] < POINTAT_PRIORITIES[mTargetType])
+ {
+ return FALSE;
+ }
+
+ F32 current_time = mTimer.getElapsedTimeF32();
+
+ // type of pointat behavior or target object has changed
+ BOOL targetTypeChanged = (target_type != mTargetType) ||
+ (object != mTargetObject);
+
+ BOOL targetPosChanged = (dist_vec(position, mLastSentOffsetGlobal) > MIN_DELTAPOS_FOR_UPDATE) &&
+ ((current_time - mLastSendTime) > (1.f / MAX_SENDS_PER_SEC));
+
+ if (targetTypeChanged || targetPosChanged)
+ {
+ mLastSentOffsetGlobal = position;
+ setDuration(POINTAT_TIMEOUTS[target_type]);
+ setNeedsSendToSim(TRUE);
+// llinfos << "Sending pointat data" << llendl;
+ }
+
+ if (target_type == POINTAT_TARGET_CLEAR)
+ {
+ clearPointAtTarget();
+ }
+ else
+ {
+ mTargetType = target_type;
+ mTargetObject = object;
+ if (object)
+ {
+ mTargetOffsetGlobal.setVec(position);
+ }
+ else
+ {
+ mTargetOffsetGlobal = gAgent.getPosGlobalFromAgent(position);
+ }
+
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+
+ //set up requisite animation data
+ update();
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// clearPointAtTarget()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::clearPointAtTarget()
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal.clearVec();
+ mTargetType = POINTAT_TARGET_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// markDead()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::markDead()
+{
+ if (!mSourceObject.isNull() && mSourceObject->isAvatar())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("PointAtPoint");
+ }
+
+ clearPointAtTarget();
+ LLHUDEffect::markDead();
+}
+
+void LLHUDEffectPointAt::setSourceObject(LLViewerObject* objectp)
+{
+ // restrict source objects to avatars
+ if (objectp && objectp->isAvatar())
+ {
+ LLHUDEffect::setSourceObject(objectp);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::render()
+{
+ update();
+ if (sDebugPointAt && mTargetType != POINTAT_TARGET_NONE)
+ {
+ LLGLSNoTexture gls_no_texture;
+
+ LLVector3 target = mTargetPos + mSourceObject->getRenderPosition();
+ glPushMatrix();
+ glTranslatef(target.mV[VX], target.mV[VY], target.mV[VZ]);
+ glScalef(0.3f, 0.3f, 0.3f);
+ glBegin(GL_LINES);
+ {
+ glColor3f(1.f, 0.f, 0.f);
+ glVertex3f(-1.f, 0.f, 0.f);
+ glVertex3f(1.f, 0.f, 0.f);
+
+ glVertex3f(0.f, -1.f, 0.f);
+ glVertex3f(0.f, 1.f, 0.f);
+
+ glVertex3f(0.f, 0.f, -1.f);
+ glVertex3f(0.f, 0.f, 1.f);
+ } glEnd();
+ glPopMatrix();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::update()
+{
+ // If the target object is dead, set the target object to NULL
+ if (!mTargetObject.isNull() && mTargetObject->isDead())
+ {
+ clearPointAtTarget();
+ }
+
+ if (mSourceObject.isNull() || mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+
+ // clear out the effect if time is up
+ if (mKillTime != 0.f && time > mKillTime)
+ {
+ mTargetType = POINTAT_TARGET_NONE;
+ }
+
+ if (mSourceObject->isAvatar())
+ {
+ if (mTargetType == POINTAT_TARGET_NONE)
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("PointAtPoint");
+ }
+ else
+ {
+ calcTargetPosition();
+
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_EDITING);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcTargetPosition()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::calcTargetPosition()
+{
+ LLViewerObject *targetObject = (LLViewerObject *)mTargetObject;
+ LLVector3 local_offset;
+
+ if (targetObject)
+ {
+ local_offset.setVec(mTargetOffsetGlobal);
+ }
+ else
+ {
+ local_offset = gAgent.getPosAgentFromGlobal(mTargetOffsetGlobal);
+ }
+
+ if (targetObject && targetObject->mDrawable.notNull())
+ {
+ LLQuaternion objRot;
+ if (targetObject->isAvatar())
+ {
+ LLVOAvatar *avatarp = (LLVOAvatar *)targetObject;
+ mTargetPos = avatarp->mHeadp->getWorldPosition();
+ objRot = avatarp->mPelvisp->getWorldRotation();
+ }
+ else
+ {
+ if (targetObject->mDrawable->getGeneration() == -1)
+ {
+ mTargetPos = targetObject->getPositionAgent();
+ objRot = targetObject->getWorldRotation();
+ }
+ else
+ {
+ mTargetPos = targetObject->getRenderPosition();
+ objRot = targetObject->getRenderRotation();
+ }
+ }
+
+ mTargetPos += (local_offset * objRot);
+ }
+ else
+ {
+ mTargetPos = local_offset;
+ }
+
+ mTargetPos -= mSourceObject->getRenderPosition();
+
+ if (mSourceObject->isAvatar())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->setAnimationData("PointAtPoint", (void *)&mTargetPos);
+ }
+}
+
+const LLVector3d LLHUDEffectPointAt::getPointAtPosGlobal()
+{
+ LLVector3d global_pos;
+ global_pos.setVec(mTargetPos);
+ if (mSourceObject.notNull())
+ {
+ global_pos += mSourceObject->getPositionGlobal();
+ }
+
+ return global_pos;
+}
diff --git a/indra/newview/llhudeffectpointat.h b/indra/newview/llhudeffectpointat.h
new file mode 100644
index 0000000000..a4254501eb
--- /dev/null
+++ b/indra/newview/llhudeffectpointat.h
@@ -0,0 +1,65 @@
+/**
+ * @file llhudeffectpointat.h
+ * @brief LLHUDEffectPointAt class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTPOINTAT_H
+#define LL_LLHUDEFFECTPOINTAT_H
+
+#include "llhudeffect.h"
+
+class LLViewerObject;
+class LLVOAvatar;
+
+typedef enum e_pointat_type
+{
+ POINTAT_TARGET_NONE,
+ POINTAT_TARGET_SELECT,
+ POINTAT_TARGET_GRAB,
+ POINTAT_TARGET_CLEAR,
+ POINTAT_NUM_TARGETS
+} EPointAtType;
+
+class LLHUDEffectPointAt : public LLHUDEffect
+{
+public:
+ friend class LLHUDObject;
+
+ /*virtual*/ void markDead();
+ /*virtual*/ void setSourceObject(LLViewerObject* objectp);
+
+ BOOL setPointAt(EPointAtType target_type, LLViewerObject *object, LLVector3 position);
+ void clearPointAtTarget();
+
+ EPointAtType getPointAtType() { return mTargetType; }
+ const LLVector3& getPointAtPosAgent() { return mTargetPos; }
+ const LLVector3d getPointAtPosGlobal();
+protected:
+ LLHUDEffectPointAt(const U8 type);
+ ~LLHUDEffectPointAt();
+
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+
+ // lookat behavior has either target position or target object with offset
+ void setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset);
+ void setTargetPosGlobal(const LLVector3d &target_pos_global);
+ void calcTargetPosition();
+ void update();
+public:
+ static BOOL sDebugPointAt;
+private:
+ EPointAtType mTargetType;
+ LLVector3d mTargetOffsetGlobal;
+ LLVector3 mLastSentOffsetGlobal;
+ F32 mKillTime;
+ LLFrameTimer mTimer;
+ LLVector3 mTargetPos;
+ F32 mLastSendTime;
+};
+
+#endif // LL_LLHUDEFFECTPOINTAT_H
diff --git a/indra/newview/llhudeffecttrail.cpp b/indra/newview/llhudeffecttrail.cpp
new file mode 100644
index 0000000000..d76250d10a
--- /dev/null
+++ b/indra/newview/llhudeffecttrail.cpp
@@ -0,0 +1,271 @@
+/**
+ * @file llhudeffecttrail.cpp
+ * @brief LLHUDEffectSpiral class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffecttrail.h"
+
+#include "llviewercontrol.h"
+#include "llimagegl.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbox.h"
+#include "lldrawable.h"
+#include "llhudrender.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerpartsim.h"
+#include "llviewerpartsource.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+
+
+const F32 PARTICLE_SPACING = 0.01f;
+const F32 MAX_SIZE = 0.025f;
+const F32 START_POS_MAG = 1.f;
+const F32 END_POS_MAG = 1.2f;
+
+
+LLHUDEffectSpiral::LLHUDEffectSpiral(const U8 type) : LLHUDEffect(type), mbInit(FALSE)
+{
+ mKillTime = 10.f;
+ mVMag = 1.f;
+ mVOffset = 0.f;
+ mInitialRadius = 1.f;
+ mFinalRadius = 1.f;
+ mSpinRate = 10.f;
+ mFlickerRate = 50.f;
+ mScaleBase = 0.1f;
+ mScaleVar = 0.f;
+
+ mFadeInterp.setStartTime(0.f);
+ mFadeInterp.setEndTime(mKillTime);
+ mFadeInterp.setStartVal(1.f);
+ mFadeInterp.setEndVal(1.f);
+}
+
+LLHUDEffectSpiral::~LLHUDEffectSpiral()
+{
+}
+
+void LLHUDEffectSpiral::markDead()
+{
+ if (mPartSourcep)
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ LLHUDObject::markDead();
+}
+
+void LLHUDEffectSpiral::packData(LLMessageSystem *mesgsys)
+{
+ if (!mSourceObject)
+ {
+ //llwarns << "Missing object in trail pack!" << llendl;
+ }
+ LLHUDEffect::packData(mesgsys);
+
+ U8 packed_data[56];
+ memset(packed_data, 0, 56);
+
+ if (mSourceObject)
+ {
+ htonmemcpy(packed_data, mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+ if (mTargetObject)
+ {
+ htonmemcpy(packed_data + 16, mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ if (!mPositionGlobal.isExactlyZero())
+ {
+ htonmemcpy(packed_data + 32, mPositionGlobal.mdV, MVT_LLVector3d, 24);
+ }
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 56);
+}
+
+void LLHUDEffectSpiral::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ U8 packed_data[56];
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID object_id, target_object_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != 56)
+ {
+ llwarns << "Spiral effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 56, blocknum);
+
+ htonmemcpy(object_id.mData, packed_data, MVT_LLUUID, 16);
+ htonmemcpy(target_object_id.mData, packed_data + 16, MVT_LLUUID, 16);
+ htonmemcpy(mPositionGlobal.mdV, packed_data + 32, MVT_LLVector3d, 24);
+
+ LLViewerObject *objp = NULL;
+
+ if (object_id.isNull())
+ {
+ setSourceObject(NULL);
+ }
+ else
+ {
+ LLViewerObject *objp = gObjectList.findObject(object_id);
+ if (objp)
+ {
+ setSourceObject(objp);
+ }
+ else
+ {
+ // We don't have this object, kill this effect
+ markDead();
+ return;
+ }
+ }
+
+ if (target_object_id.isNull())
+ {
+ setTargetObject(NULL);
+ }
+ else
+ {
+ objp = gObjectList.findObject(target_object_id);
+ if (objp)
+ {
+ setTargetObject(objp);
+ }
+ else
+ {
+ // We don't have this object, kill this effect
+ markDead();
+ return;
+ }
+ }
+
+ triggerLocal();
+}
+
+void LLHUDEffectSpiral::triggerLocal()
+{
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+
+ BOOL show_beam = gSavedSettings.getBOOL("ShowSelectionBeam");
+
+ LLColor4 color;
+ color.setVec(mColor);
+
+ if (!mPartSourcep)
+ {
+ if (!mTargetObject.isNull() && !mSourceObject.isNull())
+ {
+ if (show_beam)
+ {
+ LLViewerPartSourceBeam *psb = new LLViewerPartSourceBeam;
+ psb->setColor(color);
+ psb->setSourceObject(mSourceObject);
+ psb->setTargetObject(mTargetObject);
+ psb->setOwnerUUID(gAgent.getID());
+ gWorldPointer->mPartSim.addPartSource(psb);
+ mPartSourcep = psb;
+ }
+ }
+ else
+ {
+ if (!mSourceObject.isNull() && !mPositionGlobal.isExactlyZero())
+ {
+ if (show_beam)
+ {
+ LLViewerPartSourceBeam *psb = new LLViewerPartSourceBeam;
+ psb->setSourceObject(mSourceObject);
+ psb->setTargetObject(NULL);
+ psb->setColor(color);
+ psb->mLKGTargetPosGlobal = mPositionGlobal;
+ psb->setOwnerUUID(gAgent.getID());
+ gWorldPointer->mPartSim.addPartSource(psb);
+ mPartSourcep = psb;
+ }
+ }
+ else
+ {
+ LLVector3 pos;
+ if (mSourceObject)
+ {
+ pos = mSourceObject->getPositionAgent();
+ }
+ else
+ {
+ pos = gAgent.getPosAgentFromGlobal(mPositionGlobal);
+ }
+ LLViewerPartSourceSpiral *pss = new LLViewerPartSourceSpiral(pos);
+ if (!mSourceObject.isNull())
+ {
+ pss->setSourceObject(mSourceObject);
+ }
+ pss->setColor(color);
+ pss->setOwnerUUID(gAgent.getID());
+ gWorldPointer->mPartSim.addPartSource(pss);
+ mPartSourcep = pss;
+ }
+ }
+ }
+ else
+ {
+ LLViewerPartSource *ps = mPartSourcep;
+ if (mPartSourcep->getType() == LLViewerPartSource::LL_PART_SOURCE_BEAM)
+ {
+ LLViewerPartSourceBeam *psb = (LLViewerPartSourceBeam *)ps;
+ psb->setSourceObject(mSourceObject);
+ psb->setTargetObject(mTargetObject);
+ psb->setColor(color);
+ if (mTargetObject.isNull())
+ {
+ psb->mLKGTargetPosGlobal = mPositionGlobal;
+ }
+ }
+ else
+ {
+ LLViewerPartSourceSpiral *pss = (LLViewerPartSourceSpiral *)ps;
+ pss->setSourceObject(mSourceObject);
+ }
+ }
+
+ mbInit = TRUE;
+}
+
+void LLHUDEffectSpiral::setTargetObject(LLViewerObject *objp)
+{
+ if (objp == mTargetObject)
+ {
+ return;
+ }
+
+ mTargetObject = objp;
+}
+
+void LLHUDEffectSpiral::render()
+{
+ if (!mSourceObject.isNull() && mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ if(!mTargetObject.isNull() && mTargetObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+ if (mKillTime < time)
+ {
+ markDead();
+ return;
+ }
+}
diff --git a/indra/newview/llhudeffecttrail.h b/indra/newview/llhudeffecttrail.h
new file mode 100644
index 0000000000..f930739847
--- /dev/null
+++ b/indra/newview/llhudeffecttrail.h
@@ -0,0 +1,76 @@
+/**
+ * @file llhudeffecttrail.h
+ * @brief LLHUDEffectSpiral class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTTRAIL_H
+#define LL_LLHUDEFFECTTRAIL_H
+
+#include "llhudeffect.h"
+
+#include "llframetimer.h"
+
+#include "llinterp.h"
+#include "llviewerpartsim.h"
+
+class LLViewerObject;
+
+const U32 NUM_TRAIL_POINTS = 40;
+
+
+class LLHUDEffectSpiral : public LLHUDEffect
+{
+public:
+ /*virtual*/ void markDead();
+ /*virtual*/ void setTargetObject(LLViewerObject* objectp);
+ void setVMag(F32 vmag) { mVMag = vmag; }
+ void setVOffset(F32 offset) { mVOffset = offset; }
+ void setInitialRadius(F32 radius) { mInitialRadius = radius; }
+ void setFinalRadius(F32 radius) { mFinalRadius = radius; }
+ void setScaleBase(F32 scale) { mScaleBase = scale; }
+ void setScaleVar(F32 scale) { mScaleVar = scale; }
+ void setSpinRate(F32 rate) { mSpinRate = rate; }
+ void setFlickerRate(F32 rate) { mFlickerRate = rate; }
+
+ // Start the effect playing locally.
+ void triggerLocal();
+
+ friend class LLHUDObject;
+protected:
+ LLHUDEffectSpiral(const U8 type);
+ ~LLHUDEffectSpiral();
+
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+private:
+ /*
+ void setupParticle(const S32 i, const F32 start_time);
+
+
+ LLInterpExp<F32> mRadius[NUM_TRAIL_POINTS];
+ LLInterpLinear<LLVector3d> mDistance[NUM_TRAIL_POINTS];
+ LLInterpLinear<F32> mScale[NUM_TRAIL_POINTS];
+ F32 mOffset[NUM_TRAIL_POINTS];
+ */
+
+ BOOL mbInit;
+ LLPointer<LLViewerPartSource> mPartSourcep;
+
+ F32 mKillTime;
+ F32 mVMag;
+ F32 mVOffset;
+ F32 mInitialRadius;
+ F32 mFinalRadius;
+ F32 mSpinRate;
+ F32 mFlickerRate;
+ F32 mScaleBase;
+ F32 mScaleVar;
+ LLFrameTimer mTimer;
+ LLInterpLinear<F32> mFadeInterp;
+};
+
+#endif // LL_LLHUDEFFECTGLOW_H
diff --git a/indra/newview/llhudicon.cpp b/indra/newview/llhudicon.cpp
new file mode 100644
index 0000000000..f2ab2dab11
--- /dev/null
+++ b/indra/newview/llhudicon.cpp
@@ -0,0 +1,253 @@
+/**
+ * @file llhudicon.cpp
+ * @brief LLHUDIcon class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudicon.h"
+
+#include "llgl.h"
+
+#include "llviewerobject.h"
+#include "lldrawable.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+
+//-----------------------------------------------------------------------------
+// Local consts
+//-----------------------------------------------------------------------------
+const F32 ANIM_TIME = 0.4f;
+const F32 DIST_START_FADE = 15.f;
+const F32 DIST_END_FADE = 30.f;
+const F32 MAX_VISIBLE_TIME = 15.f;
+const F32 FADE_OUT_TIME = 1.f;
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+static F32 calc_bouncy_animation(F32 x)
+{
+ return -(cosf(x * F_PI * 2.5f - F_PI_BY_TWO))*(0.4f + x * -0.1f) + x * 1.3f;
+}
+
+
+//-----------------------------------------------------------------------------
+// static declarations
+//-----------------------------------------------------------------------------
+LLHUDIcon::icon_instance_t LLHUDIcon::sIconInstances;
+
+LLHUDIcon::LLHUDIcon(const U8 type) :
+ LLHUDObject(type),
+ mImagep(NULL),
+ mPickID(0),
+ mScale(0.1f)
+{
+ sIconInstances.push_back(this);
+}
+
+LLHUDIcon::~LLHUDIcon()
+{
+ mImagep = NULL;
+}
+
+void LLHUDIcon::renderIcon(BOOL for_select)
+{
+ LLGLSUIDefault texture_state;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLState no_texture(GL_TEXTURE_2D, for_select ? FALSE : TRUE);
+
+ if (mSourceObject.isNull() || mImagep.isNull())
+ {
+ markDead();
+ return;
+ }
+
+ LLVector3 obj_position = mSourceObject->getRenderPosition();
+
+ // put icon above object, and in front
+ // RN: don't use drawable radius, it's fricking HUGE
+ LLVector3 icon_relative_pos = (gCamera->getUpAxis() * ~mSourceObject->getRenderRotation());
+ icon_relative_pos.abs();
+
+ F32 distance_scale = llmin(mSourceObject->getScale().mV[VX] / icon_relative_pos.mV[VX],
+ mSourceObject->getScale().mV[VY] / icon_relative_pos.mV[VY],
+ mSourceObject->getScale().mV[VZ] / icon_relative_pos.mV[VZ]);
+ F32 up_distance = 0.5f * distance_scale;
+ LLVector3 icon_position = obj_position + (up_distance * gCamera->getUpAxis()) * 1.2f;
+
+ LLVector3 icon_to_cam = gCamera->getOrigin() - icon_position;
+ icon_to_cam.normVec();
+
+ icon_position += icon_to_cam * mSourceObject->mDrawable->getRadius() * 1.1f;
+
+ mDistance = dist_vec(icon_position, gCamera->getOrigin());
+
+ F32 alpha_factor = for_select ? 1.f : clamp_rescale(mDistance, DIST_START_FADE, DIST_END_FADE, 1.f, 0.f);
+
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+
+ gCamera->getPixelVectors(icon_position, y_pixel_vec, x_pixel_vec);
+
+ F32 scale_factor = 1.f;
+ if (mAnimTimer.getElapsedTimeF32() < ANIM_TIME)
+ {
+ scale_factor = llmax(0.f, calc_bouncy_animation(mAnimTimer.getElapsedTimeF32() / ANIM_TIME));
+ }
+
+ F32 time_elapsed = mLifeTimer.getElapsedTimeF32();
+ if (time_elapsed > MAX_VISIBLE_TIME)
+ {
+ markDead();
+ return;
+ }
+
+ if (time_elapsed > MAX_VISIBLE_TIME - FADE_OUT_TIME)
+ {
+ alpha_factor *= clamp_rescale(time_elapsed, MAX_VISIBLE_TIME - FADE_OUT_TIME, MAX_VISIBLE_TIME, 1.f, 0.f);
+ }
+
+ F32 image_aspect = (F32)mImagep->mFullWidth / (F32)mImagep->mFullHeight;
+ LLVector3 x_scale = image_aspect * (F32)gViewerWindow->getWindowHeight() * mScale * scale_factor * x_pixel_vec;
+ LLVector3 y_scale = (F32)gViewerWindow->getWindowHeight() * mScale * scale_factor * y_pixel_vec;
+
+ LLVector3 lower_left = icon_position - (x_scale * 0.5f);
+ LLVector3 lower_right = icon_position + (x_scale * 0.5f);
+ LLVector3 upper_left = icon_position - (x_scale * 0.5f) + y_scale;
+ LLVector3 upper_right = icon_position + (x_scale * 0.5f) + y_scale;
+
+ if (for_select)
+ {
+ // set color to unique color id for picking
+ LLColor4U coloru((U8)(mPickID >> 16), (U8)(mPickID >> 8), (U8)mPickID);
+ glColor4ubv(coloru.mV);
+ }
+ else
+ {
+ LLColor4 icon_color = LLColor4::white;
+ icon_color.mV[VALPHA] = alpha_factor;
+ glColor4fv(icon_color.mV);
+ LLViewerImage::bindTexture(mImagep);
+ }
+
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(0.f, 1.f);
+ glVertex3fv(upper_left.mV);
+ glTexCoord2f(0.f, 0.f);
+ glVertex3fv(lower_left.mV);
+ glTexCoord2f(1.f, 0.f);
+ glVertex3fv(lower_right.mV);
+ glTexCoord2f(1.f, 1.f);
+ glVertex3fv(upper_right.mV);
+ }
+ glEnd();
+}
+
+void LLHUDIcon::setImage(LLViewerImage* imagep)
+{
+ mImagep = imagep;
+ mImagep->setClamp(TRUE, TRUE);
+}
+
+void LLHUDIcon::setScale(F32 fraction_of_fov)
+{
+ mScale = fraction_of_fov;
+}
+
+void LLHUDIcon::markDead()
+{
+ if (mSourceObject)
+ {
+ mSourceObject->clearIcon();
+ }
+ LLHUDObject::markDead();
+}
+
+void LLHUDIcon::render()
+{
+ renderIcon(FALSE);
+}
+
+void LLHUDIcon::renderForSelect()
+{
+ renderIcon(TRUE);
+}
+
+
+//static
+S32 LLHUDIcon::generatePickIDs(S32 start_id, S32 step_size)
+{
+ S32 cur_id = start_id;
+ icon_instance_t::iterator icon_it;
+
+ for(icon_it = sIconInstances.begin(); icon_it != sIconInstances.end(); ++icon_it)
+ {
+ (*icon_it)->mPickID = cur_id;
+ cur_id += step_size;
+ }
+
+ return cur_id;
+}
+
+//static
+LLHUDIcon* LLHUDIcon::handlePick(S32 pick_id)
+{
+ icon_instance_t::iterator icon_it;
+
+ for(icon_it = sIconInstances.begin(); icon_it != sIconInstances.end(); ++icon_it)
+ {
+ if (pick_id == (*icon_it)->mPickID)
+ {
+ return *icon_it;
+ }
+ }
+
+ return NULL;
+}
+
+ //static
+void LLHUDIcon::updateAll()
+{
+ cleanupDeadIcons();
+}
+
+//static
+BOOL LLHUDIcon::iconsNearby()
+{
+ return !sIconInstances.empty();
+}
+
+//static
+void LLHUDIcon::cleanupDeadIcons()
+{
+ icon_instance_t::iterator icon_it;
+
+ icon_instance_t icons_to_erase;
+ for(icon_it = sIconInstances.begin(); icon_it != sIconInstances.end(); ++icon_it)
+ {
+ if ((*icon_it)->mDead)
+ {
+ icons_to_erase.push_back(*icon_it);
+ }
+ }
+
+ for(icon_it = icons_to_erase.begin(); icon_it != icons_to_erase.end(); ++icon_it)
+ {
+ icon_instance_t::iterator found_it = std::find(sIconInstances.begin(), sIconInstances.end(), *icon_it);
+ if (found_it != sIconInstances.end())
+ {
+ sIconInstances.erase(found_it);
+ }
+ }
+}
+
+//static
+S32 LLHUDIcon::getNumInstances()
+{
+ return (S32)sIconInstances.size();
+}
diff --git a/indra/newview/llhudicon.h b/indra/newview/llhudicon.h
new file mode 100644
index 0000000000..9835ff455b
--- /dev/null
+++ b/indra/newview/llhudicon.h
@@ -0,0 +1,72 @@
+/**
+ * @file llhudicon.h
+ * @brief LLHUDIcon class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDICON_H
+#define LL_LLHUDICON_H
+
+#include "llmemory.h"
+#include "lldarrayptr.h"
+
+#include "llhudobject.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "llrect.h"
+#include "llframetimer.h"
+#include "llfontgl.h"
+#include <set>
+#include <vector>
+#include "lldarray.h"
+
+// Renders a 2D icon billboard floating at the location specified.
+class LLDrawable;
+class LLViewerObject;
+
+class LLHUDIcon : public LLHUDObject
+{
+friend class LLHUDObject;
+
+public:
+ /*virtual*/ void render();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void markDead();
+ /*virtual*/ F32 getDistance() const { return mDistance; }
+
+ void setImage(LLViewerImage* imagep);
+ void setScale(F32 fraction_of_fov);
+
+ void restartLifeTimer() { mLifeTimer.reset(); }
+
+ static S32 generatePickIDs(S32 start_id, S32 step_size);
+ static LLHUDIcon* handlePick(S32 pick_id);
+
+ static void updateAll();
+ static void cleanupDeadIcons();
+ static S32 getNumInstances();
+
+ static BOOL iconsNearby();
+
+protected:
+ LLHUDIcon(const U8 type);
+ ~LLHUDIcon();
+
+ void renderIcon(BOOL for_select); // common render code
+
+private:
+ LLPointer<LLViewerImage> mImagep;
+ LLFrameTimer mAnimTimer;
+ LLFrameTimer mLifeTimer;
+ F32 mDistance;
+ S32 mPickID;
+ F32 mScale;
+
+ typedef std::vector<LLPointer<LLHUDIcon> > icon_instance_t;
+ static icon_instance_t sIconInstances;
+};
+
+#endif // LL_LLHUDICON_H
diff --git a/indra/newview/llhudmanager.cpp b/indra/newview/llhudmanager.cpp
new file mode 100644
index 0000000000..40e140253e
--- /dev/null
+++ b/indra/newview/llhudmanager.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file llhudmanager.cpp
+ * @brief LLHUDManager class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudmanager.h"
+
+#include "message.h"
+#include "object_flags.h"
+
+#include "llagent.h"
+#include "llhudconnector.h"
+#include "llhudeffect.h"
+#include "pipeline.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+
+LLHUDManager *gHUDManager = NULL;
+
+extern BOOL gNoRender;
+
+// These are loaded from saved settings.
+LLColor4 LLHUDManager::sParentColor;
+LLColor4 LLHUDManager::sChildColor;
+
+LLHUDManager::LLHUDManager()
+{
+ mShowPhysical = FALSE;
+
+ LLHUDManager::sParentColor = gColors.getColor("FocusColor");
+ // rdw commented out since it's not used. Also removed from colors_base.xml
+ //LLHUDManager::sChildColor = gColors.getColor("FocusSecondaryColor");
+}
+
+LLHUDManager::~LLHUDManager()
+{
+ mHUDJoints.reset();
+ mHUDSelectedJoints.reset();
+ mHUDEffects.reset();
+}
+
+
+void LLHUDManager::toggleShowPhysical(const BOOL show_physical)
+{
+ if (show_physical == mShowPhysical)
+ {
+ return;
+ }
+
+ mShowPhysical = show_physical;
+ if (show_physical)
+ {
+ S32 i;
+ for (i = 0; i < gObjectList.getNumObjects(); i++)
+ {
+ LLViewerObject *vobjp = gObjectList.getObject(i);
+
+ if (vobjp && vobjp->isJointChild() && vobjp->getParent())
+ {
+ LLHUDConnector *connectorp = (LLHUDConnector *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_CONNECTOR);
+ connectorp->setTargets(vobjp, (LLViewerObject *)vobjp->getParent());
+ connectorp->setColors(LLColor4(1.f, 1.f, 1.f, 1.f), sChildColor, sParentColor);
+ EHavokJointType joint_type = vobjp->getJointType();
+ if (HJT_HINGE == joint_type)
+ {
+ connectorp->setLabel("Hinge");
+ }
+ else if (HJT_POINT == joint_type)
+ {
+ connectorp->setLabel("P2P");
+ }
+#if 0
+ else if (HJT_LPOINT == joint_type)
+ {
+ connectorp->setLabel("LP2P");
+ }
+#endif
+#if 0
+ else if (HJT_WHEEL == joint_type)
+ {
+ connectorp->setLabel("Wheel");
+ }
+#endif
+ mHUDJoints.put(connectorp);
+ }
+ }
+ }
+ else
+ {
+ mHUDJoints.reset();
+ }
+}
+
+void LLHUDManager::showJoints(LLDynamicArray < LLViewerObject* > *object_list)
+{
+ for (S32 i=0; i<object_list->count(); i++)
+ {
+ LLViewerObject *vobjp = object_list->get(i);
+ if (vobjp && vobjp->isJointChild() && vobjp->getParent())
+ {
+ LLHUDConnector *connectorp = (LLHUDConnector *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_CONNECTOR);
+ connectorp->setTargets(vobjp, (LLViewerObject *)vobjp->getParent());
+ connectorp->setColors(LLColor4(1.f, 1.f, 1.f, 1.f), sChildColor, sParentColor);
+
+ EHavokJointType joint_type = vobjp->getJointType();
+ if (HJT_HINGE == joint_type)
+ {
+ connectorp->setLabel("Hinge");
+ }
+ else if (HJT_POINT == joint_type)
+ {
+ connectorp->setLabel("P2P");
+ }
+#if 0
+ else if (HJT_LPOINT == joint_type)
+ {
+ connectorp->setLabel("LP2P");
+ }
+#endif
+#if 0
+ else if (HJT_WHEEL == joint_type)
+ {
+ connectorp->setLabel("Wheel");
+ }
+#endif
+ mHUDSelectedJoints.put(connectorp);
+ }
+ }
+}
+
+void LLHUDManager::clearJoints()
+{
+ mHUDSelectedJoints.reset();
+}
+
+BOOL LLHUDManager::getShowPhysical() const
+{
+ return mShowPhysical;
+}
+
+void LLHUDManager::updateEffects()
+{
+ LLFastTimer ftm(LLFastTimer::FTM_HUD_EFFECTS);
+ S32 i;
+ for (i = 0; i < mHUDEffects.count(); i++)
+ {
+ LLHUDEffect *hep = mHUDEffects[i];
+ if (hep->isDead())
+ {
+ continue;
+ }
+ hep->update();
+ }
+}
+
+void LLHUDManager::sendEffects()
+{
+ S32 i;
+ for (i = 0; i < mHUDEffects.count(); i++)
+ {
+ LLHUDEffect *hep = mHUDEffects[i];
+ if (hep->isDead())
+ {
+ llwarns << "Trying to send dead effect!" << llendl;
+ continue;
+ }
+ if (hep->mType <= LLHUDObject::LL_HUD_CONNECTOR)
+ {
+ llwarns << "Trying to send effect of type " << hep->mType << " which isn't really an effect and shouldn't be in this list!" << llendl;
+ continue;
+ }
+ if (hep->getNeedsSendToSim() && hep->getOriginatedHere())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_ViewerEffect);
+ gMessageSystem->nextBlockFast(_PREHASH_Effect);
+ hep->packData(gMessageSystem);
+ hep->setNeedsSendToSim(FALSE);
+ gAgent.sendMessage();
+ }
+ }
+}
+
+void LLHUDManager::cleanupEffects()
+{
+ S32 i = 0;
+
+ while (i < mHUDEffects.count())
+ {
+ if (mHUDEffects[i]->isDead())
+ {
+ mHUDEffects.remove(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+LLHUDEffect *LLHUDManager::createViewerEffect(const U8 type, BOOL send_to_sim, BOOL originated_here)
+{
+ // Should assert that this is actually an LLHUDEffect
+ LLHUDEffect *hep = (LLHUDEffect *)LLHUDObject::addHUDObject(type);
+ if (!hep)
+ {
+ return NULL;
+ }
+
+ LLUUID tmp;
+ tmp.generate();
+ hep->setID(tmp);
+ hep->setNeedsSendToSim(send_to_sim);
+ hep->setOriginatedHere(originated_here);
+
+ mHUDEffects.put(hep);
+ return hep;
+}
+
+
+//static
+void LLHUDManager::processViewerEffect(LLMessageSystem *mesgsys, void **user_data)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (!gHUDManager)
+ {
+ llwarns << "No gHUDManager!" << llendl;
+ return;
+ }
+
+ LLHUDEffect *effectp = NULL;
+ LLUUID effect_id;
+ U8 effect_type = 0;
+ S32 number_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_Effect);
+ S32 k;
+
+ for (k = 0; k < number_blocks; k++)
+ {
+ effectp = NULL;
+ LLHUDEffect::getIDType(mesgsys, k, effect_id, effect_type);
+
+ S32 i;
+ for (i = 0; i < gHUDManager->mHUDEffects.count(); i++)
+ {
+ LLHUDEffect *cur_effectp = gHUDManager->mHUDEffects[i];
+ if (!cur_effectp)
+ {
+ llwarns << "Null effect in effect manager, skipping" << llendl;
+ gHUDManager->mHUDEffects.remove(i);
+ i--;
+ continue;
+ }
+ if (cur_effectp->isDead())
+ {
+ // llwarns << "Dead effect in effect manager, removing" << llendl;
+ gHUDManager->mHUDEffects.remove(i);
+ i--;
+ continue;
+ }
+ if (cur_effectp->getID() == effect_id)
+ {
+ if (cur_effectp->getType() != effect_type)
+ {
+ llwarns << "Viewer effect update doesn't match old type!" << llendl;
+ }
+ effectp = cur_effectp;
+ break;
+ }
+ }
+
+ if (effect_type)
+ {
+ if (!effectp)
+ {
+ effectp = gHUDManager->createViewerEffect(effect_type, FALSE, FALSE);
+ }
+
+ if (effectp)
+ {
+ effectp->unpackData(mesgsys, k);
+ }
+ }
+ else
+ {
+ llwarns << "Received viewer effect of type " << effect_type << " which isn't really an effect!" << llendl;
+ }
+ }
+
+ //llinfos << "Got viewer effect: " << effect_id << llendl;
+}
+
diff --git a/indra/newview/llhudmanager.h b/indra/newview/llhudmanager.h
new file mode 100644
index 0000000000..4cb22a2477
--- /dev/null
+++ b/indra/newview/llhudmanager.h
@@ -0,0 +1,60 @@
+/**
+ * @file llhudmanager.h
+ * @brief LLHUDManager class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDMANAGER_H
+#define LL_LLHUDMANAGER_H
+
+// Responsible for managing all HUD elements.
+
+#include "llhudobject.h"
+#include "lldarray.h"
+#include "llanimalcontrols.h"
+#include "lllocalanimationobject.h"
+#include "llcape.h"
+
+class LLViewerObject;
+class LLHUDEffect;
+//Ventrella 9/16/05
+class LLHUDAnimalControls;
+// End Ventrella
+class LLMessageSystem;
+
+class LLHUDManager
+{
+public:
+ LLHUDManager();
+ ~LLHUDManager();
+
+ void toggleShowPhysical(const BOOL show_physical);
+ void showJoints(LLDynamicArray < LLViewerObject* > *object_list);
+ void clearJoints();
+ BOOL getShowPhysical() const;
+
+ LLHUDEffect *createViewerEffect(const U8 type, BOOL send_to_sim = TRUE, BOOL originated_here = TRUE);
+
+ void updateEffects();
+ void sendEffects();
+ void cleanupEffects();
+
+ static void processViewerEffect(LLMessageSystem *mesgsys, void **user_data);
+
+ static LLColor4 sParentColor;
+ static LLColor4 sChildColor;
+
+protected:
+ LLDynamicArrayPtr<LLPointer<LLHUDObject> > mHUDJoints;
+ LLDynamicArrayPtr<LLPointer<LLHUDObject> > mHUDSelectedJoints;
+ LLDynamicArrayPtr<LLPointer<LLHUDEffect> > mHUDEffects;
+
+ // ALT held down this frame?
+ BOOL mShowPhysical;
+};
+
+extern LLHUDManager *gHUDManager;
+
+#endif // LL_LLHUDMANAGER_H
diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp
new file mode 100644
index 0000000000..403e6c6dff
--- /dev/null
+++ b/indra/newview/llhudobject.cpp
@@ -0,0 +1,277 @@
+/**
+ * @file llhudobject.cpp
+ * @brief LLHUDObject class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// llhudobject.cpp
+// Copyright 2002, Linden Research, Inc.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudobject.h"
+#include "llviewerobject.h"
+
+#include "llhudtext.h"
+#include "llhudicon.h"
+#include "llhudconnector.h"
+#include "llhudeffectbeam.h"
+#include "llhudeffecttrail.h"
+#include "llhudeffectlookat.h"
+
+//Ventrella
+#include "llanimalcontrols.h"
+#include "lllocalanimationobject.h"
+#include "llcape.h"
+// End Ventrella
+
+#include "llagent.h"
+
+// statics
+std::list<LLPointer<LLHUDObject> > LLHUDObject::sHUDObjects;
+
+struct hud_object_further_away
+{
+ bool operator()(const LLPointer<LLHUDObject>& lhs, const LLPointer<LLHUDObject>& rhs) const;
+};
+
+
+bool hud_object_further_away::operator()(const LLPointer<LLHUDObject>& lhs, const LLPointer<LLHUDObject>& rhs) const
+{
+ return (lhs->getDistance() > rhs->getDistance()) ? true : false;
+}
+
+
+LLHUDObject::LLHUDObject(const U8 type) :
+ mPositionGlobal(),
+ mSourceObject(NULL),
+ mTargetObject(NULL)
+{
+ mVisible = TRUE;
+ mType = type;
+ mDead = FALSE;
+ mOnHUDAttachment = FALSE;
+}
+
+LLHUDObject::~LLHUDObject()
+{
+}
+
+void LLHUDObject::markDead()
+{
+ mVisible = FALSE;
+ mDead = TRUE;
+ mSourceObject = NULL;
+ mTargetObject = NULL;
+}
+
+F32 LLHUDObject::getDistance() const
+{
+ return 0.f;
+}
+
+void LLHUDObject::setSourceObject(LLViewerObject* objectp)
+{
+ if (objectp == mSourceObject)
+ {
+ return;
+ }
+
+ mSourceObject = objectp;
+}
+
+void LLHUDObject::setTargetObject(LLViewerObject* objectp)
+{
+ if (objectp == mTargetObject)
+ {
+ return;
+ }
+
+ mTargetObject = objectp;
+}
+
+void LLHUDObject::setPositionGlobal(const LLVector3d &position_global)
+{
+ mPositionGlobal = position_global;
+}
+
+void LLHUDObject::setPositionAgent(const LLVector3 &position_agent)
+{
+ mPositionGlobal = gAgent.getPosGlobalFromAgent(position_agent);
+}
+
+// static
+void LLHUDObject::cleanupHUDObjects()
+{
+ LLHUDIcon::cleanupDeadIcons();
+ hud_object_list_t::iterator object_it;
+ for (object_it = sHUDObjects.begin(); object_it != sHUDObjects.end(); ++object_it)
+ {
+ (*object_it)->markDead();
+ if ((*object_it)->getNumRefs() > 1)
+ {
+ llinfos << "LLHUDObject " << (LLHUDObject *)(*object_it) << " has " << (*object_it)->getNumRefs() << " refs!" << llendl;
+ }
+ }
+ sHUDObjects.clear();
+}
+
+// static
+LLHUDObject *LLHUDObject::addHUDObject(const U8 type)
+{
+ LLHUDObject *hud_objectp = NULL;
+
+ switch (type)
+ {
+ case LL_HUD_TEXT:
+ hud_objectp = new LLHUDText(type);
+ break;
+ case LL_HUD_ICON:
+ hud_objectp = new LLHUDIcon(type);
+ break;
+ case LL_HUD_CONNECTOR:
+ hud_objectp = new LLHUDConnector(type);
+ break;
+ case LL_HUD_EFFECT_BEAM:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(0.7f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(0.2f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(10.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.05f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.02f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_GLOW:
+ llerrs << "Glow not implemented!" << llendl;
+ break;
+ case LL_HUD_EFFECT_POINT:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(10.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_SPHERE:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(20.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_SPIRAL:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(-2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(10.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(20.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.02f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.02f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_EDIT:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(-1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(1.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(4.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(200.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_LOOKAT:
+ hud_objectp = new LLHUDEffectLookAt(type);
+ break;
+ case LL_HUD_EFFECT_POINTAT:
+ hud_objectp = new LLHUDEffectPointAt(type);
+ break;
+ default:
+ llwarns << "Unknown type of hud object:" << (U32) type << llendl;
+ }
+
+ if (hud_objectp)
+ {
+ sHUDObjects.push_back(hud_objectp);
+ }
+ return hud_objectp;
+}
+
+// static
+void LLHUDObject::updateAll()
+{
+ LLFastTimer ftm(LLFastTimer::FTM_HUD_UPDATE);
+ LLHUDText::updateAll();
+ LLHUDIcon::updateAll();
+ sortObjects();
+}
+
+// static
+void LLHUDObject::renderAll()
+{
+ LLHUDObject *hud_objp;
+
+ hud_object_list_t::iterator object_it;
+ for (object_it = sHUDObjects.begin(); object_it != sHUDObjects.end(); )
+ {
+ hud_object_list_t::iterator cur_it = object_it++;
+ hud_objp = (*cur_it);
+ if (hud_objp->getNumRefs() == 1)
+ {
+ sHUDObjects.erase(cur_it);
+ }
+ else if (hud_objp->isVisible())
+ {
+ hud_objp->render();
+ }
+ }
+}
+
+// static
+void LLHUDObject::renderAllForSelect()
+{
+ LLHUDObject *hud_objp;
+
+ hud_object_list_t::iterator object_it;
+ for (object_it = sHUDObjects.begin(); object_it != sHUDObjects.end(); )
+ {
+ hud_object_list_t::iterator cur_it = object_it++;
+ hud_objp = (*cur_it);
+ if (hud_objp->getNumRefs() == 1)
+ {
+ sHUDObjects.erase(cur_it);
+ }
+ else if (hud_objp->isVisible())
+ {
+ hud_objp->renderForSelect();
+ }
+ }
+}
+
+// static
+void LLHUDObject::sortObjects()
+{
+ sHUDObjects.sort(hud_object_further_away());
+}
diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h
new file mode 100644
index 0000000000..1148dd6521
--- /dev/null
+++ b/indra/newview/llhudobject.h
@@ -0,0 +1,96 @@
+/**
+ * @file llhudobject.h
+ * @brief LLHUDObject class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDOBJECT_H
+#define LL_LLHUDOBJECT_H
+
+/**
+ * Base class and manager for in-world 2.5D non-interactive objects
+ */
+
+#include "llmemory.h"
+
+#include "v4color.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "linked_lists.h"
+#include "lldrawpool.h"
+#include <list>
+
+class LLViewerCamera;
+class LLFontGL;
+class LLFace;
+class LLViewerObject;
+
+class LLHUDObject : public LLRefCount
+{
+public:
+ virtual void markDead();
+ virtual F32 getDistance() const;
+ virtual void setSourceObject(LLViewerObject* objectp);
+ virtual void setTargetObject(LLViewerObject* objectp);
+ virtual LLViewerObject* getSourceObject() { return mSourceObject; }
+ virtual LLViewerObject* getTargetObject() { return mTargetObject; }
+
+ void setPositionGlobal(const LLVector3d &position_global);
+ void setPositionAgent(const LLVector3 &position_agent);
+
+ BOOL isVisible() const { return mVisible; }
+
+ U8 getType() const { return mType; }
+
+ static LLHUDObject *addHUDObject(const U8 type);
+ static void updateAll();
+ static void renderAll();
+ static void renderAllForSelect();
+
+ static void cleanupHUDObjects();
+
+ enum
+ {
+ LL_HUD_TEXT,
+ LL_HUD_ICON,
+ LL_HUD_CONNECTOR,
+ LL_HUD_FLEXIBLE_OBJECT, // Ventrella
+ LL_HUD_ANIMAL_CONTROLS, // Ventrella
+ LL_HUD_LOCAL_ANIMATION_OBJECT, // Ventrella
+ LL_HUD_CLOTH, // Ventrella
+ LL_HUD_EFFECT_BEAM,
+ LL_HUD_EFFECT_GLOW,
+ LL_HUD_EFFECT_POINT,
+ LL_HUD_EFFECT_TRAIL,
+ LL_HUD_EFFECT_SPHERE,
+ LL_HUD_EFFECT_SPIRAL,
+ LL_HUD_EFFECT_EDIT,
+ LL_HUD_EFFECT_LOOKAT,
+ LL_HUD_EFFECT_POINTAT
+ };
+protected:
+ static void sortObjects();
+
+ LLHUDObject(const U8 type);
+ virtual ~LLHUDObject();
+
+ virtual void render() = 0;
+ virtual void renderForSelect() {};
+
+protected:
+ U8 mType;
+ BOOL mDead;
+ BOOL mVisible;
+ LLVector3d mPositionGlobal;
+ BOOL mOnHUDAttachment;
+ LLPointer<LLViewerObject> mSourceObject;
+ LLPointer<LLViewerObject> mTargetObject;
+
+private:
+ typedef std::list<LLPointer<LLHUDObject> > hud_object_list_t;
+ static hud_object_list_t sHUDObjects;
+};
+
+#endif // LL_LLHUDOBJECT_H
diff --git a/indra/newview/llhudrender.cpp b/indra/newview/llhudrender.cpp
new file mode 100644
index 0000000000..9b74b3963b
--- /dev/null
+++ b/indra/newview/llhudrender.cpp
@@ -0,0 +1,113 @@
+/**
+ * @file llhudrender.cpp
+ * @brief LLHUDRender class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudrender.h"
+
+#include "llgl.h"
+#include "llviewercamera.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llfontgl.h"
+#include "llimagegl.h"
+#include "llglheaders.h"
+#include "llviewerwindow.h"
+
+void hud_render_utf8text(const std::string &str, const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset, const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic)
+{
+ LLWString wstr(utf8str_to_wstring(str));
+ hud_render_text(wstr, pos_agent, font, style, x_offset, y_offset, color, orthographic);
+}
+
+void hud_render_text(const LLWString &wstr, const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset, const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic)
+{
+ // Do cheap plane culling
+ LLVector3 dir_vec = pos_agent - gCamera->getOrigin();
+ dir_vec /= dir_vec.magVec();
+
+ if (wstr.empty() || (!orthographic && dir_vec * gCamera->getAtAxis() <= 0.f))
+ {
+ return;
+ }
+
+ LLVector3 right_axis;
+ LLVector3 up_axis;
+ if (orthographic)
+ {
+ right_axis.setVec(0.f, -1.f / gViewerWindow->getWindowHeight(), 0.f);
+ up_axis.setVec(0.f, 0.f, 1.f / gViewerWindow->getWindowHeight());
+ }
+ else
+ {
+ gCamera->getPixelVectors(pos_agent, up_axis, right_axis);
+ }
+ LLCoordFrame render_frame = *gCamera;
+ LLQuaternion rot;
+ if (!orthographic)
+ {
+ rot = render_frame.getQuaternion();
+ rot = rot * LLQuaternion(-F_PI_BY_TWO, gCamera->getYAxis());
+ rot = rot * LLQuaternion(F_PI_BY_TWO, gCamera->getXAxis());
+ }
+ else
+ {
+ rot = LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 0.f, 1.f));
+ rot = rot * LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 1.f, 0.f));
+ }
+ F32 angle;
+ LLVector3 axis;
+ rot.getAngleAxis(&angle, axis);
+
+ LLVector3 render_pos = pos_agent + (floorf(x_offset) * right_axis) + (floorf(y_offset) * up_axis);
+
+ //get the render_pos in screen space
+ F64 modelview[16];
+ F64 projection[16];
+ GLint viewport[4];
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
+ glGetDoublev(GL_PROJECTION_MATRIX, projection);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ F64 winX, winY, winZ;
+ gluProject(render_pos.mV[0], render_pos.mV[1], render_pos.mV[2],
+ modelview, projection, viewport,
+ &winX, &winY, &winZ);
+
+ //fonts all render orthographically, set up projection
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLUI::pushMatrix();
+
+ gViewerWindow->setup2DRender();
+
+ LLUI::loadIdentity();
+ LLUI::translate((F32) winX*1.0f/LLFontGL::sScaleX, (F32) winY*1.0f/(LLFontGL::sScaleY), -(((F32) winZ*2.f)-1.f));
+ //glRotatef(angle * RAD_TO_DEG, axis.mV[VX], axis.mV[VY], axis.mV[VZ]);
+ //glScalef(right_scale, up_scale, 1.f);
+ F32 right_x;
+
+ font.render(wstr, 0, 0, 0, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, wstr.length(), 1000, &right_x);
+ LLUI::popMatrix();
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+}
diff --git a/indra/newview/llhudrender.h b/indra/newview/llhudrender.h
new file mode 100644
index 0000000000..31ef3f8464
--- /dev/null
+++ b/indra/newview/llhudrender.h
@@ -0,0 +1,39 @@
+/**
+ * @file llhudrender.h
+ * @brief LLHUDRender class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDRENDER_H
+#define LL_LLHUDRENDER_H
+
+#include "llfontgl.h"
+
+class LLVector3;
+class LLFontGL;
+
+// Utility classes for rendering HUD elements
+void hud_render_text(const LLWString &wstr,
+ const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset,
+ const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic);
+
+// Legacy, slower
+void hud_render_utf8text(const std::string &str,
+ const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset,
+ const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic);
+
+
+#endif //LL_LLHUDRENDER_H
+
diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp
new file mode 100644
index 0000000000..ad2ba368c9
--- /dev/null
+++ b/indra/newview/llhudtext.cpp
@@ -0,0 +1,991 @@
+/**
+ * @file llhudtext.cpp
+ * @brief LLHUDText class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudtext.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llchatbar.h"
+#include "llcriticaldamp.h"
+#include "lldrawable.h"
+#include "llfontgl.h"
+#include "llglheaders.h"
+#include "llhudrender.h"
+#include "llimagegl.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llvovolume.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llstatusbar.h"
+#include "llmenugl.h"
+#include "pipeline.h"
+#include <boost/tokenizer.hpp>
+
+
+const F32 SPRING_STRENGTH = 0.7f;
+const F32 RESTORATION_SPRING_TIME_CONSTANT = 0.1f;
+const F32 HORIZONTAL_PADDING = 15.f;
+const F32 VERTICAL_PADDING = 12.f;
+const F32 BUFFER_SIZE = 2.f;
+const F32 MIN_EDGE_OVERLAP = 3.f;
+F32 HUD_TEXT_MAX_WIDTH = 190.f;
+const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f;
+const F32 RESIZE_TIME = 0.f;
+const S32 NUM_OVERLAP_ITERATIONS = 10;
+const F32 NEIGHBOR_FORCE_FRACTION = 1.f;
+const F32 POSITION_DAMPING_TC = 0.2f;
+const F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f;
+const F32 LOD_0_SCREEN_COVERAGE = 0.15f;
+const F32 LOD_1_SCREEN_COVERAGE = 0.30f;
+const F32 LOD_2_SCREEN_COVERAGE = 0.40f;
+
+std::set<LLPointer<LLHUDText> > LLHUDText::sTextObjects;
+std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleTextObjects;
+std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleHUDTextObjects;
+
+bool lltextobject_further_away::operator()(const LLPointer<LLHUDText>& lhs, const LLPointer<LLHUDText>& rhs) const
+{
+ return (lhs->getDistance() > rhs->getDistance()) ? true : false;
+}
+
+
+LLHUDText::LLHUDText(const U8 type) :
+ LLHUDObject(type),
+ mUseBubble(FALSE),
+ mUsePixelSize(TRUE),
+ mVisibleOffScreen(FALSE),
+ mWidth(0.f),
+ mHeight(0.f),
+ mFontp(LLFontGL::sSansSerifSmall),
+ mBoldFontp(LLFontGL::sSansSerifBold),
+ mMass(1.f),
+ mMaxLines(10),
+ mOffsetY(0),
+ mTextAlignment(ALIGN_TEXT_CENTER),
+ mVertAlignment(ALIGN_VERT_CENTER),
+ mLOD(0)
+{
+ mColor = LLColor4(1.f, 1.f, 1.f, 1.f);
+ mDoFade = TRUE;
+ mFadeDistance = 8.f;
+ mFadeRange = 4.f;
+ mZCompare = TRUE;
+ mDropShadow = TRUE;
+ mOffscreen = FALSE;
+ mRadius = 0.1f;
+ LLPointer<LLHUDText> ptr(this);
+ sTextObjects.insert(ptr);
+ //LLDebugVarMessageBox::show("max width", &HUD_TEXT_MAX_WIDTH, 500.f, 1.f);
+}
+
+LLHUDText::~LLHUDText()
+{
+}
+
+
+void LLHUDText::render()
+{
+ if (!mOnHUDAttachment)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderText(FALSE);
+ }
+}
+
+void LLHUDText::renderForSelect()
+{
+ if (!mOnHUDAttachment)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderText(TRUE);
+ }
+}
+
+void LLHUDText::renderText(BOOL for_select)
+{
+ if (!mVisible)
+ {
+ return;
+ }
+
+ // don't pick text that isn't bound to a viewerobject or isn't in a bubble
+ if (for_select &&
+ (!mSourceObject || mSourceObject->mDrawable.isNull() || !mUseBubble))
+ {
+ return;
+ }
+
+ LLGLState gls_tex(GL_TEXTURE_2D, for_select ? FALSE : TRUE);
+ LLGLState gls_blend(GL_BLEND, for_select ? FALSE : TRUE);
+ LLGLState gls_alpha(GL_ALPHA_TEST, for_select ? FALSE : TRUE);
+
+ LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f);
+ F32 alpha_factor = 1.f;
+ LLColor4 text_color = mColor;
+ if (mDoFade)
+ {
+ if (mLastDistance > mFadeDistance)
+ {
+ alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange);
+ text_color.mV[3] = text_color.mV[3]*alpha_factor;
+ }
+ }
+ if (text_color.mV[3] < 0.01f)
+ {
+ return;
+ }
+ shadow_color.mV[3] = text_color.mV[3];
+
+ mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f));
+
+ //FIXME: cache this image
+ LLUUID image_id;
+ image_id.set(gViewerArt.getString("rounded_square.tga"));
+ LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ //FIXME: make this a per-text setting
+ LLColor4 bg_color = gSavedSettings.getColor4("BackgroundChatColor");
+ bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
+
+ const S32 border_height = 16;
+ const S32 border_width = 16;
+
+ //FIXME move this into helper function
+ F32 border_scale = 1.f;
+
+ if (border_height * 2 > mHeight)
+ {
+ border_scale = (F32)mHeight / ((F32)border_height * 2.f);
+ }
+ if (border_width * 2 > mWidth)
+ {
+ border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f));
+ }
+
+ // scale screen size of borders down
+ //RN: for now, text on hud objects is never occluded
+
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+
+ if (mOnHUDAttachment)
+ {
+ x_pixel_vec = LLVector3::y_axis / (F32)gViewerWindow->getWindowWidth();
+ y_pixel_vec = LLVector3::z_axis / (F32)gViewerWindow->getWindowHeight();
+ }
+ else
+ {
+ gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
+ }
+
+ LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getWidth(), (F32)border_height / (F32)imagep->getHeight());
+ LLVector3 width_vec = mWidth * x_pixel_vec;
+ LLVector3 height_vec = mHeight * y_pixel_vec;
+ LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec;
+ LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec;
+
+ mRadius = (width_vec + height_vec).magVec() * 0.5f;
+
+ LLCoordGL screen_pos;
+ gCamera->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE);
+
+ LLVector2 screen_offset;
+ if (!mUseBubble)
+ {
+ screen_offset = mPositionOffset;
+ }
+ else
+ {
+ screen_offset = updateScreenPos(mPositionOffset);
+ }
+
+ LLVector3 render_position = mPositionAgent
+ + (x_pixel_vec * screen_offset.mV[VX])
+ + (y_pixel_vec * screen_offset.mV[VY]);
+
+ //if (mOnHUD)
+ //{
+ // render_position.mV[VY] -= fmodf(render_position.mV[VY], 1.f / (F32)gViewerWindow->getWindowWidth());
+ // render_position.mV[VZ] -= fmodf(render_position.mV[VZ], 1.f / (F32)gViewerWindow->getWindowHeight());
+ //}
+ //else
+ //{
+ // render_position = gCamera->roundToPixel(render_position);
+ //}
+
+ if (mUseBubble)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ LLUI::pushMatrix();
+ {
+ LLVector3 bg_pos = render_position
+ + (F32)mOffsetY * y_pixel_vec
+ - (width_vec / 2.f)
+ - (height_vec);
+ LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
+
+ if (for_select)
+ {
+ LLGLSNoTexture no_texture_state;
+ S32 name = mSourceObject->mGLName;
+ LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name);
+ glColor4ubv(coloru.mV);
+ gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
+ LLUI::popMatrix();
+ return;
+ }
+ else
+ {
+ LLViewerImage::bindTexture(imagep);
+
+ glColor4fv(bg_color.mV);
+ gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
+
+ if ( mLabelSegments.size())
+ {
+ LLUI::pushMatrix();
+ {
+ glColor4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
+ LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec;
+ LLVector3 label_offset = height_vec - label_height;
+ LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]);
+ gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height);
+ }
+ LLUI::popMatrix();
+ }
+ }
+
+ BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f;
+ BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f);
+
+ // draw line segments pointing to parent object
+ if (!mOffscreen && (outside_width || outside_height))
+ {
+ LLUI::pushMatrix();
+ {
+ glColor4fv(bg_color.mV);
+ LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec);
+ target_pos += (width_vec / 2.f);
+ target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero;
+ target_pos -= 3.f * x_pixel_vec;
+ target_pos -= 6.f * y_pixel_vec;
+ LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]);
+ gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec);
+ }
+ LLUI::popMatrix();
+
+
+ LLGLDisable gls_texture_2d(GL_TEXTURE_2D);
+ LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE);
+
+ LLVector3 box_center_offset;
+ box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f);
+ LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]);
+ glColor4fv(bg_color.mV);
+ LLUI::setLineWidth(2.0);
+ glBegin(GL_LINES);
+ {
+ if (outside_width)
+ {
+ LLVector3 vert;
+ // draw line in x then y
+ if (mPositionOffset.mV[VX] < 0.f)
+ {
+ // start at right edge
+ vert = width_vec * 0.5f;
+ glVertex3fv(vert.mV);
+ }
+ else
+ {
+ // start at left edge
+ vert = width_vec * -0.5f;
+ glVertex3fv(vert.mV);
+ }
+ vert = -mPositionOffset.mV[VX] * x_pixel_vec;
+ glVertex3fv(vert.mV);
+ glVertex3fv(vert.mV);
+ vert -= mPositionOffset.mV[VY] * y_pixel_vec;
+ vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
+ glVertex3fv(vert.mV);
+ }
+ else
+ {
+ LLVector3 vert;
+ // draw line in y then x
+ if (mPositionOffset.mV[VY] < 0.f)
+ {
+ // start at top edge
+ vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
+ glVertex3fv(vert.mV);
+ }
+ else
+ {
+ // start at bottom edge
+ vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
+ glVertex3fv(vert.mV);
+ }
+ vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec;
+ vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
+ glVertex3fv(vert.mV);
+ }
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0);
+
+ }
+ }
+ LLUI::popMatrix();
+ }
+
+ F32 y_offset = (F32)mOffsetY;
+
+ // Render label
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ for(std::vector<LLHUDTextSegment>::iterator segment_iter = mLabelSegments.begin();
+ segment_iter != mLabelSegments.end(); ++segment_iter )
+ {
+ const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp;
+ y_offset -= fontp->getLineHeight();
+
+ F32 x_offset;
+ if (mTextAlignment == ALIGN_TEXT_CENTER)
+ {
+ x_offset = -0.5f*segment_iter->getWidth(fontp);
+ }
+ else // ALIGN_LEFT
+ {
+ x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
+ }
+
+ LLColor4 label_color(0.f, 0.f, 0.f, 1.f);
+ label_color.mV[VALPHA] = alpha_factor;
+ hud_render_text(segment_iter->getText(), render_position, *fontp, segment_iter->mStyle, x_offset, y_offset, label_color, mOnHUDAttachment);
+ }
+ }
+
+ // Render text
+ {
+ // -1 mMaxLines means unlimited lines.
+ S32 start_segment;
+ S32 max_lines = getMaxLines();
+
+ if (max_lines < 0)
+ {
+ start_segment = 0;
+ }
+ else
+ {
+ start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
+ }
+
+ for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin() + start_segment;
+ segment_iter != mTextSegments.end(); ++segment_iter )
+ {
+ const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp;
+ y_offset -= fontp->getLineHeight();
+
+ U8 style = segment_iter->mStyle;
+ if (mDropShadow)
+ {
+ style |= LLFontGL::DROP_SHADOW;
+ }
+
+ F32 x_offset;
+ if (mTextAlignment== ALIGN_TEXT_CENTER)
+ {
+ x_offset = -0.5f*segment_iter->getWidth(fontp);
+ }
+ else // ALIGN_LEFT
+ {
+ x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
+ }
+
+ text_color = segment_iter->mColor;
+ text_color.mV[VALPHA] *= alpha_factor;
+
+ hud_render_text(segment_iter->getText(), render_position, *fontp, style, x_offset, y_offset, text_color, mOnHUDAttachment);
+ }
+ }
+}
+
+void LLHUDText::setStringUTF8(const std::string &wtext)
+{
+ setString(utf8str_to_wstring(wtext));
+}
+
+void LLHUDText::setString(const LLWString &wtext)
+{
+ mTextSegments.clear();
+ addLine(wtext, mColor);
+}
+
+void LLHUDText::clearString()
+{
+ mTextSegments.clear();
+}
+
+
+void LLHUDText::addLine(const std::string &str, const LLColor4& color, const LLFontGL::StyleFlags style)
+{
+ addLine(utf8str_to_wstring(str), color, style);
+}
+
+
+void LLHUDText::addLine(const LLWString &wstr, const LLColor4& color, const LLFontGL::StyleFlags style)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+ if (!wstr.empty())
+ {
+ LLWString wline(wstr);
+ typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
+ LLWString seps(utf8str_to_wstring("\r\n"));
+ boost::char_separator<llwchar> sep(seps.c_str());
+
+ tokenizer tokens(wline, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ while (iter != tokens.end())
+ {
+ U32 line_length = 0;
+ do
+ {
+ S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wline.length(), TRUE);
+ mTextSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), style, color));
+ line_length += segment_length;
+ }
+ while (line_length != iter->size());
+ ++iter;
+ }
+ }
+}
+
+void LLHUDText::setLabel(const std::string &label)
+{
+ setLabel(utf8str_to_wstring(label));
+}
+
+void LLHUDText::setLabel(const LLWString &wlabel)
+{
+ mLabelSegments.clear();
+
+ if (!wlabel.empty())
+ {
+ LLWString wstr(wlabel);
+ LLWString seps(utf8str_to_wstring("\r\n"));
+ LLWString empty;
+
+ typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
+ boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(), boost::keep_empty_tokens);
+
+ tokenizer tokens(wstr, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ while (iter != tokens.end())
+ {
+ U32 line_length = 0;
+ do
+ {
+ S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wstr.length(), TRUE);
+ mLabelSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor));
+ line_length += segment_length;
+ }
+ while (line_length != iter->size());
+ ++iter;
+ }
+ }
+}
+
+void LLHUDText::setDropShadow(const BOOL do_shadow)
+{
+ mDropShadow = do_shadow;
+}
+
+void LLHUDText::setZCompare(const BOOL zcompare)
+{
+ mZCompare = zcompare;
+}
+
+void LLHUDText::setFont(const LLFontGL* font)
+{
+ mFontp = font;
+}
+
+
+void LLHUDText::setColor(const LLColor4 &color)
+{
+ mColor = color;
+ for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin();
+ segment_iter != mTextSegments.end(); ++segment_iter )
+ {
+ segment_iter->mColor = color;
+ }
+}
+
+
+void LLHUDText::setUsePixelSize(const BOOL use_pixel_size)
+{
+ mUsePixelSize = use_pixel_size;
+}
+
+void LLHUDText::setDoFade(const BOOL do_fade)
+{
+ mDoFade = do_fade;
+}
+
+void LLHUDText::updateVisibility()
+{
+ if (mSourceObject)
+ {
+ mSourceObject->updateText();
+ }
+
+ mPositionAgent = gAgent.getPosAgentFromGlobal(mPositionGlobal);
+
+ if (!mSourceObject)
+ {
+ //llwarns << "LLHUDText::updateScreenPos -- mSourceObject is NULL!" << llendl;
+ mVisible = TRUE;
+ if (mOnHUDAttachment)
+ {
+ sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
+ }
+ else
+ {
+ sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
+ }
+ return;
+ }
+
+ // Not visible if parent object is dead
+ if (mSourceObject->isDead())
+ {
+ mVisible = FALSE;
+ return;
+ }
+
+ // for now, all text on hud objects is visible
+ if (mOnHUDAttachment)
+ {
+ mVisible = TRUE;
+ sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
+ mLastDistance = mPositionAgent.mV[VX];
+ return;
+ }
+
+ // push text towards camera by radius of object, but not past camera
+ LLVector3 vec_from_camera = mPositionAgent - gCamera->getOrigin();
+ LLVector3 dir_from_camera = vec_from_camera;
+ dir_from_camera.normVec();
+
+ if (dir_from_camera * gCamera->getAtAxis() <= 0.f)
+ {
+ mPositionAgent -= projected_vec(vec_from_camera, gCamera->getAtAxis()) * 1.f;
+ mPositionAgent += gCamera->getAtAxis() * (gCamera->getNear() + 0.1f);
+ }
+ else if (vec_from_camera * gCamera->getAtAxis() <= gCamera->getNear() + 0.1f + mSourceObject->getVObjRadius())
+ {
+ mPositionAgent = gCamera->getOrigin() + vec_from_camera * ((gCamera->getNear() + 0.1f) / (vec_from_camera * gCamera->getAtAxis()));
+ }
+ else
+ {
+ mPositionAgent -= dir_from_camera * mSourceObject->getVObjRadius();
+ }
+
+ mLastDistance = (mPositionAgent - gCamera->getOrigin()).magVec();
+
+ if (mLOD >= 3 || !mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange)))
+ {
+ mVisible = FALSE;
+ return;
+ }
+
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+
+ gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
+
+ LLVector3 render_position = mPositionAgent +
+ (x_pixel_vec * mPositionOffset.mV[VX]) +
+ (y_pixel_vec * mPositionOffset.mV[VY]);
+
+ mOffscreen = FALSE;
+ if (!gCamera->sphereInFrustum(render_position, mRadius))
+ {
+ if (!mVisibleOffScreen)
+ {
+ mVisible = FALSE;
+ return;
+ }
+ else
+ {
+ mOffscreen = TRUE;
+ }
+ }
+
+ mVisible = TRUE;
+ sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
+}
+
+LLVector2 LLHUDText::updateScreenPos(LLVector2 &offset)
+{
+ LLCoordGL screen_pos;
+ LLVector2 screen_pos_vec;
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+ gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
+ LLVector3 world_pos = mPositionAgent + (offset.mV[VX] * x_pixel_vec) + (offset.mV[VY] * y_pixel_vec);
+ if (!gCamera->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen)
+ {
+ // bubble off-screen, so find a spot for it along screen edge
+ LLVector2 window_center(gViewerWindow->getWindowDisplayWidth() * 0.5f, gViewerWindow->getWindowDisplayHeight() * 0.5f);
+ LLVector2 delta_from_center(screen_pos.mX - window_center.mV[VX],
+ screen_pos.mY - window_center.mV[VY]);
+ delta_from_center.normVec();
+
+ F32 camera_aspect = gCamera->getAspect();
+ F32 delta_aspect = llabs(delta_from_center.mV[VX] / delta_from_center.mV[VY]);
+ if (camera_aspect / llmax(delta_aspect, 0.001f) > 1.f)
+ {
+ // camera has wider aspect ratio than offset vector, so clamp to height
+ delta_from_center *= llabs(window_center.mV[VY] / delta_from_center.mV[VY]);
+ }
+ else
+ {
+ // camera has narrower aspect ratio than offset vector, so clamp to width
+ delta_from_center *= llabs(window_center.mV[VX] / delta_from_center.mV[VX]);
+ }
+
+ screen_pos_vec = window_center + delta_from_center;
+ }
+ else
+ {
+ screen_pos_vec.setVec((F32)screen_pos.mX, (F32)screen_pos.mY);
+ }
+ S32 bottom = STATUS_BAR_HEIGHT;
+ if (gChatBar->getVisible())
+ {
+ bottom += CHAT_BAR_HEIGHT;
+ }
+
+ LLVector2 screen_center;
+ screen_center.mV[VX] = llclamp((F32)screen_pos_vec.mV[VX], mWidth * 0.5f, (F32)gViewerWindow->getWindowDisplayWidth() - mWidth * 0.5f);
+
+ if(mVertAlignment == ALIGN_VERT_TOP)
+ {
+ screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY],
+ (F32)bottom,
+ (F32)gViewerWindow->getWindowDisplayHeight() - mHeight - (F32)MENU_BAR_HEIGHT);
+ mSoftScreenRect.setLeftTopAndSize(screen_center.mV[VX] - (mWidth + BUFFER_SIZE) * 0.5f,
+ screen_center.mV[VY] + (mHeight + BUFFER_SIZE), mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
+ }
+ else
+ {
+ screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY],
+ (F32)bottom + mHeight * 0.5f,
+ (F32)gViewerWindow->getWindowDisplayHeight() - mHeight * 0.5f - (F32)MENU_BAR_HEIGHT);
+ mSoftScreenRect.setCenterAndSize(screen_center.mV[VX], screen_center.mV[VY], mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
+ }
+
+ return offset + (screen_center - LLVector2((F32)screen_pos.mX, (F32)screen_pos.mY));
+}
+
+void LLHUDText::updateSize()
+{
+ F32 width = 0.f;
+
+ S32 max_lines = getMaxLines();
+ S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines);
+
+ F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size());
+
+ S32 start_segment;
+ if (max_lines < 0) start_segment = 0;
+ else start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
+
+ std::vector<LLHUDTextSegment>::iterator iter = mTextSegments.begin() + start_segment;
+ while (iter != mTextSegments.end())
+ {
+ width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
+ ++iter;
+ }
+
+ iter = mLabelSegments.begin();
+ while (iter != mLabelSegments.end())
+ {
+ width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
+ ++iter;
+ }
+
+ if (width == 0.f)
+ {
+ return;
+ }
+
+ width += HORIZONTAL_PADDING;
+ height += VERTICAL_PADDING;
+
+ if (!mResizeTimer.getStarted() && (width != mWidth || height != mHeight))
+ {
+ mResizeTimer.start();
+ }
+
+ // *NOTE: removed logic which did a divide by zero.
+ F32 u = 1.f;//llclamp(mResizeTimer.getElapsedTimeF32() / RESIZE_TIME, 0.f, 1.f);
+ if (u == 1.f)
+ {
+ mResizeTimer.stop();
+ }
+
+ mWidth = llmax(width, lerp(mWidth, (F32)width, u));
+ mHeight = llmax(height, lerp(mHeight, (F32)height, u));
+}
+
+void LLHUDText::updateAll()
+{
+ // iterate over all text objects, calculate their restoration forces,
+ // and add them to the visible set if they are on screen and close enough
+ sVisibleTextObjects.clear();
+ sVisibleHUDTextObjects.clear();
+
+ TextObjectIterator text_it;
+ for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
+ {
+ LLHUDText* textp = (*text_it);
+ textp->mTargetPositionOffset.clearVec();
+ textp->updateSize();
+ textp->updateVisibility();
+ }
+
+ // sort back to front for rendering purposes
+ std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(), lltextobject_further_away());
+ std::sort(sVisibleHUDTextObjects.begin(), sVisibleHUDTextObjects.end(), lltextobject_further_away());
+
+ // iterate from front to back, and set LOD based on current screen coverage
+ F32 screen_area = (F32)(gViewerWindow->getWindowWidth() * gViewerWindow->getWindowHeight());
+ F32 current_screen_area = 0.f;
+ std::vector<LLPointer<LLHUDText> >::reverse_iterator r_it;
+ for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it)
+ {
+ LLHUDText* textp = (*r_it);
+ if (textp->mUseBubble)
+ {
+ if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE)
+ {
+ textp->setLOD(3);
+ }
+ else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE)
+ {
+ textp->setLOD(2);
+ }
+ else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE)
+ {
+ textp->setLOD(1);
+ }
+ else
+ {
+ textp->setLOD(0);
+ }
+ textp->updateSize();
+ // find on-screen position and initialize collision rectangle
+ textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero);
+ current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight());
+ }
+ }
+
+ LLStat* camera_vel_stat = gCamera->getVelocityStat();
+ F32 camera_vel = camera_vel_stat->getCurrent();
+ if (camera_vel > MAX_STABLE_CAMERA_VELOCITY)
+ {
+ return;
+ }
+
+ VisibleTextObjectIterator src_it;
+
+ for (S32 i = 0; i < NUM_OVERLAP_ITERATIONS; i++)
+ {
+ for (src_it = sVisibleTextObjects.begin(); src_it != sVisibleTextObjects.end(); ++src_it)
+ {
+ LLHUDText* src_textp = (*src_it);
+
+ if (!src_textp->mUseBubble)
+ {
+ continue;
+ }
+ VisibleTextObjectIterator dst_it = src_it;
+ ++dst_it;
+ for (; dst_it != sVisibleTextObjects.end(); ++dst_it)
+ {
+ LLHUDText* dst_textp = (*dst_it);
+
+ if (!dst_textp->mUseBubble)
+ {
+ continue;
+ }
+ if (src_textp->mSoftScreenRect.rectInRect(&dst_textp->mSoftScreenRect))
+ {
+ LLRectf intersect_rect = src_textp->mSoftScreenRect & dst_textp->mSoftScreenRect;
+ intersect_rect.stretch(-BUFFER_SIZE * 0.5f);
+
+ F32 src_center_x = src_textp->mSoftScreenRect.getCenterX();
+ F32 src_center_y = src_textp->mSoftScreenRect.getCenterY();
+ F32 dst_center_x = dst_textp->mSoftScreenRect.getCenterX();
+ F32 dst_center_y = dst_textp->mSoftScreenRect.getCenterY();
+ F32 intersect_center_x = intersect_rect.getCenterX();
+ F32 intersect_center_y = intersect_rect.getCenterY();
+ LLVector2 force = lerp(LLVector2(dst_center_x - intersect_center_x, dst_center_y - intersect_center_y),
+ LLVector2(intersect_center_x - src_center_x, intersect_center_y - src_center_y),
+ 0.5f);
+ force.setVec(dst_center_x - src_center_x, dst_center_y - src_center_y);
+ force.normVec();
+
+ LLVector2 src_force = -1.f * force;
+ LLVector2 dst_force = force;
+
+ LLVector2 force_strength;
+ F32 src_mult = dst_textp->mMass / (dst_textp->mMass + src_textp->mMass);
+ F32 dst_mult = 1.f - src_mult;
+ F32 src_aspect_ratio = src_textp->mSoftScreenRect.getWidth() / src_textp->mSoftScreenRect.getHeight();
+ F32 dst_aspect_ratio = dst_textp->mSoftScreenRect.getWidth() / dst_textp->mSoftScreenRect.getHeight();
+ src_force.mV[VY] *= src_aspect_ratio;
+ src_force.normVec();
+ dst_force.mV[VY] *= dst_aspect_ratio;
+ dst_force.normVec();
+
+ src_force.mV[VX] *= llmin(intersect_rect.getWidth() * src_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
+ src_force.mV[VY] *= llmin(intersect_rect.getHeight() * src_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
+ dst_force.mV[VX] *= llmin(intersect_rect.getWidth() * dst_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
+ dst_force.mV[VY] *= llmin(intersect_rect.getHeight() * dst_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
+
+ src_textp->mTargetPositionOffset += src_force;
+ dst_textp->mTargetPositionOffset += dst_force;
+ src_textp->mTargetPositionOffset = src_textp->updateScreenPos(src_textp->mTargetPositionOffset);
+ dst_textp->mTargetPositionOffset = dst_textp->updateScreenPos(dst_textp->mTargetPositionOffset);
+ }
+ }
+ }
+ }
+
+ VisibleTextObjectIterator this_object_it;
+ for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it)
+ {
+ if (!(*this_object_it)->mUseBubble)
+ {
+ continue;
+ }
+ (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC));
+ }
+}
+
+void LLHUDText::setLOD(S32 lod)
+{
+ mLOD = lod;
+ //RN: uncomment this to visualize LOD levels
+ //char label[255];
+ //sprintf(label, "%d", lod);
+ //setLabel(label);
+}
+
+S32 LLHUDText::getMaxLines()
+{
+ switch(mLOD)
+ {
+ case 0:
+ return mMaxLines;
+ case 1:
+ return mMaxLines > 0 ? mMaxLines / 2 : 5;
+ case 2:
+ return mMaxLines > 0 ? mMaxLines / 3 : 2;
+ default:
+ // label only
+ return 0;
+ }
+}
+
+void LLHUDText::markDead()
+{
+ sTextObjects.erase(LLPointer<LLHUDText>(this));
+ LLHUDObject::markDead();
+}
+
+void LLHUDText::renderAllHUD()
+{
+ LLGLEnable color_mat(GL_COLOR_MATERIAL);
+ LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+
+ VisibleTextObjectIterator text_it;
+
+ for (text_it = sVisibleHUDTextObjects.begin(); text_it != sVisibleHUDTextObjects.end(); ++text_it)
+ {
+ (*text_it)->renderText(FALSE);
+ }
+}
+
+//static
+void LLHUDText::addPickable(std::set<LLViewerObject*> &pick_list)
+{
+ //this might put an object on the pick list a second time, overriding it's mGLName, which is ok
+ //FIXME: we should probably cull against pick frustum
+ VisibleTextObjectIterator text_it;
+ for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it)
+ {
+ if (!(*text_it)->mUseBubble)
+ {
+ continue;
+ }
+ pick_list.insert((*text_it)->mSourceObject);
+ }
+}
+
+//static
+// called when UI scale changes, to flush font width caches
+void LLHUDText::reshape()
+{
+ TextObjectIterator text_it;
+ for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
+ {
+ LLHUDText* textp = (*text_it);
+ std::vector<LLHUDTextSegment>::iterator segment_iter;
+ for (segment_iter = textp->mTextSegments.begin();
+ segment_iter != textp->mTextSegments.end(); ++segment_iter )
+ {
+ segment_iter->clearFontWidthMap();
+ }
+ for(segment_iter = textp->mLabelSegments.begin();
+ segment_iter != textp->mLabelSegments.end(); ++segment_iter )
+ {
+ segment_iter->clearFontWidthMap();
+ }
+ }
+}
+
+//============================================================================
+
+F32 LLHUDText::LLHUDTextSegment::getWidth(const LLFontGL* font)
+{
+ std::map<const LLFontGL*, F32>::iterator iter = mFontWidthMap.find(font);
+ if (iter != mFontWidthMap.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ F32 width = font->getWidthF32(mText.c_str());
+ mFontWidthMap[font] = width;
+ return width;
+ }
+}
diff --git a/indra/newview/llhudtext.h b/indra/newview/llhudtext.h
new file mode 100644
index 0000000000..a2e328acd1
--- /dev/null
+++ b/indra/newview/llhudtext.h
@@ -0,0 +1,155 @@
+/**
+ * @file llhudtext.h
+ * @brief LLHUDText class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDTEXT_H
+#define LL_LLHUDTEXT_H
+
+#include "llmemory.h"
+#include "lldarrayptr.h"
+
+#include "llhudobject.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "llrect.h"
+#include "llframetimer.h"
+#include "llfontgl.h"
+#include <set>
+#include <vector>
+#include "lldarray.h"
+
+// Renders a 2D text billboard floating at the location specified.
+class LLDrawable;
+class LLHUDText;
+
+struct lltextobject_further_away
+{
+ bool operator()(const LLPointer<LLHUDText>& lhs, const LLPointer<LLHUDText>& rhs) const;
+};
+
+class LLHUDText : public LLHUDObject
+{
+protected:
+ class LLHUDTextSegment
+ {
+ public:
+ LLHUDTextSegment(const LLWString& text, const LLFontGL::StyleFlags style, const LLColor4& color)
+ : mColor(color), mStyle(style), mText(text) {}
+ F32 getWidth(const LLFontGL* font);
+ const LLWString& getText() const { return mText; };
+ void clearFontWidthMap() { mFontWidthMap.clear(); }
+
+ LLColor4 mColor;
+ LLFontGL::StyleFlags mStyle;
+ private:
+ LLWString mText;
+ std::map<const LLFontGL*, F32> mFontWidthMap;
+ };
+
+public:
+ typedef enum e_text_alignment
+ {
+ ALIGN_TEXT_LEFT,
+ ALIGN_TEXT_CENTER
+ } ETextAlignment;
+
+ typedef enum e_vert_alignment
+ {
+ ALIGN_VERT_TOP,
+ ALIGN_VERT_CENTER
+ } EVertAlignment;
+
+public:
+ void setStringUTF8(const std::string &utf8string);
+ void setString(const LLWString &wstring);
+ void clearString();
+ void addLine(const std::string &text, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL);
+ void addLine(const LLWString &wtext, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL);
+ void setLabel(const std::string &label);
+ void setLabel(const LLWString &label);
+ void setDropShadow(const BOOL do_shadow);
+ void setFont(const LLFontGL* font);
+ void setColor(const LLColor4 &color);
+ void setUsePixelSize(const BOOL use_pixel_size);
+ void setZCompare(const BOOL zcompare);
+ void setDoFade(const BOOL do_fade);
+ void setVisibleOffScreen(BOOL visible) { mVisibleOffScreen = visible; }
+
+ // mMaxLines of -1 means unlimited lines.
+ void setMaxLines(S32 max_lines) { mMaxLines = max_lines; }
+ void setFadeDistance(F32 fade_distance, F32 fade_range) { mFadeDistance = fade_distance; mFadeRange = fade_range; }
+ void updateVisibility();
+ LLVector2 updateScreenPos(LLVector2 &offset_target);
+ void updateSize();
+ void setMass(F32 mass) { mMass = llmax(0.1f, mass); }
+ void setTextAlignment(ETextAlignment alignment) { mTextAlignment = alignment; }
+ void setVertAlignment(EVertAlignment alignment) { mVertAlignment = alignment; }
+ /*virtual*/ void markDead();
+ friend class LLHUDObject;
+ /*virtual*/ F32 getDistance() const { return mLastDistance; }
+ void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; }
+ S32 getLOD() { return mLOD; }
+ BOOL getVisible() { return mVisible; }
+ void setOnHUDAttachment(BOOL on_hud) { mOnHUDAttachment = on_hud; }
+
+ static void renderAllHUD();
+ static void addPickable(std::set<LLViewerObject*> &pick_list);
+ static void reshape();
+protected:
+ LLHUDText(const U8 type);
+
+ /*virtual*/ void render();
+ /*virtual*/ void renderForSelect();
+ void renderText(BOOL for_select);
+ static void updateAll();
+ void setLOD(S32 lod);
+ S32 getMaxLines();
+
+private:
+ ~LLHUDText();
+ BOOL mOnHUD;
+ BOOL mUseBubble;
+ BOOL mDropShadow;
+ BOOL mDoFade;
+ F32 mFadeRange;
+ F32 mFadeDistance;
+ F32 mLastDistance;
+ BOOL mUsePixelSize;
+ BOOL mZCompare;
+ BOOL mVisibleOffScreen;
+ BOOL mOffscreen;
+ LLColor4 mColor;
+ LLVector3 mScale;
+ F32 mWidth;
+ F32 mHeight;
+ LLColor4U mPickColor;
+ const LLFontGL* mFontp;
+ const LLFontGL* mBoldFontp;
+ LLRectf mSoftScreenRect;
+ LLVector3 mPositionAgent;
+ LLVector2 mPositionOffset;
+ LLVector2 mTargetPositionOffset;
+ F32 mMass;
+ S32 mMaxLines;
+ S32 mOffsetY;
+ F32 mRadius;
+ std::vector<LLHUDTextSegment> mTextSegments;
+ std::vector<LLHUDTextSegment> mLabelSegments;
+ LLFrameTimer mResizeTimer;
+ ETextAlignment mTextAlignment;
+ EVertAlignment mVertAlignment;
+ S32 mLOD;
+
+ static std::set<LLPointer<LLHUDText> > sTextObjects;
+ static std::vector<LLPointer<LLHUDText> > sVisibleTextObjects;
+ static std::vector<LLPointer<LLHUDText> > sVisibleHUDTextObjects;
+ typedef std::set<LLPointer<LLHUDText> >::iterator TextObjectIterator;
+ typedef std::vector<LLPointer<LLHUDText> >::iterator VisibleTextObjectIterator;
+};
+
+#endif // LL_LLHUDTEXT_H
diff --git a/indra/newview/llhudview.cpp b/indra/newview/llhudview.cpp
new file mode 100644
index 0000000000..91cb5695ed
--- /dev/null
+++ b/indra/newview/llhudview.cpp
@@ -0,0 +1,78 @@
+/**
+ * @file llhudview.cpp
+ * @brief 2D HUD overlay
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudview.h"
+
+// library includes
+#include "v4color.h"
+#include "llcoord.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "llfloaterworldmap.h"
+#include "llworldmapview.h"
+#include "lltracker.h"
+#include "llviewercamera.h"
+#include "llui.h"
+
+LLHUDView *gHUDView = NULL;
+
+const S32 HUD_ARROW_SIZE = 32;
+
+LLHUDView::LLHUDView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, FALSE)
+{ }
+
+LLHUDView::~LLHUDView()
+{ }
+
+EWidgetType LLHUDView::getWidgetType() const
+{
+ return WIDGET_TYPE_HUD_VIEW;
+}
+
+LLString LLHUDView::getWidgetTag() const
+{
+ return LL_HUD_VIEW_TAG;
+}
+
+// virtual
+void LLHUDView::draw()
+{
+ LLTracker::drawHUDArrow();
+}
+
+
+// public
+const LLColor4& LLHUDView::colorFromType(S32 type)
+{
+ switch (type)
+ {
+ case 0:
+ return LLColor4::green;
+ default:
+ return LLColor4::black;
+ }
+}
+
+
+/*virtual*/
+BOOL LLHUDView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (LLTracker::handleMouseDown(x, y))
+ {
+ return TRUE;
+ }
+ return LLView::handleMouseDown(x, y, mask);
+}
+
diff --git a/indra/newview/llhudview.h b/indra/newview/llhudview.h
new file mode 100644
index 0000000000..85267979c6
--- /dev/null
+++ b/indra/newview/llhudview.h
@@ -0,0 +1,37 @@
+/**
+ * @file llhudview.h
+ * @brief 2D HUD overlay
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDVIEW_H
+#define LL_LLHUDVIEW_H
+
+#include "llview.h"
+#include "v4color.h"
+
+class LLVector3d;
+
+class LLHUDView
+: public LLView
+{
+public:
+ LLHUDView(const std::string& name, const LLRect& rect);
+ virtual ~LLHUDView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void draw();
+
+ const LLColor4& colorFromType(S32 type);
+
+protected:
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+};
+
+extern LLHUDView *gHUDView;
+
+#endif
diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp
new file mode 100644
index 0000000000..718ea894aa
--- /dev/null
+++ b/indra/newview/llimpanel.cpp
@@ -0,0 +1,793 @@
+/**
+ * @file llimpanel.cpp
+ * @brief LLIMPanel class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llimpanel.h"
+
+#include "indra_constants.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+#include "lltextbox.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llconsole.h"
+#include "llfloater.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llfloateravatarinfo.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llresmgr.h"
+#include "lltabcontainer.h"
+#include "llimview.h"
+#include "llviewertexteditor.h"
+#include "llviewermessage.h"
+#include "llviewerstats.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+#include "lllogchat.h"
+#include "llfloaterhtml.h"
+#include "llweb.h"
+
+//
+// Constants
+//
+const S32 LINE_HEIGHT = 16;
+const S32 MIN_WIDTH = 200;
+const S32 MIN_HEIGHT = 130;
+
+//
+// Statics
+//
+//
+static LLString sTitleString = "Instant Message with [NAME]";
+static LLString sTypingStartString = "[NAME]: ...";
+
+// Member Functions
+//
+
+LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, const LLRect& rect,
+ const std::string& session_label,
+ const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ EInstantMessage dialog) :
+ LLFloater(name, rect, session_label),
+ mInputEditor(NULL),
+ mHistoryEditor(NULL),
+ mSessionLabel(session_label),
+ mSessionUUID(session_id),
+ mOtherParticipantUUID(other_participant_id),
+ mLureID(),
+ mDialog(dialog),
+ mTyping(FALSE),
+ mOtherTyping(FALSE),
+ mTypingLineStartIndex(0),
+ mSentTypingState(TRUE),
+ mFirstKeystrokeTimer(),
+ mLastKeystrokeTimer()
+{
+ init();
+ setLabel(session_label);
+ setTitle(session_label);
+ mInputEditor->setMaxTextLength(1023);
+}
+
+
+void LLFloaterIMPanel::init()
+{
+ gUICtrlFactory->buildFloater(this, "floater_instant_message.xml", NULL, FALSE);
+}
+
+
+BOOL LLFloaterIMPanel::postBuild()
+{
+ requires("chat_editor", WIDGET_TYPE_LINE_EDITOR);
+ requires("profile_btn", WIDGET_TYPE_BUTTON);
+ requires("close_btn", WIDGET_TYPE_BUTTON);
+ requires("im_history", WIDGET_TYPE_TEXT_EDITOR);
+ requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX);
+ requires("title_string", WIDGET_TYPE_TEXT_BOX);
+ requires("typing_start_string", WIDGET_TYPE_TEXT_BOX);
+
+ if (checkRequirements())
+ {
+ mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "chat_editor");
+ mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived );
+ mInputEditor->setFocusLostCallback( onInputEditorFocusLost );
+ mInputEditor->setKeystrokeCallback( onInputEditorKeystroke );
+ mInputEditor->setCallbackUserData(this);
+ mInputEditor->setCommitOnFocusLost( FALSE );
+
+ LLButton* profile_btn = LLUICtrlFactory::getButtonByName(this, "profile_btn");
+ profile_btn->setClickedCallback(&LLFloaterIMPanel::onClickProfile, this);
+
+ LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn");
+ close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this);
+
+ mHistoryEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "im_history");
+ mHistoryEditor->setParseHTML(TRUE);
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
+ {
+ LLLogChat::loadHistory(mSessionLabel, &chatFromLogFile, (void *)this);
+ }
+
+ if (IM_SESSION_GROUP_START == mDialog
+ || IM_SESSION_911_START == mDialog)
+ {
+ profile_btn->setEnabled(FALSE);
+ if(IM_SESSION_911_START == mDialog)
+ {
+ LLTextBox* live_help_text = LLUICtrlFactory::getTextBoxByName(this, "live_help_dialog");
+ addHistoryLine(live_help_text->getText());
+ }
+ }
+
+ LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string");
+ sTitleString = title->getText();
+
+ LLTextBox* typing_start = LLUICtrlFactory::getTextBoxByName(this, "typing_start_string");
+
+ sTypingStartString = typing_start->getText();
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// virtual
+void LLFloaterIMPanel::draw()
+{
+ if (mTyping)
+ {
+ // Time out if user hasn't typed for a while.
+ if (mLastKeystrokeTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)
+ {
+ setTyping(FALSE);
+ }
+
+ // If we are typing, and it's been a little while, send the
+ // typing indicator
+ if (!mSentTypingState
+ && mFirstKeystrokeTimer.getElapsedTimeF32() > 1.f)
+ {
+ sendTypingState(TRUE);
+ mSentTypingState = TRUE;
+ }
+ }
+
+ LLFloater::draw();
+}
+
+
+BOOL LLFloaterIMPanel::addParticipants(const LLDynamicArray<LLUUID>& ids)
+{
+ if(isAddAllowed())
+ {
+ llinfos << "LLFloaterIMPanel::addParticipants() - adding participants" << llendl;
+ const S32 MAX_AGENTS = 50;
+ S32 count = ids.count();
+ if(count > MAX_AGENTS) return FALSE;
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
+ msg->addUUIDFast(_PREHASH_ToAgentID, mOtherParticipantUUID);
+ msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
+ msg->addU8Fast(_PREHASH_Dialog, mDialog);
+ msg->addUUIDFast(_PREHASH_ID, mSessionUUID);
+ msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
+ std::string name;
+ gAgent.buildFullname(name);
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ msg->addStringFast(_PREHASH_Message, LLString::null);
+ msg->addU32Fast(_PREHASH_ParentEstateID, 0);
+ msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
+ msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
+ if (IM_SESSION_GROUP_START == mDialog)
+ {
+ // *HACK: binary bucket contains session label - the server
+ // will actually add agents.
+ llinfos << "Group IM session name '" << mSessionLabel
+ << "'" << llendl;
+ msg->addStringFast(_PREHASH_BinaryBucket, mSessionLabel);
+ gAgent.sendReliableMessage();
+ }
+ else if (IM_SESSION_911_START == mDialog)
+ {
+ // HACK -- we modify the name of the session going out to
+ // the helpers to help them easily identify "Help"
+ // sessions in their collection of IM panels.
+ LLString name;
+ gAgent.getName(name);
+ LLString buffer = LLString("HELP ") + name;
+ llinfos << "LiveHelp IM session '" << buffer << "'." << llendl;
+ msg->addStringFast(_PREHASH_BinaryBucket, buffer.c_str());
+
+ // automaticaly open a wormhole when this reliable message gets through
+ msg->sendReliable(
+ gAgent.getRegionHost(),
+ 3, // retries
+ TRUE, // ping-based
+ 5.0f, // timeout
+ send_lure_911,
+ (void**)&mSessionUUID);
+ }
+ else
+ {
+ if (mDialog != IM_SESSION_ADD
+ && mDialog != IM_SESSION_OFFLINE_ADD)
+ {
+ llwarns << "LLFloaterIMPanel::addParticipants() - dialog type " << mDialog
+ << " is not an ADD" << llendl;
+ }
+ // *FIX: this could suffer from endian issues
+ S32 bucket_size = UUID_BYTES * count;
+ U8* bucket = new U8[bucket_size];
+ U8* pos = bucket;
+ for(S32 i = 0; i < count; ++i)
+ {
+ memcpy(pos, &(ids.get(i)), UUID_BYTES);
+ pos += UUID_BYTES;
+ }
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, bucket, bucket_size);
+ delete[] bucket;
+ gAgent.sendReliableMessage();
+ }
+
+ }
+ else
+ {
+ llinfos << "LLFloaterIMPanel::addParticipants() - no need to add agents for "
+ << mDialog << llendl;
+ // successful add, because everyone that needed to get added
+ // was added.
+ }
+ return TRUE;
+}
+
+void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file)
+{
+ LLMultiFloater* hostp = getHost();
+ if( !getVisible() && hostp && log_to_file)
+ {
+ // Only flash for logged ("real") messages
+ LLTabContainer* parent = (LLTabContainer*) getParent();
+ parent->setTabPanelFlashing( this, TRUE );
+ }
+
+ // Now we're adding the actual line of text, so erase the
+ // "Foo is typing..." text segment, and the optional timestamp
+ // if it was present. JC
+ removeTypingIndicator();
+
+ // Actually add the line
+ LLString timestring;
+ bool prepend_newline = true;
+ if (gSavedSettings.getBOOL("IMShowTimestamps"))
+ {
+ timestring = mHistoryEditor->appendTime(prepend_newline);
+ prepend_newline = false;
+ }
+ mHistoryEditor->appendColoredText(utf8msg, false, prepend_newline, color);
+
+ if (log_to_file
+ && gSavedPerAccountSettings.getBOOL("LogInstantMessages") )
+ {
+ LLString histstr = timestring + utf8msg;
+
+ LLLogChat::saveHistory(mSessionLabel,histstr);
+ }
+}
+
+
+void LLFloaterIMPanel::setVisible(BOOL b)
+{
+ LLPanel::setVisible(b);
+
+ LLMultiFloater* hostp = getHost();
+ if( b && hostp )
+ {
+ LLTabContainer* parent = (LLTabContainer*) getParent();
+
+ // When this tab is displayed, you can stop flashing.
+ parent->setTabPanelFlashing( this, FALSE );
+
+ /* Don't change containing floater title - leave it "Instant Message" JC
+ LLUIString title = sTitleString;
+ title.setArg("[NAME]", mSessionLabel);
+ hostp->setTitle( title );
+ */
+ }
+}
+
+
+void LLFloaterIMPanel::setInputFocus( BOOL b )
+{
+ mInputEditor->setFocus( b );
+}
+
+
+void LLFloaterIMPanel::selectAll()
+{
+ mInputEditor->selectAll();
+}
+
+
+void LLFloaterIMPanel::selectNone()
+{
+ mInputEditor->deselect();
+}
+
+
+BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+ if( getVisible() && mEnabled && !called_from_parent && gFocusMgr.childHasKeyboardFocus(this))
+ {
+ if( KEY_RETURN == key && mask == MASK_NONE)
+ {
+ sendMsg();
+ handled = TRUE;
+
+ // Close talk panels on hitting return
+ // but not shift-return or control-return
+ if ( !gSavedSettings.getBOOL("PinTalkViewOpen") && !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT) )
+ {
+ gIMView->toggle(NULL);
+ }
+ }
+ else if ( KEY_ESCAPE == key )
+ {
+ handled = TRUE;
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+
+ // Close talk panel with escape
+ if( !gSavedSettings.getBOOL("PinTalkViewOpen") )
+ {
+ gIMView->toggle(NULL);
+ }
+ }
+ }
+
+ // May need to call base class LLPanel::handleKeyHere if not handled
+ // in order to tab between buttons. JNC 1.2.2002
+ return handled;
+}
+
+BOOL LLFloaterIMPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL accepted = FALSE;
+ switch(cargo_type)
+ {
+ case DAD_CALLINGCARD:
+ accepted = dropCallingCard((LLInventoryItem*)cargo_data, drop);
+ break;
+ case DAD_CATEGORY:
+ accepted = dropCategory((LLInventoryCategory*)cargo_data, drop);
+ break;
+ default:
+ break;
+ }
+ if (accepted)
+ {
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ return TRUE;
+}
+
+BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop)
+{
+ BOOL rv = isAddAllowed();
+ if(rv && item && item->getCreatorUUID().notNull())
+ {
+ if(drop)
+ {
+ LLDynamicArray<LLUUID> ids;
+ ids.put(item->getCreatorUUID());
+ addParticipants(ids);
+ }
+ }
+ else
+ {
+ // set to false if creator uuid is null.
+ rv = FALSE;
+ }
+ return rv;
+}
+
+BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop)
+{
+ BOOL rv = isAddAllowed();
+ if(rv && category)
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLUniqueBuddyCollector buddies;
+ gInventory.collectDescendentsIf(category->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ buddies);
+ S32 count = items.count();
+ if(count == 0)
+ {
+ rv = FALSE;
+ }
+ else if(drop)
+ {
+ LLDynamicArray<LLUUID> ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.put(items.get(i)->getCreatorUUID());
+ }
+ addParticipants(ids);
+ }
+ }
+ return rv;
+}
+
+BOOL LLFloaterIMPanel::isAddAllowed() const
+{
+
+ return ((IM_SESSION_ADD == mDialog)
+ || (IM_SESSION_OFFLINE_ADD == mDialog)
+ || (IM_SESSION_GROUP_START == mDialog)
+ || (IM_SESSION_911_START == mDialog)
+ || (IM_SESSION_CARDLESS_START == mDialog));
+}
+
+
+// static
+void LLFloaterIMPanel::onTabClick(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ self->setInputFocus(TRUE);
+}
+
+
+// static
+void LLFloaterIMPanel::onClickProfile( void* userdata )
+{
+ // Bring up the Profile window
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+
+ if (self->mOtherParticipantUUID.notNull())
+ {
+ LLFloaterAvatarInfo::showFromDirectory(self->getOtherParticipantID());
+ }
+}
+
+// static
+void LLFloaterIMPanel::onClickClose( void* userdata )
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ if(self)
+ {
+ self->close();
+ }
+}
+
+void LLFloaterIMPanel::addTeleportButton(const LLUUID& lure_id)
+{
+ LLButton* btn = LLViewerUICtrlFactory::getButtonByName(this, "Teleport Btn");
+ if (!btn)
+ {
+ S32 BTN_VPAD = 2;
+ S32 BTN_HPAD = 2;
+
+ const char* teleport_label = "Teleport";
+
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ S32 p_btn_width = 75;
+ S32 c_btn_width = 60;
+ S32 t_btn_width = 75;
+
+ // adjust the size of the editor to make room for the new button
+ LLRect rect = mInputEditor->getRect();
+ S32 editor_right = rect.mRight - t_btn_width;
+ rect.mRight = editor_right;
+ mInputEditor->reshape(rect.getWidth(), rect.getHeight(), FALSE);
+ mInputEditor->setRect(rect);
+
+ const S32 IMPANEL_PAD = 1 + LLPANEL_BORDER_WIDTH;
+ const S32 IMPANEL_INPUT_HEIGHT = 20;
+
+ rect.setLeftTopAndSize(
+ mRect.getWidth() - IMPANEL_PAD - p_btn_width - c_btn_width - t_btn_width - BTN_HPAD - RESIZE_HANDLE_WIDTH,
+ IMPANEL_INPUT_HEIGHT + IMPANEL_PAD - BTN_VPAD,
+ t_btn_width,
+ BTN_HEIGHT);
+
+ btn = new LLButton(
+ "Teleport Btn", rect,
+ "","", "",
+ &LLFloaterIMPanel::onTeleport, this,
+ font, teleport_label, teleport_label );
+
+ btn->setFollowsBottom();
+ btn->setFollowsRight();
+ addChild( btn );
+ }
+ btn->setEnabled(TRUE);
+ mLureID = lure_id;
+}
+
+void LLFloaterIMPanel::removeTeleportButton()
+{
+ // TODO -- purge the button
+ LLButton* btn = LLViewerUICtrlFactory::getButtonByName(this, "Teleport Btn");
+ if (btn)
+ {
+ btn->setEnabled(FALSE);
+ }
+}
+
+// static
+void LLFloaterIMPanel::onTeleport(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ if(self)
+ {
+ send_simple_im(self->mLureID,
+ "",
+ IM_LURE_911,
+ LLUUID::null);
+ self->removeTeleportButton();
+ }
+}
+
+// static
+void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata )
+{
+ LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
+ self->mHistoryEditor->setCursorAndScrollToEnd();
+}
+
+// static
+void LLFloaterIMPanel::onInputEditorFocusLost(LLLineEditor* caller, void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ self->setTyping(FALSE);
+}
+
+// static
+void LLFloaterIMPanel::onInputEditorKeystroke(LLLineEditor* caller, void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
+ LLString text = self->mInputEditor->getText();
+ if (!text.empty())
+ {
+ self->setTyping(TRUE);
+ }
+ else
+ {
+ // Deleting all text counts as stopping typing.
+ self->setTyping(FALSE);
+ }
+}
+
+void LLFloaterIMPanel::close(bool app_quitting)
+{
+ setTyping(FALSE);
+
+ if(mSessionUUID.notNull())
+ {
+ std::string name;
+ gAgent.buildFullname(name);
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ mOtherParticipantUUID,
+ name.c_str(),
+ "",
+ IM_ONLINE,
+ IM_SESSION_DROP,
+ mSessionUUID);
+ gAgent.sendReliableMessage();
+ }
+ gIMView->removeSession(mSessionUUID);
+
+ destroy();
+}
+
+
+void LLFloaterIMPanel::sendMsg()
+{
+ LLWString text = mInputEditor->getWText();
+ LLWString::trim(text);
+ if (!gAgent.isGodlike()
+ && (mDialog == IM_NOTHING_SPECIAL)
+ && mOtherParticipantUUID.isNull())
+ {
+ llinfos << "Cannot send IM to everyone unless you're a god." << llendl;
+ return;
+ }
+ if(text.length() > 0)
+ {
+ // Truncate and convert to UTF8 for transport
+ std::string utf8_text = wstring_to_utf8str(text);
+ utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1);
+ std::string name;
+ gAgent.buildFullname(name);
+
+ const LLRelationship* info = NULL;
+ info = LLAvatarTracker::instance().getBuddyInfo(mOtherParticipantUUID);
+ U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
+
+ // default to IM_SESSION_SEND unless it's nothing special - in
+ // which case it's probably an IM to everyone.
+ U8 dialog = (mDialog == IM_NOTHING_SPECIAL)
+ ? (U8)IM_NOTHING_SPECIAL : (U8)IM_SESSION_SEND;
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ mOtherParticipantUUID,
+ name.c_str(),
+ utf8_text.c_str(),
+ offline,
+ (EInstantMessage)dialog,
+ mSessionUUID);
+ gAgent.sendReliableMessage();
+
+ // local echo
+ if((mDialog == IM_NOTHING_SPECIAL) && (mOtherParticipantUUID.notNull()))
+ {
+ std::string history_echo;
+ gAgent.buildFullname(history_echo);
+
+ // Look for IRC-style emotes here.
+ char tmpstr[5];
+ strcpy(tmpstr,utf8_text.substr(0,4).c_str());
+ if (!strncmp(tmpstr, "/me ", 4) || !strncmp(tmpstr, "/me'", 4))
+ {
+ utf8_text.replace(0,3,"");
+ }
+ else
+ {
+ history_echo += ": ";
+ }
+ history_echo += utf8_text;
+ addHistoryLine(history_echo);
+ }
+
+ gViewerStats->incStat(LLViewerStats::ST_IM_COUNT);
+ }
+ mInputEditor->setText("");
+
+ // Don't need to actually send the typing stop message, the other
+ // client will infer it from receiving the message.
+ mTyping = FALSE;
+ mSentTypingState = TRUE;
+}
+
+
+void LLFloaterIMPanel::setTyping(BOOL typing)
+{
+ if (typing)
+ {
+ // Every time you type something, reset this timer
+ mLastKeystrokeTimer.reset();
+
+ if (!mTyping)
+ {
+ // You just started typing.
+ mFirstKeystrokeTimer.reset();
+
+ // Will send typing state after a short delay.
+ mSentTypingState = FALSE;
+ }
+ }
+ else
+ {
+ if (mTyping)
+ {
+ // you just stopped typing, send state immediately
+ sendTypingState(FALSE);
+ mSentTypingState = TRUE;
+ }
+ }
+
+ mTyping = typing;
+}
+
+void LLFloaterIMPanel::sendTypingState(BOOL typing)
+{
+ // Don't want to send typing indicators to multiple people, potentially too
+ // much network traffic. Only send in person-to-person IMs.
+ if (mDialog != IM_NOTHING_SPECIAL) return;
+
+ std::string name;
+ gAgent.buildFullname(name);
+
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ mOtherParticipantUUID,
+ name.c_str(),
+ "typing",
+ IM_ONLINE,
+ (typing ? IM_TYPING_START : IM_TYPING_STOP),
+ mSessionUUID);
+ gAgent.sendReliableMessage();
+}
+
+
+void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing)
+{
+ if (typing)
+ {
+ // other user started typing
+ addTypingIndicator(im_info);
+ }
+ else
+ {
+ // other user stopped typing
+ removeTypingIndicator();
+ }
+}
+
+
+void LLFloaterIMPanel::addTypingIndicator(const LLIMInfo* im_info)
+{
+ mTypingLineStartIndex = mHistoryEditor->getText().length();
+
+ LLUIString typing_start = sTypingStartString;
+ typing_start.setArg("[NAME]", im_info->mName);
+ bool log_to_file = false;
+ addHistoryLine(typing_start, LLColor4::grey, log_to_file);
+ mOtherTyping = TRUE;
+}
+
+
+void LLFloaterIMPanel::removeTypingIndicator()
+{
+ if (mOtherTyping)
+ {
+ // Must do this first, otherwise addHistoryLine calls us again.
+ mOtherTyping = FALSE;
+
+ S32 chars_to_remove = mHistoryEditor->getText().length() - mTypingLineStartIndex;
+ mHistoryEditor->removeTextFromEnd(chars_to_remove);
+ }
+}
+
+//static
+void LLFloaterIMPanel::chatFromLogFile(LLString line, void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
+
+ //self->addHistoryLine(line, LLColor4::grey, FALSE);
+ self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey);
+
+}
diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h
new file mode 100644
index 0000000000..caed94ebc1
--- /dev/null
+++ b/indra/newview/llimpanel.h
@@ -0,0 +1,155 @@
+/**
+ * @file llimpanel.h
+ * @brief LLIMPanel class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_IMPANEL_H
+#define LL_IMPANEL_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "lldarray.h"
+#include "llinstantmessage.h"
+
+class LLLineEditor;
+class LLViewerTextEditor;
+class LLInventoryItem;
+class LLInventoryCategory;
+
+class LLFloaterIMPanel : public LLFloater
+{
+public:
+ // The session id is the id of the session this is for. The target
+ // refers to the user (or group) that where this session serves as
+ // the default. For example, if you open a session though a
+ // calling card, a new session id will be generated, but the
+ // target_id will be the agent referenced by the calling card.
+ LLFloaterIMPanel(const std::string& name, const LLRect& rect,
+ const std::string& session_label,
+ const LLUUID& session_id, const LLUUID& target_id,
+ EInstantMessage dialog);
+
+ /*virtual*/ BOOL postBuild();
+
+ // Check typing timeout timer.
+ /*virtual*/ void draw();
+
+ /*virtual*/ void close(bool app_quitting = FALSE);
+
+ // add target ids to the session.
+ // Return TRUE if successful, otherwise FALSE.
+ BOOL addParticipants(const LLDynamicArray<LLUUID>& agent_ids);
+
+ void addHistoryLine(const std::string &utf8msg,
+ const LLColor4& color = LLColor4::white,
+ bool log_to_file = true);
+ void setInputFocus( BOOL b );
+
+ void selectAll();
+ void selectNone();
+ void setVisible(BOOL b);
+
+ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type,
+ void *cargo_data, EAcceptance *accept,
+ LLString& tooltip_msg);
+
+ static void onInputEditorFocusReceived( LLUICtrl* caller, void* userdata );
+ static void onInputEditorFocusLost(LLLineEditor* caller, void* userdata);
+ static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata);
+ static void onTabClick( void* userdata );
+
+ static void onClickProfile( void* userdata ); // Profile button pressed
+ static void onClickClose( void* userdata );
+
+ //const LLUUID& getItemUUID() const { return mItemUUID; }
+ const LLUUID& getSessionID() const { return mSessionUUID; }
+ const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; }
+
+ // HACK -- for enabling a teleport button for helpers
+ static void onTeleport(void* userdata);
+ void addTeleportButton(const LLUUID& lure_id);
+ void removeTeleportButton();
+
+ // Handle other participant in the session typing.
+ void processIMTyping(const LLIMInfo* im_info, BOOL typing);
+ static void chatFromLogFile(LLString line, void* userdata);
+
+private:
+ // called by constructors
+ void init();
+
+ // Called by UI methods.
+ void sendMsg();
+
+ // for adding agents via the UI. Return TRUE if possible, do it if
+ BOOL dropCallingCard(LLInventoryItem* item, BOOL drop);
+ BOOL dropCategory(LLInventoryCategory* category, BOOL drop);
+
+ // test if local agent can add agents.
+ BOOL isAddAllowed() const;
+
+ // Called whenever the user starts or stops typing.
+ // Sends the typing state to the other user if necessary.
+ void setTyping(BOOL typing);
+
+ // Add the "User is typing..." indicator.
+ void addTypingIndicator(const LLIMInfo* im_info);
+
+ // Remove the "User is typing..." indicator.
+ void removeTypingIndicator();
+
+ void sendTypingState(BOOL typing);
+
+ static LLFloaterIMPanel* sInstance;
+
+private:
+ LLLineEditor* mInputEditor;
+ LLViewerTextEditor* mHistoryEditor;
+ std::string mSessionLabel;
+
+ // The value of the mSessionUUID depends on how the IM session was started:
+ // one-on-one ==> random id
+ // group ==> group_id
+ // inventory folder ==> folder item_id
+ // 911 ==> Gaurdian_Angel_Group_ID ^ gAgent.getID()
+ LLUUID mSessionUUID;
+
+ // The value mOtherParticipantUUID depends on how the IM session was started:
+ // one-on-one = recipient's id
+ // group ==> group_id
+ // inventory folder ==> first target id in list
+ // 911 ==> sender
+ LLUUID mOtherParticipantUUID;
+
+ // the lure ID for help IM sessions
+ LLUUID mLureID;
+
+ EInstantMessage mDialog;
+
+ // Are you currently typing?
+ BOOL mTyping;
+
+ // Is other user currently typing?
+ BOOL mOtherTyping;
+
+ // Where does the "User is typing..." line start?
+ S32 mTypingLineStartIndex;
+
+ BOOL mSentTypingState;
+
+ // Optimization: Don't send "User is typing..." until the
+ // user has actually been typing for a little while. Prevents
+ // extra IMs for brief "lol" type utterences.
+ LLFrameTimer mFirstKeystrokeTimer;
+
+ // Timer to detect when user has stopped typing.
+ LLFrameTimer mLastKeystrokeTimer;
+};
+
+
+#endif // LL_IMPANEL_H
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
new file mode 100644
index 0000000000..fb8438dc93
--- /dev/null
+++ b/indra/newview/llimview.cpp
@@ -0,0 +1,717 @@
+/**
+ * @file llimview.cpp
+ * @brief Container for Instant Messaging
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llimview.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llbutton.h"
+#include "llstring.h"
+#include "linked_lists.h"
+#include "llvieweruictrlfactory.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llviewerwindow.h"
+#include "llresmgr.h"
+#include "llfloaternewim.h"
+#include "llimpanel.h"
+#include "llresizebar.h"
+#include "lltabcontainer.h"
+#include "viewer.h"
+#include "llfloater.h"
+#include "llresizehandle.h"
+#include "llkeyboard.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llcallingcard.h"
+#include "lltoolbar.h"
+
+const EInstantMessage EVERYONE_DIALOG = IM_NOTHING_SPECIAL;
+const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START;
+const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL;
+
+//
+// Globals
+//
+LLIMView* gIMView = NULL;
+
+//
+// Statics
+//
+static LLString sOnlyUserMessage;
+static LLString sOfflineMessage;
+
+//
+// Helper Functions
+//
+
+// returns true if a should appear before b
+BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b )
+{
+ return (LLString::compareDict( a->mName, b->mName ) < 0);
+}
+
+
+// the other_participant_id is either an agent_id, a group_id, or an inventory
+// folder item_id (collection of calling cards)
+LLUUID compute_session_id(EInstantMessage dialog, const LLUUID& other_participant_id)
+{
+ LLUUID session_id;
+ if (IM_SESSION_GROUP_START == dialog)
+ {
+ // slam group session_id to the group_id (other_participant_id)
+ session_id = other_participant_id;
+ }
+ else
+ {
+ LLUUID agent_id = gAgent.getID();
+ if (other_participant_id == agent_id)
+ {
+ // if we try to send an IM to ourselves then the XOR would be null
+ // so we just make the session_id the same as the agent_id
+ session_id = agent_id;
+ }
+ else
+ {
+ // peer-to-peer or peer-to-asset session_id is the XOR
+ session_id = other_participant_id ^ agent_id;
+ }
+ }
+ return session_id;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLFloaterIM
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+LLFloaterIM::LLFloaterIM()
+{
+}
+
+BOOL LLFloaterIM::postBuild()
+{
+ requires("only_user_message", WIDGET_TYPE_TEXT_BOX);
+ requires("offline_message", WIDGET_TYPE_TEXT_BOX);
+
+ if (checkRequirements())
+ {
+ sOnlyUserMessage = childGetText("only_user_message");
+ sOfflineMessage = childGetText("offline_message");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//// virtual
+//BOOL LLFloaterIM::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+//{
+// BOOL handled = FALSE;
+// if (getEnabled()
+// && mask == (MASK_CONTROL|MASK_SHIFT))
+// {
+// if (key == 'W')
+// {
+// LLFloater* floater = getActiveFloater();
+// if (floater)
+// {
+// if (mTabContainer->getTabCount() == 1)
+// {
+// // trying to close last tab, close
+// // entire window.
+// close();
+// handled = TRUE;
+// }
+// }
+// }
+// }
+// return handled || LLMultiFloater::handleKeyHere(key, mask, called_from_parent);
+//}
+
+void LLFloaterIM::onClose(bool app_quitting)
+{
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowIM", FALSE);
+ }
+ setVisible(FALSE);
+}
+
+//virtual
+void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
+{
+ // this code is needed to fix the bug where new IMs received will resize the IM floater.
+ // SL-29075, SL-24556, and others
+ LLRect parent_rect = getRect();
+ S32 dheight = LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT;
+ LLRect rect(0, parent_rect.getHeight()-dheight, parent_rect.getWidth(), 0);
+ floaterp->reshape(rect.getWidth(), rect.getHeight(), TRUE);
+ LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIMViewFriendObserver
+//
+// Bridge to suport knowing when the inventory has changed.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLIMViewFriendObserver : public LLFriendObserver
+{
+public:
+ LLIMViewFriendObserver(LLIMView* tv) : mTV(tv) {}
+ virtual ~LLIMViewFriendObserver() {}
+ virtual void changed(U32 mask)
+ {
+ if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
+ {
+ mTV->refresh();
+ }
+ }
+protected:
+ LLIMView* mTV;
+};
+
+
+//
+// Public Static Member Functions
+//
+
+// This is a helper function to determine what kind of im session
+// should be used for the given agent.
+// static
+EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id)
+{
+ EInstantMessage type = IM_SESSION_CARDLESS_START;
+ if(is_agent_friend(agent_id))
+ {
+ if(LLAvatarTracker::instance().isBuddyOnline(agent_id))
+ {
+ type = IM_SESSION_ADD;
+ }
+ else
+ {
+ type = IM_SESSION_OFFLINE_ADD;
+ }
+ }
+ return type;
+}
+
+// static
+//void LLIMView::onPinButton(void*)
+//{
+// BOOL state = gSavedSettings.getBOOL( "PinTalkViewOpen" );
+// gSavedSettings.setBOOL( "PinTalkViewOpen", !state );
+//}
+
+// static
+void LLIMView::toggle(void*)
+{
+ static BOOL return_to_mouselook = FALSE;
+
+ // Hide the button and show the floater or vice versa.
+ llassert( gIMView );
+ BOOL old_state = gIMView->getFloaterOpen();
+
+ // If we're in mouselook and we triggered the Talk View, we want to talk.
+ if( gAgent.cameraMouselook() && old_state )
+ {
+ return_to_mouselook = TRUE;
+ gAgent.changeCameraToDefault();
+ return;
+ }
+
+ BOOL new_state = !old_state;
+
+ if (new_state)
+ {
+ // ...making visible
+ if ( gAgent.cameraMouselook() )
+ {
+ return_to_mouselook = TRUE;
+ gAgent.changeCameraToDefault();
+ }
+ }
+ else
+ {
+ // ...hiding
+ if ( gAgent.cameraThirdPerson() && return_to_mouselook )
+ {
+ gAgent.changeCameraToMouselook();
+ }
+ return_to_mouselook = FALSE;
+ }
+
+ gIMView->setFloaterOpen( new_state );
+}
+
+//
+// Member Functions
+//
+
+LLIMView::LLIMView(const std::string& name, const LLRect& rect) :
+ LLView(name, rect, FALSE),
+ mFriendObserver(NULL),
+ mIMReceived(FALSE)
+{
+ gIMView = this;
+ mFriendObserver = new LLIMViewFriendObserver(this);
+ LLAvatarTracker::instance().addObserver(mFriendObserver);
+
+ mTalkFloater = new LLFloaterIM();
+ gUICtrlFactory->buildFloater(mTalkFloater, "floater_im.xml");
+
+ // New IM Panel
+ mNewIMFloater = new LLFloaterNewIM();
+ mTalkFloater->addFloater(mNewIMFloater, TRUE);
+
+ // Tabs sometimes overlap resize handle
+ mTalkFloater->moveResizeHandleToFront();
+}
+
+LLIMView::~LLIMView()
+{
+ LLAvatarTracker::instance().removeObserver(mFriendObserver);
+ delete mFriendObserver;
+ // Children all cleaned up by default view destructor.
+}
+
+EWidgetType LLIMView::getWidgetType() const
+{
+ return WIDGET_TYPE_TALK_VIEW;
+}
+
+LLString LLIMView::getWidgetTag() const
+{
+ return LL_TALK_VIEW_TAG;
+}
+
+// Add a message to a session.
+void LLIMView::addMessage(
+ const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ const char* from,
+ const char* msg,
+ const char* session_name,
+ EInstantMessage dialog,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position)
+{
+ LLFloaterIMPanel* floater;
+ LLUUID new_session_id = session_id;
+ if (new_session_id.isNull())
+ {
+ new_session_id = compute_session_id(dialog, other_participant_id);
+ }
+ floater = findFloaterBySession(new_session_id);
+ if (!floater)
+ {
+ floater = findFloaterBySession(other_participant_id);
+ if (floater)
+ {
+ llinfos << "found the IM session " << session_id
+ << " by participant " << other_participant_id << llendl;
+ }
+ }
+ if(floater)
+ {
+ floater->addHistoryLine(msg);
+ }
+ else
+ {
+ const char* name = from;
+ if(session_name && (strlen(session_name)>1))
+ {
+ name = session_name;
+ }
+ floater = createFloater(new_session_id, other_participant_id, name, dialog, FALSE);
+
+ // When we get a new IM, and if you are a god, display a bit
+ // of information about the source. This is to help liaisons
+ // when answering questions.
+ if(gAgent.isGodlike())
+ {
+ // XUI:translate
+ std::ostringstream bonus_info;
+ bonus_info << "*** parent estate: "
+ << parent_estate_id
+ << ((parent_estate_id == 1) ? ", mainland" : "")
+ << ((parent_estate_id == 5) ? ", teen" : "");
+
+ // once we have web-services (or something) which returns
+ // information about a region id, we can print this out
+ // and even have it link to map-teleport or something.
+ //<< "*** region_id: " << region_id << std::endl
+ //<< "*** position: " << position << std::endl;
+
+ floater->addHistoryLine(bonus_info.str());
+ }
+
+ floater->addHistoryLine(msg);
+ make_ui_sound("UISndNewIncomingIMSession");
+ }
+
+ if( !mTalkFloater->getVisible() && !floater->getVisible())
+ {
+ //if the IM window is not open and the floater is not visible (i.e. not torn off)
+ LLFloater* previouslyActiveFloater = mTalkFloater->getActiveFloater();
+
+ // select the newly added floater (or the floater with the new line added to it).
+ // it should be there.
+ mTalkFloater->selectFloater(floater);
+
+ //there was a previously unseen IM, make that old tab flashing
+ //it is assumed that the most recently unseen IM tab is the one current selected/active
+ if ( previouslyActiveFloater && getIMReceived() )
+ {
+ mTalkFloater->setFloaterFlashing(previouslyActiveFloater, TRUE);
+ }
+
+ //notify of a new IM
+ notifyNewIM();
+ }
+}
+
+void LLIMView::notifyNewIM()
+{
+ if(!gIMView->getFloaterOpen())
+ {
+ mIMReceived = TRUE;
+ }
+}
+
+BOOL LLIMView::getIMReceived() const
+{
+ return mIMReceived;
+}
+
+// This method returns TRUE if the local viewer has a session
+// currently open keyed to the uuid.
+BOOL LLIMView::isIMSessionOpen(const LLUUID& uuid)
+{
+ LLFloaterIMPanel* floater = findFloaterBySession(uuid);
+ if(floater) return TRUE;
+ return FALSE;
+}
+
+// This adds a session to the talk view. The name is the local name of
+// the session, dialog specifies the type of session. If the session
+// exists, it is brought forward. Specifying id = NULL results in an
+// im session to everyone. Returns the uuid of the session.
+LLUUID LLIMView::addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id)
+{
+ LLUUID session_id = compute_session_id(dialog, other_participant_id);
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(!floater)
+ {
+ floater = createFloater(session_id, other_participant_id, name, dialog, TRUE);
+ LLDynamicArray<LLUUID> ids;
+ ids.put(other_participant_id);
+ noteOfflineUsers(floater, ids);
+ floater->addParticipants(ids);
+ mTalkFloater->showFloater(floater);
+ }
+ else
+ {
+ floater->open();
+ }
+ //mTabContainer->selectTabPanel(panel);
+ floater->setInputFocus(TRUE);
+ return floater->getSessionID();
+}
+
+// Adds a session using the given session_id. If the session already exists
+// the dialog type is assumed correct. Returns the uuid of the session.
+LLUUID LLIMView::addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& session_id,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ if (0 == ids.getLength())
+ {
+ return LLUUID::null;
+ }
+
+ LLUUID other_participant_id = ids[0];
+ LLUUID new_session_id = session_id;
+ if (new_session_id.isNull())
+ {
+ new_session_id = compute_session_id(dialog, other_participant_id);
+ }
+
+ LLFloaterIMPanel* floater = findFloaterBySession(new_session_id);
+ if(!floater)
+ {
+ // On creation, use the first element of ids as the "other_participant_id"
+ floater = createFloater(new_session_id, other_participant_id, name, dialog, TRUE);
+ noteOfflineUsers(floater, ids);
+ }
+ floater->addParticipants(ids);
+ mTalkFloater->showFloater(floater);
+ //mTabContainer->selectTabPanel(panel);
+ floater->setInputFocus(TRUE);
+ return floater->getSessionID();
+}
+
+// This removes the panel referenced by the uuid, and then restores
+// internal consistency. The internal pointer is not deleted.
+void LLIMView::removeSession(const LLUUID& session_id)
+{
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(floater)
+ {
+ mFloaters.erase(floater->getHandle());
+ mTalkFloater->removeFloater(floater);
+ //mTabContainer->removeTabPanel(floater);
+ }
+}
+
+void LLIMView::refresh()
+{
+ S32 old_scroll_pos = mNewIMFloater->getScrollPos();
+ mNewIMFloater->clearAllTargets();
+
+ // build a list of groups.
+ LLLinkedList<LLGroupData> group_list( group_dictionary_sort );
+
+ LLGroupData* group;
+ S32 count = gAgent.mGroups.count();
+ S32 i;
+ // read/sort groups on the first pass.
+ for(i = 0; i < count; ++i)
+ {
+ group = &(gAgent.mGroups.get(i));
+ group_list.addDataSorted( group );
+ }
+
+ // add groups to the floater on the second pass.
+ for(group = group_list.getFirstData();
+ group;
+ group = group_list.getNextData())
+ {
+ mNewIMFloater->addTarget(group->mID, group->mName,
+ (void*)(&GROUP_DIALOG), TRUE, FALSE);
+ }
+
+ // build a set of buddies in the current buddy list.
+ LLCollectAllBuddies collector;
+ LLAvatarTracker::instance().applyFunctor(collector);
+ LLCollectAllBuddies::buddy_map_t::iterator it;
+ LLCollectAllBuddies::buddy_map_t::iterator end;
+ it = collector.mOnline.begin();
+ end = collector.mOnline.end();
+ for( ; it != end; ++it)
+ {
+ mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), TRUE);
+ }
+ it = collector.mOffline.begin();
+ end = collector.mOffline.end();
+ for( ; it != end; ++it)
+ {
+ mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), FALSE);
+ }
+
+ if(gAgent.isGodlike())
+ {
+ // XUI:translate
+ mNewIMFloater->addTarget(LLUUID::null, "All Residents, All Grids",
+ (void*)(&EVERYONE_DIALOG), TRUE, FALSE);
+ }
+
+ mNewIMFloater->setScrollPos( old_scroll_pos );
+}
+
+// JC - This used to set console visibility. It doesn't any more.
+void LLIMView::setFloaterOpen(BOOL set_open)
+{
+ gSavedSettings.setBOOL("ShowIM", set_open);
+
+ //RN "visible" and "open" are considered synonomous for now
+ if (set_open)
+ {
+ mTalkFloater->open();
+ }
+ else
+ {
+ mTalkFloater->close();
+ }
+
+ if( set_open )
+ {
+ // notifyNewIM();
+
+ // We're showing the IM, so mark view as non-pending
+ mIMReceived = FALSE;
+ }
+}
+
+
+BOOL LLIMView::getFloaterOpen()
+{
+ return mTalkFloater->getVisible();
+}
+
+void LLIMView::pruneSessions()
+{
+ if(mNewIMFloater)
+ {
+ BOOL removed = TRUE;
+ LLFloaterIMPanel* floater = NULL;
+ while(removed)
+ {
+ removed = FALSE;
+ std::set<LLViewHandle>::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ ++handle_it)
+ {
+ floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
+ if(floater && !mNewIMFloater->isUUIDAvailable(floater->getOtherParticipantID()))
+ {
+ // remove this floater
+ removed = TRUE;
+ mFloaters.erase(handle_it++);
+ floater->close();
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+void LLIMView::disconnectAllSessions()
+{
+ if(mNewIMFloater)
+ {
+ mNewIMFloater->setEnabled(FALSE);
+ }
+ LLFloaterIMPanel* floater = NULL;
+ std::set<LLViewHandle>::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ ++handle_it)
+ {
+ floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
+ if (floater)
+ {
+ floater->setEnabled(FALSE);
+ }
+ }
+}
+
+
+// This method returns the im panel corresponding to the uuid
+// provided. The uuid can either be a session id or an agent
+// id. Returns NULL if there is no matching panel.
+LLFloaterIMPanel* LLIMView::findFloaterBySession(const LLUUID& session_id)
+{
+ LLFloaterIMPanel* rv = NULL;
+ std::set<LLViewHandle>::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ ++handle_it)
+ {
+ rv = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
+ if(rv && session_id == rv->getSessionID())
+ {
+ break;
+ }
+ rv = NULL;
+ }
+ return rv;
+}
+
+
+BOOL LLIMView::hasSession(const LLUUID& session_id)
+{
+ return (findFloaterBySession(session_id) != NULL);
+}
+
+
+// create a floater and update internal representation for
+// consistency. Returns the pointer, caller (the class instance since
+// it is a private method) is not responsible for deleting the
+// pointer. Add the floater to this but do not select it.
+LLFloaterIMPanel* LLIMView::createFloater(const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ const std::string& session_label,
+ EInstantMessage dialog,
+ BOOL user_initiated)
+{
+ if (session_id.isNull())
+ {
+ llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
+ }
+ llinfos << "LLIMView::createFloater: from " << other_participant_id
+ << " in session " << session_id << llendl;
+ LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
+ LLRect(),
+ session_label,
+ session_id,
+ other_participant_id,
+ dialog);
+ LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
+ mTalkFloater->addFloater(floater, FALSE, i_pt);
+ mFloaters.insert(floater->getHandle());
+ return floater;
+}
+
+void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ S32 count = ids.count();
+ if(count == 0)
+ {
+ floater->addHistoryLine(sOnlyUserMessage);
+ }
+ else
+ {
+ const LLRelationship* info = NULL;
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ for(S32 i = 0; i < count; ++i)
+ {
+ info = at.getBuddyInfo(ids.get(i));
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(info && !info->isOnline()
+ && gCacheName->getName(ids.get(i), first, last))
+ {
+ LLUIString offline = sOfflineMessage;
+ offline.setArg("[FIRST]", first);
+ offline.setArg("[LAST]", last);
+ floater->addHistoryLine(offline);
+ }
+ }
+ }
+}
+
+void LLIMView::processIMTypingStart(const LLIMInfo* im_info)
+{
+ processIMTypingCore(im_info, TRUE);
+}
+
+void LLIMView::processIMTypingStop(const LLIMInfo* im_info)
+{
+ processIMTypingCore(im_info, FALSE);
+}
+
+void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
+{
+ LLUUID session_id = compute_session_id(im_info->mIMType, im_info->mFromID);
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if (floater)
+ {
+ floater->processIMTyping(im_info, typing);
+ }
+}
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
new file mode 100644
index 0000000000..3be1dace1a
--- /dev/null
+++ b/indra/newview/llimview.h
@@ -0,0 +1,150 @@
+/**
+ * @file llimview.h
+ * @brief Container for Instant Messaging
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMVIEW_H
+#define LL_LLIMVIEW_H
+
+#include "llfloater.h"
+#include "llinstantmessage.h"
+#include "lluuid.h"
+
+class LLFloaterNewIM;
+class LLUUID;
+class LLFloaterIMPanel;
+class LLFriendObserver;
+class LLFloaterIM;
+
+class LLIMView : public LLView
+{
+public:
+ LLIMView(const std::string& name, const LLRect& rect);
+ ~LLIMView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // Add a message to a session. The session can keyed to sesion id
+ // or agent id.
+ void addMessage(const LLUUID& session_id, const LLUUID& target_id,
+ const char* from, const char* msg,
+ const char* session_name = NULL,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ U32 parent_estate_id = 0,
+ const LLUUID& region_id = LLUUID::null,
+ const LLVector3& position = LLVector3::zero);
+
+ // This method returns TRUE if the local viewer has a session
+ // currently open keyed to the uuid. The uuid can be keyed by
+ // either session id or agent id.
+ BOOL isIMSessionOpen(const LLUUID& uuid);
+
+ // This adds a session to the talk view. The name is the local
+ // name of the session, dialog specifies the type of
+ // session. Since sessions can be keyed off of first recipient or
+ // initiator, the session can be matched against the id
+ // provided. If the session exists, it is brought forward. This
+ // method accepts a group id or an agent id. Specifying id = NULL
+ // results in an im session to everyone. Returns the uuid of the
+ // session.
+ LLUUID addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id);
+
+ // Adds a session using the given session_id. If the session already exists
+ // the dialog type is assumed correct. Returns the uuid of the session.
+ LLUUID addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& session_id,
+ const LLDynamicArray<LLUUID>& ids);
+
+ // This removes the panel referenced by the uuid, and then
+ // restores internal consistency. The internal pointer is not
+ // deleted.
+ void removeSession(const LLUUID& session_id);
+
+ void processIMTypingStart(const LLIMInfo* im_info);
+ void processIMTypingStop(const LLIMInfo* im_info);
+
+ // Rebuild stuff
+ void refresh();
+
+ void notifyNewIM();
+
+ // IM received that you haven't seen yet
+ BOOL getIMReceived() const;
+
+ void setFloaterOpen(BOOL open);
+ BOOL getFloaterOpen();
+
+ LLFloaterIM * getFloater() { return mTalkFloater; }
+
+ // close any sessions which are not available in the newimpanel.
+ void pruneSessions();
+
+ // This method is used to go through all active sessions and
+ // disable all of them. This method is usally called when you are
+ // forced to log out or similar situations where you do not have a
+ // good connection.
+ void disconnectAllSessions();
+
+ static void toggle(void*);
+
+ // This is a helper function to determine what kind of im session
+ // should be used for the given agent.
+ static EInstantMessage defaultIMTypeForAgent(const LLUUID& agent_id);
+
+ BOOL hasSession(const LLUUID& session_id);
+
+ // This method returns the im panel corresponding to the uuid
+ // provided. The uuid must be a session id. Returns NULL if there
+ // is no matching panel.
+ LLFloaterIMPanel* findFloaterBySession(const LLUUID& session_id);
+
+private:
+ // create a panel and update internal representation for
+ // consistency. Returns the pointer, caller (the class instance
+ // since it is a private method) is not responsible for deleting
+ // the pointer.
+ LLFloaterIMPanel* createFloater(const LLUUID& session_id, const LLUUID& target_id,
+ const std::string& name, EInstantMessage dialog, BOOL user_initiated = FALSE);
+
+ // This simple method just iterates through all of the ids, and
+ // prints a simple message if they are not online. Used to help
+ // reduce 'hello' messages to the linden employees unlucky enough
+ // to have their calling card in the default inventory.
+ void noteOfflineUsers(LLFloaterIMPanel* panel, const LLDynamicArray<LLUUID>& ids);
+
+ void processIMTypingCore(const LLIMInfo* im_info, BOOL typing);
+
+public:
+ LLFloaterIM* mTalkFloater;
+ LLFloaterNewIM* mNewIMFloater;
+
+private:
+ std::set<LLViewHandle> mFloaters;
+ LLFriendObserver* mFriendObserver;
+
+ // An IM has been received that you haven't seen yet.
+ BOOL mIMReceived;
+};
+
+
+class LLFloaterIM : public LLMultiFloater
+{
+public:
+ LLFloaterIM();
+ ///*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainerCommon::END);
+};
+
+// Globals
+extern LLIMView *gIMView;
+
+#endif // LL_LLIMView_H
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
new file mode 100755
index 0000000000..ed9209935e
--- /dev/null
+++ b/indra/newview/llinventorybridge.cpp
@@ -0,0 +1,4405 @@
+/**
+ * @file llinventorybridge.cpp
+ * @brief Implementation of the Inventory-Folder-View-Bridge classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <utility> // for std::pair<>
+
+#include "llinventoryview.h"
+#include "llinventorybridge.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcheckboxctrl.h" // for radio buttons
+#include "llradiogroup.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "llui.h"
+
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterproperties.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llfolderview.h"
+#include "llgesturemgr.h"
+#include "lliconctrl.h"
+#include "llinventorymodel.h"
+#include "llinventoryclipboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llpreviewanim.h"
+#include "llpreviewgesture.h"
+#include "llpreviewlandmark.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewscript.h"
+#include "llpreviewsound.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "llscrollcontainer.h"
+#include "llimview.h"
+#include "lltooldraganddrop.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "viewer.h"
+#include "llviewermessage.h"
+#include "llviewerregion.h"
+#include "lltabcontainer.h"
+#include "llvieweruictrlfactory.h"
+#include "llselectmgr.h"
+#include "llfloateropenobject.h"
+
+// Helpers
+// bug in busy count inc/dec right now, logic is complex... do we really need it?
+void inc_busy_count()
+{
+// gViewerWindow->getWindow()->incBusyCount();
+}
+void dec_busy_count()
+{
+// gViewerWindow->getWindow()->decBusyCount();
+}
+
+// Function declarations
+struct LLWearableHoldingPattern;
+void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append);
+void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata);
+void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void*);
+void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append);
+void remove_inventory_category_from_avatar(LLInventoryCategory* category);
+void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata);
+void move_task_inventory_callback(S32 option, void* user_data);
+void confirm_replace_attachment_rez(S32 option, void* user_data);
+
+// TomY XUI: translate
+const char* FIND_HINT = "Start typing to select an item by name";
+const char* NAME_SEARCH_DESC = "Find items whose name contains (leave blank for all):";
+const char* NEW_LSL_NAME = "New Script";
+const char* NEW_NOTECARD_NAME = "New Note";
+const char* NEW_GESTURE_NAME = "New Gesture";
+
+const char* ICON_NAME[ICON_NAME_COUNT] =
+{
+ "inv_item_texture.tga",
+ "inv_item_sound.tga",
+ "inv_item_callingcard_online.tga",
+ "inv_item_callingcard_offline.tga",
+ "inv_item_landmark.tga",
+ "inv_item_landmark_visited.tga",
+ "inv_item_script.tga",
+ "inv_item_clothing.tga",
+ "inv_item_object.tga",
+ "inv_item_notecard.tga",
+ "inv_item_bodypart.tga",
+ "inv_item_snapshot.tga",
+
+ "inv_item_shape.tga",
+ "inv_item_bodypart.tga",
+ "inv_item_hair.tga",
+ "inv_item_eyes.tga",
+ "inv_item_shirt.tga",
+ "inv_item_pants.tga",
+ "inv_item_shoes.tga",
+ "inv_item_socks.tga",
+ "inv_item_jacket.tga",
+ "inv_item_gloves.tga",
+ "inv_item_undershirt.tga",
+ "inv_item_underpants.tga",
+ "inv_item_skirt.tga",
+
+ "inv_item_animation.tga",
+ "inv_item_gesture.tga",
+};
+
+struct LLWearInfo
+{
+ LLUUID mCategoryID;
+ BOOL mAppend;
+};
+
+BOOL gAddToOutfit = FALSE;
+
+// +=================================================+
+// | LLInvFVBridge |
+// +=================================================+
+
+const LLString& LLInvFVBridge::getName() const
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return obj->getName();
+ }
+ return LLString::null;
+}
+
+const LLString& LLInvFVBridge::getDisplayName() const
+{
+ return getName();
+}
+
+// Folders have full perms
+PermissionMask LLInvFVBridge::getPermissionMask() const
+{
+
+ return PERM_ALL;
+}
+
+// Folders don't have creation dates.
+U32 LLInvFVBridge::getCreationDate() const
+{
+ return 0;
+}
+
+// Can be destoryed (or moved to trash)
+BOOL LLInvFVBridge::isItemRemovable()
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ if(model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Can be moved to another folder
+BOOL LLInvFVBridge::isItemMovable()
+{
+ return TRUE;
+}
+
+//FIXME: make sure this does the right thing
+void LLInvFVBridge::showProperties()
+{
+ LLShowProps::showProperties(mUUID);
+}
+
+void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ removeBatchNoCheck(batch);
+}
+
+void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ // this method moves a bunch of items and folders to the trash. As
+ // per design guidelines for the inventory model, the message is
+ // built and the accounting is performed first. After all of that,
+ // we call LLInventoryModel::moveObject() to move everything
+ // around.
+ LLInvFVBridge* bridge;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLMessageSystem* msg = gMessageSystem;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ std::vector<LLUUID> move_ids;
+ LLInventoryModel::update_map_t update;
+ bool start_new_message = true;
+ S32 count = batch.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
+ if(item)
+ {
+ if(item->getParentUUID() == trash_id) continue;
+ move_ids.push_back(item->getUUID());
+ LLPreview::hide(item->getUUID());
+ --update[item->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
+ msg->addUUIDFast(_PREHASH_FolderID, trash_id);
+ msg->addString("NewName", NULL);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
+ if(cat)
+ {
+ if(cat->getParentUUID() == trash_id) continue;
+ move_ids.push_back(cat->getUUID());
+ --update[cat->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOL("Stamp", TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID());
+ msg->addUUIDFast(_PREHASH_ParentID, trash_id);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ }
+
+ // move everything.
+ std::vector<LLUUID>::iterator it = move_ids.begin();
+ std::vector<LLUUID>::iterator end = move_ids.end();
+ for(; it != end; ++it)
+ {
+ gInventory.moveObject((*it), trash_id);
+ }
+
+ // notify inventory observers.
+ model->notifyObservers();
+}
+
+BOOL LLInvFVBridge::isClipboardPasteable() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ BOOL is_agent_inventory = model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID());
+
+ if(LLInventoryClipboard::instance().hasContents() && is_agent_inventory)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void hideContextEntries(LLMenuGL& menu,
+ const std::vector<LLString> &entries_to_show,
+ const std::vector<LLString> &disabled_entries)
+{
+ const LLView::child_list_t *list = menu.getChildList();
+
+ LLView::child_list_t::const_iterator itor;
+ for (itor = list->begin(); itor != list->end(); ++itor)
+ {
+ LLString name = (*itor)->getName();
+ bool found = false;
+ std::vector<LLString>::const_iterator itor2;
+ for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ (*itor)->setVisible(FALSE);
+ }
+ else
+ {
+ for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ (*itor)->setEnabled(FALSE);
+ }
+ }
+ }
+ }
+}
+
+// Helper for commonly-used entries
+void LLInvFVBridge::getClipboardEntries(bool show_asset_id, std::vector<LLString> &items,
+ std::vector<LLString> &disabled_items, U32 flags)
+{
+ items.push_back("Rename");
+ if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Rename");
+ }
+
+ if (show_asset_id)
+ {
+ items.push_back("Copy Asset UUID");
+ if ( (! ( isItemPermissive() || gAgent.isGodlike() ) )
+ || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Copy Asset UUID");
+ }
+ }
+
+ items.push_back("Copy Separator");
+
+ items.push_back("Copy");
+ if (!isItemCopyable())
+ {
+ disabled_items.push_back("Copy");
+ }
+
+ items.push_back("Paste");
+ if (!isClipboardPasteable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Paste");
+ }
+
+ items.push_back("Paste Separator");
+
+ items.push_back("Delete");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Delete");
+ }
+}
+
+void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLInvFVBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+//FIXME: remove this
+BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id)
+{
+ BOOL rv = FALSE;
+
+ LLInventoryObject* obj = getInventoryObject();
+
+ if(obj)
+ {
+ *type = LLAssetType::lookupDragAndDropType(obj->getType());
+ if(*type == DAD_NONE)
+ {
+ return FALSE;
+ }
+
+ *id = obj->getUUID();
+ //object_ids.put(obj->getUUID());
+
+ if (*type == DAD_CATEGORY)
+ {
+ gInventory.startBackgroundFetch(obj->getUUID());
+ }
+
+ rv = TRUE;
+ }
+
+ return rv;
+}
+
+LLInventoryObject* LLInvFVBridge::getInventoryObject() const
+{
+ LLInventoryObject* obj = NULL;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model)
+ {
+ obj = (LLInventoryObject*)model->getObject(mUUID);
+ }
+ return obj;
+}
+
+BOOL LLInvFVBridge::isInTrash() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ return model->isObjectDescendentOf(mUUID, trash_id);
+}
+
+BOOL LLInvFVBridge::isAgentInventory() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ if(gAgent.getInventoryRootID() == mUUID) return TRUE;
+ return model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID());
+}
+
+BOOL LLInvFVBridge::isItemPermissive() const
+{
+ return FALSE;
+}
+
+// static
+void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
+ LLViewerInventoryItem* item,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(item->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setParent(new_parent);
+ new_item->updateParentOnServer(restamp);
+ model->updateItem(new_item);
+ model->notifyObservers();
+ }
+}
+
+// static
+void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,
+ LLViewerInventoryCategory* cat,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(cat->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->setParent(new_parent);
+ new_cat->updateParentOnServer(restamp);
+ model->updateCategory(new_cat);
+ model->notifyObservers();
+ }
+}
+
+
+const char* safe_inv_type_lookup(LLInventoryType::EType inv_type)
+{
+ const char* rv = LLInventoryType::lookup(inv_type);
+ if(!rv)
+ {
+ const char* INVALID_TYPE = "<invalid>";
+ rv = INVALID_TYPE;
+ }
+ return rv;
+}
+
+LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags)
+{
+ LLInvFVBridge* new_listener = NULL;
+ switch(asset_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLTextureBridge(inventory, uuid, inv_type);
+ break;
+
+ case LLAssetType::AT_SOUND:
+ if(!(inv_type == LLInventoryType::IT_SOUND))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLSoundBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LANDMARK:
+ if(!(inv_type == LLInventoryType::IT_LANDMARK))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLandmarkBridge(inventory, uuid, flags);
+ break;
+
+ case LLAssetType::AT_CALLINGCARD:
+ if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLCallingCardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_SCRIPT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLScriptBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags);
+ break;
+
+ case LLAssetType::AT_NOTECARD:
+ if(!(inv_type == LLInventoryType::IT_NOTECARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLNotecardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_ANIMATION:
+ if(!(inv_type == LLInventoryType::IT_ANIMATION))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLAnimationBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_GESTURE:
+ if(!(inv_type == LLInventoryType::IT_GESTURE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLGestureBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LSL_TEXT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLSLTextBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ if(!(inv_type == LLInventoryType::IT_WEARABLE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags);
+ break;
+
+ case LLAssetType::AT_CATEGORY:
+ case LLAssetType::AT_ROOT_CATEGORY:
+ new_listener = new LLFolderBridge(inventory, uuid);
+ break;
+
+ default:
+ llinfos << "Unhandled asset type (llassetstorage.h): "
+ << (S32)asset_type << llendl;
+ break;
+ }
+
+ new_listener->mInvType = inv_type;
+ return new_listener;
+}
+
+// +=================================================+
+// | LLItemBridge |
+// +=================================================+
+
+void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("open" == action)
+ {
+ openItem();
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ }
+ else if ("purge" == action)
+ {
+ LLInventoryCategory* cat = model->getCategory(mUUID);
+ if(cat)
+ {
+ model->purgeDescendentsOf(mUUID);
+ }
+ LLInventoryObject* obj = model->getObject(mUUID);
+ if(!obj) return;
+ obj->removeFromServer();
+ model->deleteObject(mUUID);
+ model->notifyObservers();
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ }
+ else if ("copy_uuid" == action)
+ {
+ // Single item only
+ LLInventoryItem* item = model->getItem(mUUID);
+ if(!item) return;
+ LLUUID asset_id = item->getAssetUUID();
+ char buffer[UUID_STR_LENGTH];
+ asset_id.toString(buffer);
+
+ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer));
+ return;
+ }
+ else if ("copy" == action)
+ {
+ copyToClipboard();
+ return;
+ }
+ else if ("paste" == action)
+ {
+ // Single item only
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
+ if (!folder_view_itemp) return;
+
+ folder_view_itemp->getListener()->pasteFromClipboard();
+ return;
+ }
+}
+
+void LLItemBridge::selectItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item && !item->isComplete())
+ {
+ item->fetchFromServer();
+ }
+}
+
+void LLItemBridge::restoreItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item)
+ {
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ LLUUID new_parent = model->findCategoryUUIDForType(item->getType());
+ // do not restamp on restore.
+ LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE);
+ }
+}
+
+LLViewerImage* LLItemBridge::getIcon() const
+{
+ LLString uuid_string = gViewerArt.getString(ICON_NAME[OBJECT_ICON_NAME]);
+ return gImageList.getImage(LLUUID(uuid_string), MIPMAP_FALSE, TRUE);
+}
+
+PermissionMask LLItemBridge::getPermissionMask() const
+{
+ LLViewerInventoryItem* item = getItem();
+ PermissionMask perm_mask = 0;
+ if(item)
+ {
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ if (copy) perm_mask |= PERM_COPY;
+ if (mod) perm_mask |= PERM_MODIFY;
+ if (xfer) perm_mask |= PERM_TRANSFER;
+
+ }
+ return perm_mask;
+}
+
+const LLString& LLItemBridge::getDisplayName() const
+{
+ if(mDisplayName.empty())
+ {
+ buildDisplayName(getItem(), mDisplayName);
+ }
+ return mDisplayName;
+}
+
+void LLItemBridge::buildDisplayName(LLInventoryItem* item, LLString& name)
+{
+ if(item)
+ {
+ name.assign(item->getName());
+ }
+ else
+ {
+ name.assign(LLString::null);
+ }
+}
+
+LLString LLItemBridge::getLabelSuffix() const
+{
+ LLString suffix;
+ LLInventoryItem* item = getItem();
+ if(item)
+ {
+ // it's a bit confusing to put nocopy/nomod/etc on calling cards.
+ if(LLAssetType::AT_CALLINGCARD != item->getType()
+ && item->getPermissions().getOwner() == gAgent.getID())
+ {
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+ const char* EMPTY = "";
+ const char* NO_COPY = " (no copy)";
+ const char* NO_MOD = " (no modify)";
+ const char* NO_XFER = " (no transfer)";
+ const char* scopy;
+ if(copy) scopy = EMPTY;
+ else scopy = NO_COPY;
+ const char* smod;
+ if(mod) smod = EMPTY;
+ else smod = NO_MOD;
+ const char* sxfer;
+ if(xfer) sxfer = EMPTY;
+ else sxfer = NO_XFER;
+ char buffer[MAX_STRING];
+ snprintf(
+ buffer,
+ MAX_STRING,
+ "%s%s%s",
+ scopy,
+ smod,
+ sxfer);
+ suffix.assign(buffer);
+ }
+ }
+ return suffix;
+}
+
+U32 LLItemBridge::getCreationDate() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ return item->getCreationDate();
+ }
+ return 0;
+}
+
+
+BOOL LLItemBridge::isItemRenameable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ return (item->getPermissions().allowModifyBy(gAgent.getID()));
+ }
+ return FALSE;
+}
+
+BOOL LLItemBridge::renameItem(const LLString& new_name)
+{
+ if(!isItemRenameable()) return FALSE;
+ LLPreview::rename(mUUID, getPrefix() + new_name);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+
+BOOL LLItemBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ // restamp on move to trash.
+ LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE);
+ }
+
+ // return false anyway, so that if it's called from the folder
+ // view, it doesn't remove the view - it's just being moved to the
+ // trash.
+ return FALSE;
+}
+
+BOOL LLItemBridge::isItemCopyable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ return (item->getPermissions().allowCopyBy(gAgent.getID()));
+ }
+ return FALSE;
+}
+BOOL LLItemBridge::copyToClipboard() const
+{
+ if(isItemCopyable())
+ {
+ LLInventoryClipboard::instance().add(mUUID);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+LLViewerInventoryItem* LLItemBridge::getItem() const
+{
+ LLViewerInventoryItem* item = NULL;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model)
+ {
+ item = (LLViewerInventoryItem*)model->getItem(mUUID);
+ }
+ return item;
+}
+
+BOOL LLItemBridge::isItemPermissive() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ U32 mask = item->getPermissions().getMaskBase();
+ if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// +=================================================+
+// | LLFolderBridge |
+// +=================================================+
+
+LLFolderBridge* LLFolderBridge::sSelf=NULL;
+
+// Can be moved to another folder
+BOOL LLFolderBridge::isItemMovable()
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return (LLAssetType::AT_NONE == ((LLInventoryCategory*)obj)->getPreferredType());
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::selectItem()
+{
+}
+
+
+// Can be destroyed (or moved to trash)
+BOOL LLFolderBridge::isItemRemovable()
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model)
+ {
+ return FALSE;
+ }
+
+ if(!model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()))
+ {
+ return FALSE;
+ }
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+ LLInventoryCategory* category = model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ if( LLAssetType::AT_NONE != category->getPreferredType() )
+ {
+ return FALSE;
+ }
+
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if( LLAssetType::AT_NONE != category->getPreferredType() )
+ {
+ return FALSE;
+ }
+ }
+
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if( gAgent.isWearingItem( item->getUUID() ) )
+ {
+ return FALSE;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if( avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLFolderBridge::isUpToDate() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
+}
+
+BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
+ BOOL drop)
+{
+ // This should never happen, but if an inventory item is incorrectly parented,
+ // the UI will get confused and pass in a NULL.
+ if(!inv_cat) return FALSE;
+
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ // cannot drag into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ // check to make sure source is agent inventory, and is represented there.
+ LLToolDragAndDrop::ESource source = gToolDragAndDrop->getSource();
+ BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL)
+ && (LLToolDragAndDrop::SOURCE_AGENT == source);
+
+ BOOL accept = FALSE;
+ S32 i;
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ if(is_agent_inventory)
+ {
+ const LLUUID& cat_id = inv_cat->getUUID();
+
+ // Is the destination the trash?
+ LLUUID trash_id;
+ trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id)
+ || model->isObjectDescendentOf(mUUID, trash_id);
+ BOOL is_movable = (LLAssetType::AT_NONE == inv_cat->getPreferredType());
+ if( is_movable )
+ {
+ gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE );
+
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if( LLAssetType::AT_NONE != category->getPreferredType() )
+ {
+ // ...can't move "special folders" like Textures
+ is_movable = FALSE;
+ break;
+ }
+ }
+
+ if( is_movable )
+ {
+ if( move_is_into_trash )
+ {
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if( gAgent.isWearingItem( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if( avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ accept = is_movable
+ && (mUUID != cat_id) // Can't move a folder into itself
+ && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing
+ && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity
+ if(accept && drop)
+ {
+ // Look for any gestures and deactivate them
+ if (move_is_into_trash)
+ {
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && gGestureManager.isGestureActive(item->getUUID()))
+ {
+ gGestureManager.deactivateGesture(item->getUUID());
+ }
+ }
+ }
+
+ // Reparent the folder and restamp children if it's moving
+ // into trash.
+ LLInvFVBridge::changeCategoryParent(
+ model,
+ (LLViewerInventoryCategory*)inv_cat,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // content category has same ID as object itself
+ LLUUID object_id = inv_cat->getUUID();
+ LLUUID category_id = mUUID;
+ accept = move_inv_category_world_to_agent(object_id, category_id, drop);
+ }
+ return accept;
+}
+
+void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv)
+{
+ const char* dialog = NULL;
+ if (object->flagScripted())
+ {
+ dialog = "MoveInventoryFromScriptedObject";
+ }
+ else
+ {
+ dialog = "MoveInventoryFromObject";
+ }
+ gViewerWindow->alertXml(dialog, move_task_inventory_callback, move_inv);
+}
+
+// Move/copy all inventory items from the Contents folder of an in-world
+// object to the agent's inventory, inside a given category.
+BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
+ const LLUUID& category_id,
+ BOOL drop,
+ void (*callback)(S32, void*),
+ void* user_data)
+{
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ // content category has same ID as object itself
+ LLViewerObject* object = gObjectList.findObject(object_id);
+ if(!object)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ return FALSE;
+ }
+
+ // this folder is coming from an object, as there is only one folder in an object, the root,
+ // we need to collect the entire contents and handle them as a group
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+
+ if (inventory_objects.empty())
+ {
+ llinfos << "Object contents not found for drop." << llendl;
+ return FALSE;
+ }
+
+ BOOL accept = TRUE;
+ BOOL is_move = FALSE;
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ InventoryObjectList::iterator it = inventory_objects.begin();
+ InventoryObjectList::iterator end = inventory_objects.end();
+ for ( ; it != end; ++it)
+ {
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions());
+ if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
+ && perm.allowTransferTo(gAgent.getID())))
+// || gAgent.isGodlike())
+ {
+ accept = TRUE;
+ }
+ else if(object->permYouOwner())
+ {
+ // If the object cannot be copied, but the object the
+ // inventory is owned by the agent, then the item can be
+ // moved from the task to agent inventory.
+ is_move = TRUE;
+ accept = TRUE;
+ }
+ else
+ {
+ accept = FALSE;
+ break;
+ }
+ }
+
+ if(drop && accept)
+ {
+ it = inventory_objects.begin();
+ InventoryObjectList::iterator first_it = inventory_objects.begin();
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = object_id;
+ move_inv->mCategoryID = category_id;
+ move_inv->mCallback = callback;
+ move_inv->mUserData = user_data;
+
+ for ( ; it != end; ++it)
+ {
+ two_uuids_t two(category_id, (*it)->getUUID());
+ move_inv->mMoveList.push_back(two);
+ }
+
+ if(is_move)
+ {
+ // Callback called from within here.
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ move_task_inventory_callback(0, (void*)(move_inv));
+ }
+ }
+ return accept;
+}
+
+class LLFindWearables : public LLInventoryCollectFunctor
+{
+public:
+ LLFindWearables() {}
+ virtual ~LLFindWearables() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
+bool LLFindWearables::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((item->getType() == LLAssetType::AT_CLOTHING)
+ || (item->getType() == LLAssetType::AT_BODYPART))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver
+{
+public:
+ LLRightClickInventoryFetchObserver() {};
+ LLRightClickInventoryFetchObserver(const LLUUID& cat_id, bool copy_items) :
+ mCatID(cat_id),
+ mCopyItems(copy_items)
+ { };
+ virtual void done()
+ {
+ // we've downloaded all the items, so repaint the dialog
+ LLFolderBridge::staticFolderOptionsMenu();
+
+ gInventory.removeObserver(this);
+ delete this;
+ }
+
+
+protected:
+ LLUUID mCatID;
+ bool mCopyItems;
+
+};
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLRightClickInventoryFetchDescendentsObserver(bool copy_items) : mCopyItems(copy_items) {}
+ ~LLRightClickInventoryFetchDescendentsObserver() {}
+ virtual void done();
+protected:
+ bool mCopyItems;
+};
+
+void LLRightClickInventoryFetchDescendentsObserver::done()
+{
+ // What we do here is get the complete information on the items in
+ // the library, and set up an observer that will wait for that to
+ // happen.
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendents(mCompleteFolders.front(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ S32 count = item_array.count();
+#if 0
+ // This early causes a giant menu to get produced, and doesn't seem to be needed.
+ if(!count)
+ {
+ llwarns << "Nothing fetched in category " << mCompleteFolders.front()
+ << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+#endif
+
+ LLRightClickInventoryFetchObserver* outfit;
+ outfit = new LLRightClickInventoryFetchObserver(mCompleteFolders.front(), mCopyItems);
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.push_back(item_array.get(i)->getUUID());
+ }
+
+ // clean up, and remove this as an observer since the call to the
+ // outfit could notify observers and throw us into an infinite
+ // loop.
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+
+ // increment busy count and either tell the inventory to check &
+ // call done, or add this object to the inventory for observation.
+ inc_busy_count();
+
+ // do the fetch
+ outfit->fetchItems(ids);
+ outfit->done(); //Not interested in waiting and this will be right 99% of the time.
+//Uncomment the following code for laggy Inventory UI.
+/* if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }*/
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryWearObserver
+//
+// Observer for "copy and wear" operation to support knowing
+// when the all of the contents have been added to inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryCopyAndWearObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) :mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {}
+ virtual ~LLInventoryCopyAndWearObserver() {}
+ virtual void changed(U32 mask);
+
+protected:
+ LLUUID mCatID;
+ int mContentsCount;
+ BOOL mFolderAdded;
+};
+
+
+
+void LLInventoryCopyAndWearObserver::changed(U32 mask)
+{
+ if((mask & (LLInventoryObserver::ADD)) != 0)
+ {
+ if (!mFolderAdded)
+ {
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ if ((*id_it) == mCatID)
+ {
+ mFolderAdded = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (mFolderAdded)
+ {
+ LLViewerInventoryCategory* category = gInventory.getCategory(mCatID);
+
+ if (category->getDescendentCount() == mContentsCount)
+ {
+ gInventory.removeObserver(this);
+ wear_inventory_category(category, FALSE, TRUE);
+ delete this;
+ }
+ }
+
+ }
+}
+
+
+
+void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("open" == action)
+ {
+ openItem();
+ }
+ else if ("paste" == action)
+ {
+ pasteFromClipboard();
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ }
+ else if ("replaceoutfit" == action)
+ {
+ modifyOutfit(FALSE);
+ }
+ else if ("addtooutfit" == action)
+ {
+ modifyOutfit(TRUE);
+ }
+ else if ("removefromoutfit" == action)
+ {
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ remove_inventory_category_from_avatar ( cat );
+ }
+ else if ("purge" == action)
+ {
+ LLViewerInventoryCategory* cat;
+ cat = (LLViewerInventoryCategory*)getCategory();
+
+ if(cat)
+ {
+ model->purgeDescendentsOf(mUUID);
+ }
+ LLInventoryObject* obj = model->getObject(mUUID);
+ if(!obj) return;
+ obj->removeFromServer();
+ model->deleteObject(mUUID);
+ model->notifyObservers();
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ }
+}
+
+void LLFolderBridge::openItem()
+{
+ lldebugs << "LLFolderBridge::openItem()" << llendl;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ model->fetchDescendentsOf(mUUID);
+}
+
+BOOL LLFolderBridge::isItemRenameable() const
+{
+ LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat && (cat->getPreferredType() == LLAssetType::AT_NONE)
+ && (cat->getOwnerID() == gAgent.getID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::restoreItem()
+{
+ LLViewerInventoryCategory* cat;
+ cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat)
+ {
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ LLUUID new_parent = model->findCategoryUUIDForType(cat->getType());
+ // do not restamp children on restore
+ LLInvFVBridge::changeCategoryParent(model, cat, new_parent, FALSE);
+ }
+}
+
+// Icons for folders are based on the preferred type
+LLViewerImage* LLFolderBridge::getIcon() const
+{
+ const char* control = NULL;
+ LLAssetType::EType preferred_type = LLAssetType::AT_NONE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ preferred_type = cat->getPreferredType();
+ }
+ switch(preferred_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ control = "inv_folder_texture.tga";
+ break;
+ case LLAssetType::AT_SOUND:
+ control = "inv_folder_sound.tga";
+ break;
+ case LLAssetType::AT_CALLINGCARD:
+ control = "inv_folder_callingcard.tga";
+ break;
+ case LLAssetType::AT_LANDMARK:
+ control = "inv_folder_landmark.tga";
+ break;
+ case LLAssetType::AT_SCRIPT:
+ case LLAssetType::AT_LSL_TEXT:
+ control = "inv_folder_script.tga";
+ break;
+ case LLAssetType::AT_OBJECT:
+ control = "inv_folder_object.tga";
+ break;
+ case LLAssetType::AT_NOTECARD:
+ control = "inv_folder_notecard.tga";
+ break;
+ case LLAssetType::AT_CATEGORY:
+ control = "inv_folder_plain_closed.tga";
+ break;
+ case LLAssetType::AT_CLOTHING:
+ control = "inv_folder_clothing.tga";
+ break;
+ case LLAssetType::AT_BODYPART:
+ control = "inv_folder_bodypart.tga";
+ break;
+ case LLAssetType::AT_TRASH:
+ control = "inv_folder_trash.tga";
+ break;
+ case LLAssetType::AT_SNAPSHOT_CATEGORY:
+ control = "inv_folder_snapshot.tga";
+ break;
+ case LLAssetType::AT_LOST_AND_FOUND:
+ control = "inv_folder_lostandfound.tga";
+ break;
+ case LLAssetType::AT_ANIMATION:
+ control = "inv_folder_animation.tga";
+ break;
+ case LLAssetType::AT_GESTURE:
+ control = "inv_folder_gesture.tga";
+ break;
+ default:
+ control = "inv_folder_plain_closed.tga";
+ break;
+ }
+ LLString uuid_string = gViewerArt.getString(control);
+ return gImageList.getImage(LLUUID(uuid_string), MIPMAP_FALSE, TRUE);
+}
+
+BOOL LLFolderBridge::renameItem(const LLString& new_name)
+{
+ if(!isItemRenameable()) return FALSE;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat && (cat->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->rename(new_name);
+ new_cat->updateServer(FALSE);
+ model->updateCategory(new_cat);
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+BOOL LLFolderBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+
+ LLUUID trash_id;
+ trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+
+ // Look for any gestures and deactivate them
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && gGestureManager.isGestureActive(item->getUUID()))
+ {
+ gGestureManager.deactivateGesture(item->getUUID());
+ }
+ }
+
+ // go ahead and do the normal remove if no 'last calling
+ // cards' are being removed.
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE);
+ }
+
+ // return false anyway, so that if it's called from the folder
+ // view, it doesn't remove the view - it's just being moved to the
+ // trash.
+ return FALSE;
+
+}
+
+BOOL LLFolderBridge::isClipboardPasteable() const
+{
+ if(LLInventoryClipboard::instance().hasContents() && isAgentInventory())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::pasteFromClipboard()
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model && isClipboardPasteable())
+ {
+ LLInventoryItem* item = NULL;
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ LLUUID parent_id(mUUID);
+ for(S32 i = 0; i < count; i++)
+ {
+ item = model->getItem(objects.get(i));
+ if (item)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ parent_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+}
+
+void LLFolderBridge::staticFolderOptionsMenu()
+{
+ if (!sSelf) return;
+ sSelf->folderOptionsMenu();
+}
+
+void LLFolderBridge::folderOptionsMenu()
+{
+ std::vector<LLString> disabled_items;
+
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+
+ // calling card related functionality for folders.
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mItems.push_back("Calling Card Separator");
+ mItems.push_back("IM Contacts In Folder");
+ mItems.push_back("IM All Contacts In Folder");
+ }
+
+ // wearables related functionality for folders.
+ //is_wearable
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (mWearables ||
+ checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mItems.push_back("Folder Wearables Separator");
+ mItems.push_back("Add To Outfit");
+ mItems.push_back("Replace Outfit");
+ mItems.push_back("Take Off Items");
+ }
+ hideContextEntries(*mMenu, mItems, disabled_items);
+}
+
+BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_type);
+ return ((item_array.count() > 0) ? TRUE : FALSE );
+}
+
+// Flags unused
+void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLFolderBridge::buildContextMenu()" << llendl;
+// std::vector<LLString> disabled_items;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(trash_id == mUUID)
+ {
+ // This is the trash.
+ mItems.push_back("Empty Trash");
+ }
+ else if(model->isObjectDescendentOf(mUUID, trash_id))
+ {
+ // This is a folder in the trash.
+ mItems.clear(); // clear any items that used to exist
+ mItems.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ mDisabledItems.push_back("Purge Item");
+ }
+
+ mItems.push_back("Restore Item");
+ }
+ else if(isAgentInventory()) // do not allow creating in library
+ {
+ // only mature accounts can create undershirts/underwear
+ /*if (gAgent.mAccess >= SIM_ACCESS_MATURE)
+ {
+ sub_menu->append(new LLMenuItemCallGL("New Undershirt",
+ &createNewUndershirt,
+ NULL,
+ (void*)this));
+ sub_menu->append(new LLMenuItemCallGL("New Underpants",
+ &createNewUnderpants,
+ NULL,
+ (void*)this));
+ }*/
+
+/* BOOL contains_calling_cards = FALSE;
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_callingcard);
+ if(item_array.count() > 0) contains_calling_cards = TRUE;
+*/
+ mItems.push_back("New Folder");
+ mItems.push_back("New Script");
+ mItems.push_back("New Note");
+ mItems.push_back("New Gesture");
+ mItems.push_back("New Clothes");
+ mItems.push_back("New Body Parts");
+
+ getClipboardEntries(false, mItems, mDisabledItems, flags);
+
+ //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06
+ mCallingCards = mWearables = FALSE;
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mCallingCards=TRUE;
+ }
+
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mWearables=TRUE;
+ }
+
+ mMenu = &menu;
+ sSelf = this;
+ LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(FALSE);
+
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ folders.push_back(category->getUUID());
+ fetch->fetchDescendents(folders);
+ inc_busy_count();
+ if(fetch->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ fetch->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(fetch);
+ }
+ }
+ else
+ {
+ mItems.push_back("--no options--");
+ mDisabledItems.push_back("--no options--");
+ }
+ hideContextEntries(menu, mItems, mDisabledItems);
+}
+
+BOOL LLFolderBridge::hasChildren() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLInventoryModel::EHasChildren has_children;
+ has_children = gInventory.categoryHasChildren(mUUID);
+ return has_children != LLInventoryModel::CHILDREN_NO;
+}
+
+BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ //llinfos << "LLFolderBridge::dragOrDrop()" << llendl;
+ BOOL accept = FALSE;
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_CALLINGCARD:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ accept = dragItemIntoFolder((LLInventoryItem*)cargo_data,
+ drop);
+ break;
+ case DAD_CATEGORY:
+ accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data,
+ drop);
+ break;
+ default:
+ break;
+ }
+ return accept;
+}
+
+LLViewerInventoryCategory* LLFolderBridge::getCategory() const
+{
+ LLViewerInventoryCategory* cat = NULL;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model)
+ {
+ cat = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ }
+ return cat;
+}
+
+
+// static
+void LLFolderBridge::pasteClipboard(void* user_data)
+{
+ LLFolderBridge* self = (LLFolderBridge*)user_data;
+ if(self) self->pasteFromClipboard();
+}
+
+void LLFolderBridge::createNewCategory(void* user_data)
+{
+ LLFolderBridge* bridge = (LLFolderBridge*)user_data;
+ if(!bridge) return;
+ LLInventoryPanel* panel = bridge->mInventoryPanel;
+ LLInventoryModel* model = panel->getModel();
+ if(!model) return;
+ LLUUID id;
+ id = model->createNewCategory(bridge->getUUID(),
+ LLAssetType::AT_NONE,
+ NULL);
+ model->notifyObservers();
+
+ // At this point, the bridge has probably been deleted, but the
+ // view is still there.
+ panel->setSelection(id, TAKE_FOCUS_YES);
+}
+
+void LLFolderBridge::createNewShirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHIRT);
+}
+
+void LLFolderBridge::createNewPants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PANTS);
+}
+
+void LLFolderBridge::createNewShoes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHOES);
+}
+
+void LLFolderBridge::createNewSocks(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SOCKS);
+}
+
+void LLFolderBridge::createNewJacket(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_JACKET);
+}
+
+void LLFolderBridge::createNewSkirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIRT);
+}
+
+void LLFolderBridge::createNewGloves(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_GLOVES);
+}
+
+void LLFolderBridge::createNewUndershirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERSHIRT);
+}
+
+void LLFolderBridge::createNewUnderpants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS);
+}
+
+void LLFolderBridge::createNewShape(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE);
+}
+
+void LLFolderBridge::createNewSkin(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIN);
+}
+
+void LLFolderBridge::createNewHair(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_HAIR);
+}
+
+void LLFolderBridge::createNewEyes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES);
+}
+
+// static
+void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type)
+{
+ if(!bridge) return;
+ LLUUID parent_id = bridge->getUUID();
+ createWearable(parent_id, type);
+}
+
+// Separate function so can be called by global menu as well as right-click
+// menu.
+// static
+void LLFolderBridge::createWearable(LLUUID parent_id, EWearableType type)
+{
+ LLWearable* wearable = gWearableList.createNewWearable(type);
+ LLAssetType::EType asset_type = wearable->getAssetType();
+ LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE;
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ parent_id, wearable->getTransactionID(), wearable->getName(),
+ wearable->getDescription(), asset_type, inv_type, wearable->getType(),
+ wearable->getPermissions().getMaskNextOwner(),
+ LLPointer<LLInventoryCallback>(NULL));
+}
+
+void LLFolderBridge::beginIMSession(BOOL only_online)
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+ LLUniqueBuddyCollector is_buddy;
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_buddy);
+ S32 count = item_array.count();
+ if(count > 0)
+ {
+ // create the session
+ gIMView->setFloaterOpen(TRUE);
+ LLDynamicArray<LLUUID> members;
+ //members.put(gAgent.getID());
+ S32 i;
+ EInstantMessage type = IM_SESSION_ADD;
+ if(only_online)
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLUUID id;
+ for(i = 0; i < count; ++i)
+ {
+ id = item_array.get(i)->getCreatorUUID();
+ if(at.isBuddyOnline(id))
+ {
+ members.put(id);
+ }
+ }
+ }
+ else
+ {
+ type = IM_SESSION_OFFLINE_ADD;
+ for(i = 0; i < count; ++i)
+ {
+ members.put(item_array.get(i)->getCreatorUUID());
+ }
+ }
+ // the session_id is always the item_id of the inventory folder
+ gIMView->addSession(cat->getName(),
+ type,
+ mUUID,
+ members);
+ }
+}
+
+void LLFolderBridge::modifyOutfit(BOOL append)
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ wear_inventory_category_on_avatar( cat, append );
+}
+
+// helper stuff
+void move_task_inventory_callback(S32 option, void* user_data)
+{
+ LLMoveInv* move_inv = (LLMoveInv*)user_data;
+ LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData;
+ LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID);
+
+ if(option == 0 && object)
+ {
+ if (cat_and_wear && cat_and_wear->mWear)
+ {
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+ int contents_count = inventory_objects.size()-1; //subtract one for containing folder
+
+ LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count);
+ gInventory.addObserver(inventoryObserver);
+ }
+
+ two_uuids_list_t::iterator move_it;
+ for (move_it = move_inv->mMoveList.begin();
+ move_it != move_inv->mMoveList.end();
+ ++move_it)
+ {
+ object->moveInventory(move_it->first, move_it->second);
+ }
+
+ // update the UI.
+ dialog_refresh_all();
+ }
+
+ if (move_inv->mCallback)
+ {
+ move_inv->mCallback(option, move_inv->mUserData);
+ }
+
+ delete move_inv;
+}
+
+BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
+ BOOL drop)
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+
+ // cannot drag into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ LLToolDragAndDrop::ESource source = gToolDragAndDrop->getSource();
+ BOOL accept = FALSE;
+ LLViewerObject* object = NULL;
+ if(LLToolDragAndDrop::SOURCE_AGENT == source)
+ {
+
+ BOOL is_movable = TRUE;
+ switch( inv_item->getType() )
+ {
+ case LLAssetType::AT_ROOT_CATEGORY:
+ is_movable = FALSE;
+ break;
+
+ case LLAssetType::AT_CATEGORY:
+ is_movable = ( LLAssetType::AT_NONE == ((LLInventoryCategory*)inv_item)->getPreferredType() );
+ break;
+ }
+
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
+ if(is_movable && move_is_into_trash)
+ {
+ switch(inv_item->getType())
+ {
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ is_movable = !gAgent.isWearingItem(inv_item->getUUID());
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ is_movable = !avatar->isWearingAttachment(inv_item->getUUID());
+ break;
+ }
+ }
+
+ accept = is_movable && (mUUID != inv_item->getParentUUID());
+ if(accept && drop)
+ {
+ if (inv_item->getType() == LLAssetType::AT_GESTURE
+ && gGestureManager.isGestureActive(inv_item->getUUID()))
+ {
+ gGestureManager.deactivateGesture(inv_item->getUUID());
+ }
+ // If an item is being dragged between windows, unselect
+ // everything in the active window so that we don't follow
+ // the selection to its new location (which is very
+ // annoying).
+ LLInventoryPanel* active_panel = LLInventoryView::getActiveInventory()->getPanel();
+ if (mInventoryPanel != active_panel)
+ {
+ active_panel->unSelectAll();
+ }
+
+ // restamp if the move is into the trash.
+ LLInvFVBridge::changeItemParent(
+ model,
+ (LLViewerInventoryItem*)inv_item,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ object = gObjectList.findObject(inv_item->getParentUUID());
+ if(!object)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ return FALSE;
+ }
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(inv_item->getPermissions());
+ BOOL is_move = FALSE;
+ if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
+ && perm.allowTransferTo(gAgent.getID())))
+// || gAgent.isGodlike())
+
+ {
+ accept = TRUE;
+ }
+ else if(object->permYouOwner())
+ {
+ // If the object cannot be copied, but the object the
+ // inventory is owned by the agent, then the item can be
+ // moved from the task to agent inventory.
+ is_move = TRUE;
+ accept = TRUE;
+ }
+ if(drop && accept)
+ {
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = inv_item->getParentUUID();
+ two_uuids_t item_pair(mUUID, inv_item->getUUID());
+ move_inv->mMoveList.push_back(item_pair);
+ move_inv->mCallback = NULL;
+ move_inv->mUserData = NULL;
+ if(is_move)
+ {
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ move_task_inventory_callback(0, (void*)(move_inv));
+ }
+ }
+
+ }
+ else if(LLToolDragAndDrop::SOURCE_NOTECARD == source)
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_from_notecard(gToolDragAndDrop->getObjectID(),
+ gToolDragAndDrop->getSourceID(), inv_item);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_LIBRARY == source)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item;
+ if(item && item->isComplete())
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ else
+ {
+ llwarns << "unhandled drag source" << llendl;
+ }
+ return accept;
+}
+
+// +=================================================+
+// | LLScriptBridge (DEPRECTED) |
+// +=================================================+
+
+LLViewerImage* LLScriptBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0);
+}
+
+// +=================================================+
+// | LLTextureBridge |
+// +=================================================+
+
+LLString LLTextureBridge::sPrefix("Texture: ");
+
+
+LLViewerImage* LLTextureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_TEXTURE, mInvType, 0);
+}
+
+void open_texture(const LLUUID& item_id,
+ const LLString& title,
+ BOOL show_keep_discard,
+ const LLUUID& source_id,
+ BOOL take_focus)
+{
+ // See if we can bring an exiting preview to the front
+ if( !LLPreview::show( item_id, take_focus ) )
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewTextureRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewTexture* preview;
+ preview = new LLPreviewTexture("preview texture",
+ rect,
+ title,
+ item_id,
+ LLUUID::null,
+ show_keep_discard);
+ preview->setSourceID(source_id);
+ if(take_focus) preview->setFocus(TRUE);
+
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+void LLTextureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ open_texture(mUUID, getPrefix() + item->getName(), FALSE);
+ }
+}
+
+// +=================================================+
+// | LLSoundBridge |
+// +=================================================+
+
+LLString LLSoundBridge::sPrefix("Sound: ");
+
+
+LLViewerImage* LLSoundBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0);
+}
+
+void LLSoundBridge::openItem()
+{
+// Changed this back to the way it USED to work:
+// only open the preview dialog through the contextual right-click menu
+// double-click just plays the sound
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ openSoundPreview((void*)this);
+ //send_uuid_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+
+// if(!LLPreview::show(mUUID))
+// {
+// S32 left, top;
+// gFloaterView->getNewFloaterPosition(&left, &top);
+// LLRect rect = gSavedSettings.getRect("PreviewSoundRect");
+// rect.translate(left - rect.mLeft, top - rect.mTop);
+// new LLPreviewSound("preview sound",
+// rect,
+// getPrefix() + getName(),
+// mUUID));
+// }
+}
+
+void LLSoundBridge::previewItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ send_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+}
+
+void LLSoundBridge::openSoundPreview(void* which)
+{
+ LLSoundBridge *me = (LLSoundBridge *)which;
+ if(!LLPreview::show(me->mUUID))
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewSoundRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLPreviewSound* preview = new LLPreviewSound("preview sound",
+ rect,
+ me->getPrefix() + me->getName(),
+ me->mUUID);
+ preview->setFocus(TRUE);
+ // Keep entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLTextureBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Sound Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back("Sound Separator");
+ items.push_back("Sound Play");
+
+ hideContextEntries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLLandmarkBridge |
+// +=================================================+
+
+LLString LLLandmarkBridge::sPrefix("Landmark: ");
+
+LLViewerImage* LLLandmarkBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited);
+}
+
+void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Landmark Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back("Landmark Separator");
+ items.push_back("Teleport To Landmark");
+
+ hideContextEntries(menu, items, disabled_items);
+
+}
+
+// virtual
+void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("teleport" == action)
+ {
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ gAgent.teleportViaLandmark(item->getAssetUUID());
+
+ // we now automatically track the landmark you're teleporting to
+ // because you'll probably arrive at a telehub instead
+ if( gFloaterWorldMap )
+ {
+ gFloaterWorldMap->trackLandmark( item->getAssetUUID() );
+ }
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void open_landmark(const LLUUID& item_id,
+ const LLString& title,
+ BOOL show_keep_discard,
+ const LLUUID& source_id,
+ BOOL take_focus)
+{
+ // See if we can bring an exiting preview to the front
+ if( !LLPreview::show( item_id, take_focus ) )
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewLandmarkRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewLandmark* preview = new LLPreviewLandmark("preview landmark",
+ rect,
+ title,
+ item_id,
+ show_keep_discard);
+ preview->setSourceID(source_id);
+ if(take_focus) preview->setFocus(TRUE);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+void LLLandmarkBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ open_landmark(mUUID, LLString(" ") + getPrefix() + item->getName(), FALSE);
+ }
+}
+
+
+// +=================================================+
+// | LLCallingCardObserver |
+// +=================================================+
+void LLCallingCardObserver::changed(U32 mask)
+{
+ mBridgep->refreshFolderViewItem();
+}
+
+// +=================================================+
+// | LLCallingCardBridge |
+// +=================================================+
+
+LLString LLCallingCardBridge::sPrefix("Calling Card: ");
+
+LLCallingCardBridge::LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid)
+{
+ mObserver = new LLCallingCardObserver(this);
+ LLAvatarTracker::instance().addObserver(mObserver);
+}
+
+LLCallingCardBridge::~LLCallingCardBridge()
+{
+ LLAvatarTracker::instance().removeObserver(mObserver);
+ delete mObserver;
+}
+
+void LLCallingCardBridge::refreshFolderViewItem()
+{
+ LLFolderViewItem* itemp = mInventoryPanel->getRootFolder()->getItemByID(mUUID);
+ if (itemp)
+ {
+ itemp->refresh();
+ }
+}
+
+// virtual
+void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("begin_im" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ gIMView->setFloaterOpen(TRUE);
+ gIMView->addSession(item->getName(), IM_NOTHING_SPECIAL, item->getCreatorUUID());
+ }
+ }
+ else if ("lure" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ handle_lure(item->getCreatorUUID());
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+LLViewerImage* LLCallingCardBridge::getIcon() const
+{
+ BOOL online = FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
+ }
+ return get_item_icon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online);
+}
+
+LLString LLCallingCardBridge::getLabelSuffix() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (online)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void LLCallingCardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item && !item->getCreatorUUID().isNull())
+ {
+ BOOL online;
+ online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
+ LLFloaterAvatarInfo::showFromFriend(item->getCreatorUUID(), online);
+ }
+}
+
+void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLCallingCardBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLInventoryItem* item = getItem();
+ BOOL good_card = (item
+ && (LLUUID::null != item->getCreatorUUID())
+ && (item->getCreatorUUID() != gAgent.getID()));
+
+ items.push_back("Send Instant Message");
+ items.push_back("Offer Teleport...");
+
+ if (!good_card)
+ {
+ disabled_items.push_back("Send Instant Message");
+ disabled_items.push_back("Offer Teleport...");
+ }
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ LLViewerInventoryItem* item = getItem();
+ BOOL rv = FALSE;
+ if(item)
+ {
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_CLOTHING:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+ const LLPermissions& perm = inv_item->getPermissions();
+ if(gInventory.getItem(inv_item->getUUID())
+ && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventory(item->getCreatorUUID(),
+ (LLInventoryItem*)cargo_data);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ {
+ LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data;
+ if( gInventory.getCategory( inv_cat->getUUID() ) )
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventoryCategory(
+ item->getCreatorUUID(),
+ inv_cat);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+// +=================================================+
+// | LLNotecardBridge |
+// +=================================================+
+
+LLString LLNotecardBridge::sPrefix("Note: ");
+
+
+LLViewerImage* LLNotecardBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0);
+}
+
+void open_notecard(const LLUUID& item_id,
+ const LLString& title,
+ BOOL show_keep_discard,
+ const LLUUID& source_id,
+ BOOL take_focus)
+{
+ // See if we can bring an existing preview to the front
+ if(!LLPreview::show(item_id, take_focus))
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("NotecardEditorRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLPreviewNotecard* preview;
+ preview = new LLPreviewNotecard("preview notecard",
+ rect,
+ title,
+ item_id,
+ LLUUID::null,
+ LLUUID::null,
+ show_keep_discard);
+ preview->setSourceID(source_id);
+ if(take_focus) preview->setFocus(TRUE);
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+
+ //if (source_id.notNull())
+ //{
+ // // look for existing tabbed view for content from same source
+ // LLPreview* existing_preview = LLPreview::getPreviewForSource(source_id);
+ // if (existing_preview)
+ // {
+ // // found existing preview from this source
+ // // is it already hosted in a multi-preview window?
+ // LLMultiPreview* preview_hostp = (LLMultiPreview*)existing_preview->getHost();
+ // if (!preview_hostp)
+ // {
+ // // create new multipreview if it doesn't exist
+ // LLMultiPreview* preview_hostp = new LLMultiPreview(existing_preview->getRect());
+
+ // preview_hostp->addFloater(existing_preview);
+ // }
+ // // add this preview to existing host
+ // preview_hostp->addFloater(preview);
+ // }
+ //}
+
+ }
+}
+
+void LLNotecardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ open_notecard(mUUID, getPrefix() + item->getName(), FALSE);
+ }
+}
+
+
+// +=================================================+
+// | LLGestureBridge |
+// +=================================================+
+
+LLString LLGestureBridge::sPrefix("Gesture: ");
+
+LLViewerImage* LLGestureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0);
+}
+
+LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const
+{
+ if( gGestureManager.isGestureActive(mUUID) )
+ {
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+LLString LLGestureBridge::getLabelSuffix() const
+{
+ if( gGestureManager.isGestureActive(mUUID) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (active)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+// virtual
+void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("activate" == action)
+ {
+ gGestureManager.activateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else if ("deactivate" == action)
+ {
+ gGestureManager.deactivateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLGestureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (!item) return;
+
+ // See if we can bring an existing preview to the front
+ if(!LLPreview::show(mUUID))
+ {
+ LLUUID item_id = mUUID;
+ LLString title = getPrefix() + item->getName();
+ LLUUID object_id = LLUUID::null;
+
+ // TODO: save the rectangle
+ LLPreviewGesture* preview = LLPreviewGesture::show(title, item_id, object_id);
+ preview->setFocus(TRUE);
+
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+BOOL LLGestureBridge::removeItem()
+{
+ // Force close the preview window, if it exists
+ LLPreview::hide(mUUID);
+ gGestureManager.deactivateGesture(mUUID);
+ return LLItemBridge::removeItem();
+}
+
+void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLGestureBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back("Gesture Separator");
+ items.push_back("Activate");
+ items.push_back("Deactivate");
+
+ /*menu.append(new LLMenuItemCallGL("Activate",
+ handleActivateGesture,
+ enableActivateGesture,
+ (void*)this));
+ menu.append(new LLMenuItemCallGL("Deactivate",
+ handleDeactivateGesture,
+ enableDeactivateGesture,
+ (void*)this));*/
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLAnimationBridge |
+// +=================================================+
+
+LLString LLAnimationBridge::sPrefix("Animation: ");
+
+
+LLViewerImage* LLAnimationBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0);
+}
+
+void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Animation Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back("Animation Separator");
+ items.push_back("Animation Play");
+ items.push_back("Animation Audition");
+
+ hideContextEntries(menu, items, disabled_items);
+
+}
+
+// virtual
+void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ S32 activate = 0;
+
+ if ((action == "playworld") || (action == "playlocal"))
+ {
+
+ if ("playworld" == action) activate = 1;
+ if ("playlocal" == action) activate = 2;
+
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( mUUID ) )
+ {
+ // There isn't one, so make a new preview
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewAnimRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ LLPreviewAnim* preview = new LLPreviewAnim("preview anim",
+ rect,
+ getPrefix() + item->getName(),
+ mUUID,
+ activate);
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+ }
+ }
+ else
+ {
+ LLItemBridge::performAction(folder, model, action);
+ }
+}
+
+void LLAnimationBridge::openItem()
+{
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( mUUID ) )
+ {
+ // There isn't one, so make a new preview
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewAnimRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ LLPreviewAnim* preview = new LLPreviewAnim("preview anim",
+ rect,
+ getPrefix() + item->getName(),
+ mUUID,
+ 0);
+ preview->setFocus(TRUE);
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+ }
+}
+
+// +=================================================+
+// | LLObjectBridge |
+// +=================================================+
+
+// static
+LLString LLObjectBridge::sPrefix("Object: ");
+
+// static
+LLUUID LLObjectBridge::sContextMenuItemID;
+
+BOOL LLObjectBridge::isItemRemovable()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+ if(avatar->isWearingAttachment(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+LLViewerImage* LLObjectBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt);
+}
+
+void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment);
+
+// virtual
+void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("attach" == action)
+ {
+ LLUUID object_id = mUUID;
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(object_id);
+ if(item && gInventory.isObjectDescendentOf(object_id, gAgent.getInventoryRootID()))
+ {
+ rez_attachment(item, NULL);
+ }
+ else if(item && item->isComplete())
+ {
+ // must be in library. copy it to our inventory and put it on.
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+ else if ("detach" == action)
+ {
+ LLInventoryItem* item = gInventory.getItem(mUUID);
+ if( item )
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getUUID() );
+
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
+ }
+ // this object might have been selected, so let the selection manager know it's gone now
+ gSelectMgr->remove(gObjectList.findObject(item->getUUID()));
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLObjectBridge::openItem()
+{
+ /* Disabled -- this preview isn't useful. JC */
+ // CP: actually, this code is required - made changes to match LLAnimationBridge::openItem() idiom
+ // The properties preview is useful, converting to show object properties. - DaveP
+ LLShowProps::showProperties(mUUID);
+}
+
+LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+LLString LLObjectBridge::getLabelSuffix() const
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ LLString attachment_point_name = avatar->getAttachedPointName(mUUID);
+ LLString::toLower(attachment_point_name);
+ return LLItemBridge::getLabelSuffix() + LLString(" (worn on ") + attachment_point_name + LLString(")");
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
+{
+ LLAttachmentRezAction* rez_action = new LLAttachmentRezAction;
+ rez_action->mItemID = item->getUUID();
+ rez_action->mAttachPt = gAgent.getAvatarObject()->mAttachmentPoints.reverseLookup(attachment);
+
+ if (attachment && attachment->getObject(0))
+ {
+ gViewerWindow->alertXml("ReplaceAttachment", confirm_replace_attachment_rez, (void*)rez_action);
+ }
+ else
+ {
+ confirm_replace_attachment_rez(0/*YES*/, (void*)rez_action);
+ }
+}
+
+void confirm_replace_attachment_rez(S32 option, void* user_data)
+{
+ LLAttachmentRezAction* rez_action = (LLAttachmentRezAction*)user_data;
+ if (option == 0/*YES*/)
+ {
+ if (rez_action)
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(rez_action->mItemID);
+
+ if (itemp)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID());
+ msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner());
+ msg->addU8Fast(_PREHASH_AttachmentPt, rez_action->mAttachPt);
+ pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions());
+ msg->addStringFast(_PREHASH_Name, itemp->getName());
+ msg->addStringFast(_PREHASH_Description, itemp->getDescription());
+ msg->sendReliable(gAgent.getRegion()->getHost());
+ }
+ }
+ }
+ delete rez_action;
+}
+
+void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLObjectBridge::sContextMenuItemID = mUUID;
+
+ LLInventoryItem* item = getItem();
+ if(item)
+ {
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if( !avatarp )
+ {
+ return;
+ }
+
+ if( avatarp->isWearingAttachment( mUUID ) )
+ {
+ items.push_back("Detach From Yourself");
+ }
+ else
+ if( !isInTrash() )
+ {
+ items.push_back("Attach Separator");
+ items.push_back("Object Wear");
+ items.push_back("Attach To");
+ items.push_back("Attach To HUD");
+
+ LLMenuGL* attach_menu = menu.getChildMenuByName("Attach To", TRUE);
+ LLMenuGL* attach_hud_menu = menu.getChildMenuByName("Attach To HUD", TRUE);
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if (attach_menu && (attach_menu->getChildCount() == 0) &&
+ attach_hud_menu && (attach_hud_menu->getChildCount() == 0) &&
+ avatarp)
+ {
+ for (LLViewerJointAttachment* attachment = avatarp->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = gAgent.getAvatarObject()->mAttachmentPoints.getNextData())
+ {
+ LLMenuItemCallGL *new_item;
+ if (attachment->getIsHUDAttachment())
+ {
+ attach_hud_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(),
+ NULL, //&LLObjectBridge::attachToAvatar,
+ NULL, &attach_label, (void*)attachment));
+ }
+ else
+ {
+ attach_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(),
+ NULL, //&LLObjectBridge::attachToAvatar,
+ NULL, &attach_label, (void*)attachment));
+ }
+
+ LLSimpleListener* callback = mInventoryPanel->getListenerByName("Inventory.AttachObject");
+
+ if (callback)
+ {
+ new_item->addListener(callback, "on_click", LLSD(attachment->getName()));
+ }
+ }
+ }
+ }
+ }
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+BOOL LLObjectBridge::renameItem(const LLString& new_name)
+{
+ if(!isItemRenameable()) return FALSE;
+ LLPreview::rename(mUUID, getPrefix() + new_name);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+ model->notifyObservers();
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() );
+ if( obj )
+ {
+ gSelectMgr->deselectAll();
+ gSelectMgr->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
+ gSelectMgr->setObjectName( new_name );
+ gSelectMgr->deselectAll();
+ }
+ }
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+// +=================================================+
+// | LLLSLTextBridge |
+// +=================================================+
+
+LLString LLLSLTextBridge::sPrefix("Script: ");
+
+LLViewerImage* LLLSLTextBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0);
+}
+
+void LLLSLTextBridge::openItem()
+{
+ // See if we can bring an exiting preview to the front
+ if(!LLPreview::show(mUUID))
+ {
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewScriptRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+
+ LLPreviewLSL* preview = new LLPreviewLSL("preview lsl text",
+ rect,
+ getPrefix() + item->getName(),
+ mUUID);
+ preview->setFocus(TRUE);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+ }
+}
+
+// +=================================================+
+// | LLWearableBridge |
+// +=================================================+
+
+// HACK to get from avatar inventory to avatar
+void wear_inventory_item_on_avatar( LLInventoryItem* item )
+{
+ if(item)
+ {
+ lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
+ << " )" << llendl;
+
+ gWearableList.getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onWearOnAvatarArrived,
+ new LLUUID(item->getUUID()));
+ }
+}
+
+struct LLFoundData
+{
+ LLFoundData(const LLUUID& item_id,
+ const LLUUID& asset_id,
+ const LLString& name,
+ LLAssetType::EType asset_type) :
+ mItemID(item_id),
+ mAssetID(asset_id),
+ mAssetType(asset_type),
+ mName(name),
+ mWearable( NULL ) {}
+
+ LLUUID mItemID;
+ LLUUID mAssetID;
+ LLString mName;
+ LLAssetType::EType mAssetType;
+ LLWearable* mWearable;
+};
+
+struct LLWearableHoldingPattern
+{
+ LLWearableHoldingPattern() : mResolved(0) {}
+ ~LLWearableHoldingPattern() { mFoundList.deleteAllData(); }
+ LLDoubleLinkedList<LLFoundData> mFoundList;
+ S32 mResolved;
+};
+
+
+class LLOutfitObserver : public LLInventoryFetchObserver
+{
+public:
+ LLOutfitObserver(const LLUUID& cat_id, bool copy_items, bool append) :
+ mCatID(cat_id),
+ mCopyItems(copy_items),
+ mAppend(append)
+ {}
+ ~LLOutfitObserver() {}
+ virtual void done(); //public
+
+protected:
+ LLUUID mCatID;
+ bool mCopyItems;
+ bool mAppend;
+};
+
+class LLWearInventoryCategoryCallback : public LLInventoryCallback
+{
+public:
+ LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append)
+ {
+ mCatID = cat_id;
+ mAppend = append;
+ }
+ void fire(const LLUUID& item_id)
+ {
+ /*
+ * Do nothing. We only care about the destructor
+ */
+ }
+ ~LLWearInventoryCategoryCallback()
+ {
+ wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend);
+ }
+private:
+ LLUUID mCatID;
+ bool mAppend;
+};
+
+void LLOutfitObserver::done()
+{
+ // We now have an outfit ready to be copied to agent inventory. Do
+ // it, and wear that outfit normally.
+ if(mCopyItems)
+ {
+ LLInventoryCategory* cat = gInventory.getCategory(mCatID);
+ LLString name;
+ if(!cat)
+ {
+ // should never happen.
+ name = "New Outfit";
+ }
+ else
+ {
+ name = cat->getName();
+ }
+ LLViewerInventoryItem* item = NULL;
+ item_ref_t::iterator it = mComplete.begin();
+ item_ref_t::iterator end = mComplete.end();
+ LLUUID pid;
+ for(; it < end; ++it)
+ {
+ item = (LLViewerInventoryItem*)gInventory.getItem(*it);
+ if(item)
+ {
+ if(LLInventoryType::IT_GESTURE == item->getInventoryType())
+ {
+ pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_GESTURE);
+ }
+ else
+ {
+ pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING);
+ }
+ break;
+ }
+ }
+ if(pid.isNull())
+ {
+ pid = gAgent.getInventoryRootID();
+ }
+
+ LLUUID cat_id = gInventory.createNewCategory(
+ pid,
+ LLAssetType::AT_NONE,
+ name);
+ mCatID = cat_id;
+ LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(mCatID, mAppend);
+ it = mComplete.begin();
+ for(; it < end; ++it)
+ {
+ item = (LLViewerInventoryItem*)gInventory.getItem(*it);
+ if(item)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ cat_id,
+ std::string(),
+ cb);
+ }
+ }
+ }
+ else
+ {
+ // Wear the inventory category.
+ wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend);
+ }
+}
+
+class LLOutfitFetch : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLOutfitFetch(bool copy_items, bool append) : mCopyItems(copy_items), mAppend(append) {}
+ ~LLOutfitFetch() {}
+ virtual void done();
+protected:
+ bool mCopyItems;
+ bool mAppend;
+};
+
+void LLOutfitFetch::done()
+{
+ // What we do here is get the complete information on the items in
+ // the library, and set up an observer that will wait for that to
+ // happen.
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendents(mCompleteFolders.front(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ S32 count = item_array.count();
+ if(!count)
+ {
+ llwarns << "Nothing fetched in category " << mCompleteFolders.front()
+ << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+
+ LLOutfitObserver* outfit;
+ outfit = new LLOutfitObserver(mCompleteFolders.front(), mCopyItems, mAppend);
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.push_back(item_array.get(i)->getUUID());
+ }
+
+ // clean up, and remove this as an observer since the call to the
+ // outfit could notify observers and throw us into an infinite
+ // loop.
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+
+ // increment busy count and either tell the inventory to check &
+ // call done, or add this object to the inventory for observation.
+ inc_busy_count();
+
+ // do the fetch
+ outfit->fetchItems(ids);
+ if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }
+}
+
+void wear_outfit_by_name(const char* name)
+{
+ llinfos << "Wearing category " << name << llendl;
+ inc_busy_count();
+
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLNameCategoryCollector has_name(name);
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ has_name);
+ bool copy_items = false;
+ LLInventoryCategory* cat = NULL;
+ if (cat_array.count() > 0)
+ {
+ // Just wear the first one that matches
+ cat = cat_array.get(0);
+ }
+ else
+ {
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ has_name);
+ if(cat_array.count() > 0)
+ {
+ cat = cat_array.get(0);
+ copy_items = true;
+ }
+ }
+
+ if(cat)
+ {
+ wear_inventory_category(cat, copy_items, false);
+ }
+ else
+ {
+ llwarns << "Couldn't find outfit " <<name<< " in wear_outfit_by_name()"
+ << llendl;
+ }
+
+ dec_busy_count();
+}
+
+void wear_inventory_category(LLInventoryCategory* category, bool copy, bool append)
+{
+ if(!category) return;
+
+ lldebugs << "wear_inventory_category( " << category->getName()
+ << " )" << llendl;
+ // What we do here is get the complete information on the items in
+ // the inventory, and set up an observer that will wait for that to
+ // happen.
+ LLOutfitFetch* outfit;
+ outfit = new LLOutfitFetch(copy, append);
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ folders.push_back(category->getUUID());
+ outfit->fetchDescendents(folders);
+ inc_busy_count();
+ if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }
+}
+
+// HACK to get from avatar inventory to avatar
+void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL append )
+{
+ // Avoid unintentionally overwriting old wearables. We have to do
+ // this up front to avoid having to deal with the case of multiple
+ // wearables being dirty.
+ if(!category) return;
+ lldebugs << "wear_inventory_category_on_avatar( " << category->getName()
+ << " )" << llendl;
+
+ LLWearInfo* userdata = new LLWearInfo;
+ userdata->mAppend = append;
+ userdata->mCategoryID = category->getUUID();
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveAllIfDirty(
+ wear_inventory_category_on_avatar_step2,
+ userdata);
+ }
+ else
+ {
+ wear_inventory_category_on_avatar_step2(
+ TRUE,
+ userdata );
+ }
+}
+
+
+void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata )
+{
+ LLWearInfo* wear_info = (LLWearInfo*)userdata;
+ if (!wear_info) return;
+
+ // Find all the wearables that are in the category's subtree.
+ lldebugs << "wear_inventory_category_on_avatar_step2()" << llendl;
+ if(proceed)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLFindWearables is_wearable;
+ gInventory.collectDescendentsIf(wear_info->mCategoryID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_wearable);
+ S32 i;
+ S32 wearable_count = item_array.count();
+
+ LLInventoryModel::cat_array_t obj_cat_array;
+ LLInventoryModel::item_array_t obj_item_array;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ gInventory.collectDescendentsIf(wear_info->mCategoryID,
+ obj_cat_array,
+ obj_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+ S32 obj_count = obj_item_array.count();
+
+ // Find all gestures in this folder
+ LLInventoryModel::cat_array_t gest_cat_array;
+ LLInventoryModel::item_array_t gest_item_array;
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+ gInventory.collectDescendentsIf(wear_info->mCategoryID,
+ gest_cat_array,
+ gest_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_gesture);
+ S32 gest_count = gest_item_array.count();
+
+ if( !wearable_count && !obj_count && !gest_count)
+ {
+ gViewerWindow->alertXml("CouldNotPutOnOutfit");
+ delete wear_info;
+ return;
+ }
+
+ // Processes that take time should show the busy cursor
+ if (wearable_count > 0 || obj_count > 0)
+ {
+ inc_busy_count();
+ }
+
+ // Activate all gestures in this folder
+ if (gest_count > 0)
+ {
+ llinfos << "Activating " << gest_count << " gestures" << llendl;
+
+ gGestureManager.activateGestures(gest_item_array);
+
+ // Update the inventory item labels to reflect the fact
+ // they are active.
+ LLViewerInventoryCategory* catp = gInventory.getCategory(wear_info->mCategoryID);
+ if (catp)
+ {
+ gInventory.updateCategory(catp);
+ gInventory.notifyObservers();
+ }
+ }
+
+ if(wearable_count > 0)
+ {
+ // Note: can't do normal iteration, because if all the
+ // wearables can be resolved immediately, then the
+ // callback will be called (and this object deleted)
+ // before the final getNextData().
+ LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
+ LLFoundData* found;
+ LLDynamicArray<LLFoundData*> found_container;
+ for(i = 0; i < wearable_count; ++i)
+ {
+ found = new LLFoundData(item_array.get(i)->getUUID(),
+ item_array.get(i)->getAssetUUID(),
+ item_array.get(i)->getName(),
+ item_array.get(i)->getType());
+ holder->mFoundList.addData(found);
+ found_container.put(found);
+ }
+ for(i = 0; i < wearable_count; ++i)
+ {
+ gAddToOutfit = wear_info->mAppend;
+
+ found = found_container.get(i);
+ gWearableList.getAsset(found->mAssetID,
+ found->mName,
+ found->mAssetType,
+ wear_inventory_category_on_avatar_loop,
+ (void*)holder);
+ }
+ }
+
+
+ //If not appending and the folder doesn't contain only gestures, take off all attachments.
+ if (!wear_info->mAppend
+ && !(wearable_count == 0 && obj_count == 0 && gest_count > 0) )
+ {
+ LLAgent::userRemoveAllAttachments(NULL);
+ }
+
+ if( obj_count > 0 )
+ {
+ // We've found some attachements. Add these.
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ // Build a compound message to send all the objects that need to be rezzed.
+
+ // Limit number of packets to send
+ const S32 MAX_PACKETS_TO_SEND = 10;
+ const S32 OBJECTS_PER_PACKET = 4;
+ const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
+ if( obj_count > MAX_OBJECTS_TO_SEND )
+ {
+ obj_count = MAX_OBJECTS_TO_SEND;
+ }
+
+ // Create an id to keep the parts of the compound message together
+ LLUUID compound_msg_id;
+ compound_msg_id.generate();
+ LLMessageSystem* msg = gMessageSystem;
+
+ for(i = 0; i < obj_count; ++i)
+ {
+ if( 0 == (i % OBJECTS_PER_PACKET) )
+ {
+ // Start a new message chunk
+ msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_HeaderData);
+ msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id );
+ msg->addU8Fast(_PREHASH_TotalObjects, obj_count );
+ msg->addBOOLFast(_PREHASH_FirstDetachAll, !wear_info->mAppend );
+ }
+
+ LLInventoryItem* item = obj_item_array.get(i);
+ msg->nextBlockFast(_PREHASH_ObjectData );
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID() );
+ msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
+ msg->addU8Fast(_PREHASH_AttachmentPt, 0 ); // Wear at the previous or default attachment point
+ pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
+ msg->addStringFast(_PREHASH_Name, item->getName());
+ msg->addStringFast(_PREHASH_Description, item->getDescription());
+
+ if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
+ {
+ // End of message chunk
+ msg->sendReliable( gAgent.getRegion()->getHost() );
+ }
+ }
+ }
+ }
+ }
+ delete wear_info;
+ wear_info = NULL;
+}
+
+void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void* data)
+{
+ LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
+ BOOL append= gAddToOutfit;
+
+ if(wearable)
+ {
+ for(LLFoundData* data = holder->mFoundList.getFirstData();
+ data;
+ data = holder->mFoundList.getNextData() )
+ {
+ if(wearable->getID() == data->mAssetID)
+ {
+ data->mWearable = wearable;
+ break;
+ }
+ }
+ }
+ holder->mResolved += 1;
+ if(holder->mResolved >= holder->mFoundList.getLength())
+ {
+ wear_inventory_category_on_avatar_step3(holder, append);
+ }
+}
+
+void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append)
+{
+ lldebugs << "wear_inventory_category_on_avatar_step3()" << llendl;
+ LLInventoryItem::item_array_t items;
+ LLDynamicArray< LLWearable* > wearables;
+
+ // For each wearable type, find the first instance in the category
+ // that we recursed through.
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ for(LLFoundData* data = holder->mFoundList.getFirstData();
+ data;
+ data = holder->mFoundList.getNextData())
+ {
+ LLWearable* wearable = data->mWearable;
+ if( wearable && ((S32)wearable->getType() == i) )
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(data->mItemID);
+ if( item && (item->getAssetUUID() == wearable->getID()) )
+ {
+ //RN: after discussing with Brashears, I disabled this code
+ //Metadata should reside in the item, not the asset
+ //And this code does not handle failed asset uploads properly
+// if(!wearable->isMatchedToInventoryItem(item ))
+// {
+// wearable = gWearableList.createWearableMatchedToInventoryItem( wearable, item );
+// // Now that we have an asset that matches the
+// // item, update the item to point to the new
+// // asset.
+// item->setAssetUUID(wearable->getID());
+// item->updateAssetOnServer();
+// }
+ items.put(item);
+ wearables.put(wearable);
+ }
+ break;
+ }
+ }
+ }
+
+ if(wearables.count() > 0)
+ {
+ gAgent.setWearableOutfit(items, wearables, !append);
+ gInventory.notifyObservers();
+ }
+
+ delete holder;
+
+ dec_busy_count();
+}
+
+void remove_inventory_category_from_avatar( LLInventoryCategory* category )
+{
+ if(!category) return;
+ lldebugs << "remove_inventory_category_from_avatar( " << category->getName()
+ << " )" << llendl;
+
+
+ LLUUID* uuid = new LLUUID(category->getUUID());
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveAllIfDirty(
+ remove_inventory_category_from_avatar_step2,
+ uuid);
+ }
+ else
+ {
+ remove_inventory_category_from_avatar_step2(
+ TRUE,
+ uuid );
+ }
+}
+
+
+void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata)
+{
+
+ // Find all the wearables that are in the category's subtree.
+ LLUUID* category_id = (LLUUID *)userdata;
+
+ lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl;
+ if(proceed)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLFindWearables is_wearable;
+ gInventory.collectDescendentsIf(*category_id,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_wearable);
+ S32 i;
+ S32 wearable_count = item_array.count();
+
+ LLInventoryModel::cat_array_t obj_cat_array;
+ LLInventoryModel::item_array_t obj_item_array;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ gInventory.collectDescendentsIf(*category_id,
+ obj_cat_array,
+ obj_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+ S32 obj_count = obj_item_array.count();
+
+ // Find all gestures in this folder
+ LLInventoryModel::cat_array_t gest_cat_array;
+ LLInventoryModel::item_array_t gest_item_array;
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+ gInventory.collectDescendentsIf(*category_id,
+ gest_cat_array,
+ gest_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_gesture);
+ S32 gest_count = gest_item_array.count();
+
+ if (wearable_count > 0) //Loop through wearables. If worn, remove.
+ {
+ for(i = 0; i < wearable_count; ++i)
+ {
+ if( gAgent.isWearingItem (item_array.get(i)->getUUID()) )
+ {
+ gWearableList.getAsset(item_array.get(i)->getAssetUUID(),
+ item_array.get(i)->getName(),
+ item_array.get(i)->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new LLUUID(item_array.get(i)->getUUID()));
+
+ }
+ }
+ }
+
+
+ if (obj_count > 0)
+ {
+ for(i = 0; i < obj_count; ++i)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() );
+
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
+
+ // this object might have been selected, so let the selection manager know it's gone now
+ gSelectMgr->remove(gObjectList.findObject( obj_item_array.get(i)->getUUID()) );
+ }
+ }
+
+ if (gest_count > 0)
+ {
+ for(i = 0; i < gest_count; ++i)
+ {
+ if ( gGestureManager.isGestureActive( gest_item_array.get(i)->getUUID()) )
+ {
+ gGestureManager.deactivateGesture( gest_item_array.get(i)->getUUID() );
+ gInventory.updateItem( gest_item_array.get(i) );
+ gInventory.notifyObservers();
+ }
+
+ }
+ }
+ }
+ delete category_id;
+ category_id = NULL;
+}
+
+BOOL LLWearableBridge::renameItem(const LLString& new_name)
+{
+ if( gAgent.isWearingItem( mUUID ) )
+ {
+ gAgent.setWearableName( mUUID, new_name );
+ }
+ return LLItemBridge::renameItem(new_name);
+}
+
+BOOL LLWearableBridge::isItemRemovable()
+{
+ if(gAgent.isWearingItem(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+LLFontGL::StyleFlags LLWearableBridge::getLabelStyle() const
+{
+ if( gAgent.isWearingItem( mUUID ) )
+ {
+ // llinfos << "BOLD" << llendl;
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+LLString LLWearableBridge::getLabelSuffix() const
+{
+ if( gAgent.isWearingItem( mUUID ) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (worn)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+LLViewerImage* LLWearableBridge::getIcon() const
+{
+ return get_item_icon(mAssetType, mInvType, mWearableType);
+}
+
+// virtual
+void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("wear" == action)
+ {
+ wearOnAvatar();
+ }
+ else if ("edit" == action)
+ {
+ editOnAvatar();
+ return;
+ }
+ else if ("take_off" == action)
+ {
+ if(gAgent.isWearingItem(mUUID))
+ {
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ gWearableList.getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new LLUUID(mUUID));
+ }
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLWearableBridge::openItem()
+{
+ if( isInTrash() )
+ {
+ gViewerWindow->alertXml("CannotWearTrash");
+ }
+ else if(isAgentInventory())
+ {
+ if( !gAgent.isWearingItem( mUUID ) )
+ {
+ wearOnAvatar();
+ }
+ }
+ else
+ {
+ // must be in the inventory library. copy it to our inventory
+ // and put it on right away.
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->isComplete())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else if(item)
+ {
+ // *FIX: Could in theory fetch and then do the operation above.
+ gViewerWindow->alertXml("CannotWearInfoNotComplete");
+ }
+ }
+}
+
+void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLWearableBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ BOOL no_open = ((flags & SUPPRESS_OPEN_ITEM) == SUPPRESS_OPEN_ITEM);
+ if (!no_open)
+ {
+ items.push_back("Open");
+ }
+
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back("Wearable Separator");
+ items.push_back("Wearable Wear");
+ items.push_back("Wearable Edit");
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Wearable Edit");
+ }
+ /*menu.appendSeparator();
+ menu.append(new LLMenuItemCallGL("Wear",
+ LLWearableBridge::onWearOnAvatar,
+ LLWearableBridge::canWearOnAvatar,
+ (void*)this));
+ menu.append(new LLMenuItemCallGL("Edit",
+ LLWearableBridge::onEditOnAvatar,
+ LLWearableBridge::canEditOnAvatar,
+ (void*)this));*/
+
+ LLViewerInventoryItem* item = getItem();
+ if( item && (item->getType() == LLAssetType::AT_CLOTHING) )
+ {
+ items.push_back("Take Off");
+ /*menu.append(new LLMenuItemCallGL("Take Off",
+ LLWearableBridge::onRemoveFromAvatar,
+ LLWearableBridge::canRemoveFromAvatar,
+ (void*)this));*/
+ }
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+// Called from menus
+// static
+BOOL LLWearableBridge::canWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+ if(!self->isAgentInventory())
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
+ if(!item || !item->isComplete()) return FALSE;
+ }
+ return (!gAgent.isWearingItem(self->mUUID));
+}
+
+// Called from menus
+// static
+void LLWearableBridge::onWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ self->wearOnAvatar();
+}
+
+void LLWearableBridge::wearOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgent.areWearablesLoaded())
+ {
+ gViewerWindow->alertXml("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userdata )
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ LLViewerInventoryItem* item = NULL;
+ item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
+ if(item)
+ {
+ if(item->getAssetUUID() == wearable->getID())
+ {
+ //RN: after discussing with Brashears, I disabled this code
+ //Metadata should reside in the item, not the asset
+ //And this code does not handle failed asset uploads properly
+
+// if(!wearable->isMatchedToInventoryItem(item))
+// {
+// LLWearable* new_wearable = gWearableList.createWearableMatchedToInventoryItem( wearable, item );
+//
+// // Now that we have an asset that matches the
+// // item, update the item to point to the new
+// // asset.
+// item->setAssetUUID(new_wearable->getID());
+// item->updateAssetOnServer();
+// wearable = new_wearable;
+// }
+ gAgent.setWearable(item, wearable);
+ gInventory.notifyObservers();
+ //self->getFolderItem()->refreshFromRoot();
+ }
+ else
+ {
+ llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
+ }
+ }
+ }
+ delete item_id;
+}
+
+// static
+BOOL LLWearableBridge::canEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+
+ return (gAgent.isWearingItem(self->mUUID));
+}
+
+// static
+void LLWearableBridge::onEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(self)
+ {
+ self->editOnAvatar();
+ }
+}
+
+void LLWearableBridge::editOnAvatar()
+{
+ LLWearable* wearable = gAgent.getWearableFromWearableItem(mUUID);
+ if( wearable )
+ {
+ // Set the tab to the right wearable.
+ LLFloaterCustomize::setCurrentWearableType( wearable->getType() );
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() )
+ {
+ // Start Avatar Customization
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+ }
+}
+
+// static
+BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if( self && (LLAssetType::AT_BODYPART != self->mAssetType) )
+ {
+ return gAgent.isWearingItem( self->mUUID );
+ }
+ return FALSE;
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ if(gAgent.isWearingItem(self->mUUID))
+ {
+ LLViewerInventoryItem* item = self->getItem();
+ if (item)
+ {
+ gWearableList.getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ onRemoveFromAvatarArrived,
+ new LLUUID(self->mUUID));
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable,
+ void* userdata)
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ if( gAgent.isWearingItem( *item_id ) )
+ {
+ EWearableType type = wearable->getType();
+
+ if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR ) ) //&&
+ //!((gAgent.mAccess >= SIM_ACCESS_MATURE) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
+ {
+ gAgent.removeWearable( type );
+ }
+ }
+ }
+ delete item_id;
+}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
new file mode 100755
index 0000000000..11573741a2
--- /dev/null
+++ b/indra/newview/llinventorybridge.h
@@ -0,0 +1,579 @@
+/**
+ * @file llinventorybridge.h
+ * @brief Implementation of the Inventory-Folder-View-Bridge classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llfloaterproperties.h"
+#include "llwearable.h"
+#include "llviewercontrol.h"
+#include "llcallingcard.h"
+
+enum EInventoryIcon
+{
+ TEXTURE_ICON_NAME,
+ SOUND_ICON_NAME,
+ CALLINGCARD_ONLINE_ICON_NAME,
+ CALLINGCARD_OFFLINE_ICON_NAME,
+ LANDMARK_ICON_NAME,
+ LANDMARK_VISITED_ICON_NAME,
+ SCRIPT_ICON_NAME,
+ CLOTHING_ICON_NAME,
+ OBJECT_ICON_NAME,
+ NOTECARD_ICON_NAME,
+ BODYPART_ICON_NAME,
+ SNAPSHOT_ICON_NAME,
+
+ BODYPART_SHAPE_ICON_NAME,
+ BODYPART_SKIN_ICON_NAME,
+ BODYPART_HAIR_ICON_NAME,
+ BODYPART_EYES_ICON_NAME,
+ CLOTHING_SHIRT_ICON_NAME,
+ CLOTHING_PANTS_ICON_NAME,
+ CLOTHING_SHOES_ICON_NAME,
+ CLOTHING_SOCKS_ICON_NAME,
+ CLOTHING_JACKET_ICON_NAME,
+ CLOTHING_GLOVES_ICON_NAME,
+ CLOTHING_UNDERSHIRT_ICON_NAME,
+ CLOTHING_UNDERPANTS_ICON_NAME,
+ CLOTHING_SKIRT_ICON_NAME,
+
+ ANIMATION_ICON_NAME,
+ GESTURE_ICON_NAME,
+
+ ICON_NAME_COUNT
+};
+
+extern const char* ICON_NAME[ICON_NAME_COUNT];
+
+typedef std::pair<LLUUID, LLUUID> two_uuids_t;
+typedef std::list<two_uuids_t> two_uuids_list_t;
+typedef std::pair<LLUUID, two_uuids_list_t> uuid_move_list_t;
+
+struct LLMoveInv
+{
+ LLUUID mObjectID;
+ LLUUID mCategoryID;
+ two_uuids_list_t mMoveList;
+ void (*mCallback)(S32, void*);
+ void* mUserData;
+};
+
+struct LLAttachmentRezAction
+{
+ LLUUID mItemID;
+ S32 mAttachPt;
+};
+
+
+//helper functions
+class LLShowProps
+{
+public:
+
+ static void showProperties(const LLUUID& uuid)
+ {
+ if(!LLFloaterProperties::show(uuid, LLUUID::null))
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PropertiesRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ LLFloaterProperties* floater;
+ floater = new LLFloaterProperties("item properties",
+ rect,
+ "Inventory Item Properties",
+ uuid,
+ LLUUID::null);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(floater, FALSE);
+ }
+ }
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryPanelObserver
+//
+// Bridge to support knowing when the inventory has changed.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryPanelObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryPanelObserver(LLInventoryPanel* ip) : mIP(ip) {}
+ virtual ~LLInventoryPanelObserver() {}
+ virtual void changed(U32 mask) { mIP->modelChanged(mask); }
+protected:
+ LLInventoryPanel* mIP;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInvFVBridge (& it's derived classes)
+//
+// Short for Inventory-Folder-View-Bridge. This is an
+// implementation class to be able to view inventory items.
+//
+// You'll want to call LLInvItemFVELister::createBridge() to actually create
+// an instance of this class. This helps encapsulate the
+// funcationality a bit. (except for folders, you can create those
+// manually...)
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInvFVBridge : public LLFolderViewEventListener
+{
+public:
+ // This method is a convenience function which creates the correct
+ // type of bridge based on some basic information
+ static LLInvFVBridge* createBridge(LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags = 0x00);
+ virtual ~LLInvFVBridge() {}
+
+ virtual const LLUUID& getUUID() const { return mUUID; }
+
+ virtual const LLString& getPrefix() { return LLString::null; }
+ virtual void restoreItem() {}
+
+ // LLFolderViewEventListener functions
+ virtual const LLString& getName() const;
+ virtual const LLString& getDisplayName() const;
+ virtual PermissionMask getPermissionMask() const;
+ virtual U32 getCreationDate() const;
+ virtual LLFontGL::StyleFlags getLabelStyle() const
+ {
+ return LLFontGL::NORMAL;
+ }
+ virtual LLString getLabelSuffix() const { return LLString::null; }
+ virtual void openItem() {}
+ virtual void previewItem() {openItem();}
+ virtual void showProperties();
+ virtual BOOL isItemRenameable() const { return TRUE; }
+ //virtual BOOL renameItem(const LLString& new_name) {}
+ virtual BOOL isItemRemovable();
+ virtual BOOL isItemMovable();
+ //virtual BOOL removeItem() = 0;
+ virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch);
+ virtual void move(LLFolderViewEventListener* new_parent_bridge) {}
+ virtual BOOL isItemCopyable() const { return FALSE; }
+ virtual BOOL copyToClipboard() const { return FALSE; }
+ virtual void cutToClipboard() {}
+ virtual BOOL isClipboardPasteable() const;
+ virtual void pasteFromClipboard() {}
+ void getClipboardEntries(bool show_asset_id, std::vector<LLString> &items,
+ std::vector<LLString> &disabled_items, U32 flags);
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id);
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data) { return FALSE; }
+ virtual LLInventoryType::EType getInventoryType() const { return mInvType; }
+
+ // LLInvFVBridge functionality
+ virtual void clearDisplayName() {}
+
+protected:
+ LLInvFVBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ mInventoryPanel(inventory), mUUID(uuid) {}
+
+ LLInventoryObject* getInventoryObject() const;
+ BOOL isInTrash() const;
+ // return true if the item is in agent inventory. if false, it
+ // must be lost or in the inventory library.
+ BOOL isAgentInventory() const;
+ virtual BOOL isItemPermissive() const;
+ static void changeItemParent(LLInventoryModel* model,
+ LLViewerInventoryItem* item,
+ const LLUUID& new_parent,
+ BOOL restamp);
+ static void changeCategoryParent(LLInventoryModel* model,
+ LLViewerInventoryCategory* item,
+ const LLUUID& new_parent,
+ BOOL restamp);
+ void removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch);
+
+protected:
+ LLInventoryPanel* mInventoryPanel;
+ LLUUID mUUID; // item id
+ LLInventoryType::EType mInvType;
+};
+
+
+class LLItemBridge : public LLInvFVBridge
+{
+public:
+ LLItemBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLInvFVBridge(inventory, uuid) {}
+
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+
+ virtual void selectItem();
+ virtual void restoreItem();
+
+ virtual LLViewerImage* getIcon() const;
+ virtual const LLString& getDisplayName() const;
+ virtual LLString getLabelSuffix() const;
+ virtual PermissionMask getPermissionMask() const;
+ virtual U32 getCreationDate() const;
+ virtual BOOL isItemRenameable() const;
+ virtual BOOL renameItem(const LLString& new_name);
+ virtual BOOL removeItem();
+ virtual BOOL isItemCopyable() const;
+ virtual BOOL copyToClipboard() const;
+ virtual BOOL hasChildren() const { return FALSE; }
+ virtual BOOL isUpToDate() const { return TRUE; }
+
+ // override for LLInvFVBridge
+ virtual void clearDisplayName() { mDisplayName.clear(); }
+
+ LLViewerInventoryItem* getItem() const;
+
+protected:
+ virtual BOOL isItemPermissive() const;
+ static void buildDisplayName(LLInventoryItem* item, LLString& name);
+ mutable LLString mDisplayName;
+};
+
+
+class LLFolderBridge : public LLInvFVBridge
+{
+ friend class LLInvFVBridge;
+public:
+ BOOL dragItemIntoFolder(LLInventoryItem* inv_item,
+ BOOL drop);
+ BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category,
+ BOOL drop);
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual BOOL isItemRenameable() const;
+ virtual void selectItem();
+ virtual void restoreItem();
+
+
+ virtual LLViewerImage* getIcon() const;
+ virtual BOOL renameItem(const LLString& new_name);
+ virtual BOOL removeItem();
+ virtual BOOL isClipboardPasteable() const;
+ virtual void pasteFromClipboard();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual BOOL hasChildren() const;
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data);
+
+ virtual BOOL isItemRemovable();
+ virtual BOOL isItemMovable();
+ virtual BOOL isUpToDate() const;
+
+ static void createWearable(LLFolderBridge* bridge, EWearableType type);
+ static void createWearable(LLUUID parent_folder_id, EWearableType type);
+
+ LLViewerInventoryCategory* getCategory() const;
+
+protected:
+ LLFolderBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLInvFVBridge(inventory, uuid) {}
+
+ // menu callbacks
+ static void pasteClipboard(void* user_data);
+ static void createNewCategory(void* user_data);
+
+ static void createNewShirt(void* user_data);
+ static void createNewPants(void* user_data);
+ static void createNewShoes(void* user_data);
+ static void createNewSocks(void* user_data);
+ static void createNewJacket(void* user_data);
+ static void createNewSkirt(void* user_data);
+ static void createNewGloves(void* user_data);
+ static void createNewUndershirt(void* user_data);
+ static void createNewUnderpants(void* user_data);
+ static void createNewShape(void* user_data);
+ static void createNewSkin(void* user_data);
+ static void createNewHair(void* user_data);
+ static void createNewEyes(void* user_data);
+
+ BOOL checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& typeToCheck);
+
+ void beginIMSession(BOOL only_online);
+ void modifyOutfit(BOOL append);
+public:
+ static LLFolderBridge* sSelf;
+ static void staticFolderOptionsMenu();
+ void folderOptionsMenu();
+private:
+ BOOL mCallingCards;
+ BOOL mWearables;
+ LLMenuGL* mMenu;
+ std::vector<LLString> mItems;
+ std::vector<LLString> mDisabledItems;
+};
+
+// DEPRECATED
+class LLScriptBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ LLViewerImage* getIcon() const;
+
+protected:
+ LLScriptBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid) {}
+};
+
+
+class LLTextureBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLTextureBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type) :
+ LLItemBridge(inventory, uuid), mInvType(type) {}
+ static LLString sPrefix;
+ LLInventoryType::EType mInvType;
+};
+
+class LLSoundBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+ virtual void previewItem();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ static void openSoundPreview(void*);
+
+protected:
+ LLSoundBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLItemBridge(inventory, uuid) {}
+ static LLString sPrefix;
+};
+
+class LLLandmarkBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLLandmarkBridge(LLInventoryPanel* inventory, const LLUUID& uuid, U32 flags = 0x00) :
+ LLItemBridge(inventory, uuid)
+ {
+ mVisited = FALSE;
+ if (flags & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ mVisited = TRUE;
+ }
+ }
+
+protected:
+ static LLString sPrefix;
+ BOOL mVisited;
+};
+
+class LLCallingCardBridge;
+
+class LLCallingCardObserver : public LLFriendObserver
+{
+public:
+ LLCallingCardObserver(LLCallingCardBridge* bridge) : mBridgep(bridge) {}
+ virtual ~LLCallingCardObserver() { mBridgep = NULL; }
+ virtual void changed(U32 mask);
+
+protected:
+ LLCallingCardBridge* mBridgep;
+};
+
+class LLCallingCardBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLString getLabelSuffix() const;
+ //virtual const LLString& getDisplayName() const;
+ virtual LLViewerImage* getIcon() const;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ //virtual void renameItem(const LLString& new_name);
+ //virtual BOOL removeItem();
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data);
+ void refreshFolderViewItem();
+
+protected:
+ LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid );
+ ~LLCallingCardBridge();
+
+protected:
+ static LLString sPrefix;
+ LLCallingCardObserver* mObserver;
+};
+
+
+class LLNotecardBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLNotecardBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+class LLGestureBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+
+ // Only suffix for gesture items, not task items, because only
+ // gestures in your inventory can be active.
+ virtual LLFontGL::StyleFlags getLabelStyle() const;
+ virtual LLString getLabelSuffix() const;
+
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual BOOL removeItem();
+
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+protected:
+ LLGestureBridge(LLInventoryPanel* inventory, const LLUUID& uuid)
+ : LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+
+class LLAnimationBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLAnimationBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+
+class LLObjectBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual LLFontGL::StyleFlags getLabelStyle() const;
+ virtual LLString getLabelSuffix() const;
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual BOOL isItemRemovable();
+ virtual BOOL renameItem(const LLString& new_name);
+
+protected:
+ LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) :
+ LLItemBridge(inventory, uuid), mInvType(type)
+ {
+ mAttachPt = (flags & 0xff); // low bye of inventory flags
+ }
+
+protected:
+ static LLString sPrefix;
+ static LLUUID sContextMenuItemID; // Only valid while the context menu is open.
+ LLInventoryType::EType mInvType;
+ U32 mAttachPt;
+
+};
+
+
+class LLLSLTextBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLLSLTextBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+
+class LLWearableBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual LLViewerImage* getIcon() const;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual LLFontGL::StyleFlags getLabelStyle() const;
+ virtual LLString getLabelSuffix() const;
+ virtual BOOL isItemRemovable();
+ virtual BOOL renameItem(const LLString& new_name);
+
+ static void onWearOnAvatar( void* userdata ); // Access to wearOnAvatar() from menu
+ static BOOL canWearOnAvatar( void* userdata );
+ static void onWearOnAvatarArrived( LLWearable* wearable, void* userdata );
+ void wearOnAvatar();
+
+ static BOOL canEditOnAvatar( void* userdata ); // Access to editOnAvatar() from menu
+ static void onEditOnAvatar( void* userdata );
+ void editOnAvatar();
+
+ static BOOL canRemoveFromAvatar( void* userdata );
+ static void onRemoveFromAvatar( void* userdata );
+ static void onRemoveFromAvatarArrived( LLWearable* wearable, void* userdata );
+
+protected:
+ LLWearableBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLAssetType::EType asset_type, LLInventoryType::EType inv_type, EWearableType wearable_type) :
+ LLItemBridge(inventory, uuid),
+ mAssetType( asset_type ),
+ mInvType(inv_type),
+ mWearableType(wearable_type)
+ {}
+
+protected:
+ LLAssetType::EType mAssetType;
+ LLInventoryType::EType mInvType;
+ EWearableType mWearableType;
+};
diff --git a/indra/newview/llinventoryclipboard.cpp b/indra/newview/llinventoryclipboard.cpp
new file mode 100644
index 0000000000..8008edd2ad
--- /dev/null
+++ b/indra/newview/llinventoryclipboard.cpp
@@ -0,0 +1,80 @@
+/**
+ * @file llinventoryclipboard.cpp
+ * @brief LLInventoryClipboard class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llinventoryclipboard.h"
+
+
+LLInventoryClipboard LLInventoryClipboard::sInstance;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryClipboard
+///----------------------------------------------------------------------------
+
+LLInventoryClipboard::LLInventoryClipboard()
+{
+}
+
+LLInventoryClipboard::~LLInventoryClipboard()
+{
+ reset();
+}
+
+void LLInventoryClipboard::add(const LLUUID& object)
+{
+ mObjects.put(object);
+}
+
+// this stores a single inventory object
+void LLInventoryClipboard::store(const LLUUID& object)
+{
+ reset();
+ mObjects.put(object);
+}
+
+void LLInventoryClipboard::store(const LLDynamicArray<LLUUID>& inv_objects)
+{
+ reset();
+ S32 count = inv_objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ mObjects.put(inv_objects[i]);
+ }
+}
+
+void LLInventoryClipboard::retrieve(LLDynamicArray<LLUUID>& inv_objects) const
+{
+ inv_objects.reset();
+ S32 count = mObjects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ inv_objects.put(mObjects[i]);
+ }
+}
+
+void LLInventoryClipboard::reset()
+{
+ mObjects.reset();
+}
+
+// returns true if the clipboard has something pasteable in it.
+BOOL LLInventoryClipboard::hasContents() const
+{
+ return (mObjects.count() > 0);
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/llinventoryclipboard.h b/indra/newview/llinventoryclipboard.h
new file mode 100644
index 0000000000..4250822189
--- /dev/null
+++ b/indra/newview/llinventoryclipboard.h
@@ -0,0 +1,65 @@
+/**
+ * @file llinventoryclipboard.h
+ * @brief LLInventoryClipboard class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINVENTORYCLIPBOARD_H
+#define LL_LLINVENTORYCLIPBOARD_H
+
+#include "lldarray.h"
+#include "lluuid.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryClipboard
+//
+// This class is used to cut/copy/paste inventory items around the
+// world. This class is accessed through a singleton (only one
+// inventory clipboard for now) which can be referenced using the
+// instance() method.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryClipboard
+{
+public:
+ // calling this before main() is undefined
+ static LLInventoryClipboard& instance() { return sInstance; }
+
+ // this method adds to the current list.
+ void add(const LLUUID& object);
+
+ // this stores a single inventory object
+ void store(const LLUUID& object);
+
+ // this method stores an array of objects
+ void store(const LLDynamicArray<LLUUID>& inventory_objects);
+
+ // this method gets the objects in the clipboard by copying them
+ // into the array provided.
+ void retrieve(LLDynamicArray<LLUUID>& inventory_objects) const;
+
+ // this method empties out the clipboard
+ void reset();
+
+ // returns true if the clipboard has something pasteable in it.
+ BOOL hasContents() const;
+
+protected:
+ static LLInventoryClipboard sInstance;
+
+ LLDynamicArray<LLUUID> mObjects;
+
+public:
+ // please don't actually call these
+ LLInventoryClipboard();
+ ~LLInventoryClipboard();
+private:
+ // please don't implement these
+ LLInventoryClipboard(const LLInventoryClipboard&);
+ LLInventoryClipboard& operator=(const LLInventoryClipboard&);
+};
+
+
+#endif // LL_LLINVENTORYCLIPBOARD_H
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
new file mode 100644
index 0000000000..509f040c34
--- /dev/null
+++ b/indra/newview/llinventorymodel.cpp
@@ -0,0 +1,3493 @@
+/**
+ * @file llinventorymodel.cpp
+ * @brief Implementation of the inventory model used to track agent inventory.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llinventorymodel.h"
+
+#include "llassetstorage.h"
+#include "llcrc.h"
+#include "lldir.h"
+#include "llsys.h"
+#include "llxfermanager.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llfloater.h"
+#include "llfocusmgr.h"
+#include "llinventoryview.h"
+#include "llviewerinventory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "lldbstrings.h"
+#include "llviewerstats.h"
+#include "llmutelist.h"
+#include "llnotify.h"
+#include "llcallbacklist.h"
+#include <deque>
+
+//#define DIFF_INVENTORY_FILES
+#ifdef DIFF_INVENTORY_FILES
+#include "process.h"
+#endif
+
+BOOL LLInventoryModel::sBackgroundFetchActive = FALSE;
+BOOL LLInventoryModel::sAllFoldersFetched = FALSE;
+BOOL LLInventoryModel::sFullFetchStarted = FALSE;
+S32 LLInventoryModel::sNumFetchRetries = 0;
+F32 LLInventoryModel::sMinTimeBetweenFetches = 0.3f;
+F32 LLInventoryModel::sMaxTimeBetweenFetches = 10.f;
+BOOL LLInventoryModel::sTimelyFetchPending = FALSE;
+LLFrameTimer LLInventoryModel::sFetchTimer;
+
+// RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue
+static std::deque<LLUUID> sFetchQueue;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+//BOOL decompress_file(const char* src_filename, const char* dst_filename);
+const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f;
+const S32 MAX_FETCH_RETRIES = 5;
+const char CACHE_FORMAT_STRING[] = "%s.inv";
+const char* NEW_CATEGORY_NAME = "New Folder";
+const char* NEW_CATEGORY_NAMES[LLAssetType::AT_COUNT] =
+{
+ "Textures", // AT_TEXTURE
+ "Sounds", // AT_SOUND
+ "Calling Cards", // AT_CALLINGCARD
+ "Landmarks", // AT_LANDMARK
+ "Scripts", // AT_SCRIPT (deprecated?)
+ "Clothing", // AT_CLOTHING
+ "Objects", // AT_OBJECT
+ "Notecards", // AT_NOTECARD
+ "New Folder", // AT_CATEGORY
+ "Inventory", // AT_ROOT_CATEGORY
+ "Scripts", // AT_LSL_TEXT
+ "Scripts", // AT_LSL_BYTECODE
+ "Uncompressed Images", // AT_TEXTURE_TGA
+ "Body Parts", // AT_BODYPART
+ "Trash", // AT_TRASH
+ "Photo Album", // AT_SNAPSHOT_CATEGORY
+ "Lost And Found", // AT_LOST_AND_FOUND
+ "Uncompressed Sounds", // AT_SOUND_WAV
+ "Uncompressed Images", // AT_IMAGE_TGA
+ "Uncompressed Images", // AT_IMAGE_JPEG
+ "Animations", // AT_ANIMATION
+ "Gestures", // AT_GESTURE
+};
+
+struct InventoryIDPtrLess
+{
+ bool operator()(const LLViewerInventoryCategory* i1, const LLViewerInventoryCategory* i2) const
+ {
+ return (i1->getUUID() < i2->getUUID());
+ }
+};
+
+class LLCanCache : public LLInventoryCollectFunctor
+{
+public:
+ LLCanCache(LLInventoryModel* model) : mModel(model) {}
+ virtual ~LLCanCache() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+protected:
+ LLInventoryModel* mModel;
+ std::set<LLUUID> mCachedCatIDs;
+};
+
+bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ bool rv = false;
+ if(item)
+ {
+ if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end())
+ {
+ rv = true;
+ }
+ }
+ else if(cat)
+ {
+ // HACK: downcast
+ LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat;
+ if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ S32 descendents_server = c->getDescendentCount();
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ mModel->getDirectDescendentsOf(
+ c->getUUID(),
+ cats,
+ items);
+ S32 descendents_actual = 0;
+ if(cats && items)
+ {
+ descendents_actual = cats->count() + items->count();
+ }
+ if(descendents_server == descendents_actual)
+ {
+ mCachedCatIDs.insert(c->getUUID());
+ rv = true;
+ }
+ }
+ }
+ return rv;
+}
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryModel
+///----------------------------------------------------------------------------
+
+// global for the agent inventory.
+LLInventoryModel gInventory;
+
+// Default constructor
+LLInventoryModel::LLInventoryModel() :
+ mModifyMask(LLInventoryObserver::ALL),
+ mLastItem(NULL)
+{
+}
+
+// Destroys the object
+LLInventoryModel::~LLInventoryModel()
+{
+ empty();
+ for (observer_list_t::iterator iter = mObservers.begin();
+ iter != mObservers.end(); ++iter)
+ {
+ delete *iter;
+ }
+}
+
+// This is a convenience function to check if one object has a parent
+// chain up to the category specified by UUID.
+BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
+ const LLUUID& cat_id)
+{
+ LLInventoryObject* obj = getObject(obj_id);
+ while(obj)
+ {
+ const LLUUID& parent_id = obj->getParentUUID();
+ if( parent_id.isNull() )
+ {
+ return FALSE;
+ }
+ if(parent_id == cat_id)
+ {
+ return TRUE;
+ }
+ // Since we're scanning up the parents, we only need to check
+ // in the category list.
+ obj = getCategory(parent_id);
+ }
+ return FALSE;
+}
+
+// Get the object by id. Returns NULL if not found.
+LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const
+{
+ LLViewerInventoryCategory* cat = getCategory(id);
+ if (cat)
+ {
+ return cat;
+ }
+ LLViewerInventoryItem* item = getItem(id);
+ if (item)
+ {
+ return item;
+ }
+ return NULL;
+}
+
+// Get the item by id. Returns NULL if not found.
+LLViewerInventoryItem* LLInventoryModel::getItem(const LLUUID& id) const
+{
+ LLViewerInventoryItem* item = NULL;
+ if(mLastItem.notNull() && mLastItem->getUUID() == id)
+ {
+ item = mLastItem;
+ }
+ else
+ {
+ item_map_t::const_iterator iter = mItemMap.find(id);
+ if (iter != mItemMap.end())
+ {
+ item = iter->second;
+ mLastItem = item;
+ }
+ }
+ return item;
+}
+
+// Get the category by id. Returns NULL if not found
+LLViewerInventoryCategory* LLInventoryModel::getCategory(const LLUUID& id) const
+{
+ LLViewerInventoryCategory* category = NULL;
+ cat_map_t::const_iterator iter = mCategoryMap.find(id);
+ if (iter != mCategoryMap.end())
+ {
+ category = iter->second;
+ }
+ return category;
+}
+
+S32 LLInventoryModel::getItemCount() const
+{
+ return mItemMap.size();
+}
+
+S32 LLInventoryModel::getCategoryCount() const
+{
+ return mCategoryMap.size();
+}
+
+// Return the direct descendents of the id provided. The array
+// provided points straight into the guts of this object, and
+// should only be used for read operations, since modifications
+// may invalidate the internal state of the inventory. Set passed
+// in values to NULL if the call fails.
+void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
+ cat_array_t*& categories,
+ item_array_t*& items) const
+{
+ categories = get_ptr_in_map(mParentChildCategoryTree, cat_id);
+ items = get_ptr_in_map(mParentChildItemTree, cat_id);
+}
+
+// findCategoryUUIDForType() returns the uuid of the category that
+// specifies 'type' as what it defaults to containing. The category is
+// not necessarily only for that type. *NOTE: This will create a new
+// inventory category on the fly if one does not exist.
+LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t)
+{
+ LLUUID rv = findCatUUID(t);
+ if(rv.isNull())
+ {
+ rv = gAgent.getInventoryRootID();
+ if(rv.notNull())
+ {
+ rv = createNewCategory(rv, t, NULL);
+ }
+ }
+ return rv;
+}
+
+// Internal method which looks for a category with the specified
+// preferred type. Returns LLUUID::null if not found.
+LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type)
+{
+ LLUUID root_id = gAgent.getInventoryRootID();
+ if(LLAssetType::AT_CATEGORY == preferred_type)
+ {
+ return root_id;
+ }
+ if(root_id.notNull())
+ {
+ cat_array_t* cats = NULL;
+ cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
+ if(cats)
+ {
+ S32 count = cats->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(cats->get(i)->getPreferredType() == preferred_type)
+ {
+ return cats->get(i)->getUUID();
+ }
+ }
+ }
+ }
+ return LLUUID::null;
+}
+
+// Convenience function to create a new category. You could call
+// updateCatgory() with a newly generated UUID category, but this
+// version will take care of details like what the name should be
+// based on preferred type. Returns the UUID of the new category.
+LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
+ LLAssetType::EType preferred_type,
+ const LLString& pname)
+{
+ LLUUID id;
+ id.generate();
+ LLString name = pname;
+ if(!pname.empty())
+ {
+ name.assign(pname);
+ }
+ else if((preferred_type >= LLAssetType::AT_TEXTURE) &&
+ (preferred_type < LLAssetType::AT_COUNT))
+ {
+ name.assign(NEW_CATEGORY_NAMES[preferred_type]);
+ }
+ else
+ {
+ name.assign(NEW_CATEGORY_NAME);
+ }
+
+ // Add the category to the internal representation
+ LLPointer<LLViewerInventoryCategory> cat =
+ new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
+ cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
+ cat->setDescendentCount(0);
+ LLCategoryUpdate update(cat->getParentUUID(), 1);
+ accountForUpdate(update);
+ updateCategory(cat);
+
+ // Create the category on the server. We do this to prevent people
+ // from munging their protected folders.
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("CreateInventoryFolder");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("FolderData");
+ cat->packMessage(msg);
+ gAgent.sendReliableMessage();
+
+ // return the folder id of the newly created folder
+ return id;
+}
+
+// Starting with the object specified, add it's descendents to the
+// array provided, but do not add the inventory object specified by
+// id. There is no guaranteed order. Neither array will be erased
+// before adding objects to it. Do not store a copy of the pointers
+// collected - use them, and collect them again later if you need to
+// reference the same objects.
+
+class LLAlwaysCollect : public LLInventoryCollectFunctor
+{
+public:
+ virtual ~LLAlwaysCollect() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ return TRUE;
+ }
+};
+
+void LLInventoryModel::collectDescendents(const LLUUID& id,
+ cat_array_t& cats,
+ item_array_t& items,
+ BOOL include_trash)
+{
+ LLAlwaysCollect always;
+ collectDescendentsIf(id, cats, items, include_trash, always);
+}
+
+void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
+ cat_array_t& cats,
+ item_array_t& items,
+ BOOL include_trash,
+ LLInventoryCollectFunctor& add)
+{
+ // Start with categories
+ if(!include_trash)
+ {
+ LLUUID trash_id(findCatUUID(LLAssetType::AT_TRASH));
+ if(trash_id.notNull() && (trash_id == id))
+ return;
+ }
+ cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
+ if(cat_array)
+ {
+ S32 count = cat_array->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = cat_array->get(i);
+ if(add(cat,NULL))
+ {
+ cats.put(cat);
+ }
+ collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add);
+ }
+ }
+
+ // Move onto items
+ LLViewerInventoryItem* item = NULL;
+ item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
+ if(item_array)
+ {
+ S32 count = item_array->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ item = item_array->get(i);
+ if(add(NULL, item))
+ {
+ items.put(item);
+ }
+ }
+ }
+}
+
+// Generates a string containing the path to the item specified by
+// item_id.
+void LLInventoryModel::appendPath(const LLUUID& id, LLString& path)
+{
+ LLString temp;
+ LLInventoryObject* obj = getObject(id);
+ LLUUID parent_id;
+ if(obj) parent_id = obj->getParentUUID();
+ LLString forward_slash("/");
+ while(obj)
+ {
+ obj = getCategory(parent_id);
+ if(obj)
+ {
+ temp.assign(forward_slash + obj->getName() + temp);
+ parent_id = obj->getParentUUID();
+ }
+ }
+ path.append(temp);
+}
+
+// Calling this method with an inventory item will either change an
+// existing item with a matching item_id, or will add the item to the
+// current inventory.
+U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
+{
+ U32 mask = LLInventoryObserver::NONE;
+ if(item->getUUID().isNull())
+ {
+ return mask;
+ }
+ LLViewerInventoryItem* old_item = getItem(item->getUUID());
+ if(old_item)
+ {
+ // We already have an old item, modify it's values
+ LLUUID old_parent_id = old_item->getParentUUID();
+ LLUUID new_parent_id = item->getParentUUID();
+ if(old_parent_id != new_parent_id)
+ {
+ // need to update the parent-child tree
+ item_array_t* item_array;
+ item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id);
+ if(item_array)
+ {
+ item_array->removeObj(old_item);
+ }
+ item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id);
+ if(item_array)
+ {
+ item_array->put(old_item);
+ }
+ mask |= LLInventoryObserver::STRUCTURE;
+ }
+ if(old_item->getName() != item->getName())
+ {
+ mask |= LLInventoryObserver::LABEL;
+ }
+ old_item->copy(item);
+ mask |= LLInventoryObserver::INTERNAL;
+ }
+ else
+ {
+ // Simply add this item
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ addItem(new_item);
+
+ if(item->getParentUUID().isNull())
+ {
+ LLUUID category_id = findCategoryUUIDForType(new_item->getType());
+ new_item->setParent(category_id);
+ item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id);
+ if( item_array )
+ {
+ // *FIX: bit of a hack to call update server from here...
+ new_item->updateServer(TRUE);
+ item_array->put(new_item);
+ }
+ else
+ {
+ llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl;
+ }
+ }
+ else
+ {
+ // *NOTE: The general scheme is that if every byte of the
+ // uuid is 0, except for the last one or two,the use the
+ // last two bytes of the parent id, and match that up
+ // against the type. For now, we're only worried about
+ // lost & found.
+ LLUUID parent_id = item->getParentUUID();
+ if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID)
+ {
+ parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ new_item->setParent(parent_id);
+ }
+ item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
+ if(item_array)
+ {
+ item_array->put(new_item);
+ }
+ else
+ {
+ // Whoops! No such parent, make one.
+ llinfos << "Lost item: " << new_item->getUUID() << " - "
+ << new_item->getName() << llendl;
+ parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ new_item->setParent(parent_id);
+ item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
+ if(item_array)
+ {
+ // *FIX: bit of a hack to call update server from
+ // here...
+ new_item->updateServer(TRUE);
+ item_array->put(new_item);
+ }
+ else
+ {
+ llwarns << "Lost and found Not there!!" << llendl;
+ }
+ }
+ }
+ mask |= LLInventoryObserver::ADD;
+ }
+ if(item->getType() == LLAssetType::AT_CALLINGCARD)
+ {
+ mask |= LLInventoryObserver::CALLING_CARD;
+ }
+ addChangedMask(mask, item->getUUID());
+ return mask;
+}
+
+// Calling this method with an inventory category will either change
+// an existing item with the matching id, or it will add the category.
+void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
+{
+ if(cat->getUUID().isNull())
+ {
+ return;
+ }
+ LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
+ if(old_cat)
+ {
+ // We already have an old category, modify it's values
+ U32 mask = LLInventoryObserver::NONE;
+ LLUUID old_parent_id = old_cat->getParentUUID();
+ LLUUID new_parent_id = cat->getParentUUID();
+ if(old_parent_id != new_parent_id)
+ {
+ // need to update the parent-child tree
+ cat_array_t* cat_array;
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, old_parent_id);
+ if(cat_array)
+ {
+ cat_array->removeObj(old_cat);
+ }
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, new_parent_id);
+ if(cat_array)
+ {
+ cat_array->put(old_cat);
+ }
+ mask |= LLInventoryObserver::STRUCTURE;
+ }
+ if(old_cat->getName() != cat->getName())
+ {
+ mask |= LLInventoryObserver::LABEL;
+ }
+ old_cat->copy(cat);
+ addChangedMask(mask, cat->getUUID());
+ }
+ else
+ {
+ // add this category
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat->getParentUUID());
+ new_cat->copy(cat);
+ addCategory(new_cat);
+
+ // make sure this category is correctly referenced by it's parent.
+ cat_array_t* cat_array;
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(cat_array)
+ {
+ cat_array->put(new_cat);
+ }
+
+ // make space in the tree for this category's children.
+ cat_array_t* catsp = new cat_array_t;
+ item_array_t* itemsp = new item_array_t;
+ mParentChildCategoryTree[new_cat->getUUID()] = catsp;
+ mParentChildItemTree[new_cat->getUUID()] = itemsp;
+ addChangedMask(LLInventoryObserver::ADD, cat->getUUID());
+ }
+}
+
+void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
+{
+ lldebugs << "LLInventoryModel::moveObject()" << llendl;
+ if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))
+ {
+ llwarns << "Could not move inventory object " << object_id << " to "
+ << cat_id << llendl;
+ return;
+ }
+ LLViewerInventoryCategory* cat = getCategory(object_id);
+ if(cat && (cat->getParentUUID() != cat_id))
+ {
+ cat_array_t* cat_array;
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(cat_array) cat_array->removeObj(cat);
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, cat_id);
+ cat->setParent(cat_id);
+ if(cat_array) cat_array->put(cat);
+ addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
+ return;
+ }
+ LLViewerInventoryItem* item = getItem(object_id);
+ if(item && (item->getParentUUID() != cat_id))
+ {
+ item_array_t* item_array;
+ item_array = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
+ if(item_array) item_array->removeObj(item);
+ item_array = get_ptr_in_map(mParentChildItemTree, cat_id);
+ item->setParent(cat_id);
+ if(item_array) item_array->put(item);
+ addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
+ return;
+ }
+}
+
+// Delete a particular inventory object by ID.
+void LLInventoryModel::deleteObject(const LLUUID& id)
+{
+ lldebugs << "LLInventoryModel::deleteObject()" << llendl;
+ LLPointer<LLInventoryObject> obj = getObject(id);
+ if(obj)
+ {
+ lldebugs << "Deleting inventory object " << id << llendl;
+ mLastItem = NULL;
+ LLUUID parent_id = obj->getParentUUID();
+ mCategoryMap.erase(id);
+ mItemMap.erase(id);
+ //mInventory.erase(id);
+ item_array_t* item_list = get_ptr_in_map(mParentChildItemTree, parent_id);
+ if(item_list)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
+ item_list->removeObj(item);
+ }
+ cat_array_t* cat_list = get_ptr_in_map(mParentChildCategoryTree, parent_id);
+ if(cat_list)
+ {
+ LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
+ cat_list->removeObj(cat);
+ }
+ item_list = get_ptr_in_map(mParentChildItemTree, id);
+ if(item_list)
+ {
+ delete item_list;
+ mParentChildItemTree.erase(id);
+ }
+ cat_list = get_ptr_in_map(mParentChildCategoryTree, id);
+ if(cat_list)
+ {
+ delete cat_list;
+ mParentChildCategoryTree.erase(id);
+ }
+ addChangedMask(LLInventoryObserver::REMOVE, id);
+ obj = NULL; // delete obj
+ }
+}
+
+// This is a method which collects the descendents of the id
+// provided. If the category is not found, no action is
+// taken. This method goes through the long winded process of
+// cancelling any calling cards, removing server representation of
+// folders, items, etc in a fairly efficient manner.
+void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
+{
+ EHasChildren children = categoryHasChildren(id);
+ if(children == CHILDREN_NO)
+ {
+ llinfos << "Not purging descendents of " << id << llendl;
+ return;
+ }
+ LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
+ if(cat.notNull())
+ {
+ // do the cache accounting
+ llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
+ << llendl;
+ S32 descendents = cat->getDescendentCount();
+ if(descendents > 0)
+ {
+ LLCategoryUpdate up(id, -descendents);
+ accountForUpdate(up);
+ }
+
+ // we know that descendent count is 0, aide since the
+ // accounting may actually not do an update, we should force
+ // it here.
+ cat->setDescendentCount(0);
+
+ // send it upstream
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("PurgeInventoryDescendents");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("FolderID", id);
+ gAgent.sendReliableMessage();
+
+ // unceremoniously remove anything we have locally stored.
+ cat_array_t categories;
+ item_array_t items;
+ collectDescendents(id,
+ categories,
+ items,
+ INCLUDE_TRASH);
+ S32 count = items.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ deleteObject(items.get(i)->getUUID());
+ }
+ count = categories.count();
+ for(i = 0; i < count; ++i)
+ {
+ deleteObject(categories.get(i)->getUUID());
+ }
+ }
+}
+
+void LLInventoryModel::deleteFromServer(LLDynamicArray<LLUUID>& category_ids,
+ LLDynamicArray<LLUUID>& item_ids)
+{
+ // Store off tre UUIDS of parents which are being deleted (thus no
+ // need to increment) and the parents which are being modified. We
+ // have to increment the version of the parent with each message
+ // sent upstream since the dataserver will increment each unique
+ // parent per update message.
+ std::set<LLUUID> ignore_parents;
+ update_map_t inc_parents;
+
+ S32 i;
+ S32 count = category_ids.count();
+ BOOL start_new_message = TRUE;
+ LLMessageSystem* msg = gMessageSystem;
+ LLPointer<LLViewerInventoryCategory> cat;
+ for(i = 0; i < count; i++)
+ {
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ LLUUID cat_id = category_ids.get(i);
+
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, cat_id);
+ cat = getCategory(cat_id);
+ ignore_parents.insert(cat_id);
+ addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, cat_id);
+ if(cat.notNull() && (ignore_parents.find(cat->getParentUUID())==ignore_parents.end()))
+ {
+ --inc_parents[cat->getParentUUID()];
+ }
+ if(msg->isSendFullFast(_PREHASH_FolderData))
+ {
+ start_new_message = TRUE;
+ msg->nextBlockFast(_PREHASH_ItemData);
+ msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
+ gAgent.sendReliableMessage();
+ accountForUpdate(inc_parents);
+ inc_parents.clear();
+ }
+ }
+
+ count = item_ids.count();
+ std::set<LLUUID>::iterator not_ignored = ignore_parents.end();
+ LLPointer<LLViewerInventoryItem> item;
+ if((0 == count) && (!start_new_message))
+ {
+ msg->nextBlockFast(_PREHASH_ItemData);
+ msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
+ }
+ for(i = 0; i < count; i++)
+ {
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, LLUUID::null);
+ }
+ LLUUID item_id = item_ids.get(i);
+ msg->nextBlockFast(_PREHASH_ItemData);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ item = getItem(item_id);
+ addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, item_id);
+ if(item.notNull() && (ignore_parents.find(item->getParentUUID()) == not_ignored))
+ {
+ --inc_parents[item->getParentUUID()];
+ }
+ if(msg->isSendFullFast(_PREHASH_ItemData))
+ {
+ start_new_message = TRUE;
+ gAgent.sendReliableMessage();
+ accountForUpdate(inc_parents);
+ inc_parents.clear();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ accountForUpdate(inc_parents);
+ }
+}
+
+// Add/remove an observer. If the observer is destroyed, be sure to
+// remove it.
+void LLInventoryModel::addObserver(LLInventoryObserver* observer)
+{
+ mObservers.insert(observer);
+}
+
+void LLInventoryModel::removeObserver(LLInventoryObserver* observer)
+{
+ mObservers.erase(observer);
+}
+
+// Call this method when it's time to update everyone on a new state,
+// by default, the inventory model will not update observers
+// automatically.
+void LLInventoryModel::notifyObservers()
+{
+ for (observer_list_t::iterator iter = mObservers.begin();
+ iter != mObservers.end(); )
+ {
+ LLInventoryObserver* observer = *iter;
+ observer->changed(mModifyMask);
+ // safe way to incrament since changed may delete entries! (@!##%@!@&*!)
+ iter = mObservers.upper_bound(observer);
+ }
+
+ mModifyMask = LLInventoryObserver::NONE;
+ mChangedItemIDs.clear();
+}
+
+// store flag for change
+// and id of object change applies to
+void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)
+{
+ mModifyMask |= mask;
+ if (referent.notNull())
+ {
+ mChangedItemIDs.insert(referent);
+ }
+}
+
+// This method to prepares a set of mock inventory which provides
+// minimal functionality before the actual arrival of inventory.
+/*
+void LLInventoryModel::mock(const LLUUID& root_id)
+{
+ llinfos << "LLInventoryModel::mock() " << root_id << llendl;
+ if(root_id.isNull())
+ {
+ llwarns << "Not a valid root id" << llendl;
+ return;
+ }
+ LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(
+ root_id,
+ LLUUID::null,
+ LLAssetType::AT_CATEGORY,
+ NEW_CATEGORY_NAMES[LLAssetType::AT_ROOT_CATEGORY],
+ gAgent.getID());
+ addCategory(cat);
+ gInventory.buildParentChildMap();
+}
+*/
+
+void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id)
+{
+ LLViewerInventoryCategory* cat = getCategory(folder_id);
+ if(!cat)
+ {
+ llwarns << "Asked to fetch descendents of non-existent folder: "
+ << folder_id << llendl;
+ return;
+ }
+ //S32 known_descendents = 0;
+ ///cat_array_t* categories = get_ptr_in_map(mParentChildCategoryTree, folder_id);
+ //item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id);
+ //if(categories)
+ //{
+ // known_descendents += categories->count();
+ //}
+ //if(items)
+ //{
+ // known_descendents += items->count();
+ //}
+ if(!cat->fetchDescendents())
+ {
+ //llinfos << "Not fetching descendents" << llendl;
+ }
+}
+
+// static
+bool LLInventoryModel::isEverythingFetched()
+{
+ return (sAllFoldersFetched ? true : false);
+}
+
+//static
+BOOL LLInventoryModel::backgroundFetchActive()
+{
+ return sBackgroundFetchActive;
+}
+
+//static
+void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
+{
+ if (!sAllFoldersFetched)
+ {
+ sBackgroundFetchActive = TRUE;
+ if (cat_id.isNull())
+ {
+ if (!sFullFetchStarted)
+ {
+ sFullFetchStarted = TRUE;
+ sFetchQueue.push_back(gInventoryLibraryRoot);
+ sFetchQueue.push_back(gAgent.getInventoryRootID());
+ gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
+ }
+ }
+ else
+ {
+ // specific folder requests go to front of queue
+ if (sFetchQueue.empty() || sFetchQueue.front() != cat_id)
+ {
+ sFetchQueue.push_front(cat_id);
+ gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
+ }
+ }
+ }
+}
+
+//static
+void LLInventoryModel::stopBackgroundFetch()
+{
+ if (sBackgroundFetchActive)
+ {
+ sBackgroundFetchActive = FALSE;
+ gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL);
+ }
+}
+
+//static
+void LLInventoryModel::backgroundFetch(void*)
+{
+ if (sBackgroundFetchActive)
+ {
+ // no more categories to fetch, stop fetch process
+ if (sFetchQueue.empty())
+ {
+ llinfos << "Inventory fetch completed" << llendl;
+ if (sFullFetchStarted)
+ {
+ sAllFoldersFetched = TRUE;
+ }
+ stopBackgroundFetch();
+ return;
+ }
+
+ F32 fast_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.1f);
+ F32 slow_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.5f);
+ if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() > slow_fetch_time)
+ {
+ // double timeouts on failure
+ sMinTimeBetweenFetches = llmin(sMinTimeBetweenFetches * 2.f, 10.f);
+ sMaxTimeBetweenFetches = llmin(sMaxTimeBetweenFetches * 2.f, 120.f);
+ llinfos << "Inventory fetch times grown to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
+ // fetch is no longer considered "timely" although we will wait for full time-out
+ sTimelyFetchPending = FALSE;
+ }
+
+ while(1)
+ {
+ if (sFetchQueue.empty())
+ {
+ break;
+ }
+
+ if(gDisconnected)
+ {
+ // just bail if we are disconnected.
+ break;
+ }
+
+ LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front());
+
+ // category has been deleted, remove from queue.
+ if (!cat)
+ {
+ sFetchQueue.pop_front();
+ continue;
+ }
+
+ if (sFetchTimer.getElapsedTimeF32() > sMinTimeBetweenFetches &&
+ LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
+ {
+ // category exists but has no children yet, fetch the descendants
+ // for now, just request every time and rely on retry timer to throttle
+ if (cat->fetchDescendents())
+ {
+ sFetchTimer.reset();
+ sTimelyFetchPending = TRUE;
+ }
+ else
+ {
+ // The catagory also tracks if it has expired and here it says it hasn't
+ // yet. Get out of here because nothing is going to happen until we
+ // update the timers.
+ break;
+ }
+ }
+ // do I have all my children?
+ else if (gInventory.isCategoryComplete(sFetchQueue.front()))
+ {
+ // finished with this category, remove from queue
+ sFetchQueue.pop_front();
+
+ // add all children to queue
+ parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID());
+ if (cat_it != gInventory.mParentChildCategoryTree.end())
+ {
+ cat_array_t* child_categories = cat_it->second;
+
+ for (S32 child_num = 0; child_num < child_categories->count(); child_num++)
+ {
+ sFetchQueue.push_back(child_categories->get(child_num)->getUUID());
+ }
+ }
+
+ // we received a response in less than the fast time
+ if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() < fast_fetch_time)
+ {
+ // shrink timeouts based on success
+ sMinTimeBetweenFetches = llmax(sMinTimeBetweenFetches * 0.8f, 0.3f);
+ sMaxTimeBetweenFetches = llmax(sMaxTimeBetweenFetches * 0.8f, 10.f);
+ //llinfos << "Inventory fetch times shrunk to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
+ }
+
+ sTimelyFetchPending = FALSE;
+ continue;
+ }
+ else if (sFetchTimer.getElapsedTimeF32() > sMaxTimeBetweenFetches)
+ {
+ // received first packet, but our num descendants does not match db's num descendants
+ // so try again later
+ LLUUID fetch_id = sFetchQueue.front();
+ sFetchQueue.pop_front();
+
+ if (sNumFetchRetries++ < MAX_FETCH_RETRIES)
+ {
+ // push on back of queue
+ sFetchQueue.push_back(fetch_id);
+ }
+ sTimelyFetchPending = FALSE;
+ sFetchTimer.reset();
+ break;
+ }
+
+ // not enough time has elapsed to do a new fetch
+ break;
+ }
+ }
+}
+
+void LLInventoryModel::cache(
+ const LLUUID& parent_folder_id,
+ const LLUUID& agent_id)
+{
+ lldebugs << "Caching " << parent_folder_id << " for " << agent_id
+ << llendl;
+ LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);
+ if(!root_cat) return;
+ cat_array_t categories;
+ categories.put(root_cat);
+ item_array_t items;
+
+ LLCanCache can_cache(this);
+ can_cache(root_cat, NULL);
+ collectDescendentsIf(
+ parent_folder_id,
+ categories,
+ items,
+ INCLUDE_TRASH,
+ can_cache);
+ char agent_id_str[UUID_STR_LENGTH];
+ char inventory_filename[LL_MAX_PATH];
+ agent_id.toString(agent_id_str);
+ std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_str));
+ snprintf(
+ inventory_filename,
+ LL_MAX_PATH,
+ CACHE_FORMAT_STRING,
+ path.c_str());
+ saveToFile(inventory_filename, categories, items);
+ std::string gzip_filename(inventory_filename);
+ gzip_filename.append(".gz");
+ if(gzip_file(inventory_filename, gzip_filename.c_str()))
+ {
+ lldebugs << "Successfully compressed " << inventory_filename << llendl;
+ LLFile::remove(inventory_filename);
+ }
+ else
+ {
+ llwarns << "Unable to compress " << inventory_filename << llendl;
+ }
+}
+
+
+void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
+{
+ //llinfos << "LLInventoryModel::addCategory()" << llendl;
+ if(category)
+ {
+ // Insert category uniquely into the map
+ mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
+ //mInventory[category->getUUID()] = category;
+ }
+}
+
+void LLInventoryModel::addItem(LLViewerInventoryItem* item)
+{
+ //llinfos << "LLInventoryModel::addItem()" << llendl;
+ if(item)
+ {
+ mItemMap[item->getUUID()] = item;
+ //mInventory[item->getUUID()] = item;
+ }
+}
+
+// Empty the entire contents
+void LLInventoryModel::empty()
+{
+// llinfos << "LLInventoryModel::empty()" << llendl;
+ std::for_each(
+ mParentChildCategoryTree.begin(),
+ mParentChildCategoryTree.end(),
+ DeletePairedPointer());
+ mParentChildCategoryTree.clear();
+ std::for_each(
+ mParentChildItemTree.begin(),
+ mParentChildItemTree.end(),
+ DeletePairedPointer());
+ mParentChildItemTree.clear();
+ mCategoryMap.clear(); // remove all references (should delete entries)
+ mItemMap.clear(); // remove all references (should delete entries)
+ mLastItem = NULL;
+ //mInventory.clear();
+}
+
+void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update)
+{
+ LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
+ if(cat)
+ {
+ bool accounted = false;
+ S32 version = cat->getVersion();
+ if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ S32 descendents_server = cat->getDescendentCount();
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ getDirectDescendentsOf(update.mCategoryID, cats, items);
+ S32 descendents_actual = 0;
+ if(cats && items)
+ {
+ descendents_actual = cats->count() + items->count();
+ }
+ if(descendents_server == descendents_actual)
+ {
+ accounted = true;
+ descendents_actual += update.mDescendentDelta;
+ cat->setDescendentCount(descendents_actual);
+ cat->setVersion(++version);
+ llinfos << "accounted: '" << cat->getName() << "' "
+ << version << " with " << descendents_actual
+ << " descendents." << llendl;
+ }
+ }
+ if(!accounted)
+ {
+ lldebugs << "No accounting for: '" << cat->getName() << "' "
+ << version << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "No category found for update " << update.mCategoryID
+ << llendl;
+ }
+}
+
+void LLInventoryModel::accountForUpdate(
+ const LLInventoryModel::update_list_t& update)
+{
+ update_list_t::const_iterator it = update.begin();
+ update_list_t::const_iterator end = update.end();
+ for(; it != end; ++it)
+ {
+ accountForUpdate(*it);
+ }
+}
+
+void LLInventoryModel::accountForUpdate(
+ const LLInventoryModel::update_map_t& update)
+{
+ LLCategoryUpdate up;
+ update_map_t::const_iterator it = update.begin();
+ update_map_t::const_iterator end = update.end();
+ for(; it != end; ++it)
+ {
+ up.mCategoryID = (*it).first;
+ up.mDescendentDelta = (*it).second.mValue;
+ accountForUpdate(up);
+ }
+}
+
+
+/*
+void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id)
+{
+ LLViewerInventoryCategory* cat = getCategory(category_id);
+ if(cat)
+ {
+ S32 version = cat->getVersion();
+ if(LLViewerInventoryCategory::VERSION_UNKNOWN != version)
+ {
+ cat->setVersion(version + 1);
+ llinfos << "IncrementVersion: " << cat->getName() << " "
+ << cat->getVersion() << llendl;
+ }
+ else
+ {
+ llinfos << "Attempt to increment version when unknown: "
+ << category_id << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Attempt to increment category: " << category_id << llendl;
+ }
+}
+void LLInventoryModel::incrementCategorySetVersion(
+ const std::set<LLUUID>& categories)
+{
+ if(!categories.empty())
+ {
+ std::set<LLUUID>::const_iterator it = categories.begin();
+ std::set<LLUUID>::const_iterator end = categories.end();
+ for(; it != end; ++it)
+ {
+ incrementCategoryVersion(*it);
+ }
+ }
+}
+*/
+
+
+LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
+ const LLUUID& cat_id) const
+{
+ LLViewerInventoryCategory* cat = getCategory(cat_id);
+ if(!cat) return CHILDREN_NO;
+ if(cat->getDescendentCount() > 0)
+ {
+ return CHILDREN_YES;
+ }
+ if(cat->getDescendentCount() == 0)
+ {
+ return CHILDREN_NO;
+ }
+ if((cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+ || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
+ {
+ return CHILDREN_MAYBE;
+ }
+
+ // Shouldn't have to run this, but who knows.
+ parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID());
+ if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0)
+ {
+ return CHILDREN_YES;
+ }
+ parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID());
+ if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0)
+ {
+ return CHILDREN_YES;
+ }
+
+ return CHILDREN_NO;
+}
+
+bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
+{
+ LLViewerInventoryCategory* cat = getCategory(cat_id);
+ if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
+ {
+ S32 descendents_server = cat->getDescendentCount();
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ getDirectDescendentsOf(cat_id, cats, items);
+ S32 descendents_actual = 0;
+ if(cats && items)
+ {
+ descendents_actual = cats->count() + items->count();
+ }
+ if(descendents_server == descendents_actual)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLInventoryModel::loadSkeleton(
+ const LLInventoryModel::options_t& options,
+ const LLUUID& owner_id)
+{
+ lldebugs << "importing inventory skeleton for " << owner_id << llendl;
+
+ typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
+ cat_set_t temp_cats;
+
+ update_map_t child_counts;
+
+ LLUUID id;
+ LLAssetType::EType preferred_type;
+ bool rv = true;
+ for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
+ {
+ LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id);
+ response_t::const_iterator no_response = (*it).end();
+ response_t::const_iterator skel;
+ skel = (*it).find("name");
+ if(skel == no_response) goto clean_cat;
+ cat->rename(LLString((*skel).second.c_str()));
+ skel = (*it).find("folder_id");
+ if(skel == no_response) goto clean_cat;
+ id.set((*skel).second.c_str());
+ // if an id is null, it locks the viewer.
+ if(id.isNull()) goto clean_cat;
+ cat->setUUID(id);
+ skel = (*it).find("parent_id");
+ if(skel == no_response) goto clean_cat;
+ id.set((*skel).second.c_str());
+ cat->setParent(id);
+ skel = (*it).find("type_default");
+ if(skel == no_response)
+ {
+ preferred_type = LLAssetType::AT_NONE;
+ }
+ else
+ {
+ S32 t = atoi((*skel).second.c_str());
+ preferred_type = (LLAssetType::EType)t;
+ }
+ cat->setPreferredType(preferred_type);
+ skel = (*it).find("version");
+ if(skel == no_response) goto clean_cat;
+ cat->setVersion(atoi((*skel).second.c_str()));
+ temp_cats.insert(cat);
+ continue;
+ clean_cat:
+ llwarns << "Unable to import near " << cat->getName() << llendl;
+ rv = false;
+ //delete cat; // automatic when cat is reasigned or destroyed
+ }
+
+ S32 cached_category_count = 0;
+ S32 cached_item_count = 0;
+ if(!temp_cats.empty())
+ {
+ cat_array_t categories;
+ item_array_t items;
+ char owner_id_str[UUID_STR_LENGTH];
+ owner_id.toString(owner_id_str);
+ std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str));
+ char inventory_filename[LL_MAX_PATH];
+ snprintf(
+ inventory_filename,
+ LL_MAX_PATH,
+ CACHE_FORMAT_STRING,
+ path.c_str());
+ const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN;
+ std::string gzip_filename(inventory_filename);
+ gzip_filename.append(".gz");
+ FILE* fp = LLFile::fopen(gzip_filename.c_str(), "rb");
+ bool remove_inventory_file = false;
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ if(gunzip_file(gzip_filename.c_str(), inventory_filename))
+ {
+ // we only want to remove the inventory file if it was
+ // gzipped before we loaded, and we successfully
+ // gunziped it.
+ remove_inventory_file = true;
+ }
+ else
+ {
+ llinfos << "Unable to gunzip " << gzip_filename << llendl;
+ }
+ }
+ if(loadFromFile(inventory_filename, categories, items))
+ {
+ // We were able to find a cache of files. So, use what we
+ // found to generate a set of categories we should add. We
+ // will go through each category loaded and if the version
+ // does not match, invalidate the version.
+ S32 count = categories.count();
+ cat_set_t::iterator not_cached = temp_cats.end();
+ std::set<LLUUID> cached_ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = categories[i];
+ cat_set_t::iterator cit = temp_cats.find(cat);
+ LLViewerInventoryCategory* tcat = *cit;
+
+ // we can safely ignore anything loaded from file, but
+ // not sent down in the skeleton.
+ if(cit == not_cached) continue;
+ if(cat->getVersion() != tcat->getVersion())
+ {
+ // if the cached version does not match the server version,
+ // throw away the version we have so we can fetch the
+ // correct contents the next time the viewer opens the folder.
+ tcat->setVersion(NO_VERSION);
+ }
+ else
+ {
+ cached_ids.insert(tcat->getUUID());
+ }
+ }
+
+ // go ahead and add the cats returned during the download
+ std::set<LLUUID>::iterator not_cached_id = cached_ids.end();
+ cached_category_count = cached_ids.size();
+ for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
+ {
+ if(cached_ids.find((*it)->getUUID()) == not_cached_id)
+ {
+ // this check is performed so that we do not
+ // mark new folders in the skeleton (and not in cache)
+ // as being cached.
+ LLViewerInventoryCategory *llvic = (*it);
+ llvic->setVersion(NO_VERSION);
+ }
+ addCategory(*it);
+ ++child_counts[(*it)->getParentUUID()];
+ }
+
+ // Add all the items loaded which are parented to a
+ // category with a correctly cached parent
+ count = items.count();
+ cat_map_t::iterator unparented = mCategoryMap.end();
+ for(int i = 0; i < count; ++i)
+ {
+ cat_map_t::iterator cit = mCategoryMap.find(items[i]->getParentUUID());
+
+ if(cit != unparented)
+ {
+ LLViewerInventoryCategory* cat = cit->second;
+ if(cat->getVersion() != NO_VERSION)
+ {
+ addItem(items[i]);
+ cached_item_count += 1;
+ ++child_counts[cat->getUUID()];
+ }
+ }
+ }
+ }
+ else
+ {
+ // go ahead and add everything after stripping the version
+ // information.
+ for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
+ {
+ LLViewerInventoryCategory *llvic = (*it);
+ llvic->setVersion(NO_VERSION);
+ addCategory(*it);
+ }
+ }
+
+ // At this point, we need to set the known descendents for each
+ // category which successfully cached so that we do not
+ // needlessly fetch descendents for categories which we have.
+ update_map_t::iterator no_child_counts = child_counts.end();
+ update_map_t::iterator the_count;
+ for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
+ {
+ LLViewerInventoryCategory* cat = (*it);
+ if(cat->getVersion() != NO_VERSION)
+ {
+ the_count = child_counts.find(cat->getUUID());
+ if(the_count != no_child_counts)
+ {
+ cat->setDescendentCount((*the_count).second.mValue);
+ }
+ else
+ {
+ cat->setDescendentCount(0);
+ }
+ }
+ }
+
+ if(remove_inventory_file)
+ {
+ // clean up the gunzipped file.
+ LLFile::remove(inventory_filename);
+ }
+ categories.clear(); // will unref and delete entries
+ }
+
+ llinfos << "Successfully loaded " << cached_category_count
+ << " categories and " << cached_item_count << " items from cache."
+ << llendl;
+
+ return rv;
+}
+
+bool LLInventoryModel::loadMeat(
+ const LLInventoryModel::options_t& options, const LLUUID& owner_id)
+{
+ llinfos << "importing inventory for " << owner_id << llendl;
+ LLPermissions default_perm;
+ default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null);
+ LLPointer<LLViewerInventoryItem> item;
+ LLUUID id;
+ LLAssetType::EType type;
+ LLInventoryType::EType inv_type;
+ bool rv = true;
+ for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
+ {
+ item = new LLViewerInventoryItem;
+ response_t::const_iterator no_response = (*it).end();
+ response_t::const_iterator meat;
+ meat = (*it).find("name");
+ if(meat == no_response) goto clean_item;
+ item->rename(LLString((*meat).second.c_str()));
+ meat = (*it).find("item_id");
+ if(meat == no_response) goto clean_item;
+ id.set((*meat).second.c_str());
+ item->setUUID(id);
+ meat = (*it).find("parent_id");
+ if(meat == no_response) goto clean_item;
+ id.set((*meat).second.c_str());
+ item->setParent(id);
+ meat = (*it).find("type");
+ if(meat == no_response) goto clean_item;
+ type = (LLAssetType::EType)atoi((*meat).second.c_str());
+ item->setType(type);
+ meat = (*it).find("inv_type");
+ if(meat != no_response)
+ {
+ inv_type = (LLInventoryType::EType)atoi((*meat).second.c_str());
+ item->setInventoryType(inv_type);
+ }
+ meat = (*it).find("data_id");
+ if(meat == no_response) goto clean_item;
+ id.set((*meat).second.c_str());
+ if(LLAssetType::AT_CALLINGCARD == type)
+ {
+ LLPermissions perm;
+ perm.init(id, owner_id, LLUUID::null, LLUUID::null);
+ item->setPermissions(perm);
+ }
+ else
+ {
+ meat = (*it).find("perm_mask");
+ if(meat != no_response)
+ {
+ PermissionMask perm_mask = atoi((*meat).second.c_str());
+ default_perm.initMasks(
+ perm_mask, perm_mask, perm_mask, perm_mask, perm_mask);
+ }
+ else
+ {
+ default_perm.initMasks(
+ PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
+ }
+ item->setPermissions(default_perm);
+ item->setAssetUUID(id);
+ }
+ meat = (*it).find("flags");
+ if(meat != no_response)
+ {
+ item->setFlags(strtoul((*meat).second.c_str(), NULL, 0));
+ }
+ meat = (*it).find("time");
+ if(meat != no_response)
+ {
+ item->setCreationDate(atoi((*meat).second.c_str()));
+ }
+ addItem(item);
+ continue;
+ clean_item:
+ llwarns << "Unable to import near " << item->getName() << llendl;
+ rv = false;
+ //delete item; // automatic when item is reassigned or destroyed
+ }
+ return rv;
+}
+
+// This is a brute force method to rebuild the entire parent-child
+// relations. The overall operation has O(NlogN) performance, which
+// should be sufficient for our needs.
+void LLInventoryModel::buildParentChildMap()
+{
+ llinfos << "LLInventoryModel::buildParentChildMap()" << llendl;
+
+ // *NOTE: I am skipping the logic around folder version
+ // synchronization here because it seems if a folder is lost, we
+ // might actually want to invalidate it at that point - not
+ // attempt to cache. More time & thought is necessary.
+
+ // First the categories. We'll copy all of the categories into a
+ // temporary container to iterate over (oh for real iterators.)
+ // While we're at it, we'll allocate the arrays in the trees.
+ cat_array_t cats;
+ cat_array_t* catsp;
+ item_array_t* itemsp;
+
+ for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
+ {
+ LLViewerInventoryCategory* cat = cit->second;
+ cats.put(cat);
+ if (mParentChildCategoryTree.count(cat->getUUID()) == 0)
+ {
+ catsp = new cat_array_t;
+ mParentChildCategoryTree[cat->getUUID()] = catsp;
+ }
+ if (mParentChildItemTree.count(cat->getUUID()) == 0)
+ {
+ itemsp = new item_array_t;
+ mParentChildItemTree[cat->getUUID()] = itemsp;
+ }
+ }
+
+ // Insert a special parent for the root - so that lookups on
+ // LLUUID::null as the parent work correctly. This is kind of a
+ // blatent wastes of space since we allocate a block of memory for
+ // the array, but whatever - it's not that much space.
+ if (mParentChildCategoryTree.count(LLUUID::null) == 0)
+ {
+ catsp = new cat_array_t;
+ mParentChildCategoryTree[LLUUID::null] = catsp;
+ }
+
+ // Now we have a structure with all of the categories that we can
+ // iterate over and insert into the correct place in the child
+ // category tree.
+ S32 count = cats.count();
+ S32 i;
+ S32 lost = 0;
+ for(i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = cats.get(i);
+ catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(catsp)
+ {
+ catsp->put(cat);
+ }
+ else
+ {
+ // *NOTE: This process could be a lot more efficient if we
+ // used the new MoveInventoryFolder message, but we would
+ // have to continue to do the update & build here. So, to
+ // implement it, we would need a set or map of uuid pairs
+ // which would be (folder_id, new_parent_id) to be sent up
+ // to the server.
+ llinfos << "Lost categroy: " << cat->getUUID() << " - "
+ << cat->getName() << llendl;
+ ++lost;
+ // plop it into the lost & found.
+ LLAssetType::EType pref = cat->getPreferredType();
+ if(LLAssetType::AT_NONE == pref)
+ {
+ cat->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
+ }
+ else if(LLAssetType::AT_CATEGORY == pref)
+ {
+ // it's the root
+ cat->setParent(LLUUID::null);
+ }
+ else
+ {
+ // it's a protected folder.
+ cat->setParent(gAgent.getInventoryRootID());
+ }
+ cat->updateServer(TRUE);
+ catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(catsp)
+ {
+ catsp->put(cat);
+ }
+ else
+ {
+ llwarns << "Lost and found Not there!!" << llendl;
+ }
+ }
+ }
+ if(lost)
+ {
+ llwarns << "Found " << lost << " lost categories." << llendl;
+ }
+
+ // Now the items. We allocated in the last step, so now all we
+ // have to do is iterate over the items and put them in the right
+ // place.
+ item_array_t items;
+ if(!mItemMap.empty())
+ {
+ LLPointer<LLViewerInventoryItem> item;
+ for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
+ {
+ item = (*iit).second;
+ items.put(item);
+ }
+ }
+ count = items.count();
+ lost = 0;
+ std::vector<LLUUID> lost_item_ids;
+ for(i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryItem> item;
+ item = items.get(i);
+ itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
+ if(itemsp)
+ {
+ itemsp->put(item);
+ }
+ else
+ {
+ llinfos << "Lost item: " << item->getUUID() << " - "
+ << item->getName() << llendl;
+ ++lost;
+ // plop it into the lost & found.
+ //
+ item->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
+ // move it later using a special message to move items. If
+ // we update server here, the client might crash.
+ //item->updateServer();
+ lost_item_ids.push_back(item->getUUID());
+ itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
+ if(itemsp)
+ {
+ itemsp->put(item);
+ }
+ else
+ {
+ llwarns << "Lost and found Not there!!" << llendl;
+ }
+ }
+ }
+ if(lost)
+ {
+ llwarns << "Found " << lost << " lost items." << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_new_message = TRUE;
+ LLUUID lnf = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ for(std::vector<LLUUID>::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it)
+ {
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, FALSE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, (*it));
+ msg->addUUIDFast(_PREHASH_FolderID, lnf);
+ msg->addString("NewName", NULL);
+ if(msg->mCurrentSendTotal >= MTUBYTES)
+ {
+ start_new_message = TRUE;
+ gAgent.sendReliableMessage();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+ }
+}
+
+struct LLUUIDAndName
+{
+ LLUUIDAndName() {}
+ LLUUIDAndName(const LLUUID& id, const LLString& name);
+ bool operator==(const LLUUIDAndName& rhs) const;
+ bool operator<(const LLUUIDAndName& rhs) const;
+ bool operator>(const LLUUIDAndName& rhs) const;
+
+ LLUUID mID;
+ LLString mName;
+};
+
+LLUUIDAndName::LLUUIDAndName(const LLUUID& id, const LLString& name) :
+ mID(id), mName(name)
+{
+}
+
+bool LLUUIDAndName::operator==(const LLUUIDAndName& rhs) const
+{
+ return ((mID == rhs.mID) && (mName == rhs.mName));
+}
+
+bool LLUUIDAndName::operator<(const LLUUIDAndName& rhs) const
+{
+ return (mID < rhs.mID);
+}
+
+bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const
+{
+ return (mID > rhs.mID);
+}
+
+// Given the current state of the inventory items, figure out the
+// clone information. *FIX: This is sub-optimal, since we can insert
+// this information snurgically, but this makes sure the implementation
+// works before we worry about optimization.
+//void LLInventoryModel::recalculateCloneInformation()
+//{
+// //dumpInventory();
+//
+// // This implements a 'multi-map' like structure to keep track of
+// // how many clones we find.
+// typedef LLDynamicArray<LLViewerInventoryItem*> viewer_item_array_t;
+// typedef std::map<LLUUIDAndName, viewer_item_array_t*> clone_map_t;
+// clone_map_t clone_map;
+// LLUUIDAndName id_and_name;
+// viewer_item_array_t* clones = NULL;
+// LLViewerInventoryItem* item = NULL;
+// for(item = (LLViewerInventoryItem*)mItemMap.getFirstData();
+// item != NULL;
+// item = (LLViewerInventoryItem*)mItemMap.getNextData())
+// {
+// if(item->getType() == LLAssetType::AT_CALLINGCARD)
+// {
+// // if it's a calling card, we key off of the creator id, not
+// // the asset id.
+// id_and_name.mID = item->getCreatorUUID();
+// }
+// else
+// {
+// // if it's not a calling card, we key clones from the
+// // asset id.
+// id_and_name.mID = item->getAssetUUID();
+// }
+// if(id_and_name.mID == LLUUID::null)
+// {
+// continue;
+// }
+// id_and_name.mName = item->getName();
+// if(clone_map.checkData(id_and_name))
+// {
+// clones = clone_map.getData(id_and_name);
+// }
+// else
+// {
+// clones = new viewer_item_array_t;
+// clone_map.addData(id_and_name, clones);
+// }
+// clones->put(item);
+// }
+//
+// S32 count = 0;
+// for(clones = clone_map.getFirstData();
+// clones != NULL;
+// clones = clone_map.getNextData())
+// {
+// count = clones->count();
+// for(S32 i = 0; i < count; i++)
+// {
+// item = clones->get(i);
+// item->setCloneCount(count - 1);
+// //clones[i] = NULL;
+// }
+// delete clones;
+// }
+// clone_map.removeAllData();
+// //dumpInventory();
+//}
+
+// static
+bool LLInventoryModel::loadFromFile(
+ const char* filename,
+ LLInventoryModel::cat_array_t& categories,
+ LLInventoryModel::item_array_t& items)
+{
+ llinfos << "LLInventoryModel::loadFromFile(" << filename << ")" << llendl;
+ FILE* file = LLFile::fopen(filename, "rb");
+ if(!file)
+ {
+ llinfos << "unable to load inventory from: " << filename << llendl;
+ return false;
+ }
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ while(!feof(file) && fgets(buffer, MAX_STRING, file))
+ {
+ sscanf(buffer, " %s", keyword);
+ if(0 == strcmp("inv_category", keyword))
+ {
+ LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null);
+ if(inv_cat->importFileLocal(file))
+ {
+ categories.put(inv_cat);
+ }
+ else
+ {
+ llwarns << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << llendl;
+ //delete inv_cat; // automatic when inv_cat is reassigned or destroyed
+ }
+ }
+ else if(0 == strcmp("inv_item", keyword))
+ {
+ LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem;
+ if( inv_item->importFileLocal(file) )
+ {
+ // *FIX: Need a better solution, this prevents the
+ // application from freezing, but breaks inventory
+ // caching.
+ if(inv_item->getUUID().isNull())
+ {
+ //delete inv_item; // automatic when inv_cat is reassigned or destroyed
+ llwarns << "Ignoring inventory with null item id: "
+ << inv_item->getName() << llendl;
+
+ }
+ else
+ {
+ items.put(inv_item);
+ }
+ }
+ else
+ {
+ llwarns << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << llendl;
+ //delete inv_item; // automatic when inv_cat is reassigned or destroyed
+ }
+ }
+ else
+ {
+ llwarns << "Unknown token in inventory file '" << keyword << "'"
+ << llendl;
+ }
+ }
+ fclose(file);
+ return true;
+}
+
+// static
+bool LLInventoryModel::saveToFile(
+ const char* filename,
+ const cat_array_t& categories,
+ const item_array_t& items)
+{
+ llinfos << "LLInventoryModel::saveToFile(" << filename << ")" << llendl;
+ FILE* file = LLFile::fopen(filename, "wb");
+ if(!file)
+ {
+ llwarns << "unable to save inventory to: " << filename << llendl;
+ return false;
+ }
+
+ S32 count = categories.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = categories[i];
+ if(cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ cat->exportFileLocal(file);
+ }
+ }
+
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ items[i]->exportFile(file);
+ }
+
+ fclose(file);
+ return true;
+}
+
+// message handling functionality
+// static
+void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
+{
+ //msg->setHandlerFuncFast(_PREHASH_InventoryUpdate,
+ // processInventoryUpdate,
+ // NULL);
+ //msg->setHandlerFuncFast(_PREHASH_UseCachedInventory,
+ // processUseCachedInventory,
+ // NULL);
+ msg->setHandlerFuncFast(_PREHASH_UpdateCreateInventoryItem,
+ processUpdateCreateInventoryItem,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem,
+ processRemoveInventoryItem,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder,
+ processUpdateInventoryFolder,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,
+ processRemoveInventoryFolder,
+ NULL);
+ //msg->setHandlerFuncFast(_PREHASH_ExchangeCallingCard,
+ // processExchangeCallingcard,
+ // NULL);
+ //msg->setHandlerFuncFast(_PREHASH_AddCallingCard,
+ // processAddCallingcard,
+ // NULL);
+ //msg->setHandlerFuncFast(_PREHASH_DeclineCallingCard,
+ // processDeclineCallingcard,
+ // NULL);
+ msg->setHandlerFuncFast(_PREHASH_SaveAssetIntoInventory,
+ processSaveAssetIntoInventory,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_BulkUpdateInventory,
+ processBulkUpdateInventory,
+ NULL);
+ msg->setHandlerFunc("InventoryDescendents", processInventoryDescendents);
+ msg->setHandlerFunc("MoveInventoryItem", processMoveInventoryItem);
+ msg->setHandlerFunc("FetchInventoryReply", processFetchInventoryReply);
+}
+
+/*
+//struct LLUpdateInventoryInfo
+{
+ LLString mFilenameAndPath;
+ BOOL mIsComplete;
+};
+
+// static
+void LLInventoryModel::processInventoryUpdate(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLInventoryModel::processInventoryUpdate()" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got an inventory update for the wrong agent." << llendl;
+ return;
+ }
+
+ BOOL is_complete;
+ msg->getBOOLFast(_PREHASH_InventoryData, _PREHASH_IsComplete, is_complete);
+ char filename[MAX_STRING];
+ msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, MAX_STRING, filename);
+
+ LLUpdateInventoryInfo* info = new LLUpdateInventoryInfo;
+ info->mFilenameAndPath = gDirUtilp->getExpandedFilename( LL_PATH_TEMP, filename );
+ info->mIsComplete = is_complete;
+
+ gXferManager->requestFile(info->mFilenameAndPath,
+ filename,
+ LL_PATH_CACHE, // The remote file is in the data directory
+ gUserServer,
+ TRUE, // Delete the remote file after the transfer
+ processInventoryFile,
+ (void*)info,
+ LLXferManager::HIGH_PRIORITY);
+}
+
+// static
+void LLInventoryModel::processInventoryFile(void** user_data, S32 error_code)
+{
+ llinfos << "LLInventoryModel::processInventoryFile()" << llendl;
+ //gInventory.dumpInventory();
+ LLUpdateInventoryInfo* info = (LLUpdateInventoryInfo*)user_data;
+ if(info && (0 == error_code))
+ {
+ if(info->mIsComplete)
+ {
+ if(gInventory.isLoaded())
+ {
+ // it's a complete update, and we've already loaded
+ // everything. Therefore, we need to blow away
+ // everything.
+ gInventory.empty();
+ }
+ else
+ {
+ // We want to merge with anything that's happened
+ // before the load. Therefore, we only want to get rid
+ // of the root category.
+ gInventory.deleteObject(gAgent.getInventoryRootID());
+ }
+ }
+
+ // decompress if necessary
+ const char* filename = info->mFilenameAndPath.c_str();
+ const char* ext = filename + strlen(filename) - 6;
+ char dst_filename[LL_MAX_PATH];
+ if(0 == strnicmp(ext, "gz", 2))
+ {
+ // it's a gz file. decmpress it.
+ dst_filename[0] = '\0';
+ strncat(dst_filename, filename, (ext - filename));
+ strcat(dst_filename, "tmp");
+ BOOL success = gunzip_file(filename, dst_filename);
+ LLFile::remove(filename);
+ if(!success)
+ {
+ llwarns << "Error loading inventory file. Return code: " << error_code << llendl;
+ LLNotifyBox::showXml("InventoryNetworkCorruption");
+ goto exit;
+ }
+ filename = dst_filename;
+ }
+
+#ifdef DIFF_INVENTORY_FILES
+ char agent_id_string[UUID_STR_LENGTH];
+ char inventory_filename[LL_MAX_PATH];
+ gAgent.getID().toString(agent_id_string);
+ sprintf(inventory_filename, CACHE_FORMAT_STRING, gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ char buffer[MAX_STRING];
+ sprintf(buffer,
+ "c:/cygwin/bin/diff %s %s",
+ inventory_filename,
+ filename);
+ llinfos << buffer << llendl;
+ system(buffer);
+
+#endif
+
+ // load it all up.
+ gInventory.loadFromFile(filename);
+ gInventory.buildParentChildMap();
+ //gInventory.recalculateCloneInformation();
+ gInventory.addChangedMask(LLInventoryObserver::ALL);
+ if(info->mIsComplete)
+ {
+ // Set loaded to true if it's a complete set because it's
+ // poosible for someone to accept an inventory category
+ // before they have their complete inventory. If we marked
+ // it loaded at that point, when the real inventory
+ // arrived, it would erase (on the client anyway) the
+ // existence of that inventory.
+ gInventory.mIsLoaded = TRUE;
+
+ //retrieveIM(gMessageSystem);
+ llinfos << "complete inventory update" << llendl;
+
+ // If we promised you a message on load, here it is
+ if (gViewerStats->getStat(LLViewerStats::ST_INVENTORY_TOO_LONG) > 0.0)
+ {
+ LLNotifyBox::showXml("InventoryLoaded");
+ }
+ }
+ else
+ {
+ llinfos << "incomplete inventory update" << llendl;
+ }
+ gInventory.notifyObservers();
+ LLFile::remove(filename);
+ }
+ else
+ {
+ llwarns << "Eror loading inventory file. Return code: " << error_code
+ << llendl;
+ if(error_code == LL_ERR_TCP_TIMEOUT)
+ {
+ //llwarns << "Re-requesting inventory" << llendl;
+ //gInventory.requestFromServer(gAgent.getID());
+ }
+ }
+
+exit:
+ delete info;
+ //gInventory.dumpInventory();
+}
+
+// static
+void LLInventoryModel::processUseCachedInventory(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLInventoryModel::processUseCachedInventory()" << llendl;
+ char agent_id_string[UUID_STR_LENGTH];
+ gAgent.getID().toString(agent_id_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, CACHE_FORMAT_STRING, gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ // We want to merge with anything that's happened before the
+ // load. Therefore, we only want to get rid of the root category.
+ gInventory.deleteObject(gAgent.getInventoryRootID());
+ gInventory.loadFromFile(filename);
+ gInventory.buildParentChildMap();
+ //gInventory.recalculateCloneInformation();
+ gInventory.addChangedMask(LLInventoryObserver::ALL);
+ gInventory.mIsLoaded = TRUE;
+ gInventory.notifyObservers();
+ //retrieveIM();
+}
+*/
+
+// static
+void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, void**)
+{
+ // do accounting and highlight new items if they arrive
+ if (gInventory.messageUpdateCore(msg, true, true))
+ {
+ U32 callback_id;
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
+ msg->getU32Fast(_PREHASH_InventoryData, _PREHASH_CallbackID, callback_id);
+
+ gInventoryCallbacks.fire(callback_id, item_id);
+ }
+
+}
+
+// static
+void LLInventoryModel::processFetchInventoryReply(LLMessageSystem* msg, void**)
+{
+ // no accounting
+ gInventory.messageUpdateCore(msg, false, false);
+}
+
+
+bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, bool highlight_new)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a inventory update for the wrong agent: " << agent_id
+ << llendl;
+ return false;
+ }
+ LLPointer<LLViewerInventoryItem> lastitem; // hack
+ item_array_t items;
+ update_map_t update;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ lastitem = titem;
+ titem->unpackMessage(msg, _PREHASH_InventoryData, i);
+ lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
+ << titem->getUUID() << llendl;
+ items.push_back(titem);
+ // examine update for changes.
+ LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
+ if(itemp)
+ {
+ if(titem->getParentUUID() == itemp->getParentUUID())
+ {
+ update[titem->getParentUUID()];
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ --update[itemp->getParentUUID()];
+ }
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ }
+ }
+ if(account)
+ {
+ gInventory.accountForUpdate(update);
+ }
+
+ U32 changes = 0x0;
+ for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ changes |= gInventory.updateItem(*it);
+ }
+ gInventory.notifyObservers();
+ gViewerWindow->getWindow()->decBusyCount();
+
+ // *HACK: Do the 'show' logic for a new item in the inventory if
+ // it is a newly created item.
+ if (highlight_new
+ && (changes & LLInventoryObserver::ADD) == LLInventoryObserver::ADD)
+ {
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(!gInventory.isObjectDescendentOf(lastitem->getUUID(), trash_id))
+ {
+ bool show_keep_discard = lastitem->getPermissions().getCreator() != gAgent.getID();
+ switch(lastitem->getType())
+ {
+ case LLAssetType::AT_NOTECARD:
+ open_notecard(
+ lastitem->getUUID(),
+ LLString("Note: ") + lastitem->getName(),
+ show_keep_discard);
+ break;
+ case LLAssetType::AT_LANDMARK:
+ open_landmark(
+ lastitem->getUUID(),
+ LLString(" ") + lastitem->getName(),
+ show_keep_discard);
+ break;
+ case LLAssetType::AT_TEXTURE:
+ open_texture(
+ lastitem->getUUID(),
+ LLString("Texture: ") + lastitem->getName(),
+ show_keep_discard);
+ break;
+ default:
+ break;
+ }
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ BOOL inventory_has_focus = gFocusMgr.childHasKeyboardFocus(view);
+ BOOL user_is_away = gAwayTimer.getStarted();
+
+ // don't select lost and found items if an active user is working in the inventory
+ if (!gInventory.isObjectDescendentOf(lastitem->getUUID(), lost_and_found_id) ||
+ !inventory_has_focus ||
+ user_is_away)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+ view->getPanel()->setSelection(lastitem->getUUID(), TAKE_FOCUS_NO);
+ // HACK to open inventory offers that are
+ // accepted. This information really needs to
+ // flow through the instant messages and
+ // inventory restore keyboard focus
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// static
+void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLInventoryModel::processRemoveInventoryItem()" << llendl;
+ LLUUID agent_id, item_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a RemoveInventoryItem for the wrong agent."
+ << llendl;
+ return;
+ }
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
+ std::vector<LLUUID> item_ids;
+ update_map_t update;
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
+ LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
+ if(itemp)
+ {
+ // we only bother with the delete and account if we found
+ // the item - this is usually a back-up for permissions,
+ // so frequently the item will already be gone.
+ --update[itemp->getParentUUID()];
+ item_ids.push_back(item_id);
+ }
+ }
+ gInventory.accountForUpdate(update);
+ for(std::vector<LLUUID>::iterator it = item_ids.begin(); it != item_ids.end(); ++it)
+ {
+ gInventory.deleteObject(*it);
+ }
+ gInventory.notifyObservers();
+}
+
+// static
+void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
+ void**)
+{
+ lldebugs << "LLInventoryModel::processUpdateInventoryFolder()" << llendl;
+ LLUUID agent_id, folder_id, parent_id;
+ //char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got an UpdateInventoryFolder for the wrong agent."
+ << llendl;
+ return;
+ }
+ LLPointer<LLViewerInventoryCategory> lastfolder; // hack
+ cat_array_t folders;
+ update_map_t update;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
+ lastfolder = tfolder;
+ tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
+ // make sure it's not a protected folder
+ tfolder->setPreferredType(LLAssetType::AT_NONE);
+ folders.push_back(tfolder);
+ // examine update for changes.
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
+ if(folderp)
+ {
+ if(tfolder->getParentUUID() == folderp->getParentUUID())
+ {
+ update[tfolder->getParentUUID()];
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ --update[folderp->getParentUUID()];
+ }
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ }
+ }
+ gInventory.accountForUpdate(update);
+ for (cat_array_t::iterator it = folders.begin(); it != folders.end(); ++it)
+ {
+ gInventory.updateCategory(*it);
+ }
+ gInventory.notifyObservers();
+
+ // *HACK: Do the 'show' logic for a new item in the inventory.
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO);
+ }
+}
+
+// static
+void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg,
+ void**)
+{
+ lldebugs << "LLInventoryModel::processRemoveInventoryFolder()" << llendl;
+ LLUUID agent_id, folder_id;
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a RemoveInventoryFolder for the wrong agent."
+ << llendl;
+ return;
+ }
+ std::vector<LLUUID> folder_ids;
+ update_map_t update;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, folder_id, i);
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(folder_id);
+ if(folderp)
+ {
+ --update[folderp->getParentUUID()];
+ folder_ids.push_back(folder_id);
+ }
+ }
+ gInventory.accountForUpdate(update);
+ for(std::vector<LLUUID>::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it)
+ {
+ gInventory.deleteObject(*it);
+ }
+ gInventory.notifyObservers();
+}
+
+// static
+void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
+ void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a SaveAssetIntoInventory message for the wrong agent."
+ << llendl;
+ return;
+ }
+
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
+ LLUUID new_asset_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_NewAssetID, new_asset_id);
+
+ lldebugs << "LLInventoryModel::processSaveAssetIntoInventory itemID=" << item_id << llendl;
+
+ LLViewerInventoryItem* item = gInventory.getItem( item_id );
+ if( item )
+ {
+ item->setAssetUUID(new_asset_id);
+ LLCategoryUpdate up(item->getParentUUID(), 0);
+ gInventory.accountForUpdate(up);
+ gInventory.addChangedMask( LLInventoryObserver::INTERNAL, item_id );
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llinfos << "LLInventoryModel::processSaveAssetIntoInventory item not found: " << item_id << llendl;
+ }
+ if(gViewerWindow)
+ {
+ gViewerWindow->getWindow()->decBusyCount();
+ }
+}
+
+struct InventoryCallbackInfo
+{
+ InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) :
+ mCallback(callback), mInvID(inv_id) {}
+ U32 mCallback;
+ LLUUID mInvID;
+};
+
+// static
+void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a BulkUpdateInventory for the wrong agent." << llendl;
+ return;
+ }
+ LLUUID tid;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid);
+ llinfos << "Bulk inventory: " << tid << llendl;
+
+ update_map_t update;
+ cat_array_t folders;
+ S32 count;
+ S32 i;
+ count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ for(i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
+ tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
+ //llinfos << "unpaked folder '" << tfolder->getName() << "' ("
+ // << tfolder->getUUID() << ") in " << tfolder->getParentUUID()
+ // << llendl;
+ if(tfolder->getUUID().notNull())
+ {
+ folders.push_back(tfolder);
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
+ if(folderp)
+ {
+ if(tfolder->getParentUUID() == folderp->getParentUUID())
+ {
+ update[tfolder->getParentUUID()];
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ --update[folderp->getParentUUID()];
+ }
+ }
+ else
+ {
+ // we could not find the folder, so it is probably
+ // new. However, we only want to attempt accounting
+ // for the parent if we can find the parent.
+ folderp = gInventory.getCategory(tfolder->getParentUUID());
+ if(folderp)
+ {
+ ++update[tfolder->getParentUUID()];
+ }
+ }
+ }
+ }
+
+
+ count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
+ std::vector<LLUUID> wearable_ids;
+ item_array_t items;
+ std::list<InventoryCallbackInfo> cblist;
+ for(i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ titem->unpackMessage(msg, _PREHASH_ItemData, i);
+ //llinfos << "unpaked item '" << titem->getName() << "' in "
+ // << titem->getParentUUID() << llendl;
+ U32 callback_id;
+ msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id);
+ if(titem->getUUID().notNull())
+ {
+ items.push_back(titem);
+ cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID()));
+ if (titem->getInventoryType() == LLInventoryType::IT_WEARABLE)
+ {
+ wearable_ids.push_back(titem->getUUID());
+ }
+ // examine update for changes.
+ LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
+ if(itemp)
+ {
+ if(titem->getParentUUID() == itemp->getParentUUID())
+ {
+ update[titem->getParentUUID()];
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ --update[itemp->getParentUUID()];
+ }
+ }
+ else
+ {
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID());
+ if(folderp)
+ {
+ ++update[titem->getParentUUID()];
+ }
+ }
+ }
+ else
+ {
+ cblist.push_back(InventoryCallbackInfo(callback_id, LLUUID::null));
+ }
+ }
+ gInventory.accountForUpdate(update);
+
+ for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit)
+ {
+ gInventory.updateCategory(*cit);
+ }
+ for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit)
+ {
+ gInventory.updateItem(*iit);
+ }
+ gInventory.notifyObservers();
+
+ // The incoming inventory could span more than one BulkInventoryUpdate packet,
+ // so record the transaction ID for this purchase, then wear all clothing
+ // that comes in as part of that transaction ID. JC
+ if (LLInventoryView::sWearNewClothing)
+ {
+ LLInventoryView::sWearNewClothingTransactionID = tid;
+ LLInventoryView::sWearNewClothing = FALSE;
+ }
+
+ if (tid == LLInventoryView::sWearNewClothingTransactionID)
+ {
+ count = wearable_ids.size();
+ for (i = 0; i < count; ++i)
+ {
+ LLViewerInventoryItem* wearable_item;
+ wearable_item = gInventory.getItem(wearable_ids[i]);
+ wear_inventory_item_on_avatar(wearable_item);
+ }
+ }
+
+ std::list<InventoryCallbackInfo>::iterator inv_it;
+ for (inv_it = cblist.begin(); inv_it != cblist.end(); ++inv_it)
+ {
+ InventoryCallbackInfo cbinfo = (*inv_it);
+ gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID);
+ }
+ // Don't show the inventory. We used to call showAgentInventory here.
+ //LLInventoryView* view = LLInventoryView::getActiveInventory();
+ //if(view)
+ //{
+ // const BOOL take_keyboard_focus = FALSE;
+ // view->setSelection(category.getUUID(), take_keyboard_focus );
+ // LLView* focus_view = gFocusMgr.getKeyboardFocus();
+ // LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+ // // HACK to open inventory offers that are accepted. This information
+ // // really needs to flow through the instant messages and inventory
+ // // transfer/update messages.
+ // if (LLInventoryView::sOpenNextNewItem)
+ // {
+ // view->openSelected();
+ // LLInventoryView::sOpenNextNewItem = FALSE;
+ // }
+ //
+ // // restore keyboard focus
+ // gFocusMgr.setKeyboardFocus(focus_view, callback);
+ //}
+}
+
+// static
+void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a UpdateInventoryItem for the wrong agent."
+ << llendl;
+ return;
+ }
+ LLUUID parent_id;
+ msg->getUUID("AgentData", "FolderID", parent_id);
+ LLUUID owner_id;
+ msg->getUUID("AgentData", "OwnerID", owner_id);
+ S32 version;
+ msg->getS32("AgentData", "Version", version);
+ S32 descendents;
+ msg->getS32("AgentData", "Descendents", descendents);
+ S32 i;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
+ for(i = 0; i < count; ++i)
+ {
+ tcategory->unpackMessage(msg, _PREHASH_FolderData, i);
+ gInventory.updateCategory(tcategory);
+ }
+
+ count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ for(i = 0; i < count; ++i)
+ {
+ titem->unpackMessage(msg, _PREHASH_ItemData, i);
+ gInventory.updateItem(titem);
+ }
+
+ // set version and descendentcount according to message.
+ LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id);
+ if(cat)
+ {
+ cat->setVersion(version);
+ cat->setDescendentCount(descendents);
+ }
+ gInventory.notifyObservers();
+}
+
+// static
+void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLInventoryModel::processMoveInventoryItem()" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a MoveInventoryItem message for the wrong agent."
+ << llendl;
+ return;
+ }
+
+ LLUUID item_id;
+ LLUUID folder_id;
+ char new_name[MAX_STRING];
+ bool anything_changed = false;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_FolderID, folder_id, i);
+ msg->getString("InventoryData", "NewName", MAX_STRING, new_name, i);
+
+ lldebugs << "moving item " << item_id << " to folder "
+ << folder_id << llendl;
+ update_list_t update;
+ LLCategoryUpdate old_folder(item->getParentUUID(), -1);
+ update.push_back(old_folder);
+ LLCategoryUpdate new_folder(folder_id, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ new_item->setParent(folder_id);
+ if(strlen(new_name) > 0)
+ {
+ new_item->rename(new_name);
+ }
+ gInventory.updateItem(new_item);
+ anything_changed = true;
+ }
+ else
+ {
+ llinfos << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << llendl;
+ }
+ }
+ if(anything_changed)
+ {
+ gInventory.notifyObservers();
+ }
+}
+
+// *NOTE: DEBUG functionality
+void LLInventoryModel::dumpInventory()
+{
+ llinfos << "\nBegin Inventory Dump\n**********************:" << llendl;
+ llinfos << "mCategroy[] contains " << mCategoryMap.size() << " items." << llendl;
+ for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
+ {
+ LLViewerInventoryCategory* cat = cit->second;
+ if(cat)
+ {
+ llinfos << " " << cat->getUUID() << " '" << cat->getName() << "' "
+ << cat->getVersion() << " " << cat->getDescendentCount()
+ << llendl;
+ }
+ else
+ {
+ llinfos << " NULL!" << llendl;
+ }
+ }
+ llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl;
+ for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
+ {
+ LLViewerInventoryItem* item = iit->second;
+ if(item)
+ {
+ llinfos << " " << item->getUUID() << " "
+ << item->getName() << llendl;
+ }
+ else
+ {
+ llinfos << " NULL!" << llendl;
+ }
+ }
+ llinfos << "\n**********************\nEnd Inventory Dump" << llendl;
+}
+
+
+///----------------------------------------------------------------------------
+/// LLInventoryCollectFunctor implementations
+///----------------------------------------------------------------------------
+
+bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(mType == LLAssetType::AT_CATEGORY)
+ {
+ if(cat) return TRUE;
+ }
+ if(item)
+ {
+ if(item->getType() == mType) return TRUE;
+ }
+ return FALSE;
+}
+
+bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(mType == LLAssetType::AT_CATEGORY)
+ {
+ if(cat) return FALSE;
+ }
+ if(item)
+ {
+ if(item->getType() == mType) return FALSE;
+ else return TRUE;
+ }
+ return TRUE;
+}
+
+bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(mType == LLAssetType::AT_CATEGORY)
+ {
+ if(cat)
+ {
+ return TRUE;
+ }
+ }
+ if(item)
+ {
+ if(item->getType() == mType)
+ {
+ LLPermissions perm = item->getPermissions();
+ if ((perm.getMaskBase() & mPerm) == mPerm)
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+//bool LLIsClone::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+//{
+// if(cat) return FALSE;
+// if(item)
+// {
+// if(mItemMap->getType() == LLAssetType::AT_CALLINGCARD)
+// {
+// if((item->getType() == LLAssetType::AT_CALLINGCARD)
+// && !(item->getCreatorUUID().isNull())
+// && (item->getCreatorUUID() == mItemMap->getCreatorUUID()))
+// {
+// return TRUE;
+// }
+// }
+// else
+// {
+// if((item->getType() == mItemMap->getType())
+// && !(item->getAssetUUID().isNull())
+// && (item->getAssetUUID() == mItemMap->getAssetUUID())
+// && (item->getName() == mItemMap->getName()))
+// {
+// return TRUE;
+// }
+// }
+// }
+// return FALSE;
+//}
+
+bool LLBuddyCollector::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((LLAssetType::AT_CALLINGCARD == item->getType())
+ && (!item->getCreatorUUID().isNull())
+ && (item->getCreatorUUID() != gAgent.getID()))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((LLAssetType::AT_CALLINGCARD == item->getType())
+ && (item->getCreatorUUID().notNull())
+ && (item->getCreatorUUID() != gAgent.getID()))
+ {
+ mSeen.insert(item->getCreatorUUID());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((LLAssetType::AT_CALLINGCARD == item->getType())
+ && (item->getCreatorUUID() == mBuddyID))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+bool LLNameCategoryCollector::operator()(
+ LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(cat)
+ {
+ if (!LLString::compareInsensitive(mName.c_str(), cat->getName().c_str()))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Observers
+///----------------------------------------------------------------------------
+
+void LLInventoryCompletionObserver::changed(U32 mask)
+{
+ // scan through the incomplete items and move or erase them as
+ // appropriate.
+ if(!mIncomplete.empty())
+ {
+ for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ if(item->isComplete())
+ {
+ mComplete.push_back(*it);
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ if(mIncomplete.empty())
+ {
+ done();
+ }
+ }
+}
+
+void LLInventoryCompletionObserver::watchItem(const LLUUID& id)
+{
+ if(id.notNull())
+ {
+ mIncomplete.push_back(id);
+ }
+}
+
+
+void LLInventoryFetchObserver::changed(U32 mask)
+{
+ // scan through the incomplete items and move or erase them as
+ // appropriate.
+ if(!mIncomplete.empty())
+ {
+ for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ // BUG: This can cause done() to get called prematurely below.
+ // This happens with the LLGestureInventoryFetchObserver that
+ // loads gestures at startup. JC
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ if(item->isComplete())
+ {
+ mComplete.push_back(*it);
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ if(mIncomplete.empty())
+ {
+ done();
+ }
+ }
+ //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl;
+ //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl;
+}
+
+bool LLInventoryFetchObserver::isEverythingComplete() const
+{
+ return mIncomplete.empty();
+}
+
+void LLInventoryFetchObserver::fetchItems(
+ const LLInventoryFetchObserver::item_ref_t& ids)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_new_message = TRUE;
+ LLUUID owner_id;
+ for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(item)
+ {
+ if(item->isComplete())
+ {
+ // It's complete, so put it on the complete container.
+ mComplete.push_back(*it);
+ continue;
+ }
+ else
+ {
+ owner_id = item->getPermissions().getOwner();
+ }
+ }
+ else
+ {
+ // assume it's agent inventory.
+ owner_id = gAgent.getID();
+ }
+
+ // It's incomplete, so put it on the incomplete container, and
+ // pack this on the message.
+ mIncomplete.push_back(*it);
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_FetchInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
+ msg->addUUIDFast(_PREHASH_ItemID, (*it));
+ if(msg->getCurrentSendTotal() >= MTUBYTES)
+ {
+ start_new_message = TRUE;
+ gAgent.sendReliableMessage();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+// virtual
+void LLInventoryFetchDescendentsObserver::changed(U32 mask)
+{
+ for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+ if(!cat)
+ {
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ if(isComplete(cat))
+ {
+ mCompleteFolders.push_back(*it);
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ if(mIncompleteFolders.empty())
+ {
+ done();
+ }
+}
+
+void LLInventoryFetchDescendentsObserver::fetchDescendents(
+ const folder_ref_t& ids)
+{
+ for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+ if(!cat) continue;
+ if(!isComplete(cat))
+ {
+ cat->fetchDescendents();
+ mIncompleteFolders.push_back(*it);
+ }
+ else
+ {
+ mCompleteFolders.push_back(*it);
+ }
+ }
+}
+
+bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const
+{
+ return mIncompleteFolders.empty();
+}
+
+bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat)
+{
+ S32 version = cat->getVersion();
+ S32 descendents = cat->getDescendentCount();
+ if((LLViewerInventoryCategory::VERSION_UNKNOWN == version)
+ || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents))
+ {
+ return false;
+ }
+ // it might be complete - check known descendents against
+ // currently available.
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
+ if(!cats || !items)
+ {
+ // bit of a hack - pretend we're done if they are gone or
+ // incomplete. should never know, but it would suck if this
+ // kept tight looping because of a corrupt memory state.
+ return true;
+ }
+ S32 known = cats->count() + items->count();
+ if(descendents == known)
+ {
+ // hey - we're done.
+ return true;
+ }
+ return false;
+}
+
+void LLInventoryFetchComboObserver::changed(U32 mask)
+{
+ if(!mIncompleteItems.empty())
+ {
+ for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ it = mIncompleteItems.erase(it);
+ continue;
+ }
+ if(item->isComplete())
+ {
+ mCompleteItems.push_back(*it);
+ it = mIncompleteItems.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ }
+ if(!mIncompleteFolders.empty())
+ {
+ for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+ if(!cat)
+ {
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ if(gInventory.isCategoryComplete(*it))
+ {
+ mCompleteFolders.push_back(*it);
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ }
+ if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty())
+ {
+ mDone = true;
+ done();
+ }
+}
+
+void LLInventoryFetchComboObserver::fetch(
+ const folder_ref_t& folder_ids,
+ const item_ref_t& item_ids)
+{
+ lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl;
+ for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*fit);
+ if(!cat) continue;
+ if(!gInventory.isCategoryComplete(*fit))
+ {
+ cat->fetchDescendents();
+ lldebugs << "fetching folder " << *fit <<llendl;
+ mIncompleteFolders.push_back(*fit);
+ }
+ else
+ {
+ mCompleteFolders.push_back(*fit);
+ lldebugs << "completing folder " << *fit <<llendl;
+ }
+ }
+
+ // Now for the items - we fetch everything which is not a direct
+ // descendent of an incomplete folder because the item will show
+ // up in an inventory descendents message soon enough so we do not
+ // have to fetch it individually.
+ LLUUID owner_id;
+ LLMessageSystem* msg = gMessageSystem;
+ bool start_new_message = true;
+ for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*iit);
+ if(!item)
+ {
+ lldebugs << "uanble to find item " << *iit << llendl;
+ continue;
+ }
+ if(item->isComplete())
+ {
+ // It's complete, so put it on the complete container.
+ mCompleteItems.push_back(*iit);
+ lldebugs << "completing item " << *iit << llendl;
+ continue;
+ }
+ else
+ {
+ mIncompleteItems.push_back(*iit);
+ owner_id = item->getPermissions().getOwner();
+ }
+ if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end())
+ {
+ lldebugs << "fetching item " << *iit << llendl;
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_FetchInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
+ msg->addUUIDFast(_PREHASH_ItemID, (*iit));
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ }
+ }
+ else
+ {
+ lldebugs << "not worrying about " << *iit << llendl;
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLInventoryExistenceObserver::watchItem(const LLUUID& id)
+{
+ if(id.notNull())
+ {
+ mMIA.push_back(id);
+ }
+}
+
+void LLInventoryExistenceObserver::changed(U32 mask)
+{
+ // scan through the incomplete items and move or erase them as
+ // appropriate.
+ if(!mMIA.empty())
+ {
+ for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ ++it;
+ continue;
+ }
+ mExist.push_back(*it);
+ it = mMIA.erase(it);
+ }
+ if(mMIA.empty())
+ {
+ done();
+ }
+ }
+}
+
+LLInventoryTransactionObserver::LLInventoryTransactionObserver(
+ const LLTransactionID& transaction_id) :
+ mTransactionID(transaction_id)
+{
+}
+
+void LLInventoryTransactionObserver::changed(U32 mask)
+{
+ if(mask & LLInventoryObserver::ADD)
+ {
+ // This could be it - see if we are processing a bulk update
+ LLMessageSystem* msg = gMessageSystem;
+ if(msg->getMessageName()
+ && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory")))
+ {
+ // we have a match for the message - now check the
+ // transaction id.
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id);
+ if(id == mTransactionID)
+ {
+ // woo hoo, we found it
+ folder_ref_t folders;
+ item_ref_t items;
+ S32 count;
+ count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i);
+ if(id.notNull())
+ {
+ folders.push_back(id);
+ }
+ }
+ count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
+ for(i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i);
+ if(id.notNull())
+ {
+ items.push_back(id);
+ }
+ }
+
+ // call the derived class the implements this method.
+ done(folders, items);
+ }
+ }
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// LLAssetIDMatches
+///----------------------------------------------------------------------------
+bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ return (item && item->getAssetUUID() == mAssetID);
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+
+/*
+BOOL decompress_file(const char* src_filename, const char* dst_filename)
+{
+ BOOL rv = FALSE;
+ gzFile src = NULL;
+ U8* buffer = NULL;
+ FILE* dst = NULL;
+ S32 bytes = 0;
+ const S32 DECOMPRESS_BUFFER_SIZE = 32000;
+
+ // open the files
+ src = gzopen(src_filename, "rb");
+ if(!src) goto err_decompress;
+ dst = LLFile::fopen(dst_filename, "wb");
+ if(!dst) goto err_decompress;
+
+ // decompress.
+ buffer = new U8[DECOMPRESS_BUFFER_SIZE + 1];
+
+ do
+ {
+ bytes = gzread(src, buffer, DECOMPRESS_BUFFER_SIZE);
+ if (bytes < 0)
+ {
+ goto err_decompress;
+ }
+
+ fwrite(buffer, bytes, 1, dst);
+ } while(gzeof(src) == 0);
+
+ // success
+ rv = TRUE;
+
+ err_decompress:
+ if(src != NULL) gzclose(src);
+ if(buffer != NULL) delete[] buffer;
+ if(dst != NULL) fclose(dst);
+ return rv;
+}
+*/
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
new file mode 100644
index 0000000000..25f08322be
--- /dev/null
+++ b/indra/newview/llinventorymodel.h
@@ -0,0 +1,757 @@
+/**
+ * @file llinventorymodel.h
+ * @brief LLInventoryModel class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINVENTORYMODEL_H
+#define LL_LLINVENTORYMODEL_H
+
+#include "llassetstorage.h"
+#include "lldarray.h"
+//#include "llskiplist.h"
+//#include "llptrskipmap.h"
+#include "lluuid.h"
+#include "llpermissionsflags.h"
+#include "llstring.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryObserver
+//
+// This class is designed to be a simple abstract base class which can
+// relay messages when the inventory changes.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryObserver
+{
+public:
+ // This enumeration is a way to refer to what changed in a more
+ // human readable format. You can mask the value provided by
+ // chaged() to see if the observer is interested in the change.
+ enum
+ {
+ NONE = 0,
+ LABEL = 1, // name changed
+ INTERNAL = 2, // internal change, eg, asset uuid different
+ ADD = 4, // something added
+ REMOVE = 8, // something deleted
+ STRUCTURE = 16, // structural change, eg, item or folder moved
+ CALLING_CARD = 32, // online, grant status, cancel, etc change
+ ALL = 0xffffffff
+ };
+ virtual ~LLInventoryObserver() {};
+ virtual void changed(U32 mask) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryModel
+//
+// This class represents a collection of inventory, and provides
+// efficient ways to access that information. This class could in
+// theory be used for any place where you need inventory, though it
+// optimizes for time efficiency - not space efficiency, probably
+// making it inappropriate for use on tasks.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class LLInventoryObject;
+class LLInventoryItem;
+class LLInventoryCategory;
+class LLViewerInventoryItem;
+class LLViewerInventoryCategory;
+class LLViewerInventoryItem;
+class LLViewerInventoryCategory;
+class LLMessageSystem;
+class LLInventoryCollectFunctor;
+
+class LLInventoryModel
+{
+public:
+ typedef enum e_has_children
+ {
+ CHILDREN_NO,
+ CHILDREN_YES,
+ CHILDREN_MAYBE
+ }EHasChildren;
+
+ // These are used a lot...
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryCategory> > cat_array_t;
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryItem> > item_array_t;
+
+ // construction & destruction
+ LLInventoryModel();
+ ~LLInventoryModel();
+
+ //
+ // Accessors
+ //
+
+ // This is a convenience function to check if one object has a
+ // parent chain up to the category specified by UUID.
+ BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id);
+
+ // Get the object by id. Returns NULL if not found.
+ // * WARNING: use the pointer returned for read operations - do
+ // not modify the object values in place or you will break stuff.
+ LLInventoryObject* getObject(const LLUUID& id) const;
+
+ // Get the item by id. Returns NULL if not found.
+ // * WARNING: use the pointer for read operations - use the
+ // updateItem() method to actually modify values.
+ LLViewerInventoryItem* getItem(const LLUUID& id) const;
+
+ // Get the category by id. Returns NULL if not found.
+ // * WARNING: use the pointer for read operations - use the
+ // updateCategory() method to actually modify values.
+ LLViewerInventoryCategory* getCategory(const LLUUID& id) const;
+
+ // Return the number of items or categories
+ S32 getItemCount() const;
+ S32 getCategoryCount() const;
+
+ // Return the direct descendents of the id provided.Set passed
+ // in values to NULL if the call fails.
+ // *WARNING: The array provided points straight into the guts of
+ // this object, and should only be used for read operations, since
+ // modifications may invalidate the internal state of the
+ // inventory.
+ void getDirectDescendentsOf(const LLUUID& cat_id,
+ cat_array_t*& categories,
+ item_array_t*& items) const;
+
+ // Starting with the object specified, add it's descendents to the
+ // array provided, but do not add the inventory object specified
+ // by id. There is no guaranteed order. Neither array will be
+ // erased before adding objects to it. Do not store a copy of the
+ // pointers collected - use them, and collect them again later if
+ // you need to reference the same objects.
+ enum { EXCLUDE_TRASH = FALSE, INCLUDE_TRASH = TRUE };
+ void collectDescendents(const LLUUID& id,
+ cat_array_t& categories,
+ item_array_t& items,
+ BOOL include_trash);
+
+ void collectDescendentsIf(const LLUUID& id,
+ cat_array_t& categories,
+ item_array_t& items,
+ BOOL include_trash,
+ LLInventoryCollectFunctor& add);
+
+ //
+ // Mutators
+ //
+
+ // Calling this method with an inventory item will either change
+ // an existing item with a matching item_id, or will add the item
+ // to the current inventory. Returns the change mask generated by
+ // the update. No notifcation will be sent to observers. This
+ // method will only generate network traffic if the item had to be
+ // reparented.
+ // *NOTE: In usage, you will want to perform cache accounting
+ // operations in LLInventoryModel::accountForUpdate() or
+ // LLViewerInventoryItem::updateServer() before calling this
+ // method.
+ U32 updateItem(const LLViewerInventoryItem* item);
+
+ // Calling this method with an inventory category will either
+ // change an existing item with the matching id, or it will add
+ // the category. No notifcation will be sent to observers. This
+ // method will only generate network traffic if the item had to be
+ // reparented.
+ // *NOTE: In usage, you will want to perform cache accounting
+ // operations in LLInventoryModel::accountForUpdate() or
+ // LLViewerInventoryCategory::updateServer() before calling this
+ // method.
+ void updateCategory(const LLViewerInventoryCategory* cat);
+
+ // This method will move the specified object id to the specified
+ // category, update the internal structures. No cache accounting,
+ // observer notification, or server update is performed.
+ void moveObject(const LLUUID& object_id, const LLUUID& cat_id);
+
+ // delete a particular inventory object by ID. This will purge one
+ // object from the internal data structures maintaining a
+ // cosistent internal state. No cache accounting, observer
+ // notification, or server update is performed.
+ void deleteObject(const LLUUID& id);
+
+ // This is a method which collects the descendents of the id
+ // provided. If the category is not found, no action is
+ // taken. This method goes through the long winded process of
+ // removing server representation of folders and items while doing
+ // cache accounting in a fairly efficient manner. This method does
+ // not notify observers (though maybe it shouldd...)
+ void purgeDescendentsOf(const LLUUID& id);
+
+ // This method optimally removes the referenced categories and
+ // items from the current agent's inventory in the database. It
+ // performs all of the during deletion. The local representation
+ // is not removed.
+ void deleteFromServer(LLDynamicArray<LLUUID>& category_ids,
+ LLDynamicArray<LLUUID>& item_ids);
+
+ // Add/remove an observer. If the observer is destroyed, be sure
+ // to remove it.
+ void addObserver(LLInventoryObserver* observer);
+ void removeObserver(LLInventoryObserver* observer);
+
+ //
+ // Misc Methods
+ //
+
+ // findCategoryUUIDForType() returns the uuid of the category that
+ // specifies 'type' as what it defaults to containing. The
+ // category is not necessarily only for that type. *NOTE: This
+ // will create a new inventory category on the fly if one does not
+ // exist.
+ LLUUID findCategoryUUIDForType(LLAssetType::EType preferred_type);
+
+ // Call this method when it's time to update everyone on a new
+ // state, by default, the inventory model will not update
+ // observers automatically.
+ void notifyObservers();
+
+ // This allows outsiders to tell the inventory if something has
+ // been changed 'under the hood', but outside the control of the
+ // inventory. For example, if we grant someone modify permissions,
+ // then that changes the data structures for LLAvatarTracker, but
+ // potentially affects inventory observers. This API makes sure
+ // that the next notify will include that notification.
+ void addChangedMask(U32 mask, const LLUUID& referent);
+
+ const std::set<LLUUID>& getChangedIDs() { return mChangedItemIDs; }
+
+ // This method to prepares a set of mock inventory which provides
+ // minimal functionality before the actual arrival of inventory.
+ //void mock(const LLUUID& root_id);
+
+ // make sure we have the descendents in the structure.
+ void fetchDescendentsOf(const LLUUID& folder_id);
+
+ // call this method to request the inventory.
+ //void requestFromServer(const LLUUID& agent_id);
+
+ // call this method on logout to save a terse representation
+ void cache(const LLUUID& parent_folder_id, const LLUUID& agent_id);
+
+ // Generates a string containing the path to the item specified by
+ // item_id.
+ void appendPath(const LLUUID& id, LLString& path);
+
+ // message handling functionality
+ static void registerCallbacks(LLMessageSystem* msg);
+
+ // Convenience function to create a new category. You could call
+ // updateCatgory() with a newly generated UUID category, but this
+ // version will take care of details like what the name should be
+ // based on preferred type. Returns the UUID of the new
+ // category. If you want to use the default name based on type,
+ // pass in a NULL to the 'name parameter.
+ LLUUID createNewCategory(const LLUUID& parent_id,
+ LLAssetType::EType preferred_type,
+ const LLString& name);
+
+ // methods to load up inventory skeleton & meat. These are used
+ // during authentication. return true if everything parsed.
+ typedef std::map<std::string, std::string> response_t;
+ typedef std::vector<response_t> options_t;
+ bool loadSkeleton(const options_t& options, const LLUUID& owner_id);
+ bool loadMeat(const options_t& options, const LLUUID& owner_id);
+
+ // This is a brute force method to rebuild the entire parent-child
+ // relations.
+ void buildParentChildMap();
+
+ //
+ // Category accounting.
+ //
+
+ // This structure represents the number of items added or removed
+ // from a category.
+ struct LLCategoryUpdate
+ {
+ LLCategoryUpdate() : mDescendentDelta(0) {}
+ LLCategoryUpdate(const LLUUID& category_id, S32 delta) :
+ mCategoryID(category_id),
+ mDescendentDelta(delta) {}
+ LLUUID mCategoryID;
+ S32 mDescendentDelta;
+ };
+ typedef std::vector<LLCategoryUpdate> update_list_t;
+
+ // This structure eixts to make it easier to account for deltas in
+ // a map.
+ struct LLInitializedS32
+ {
+ LLInitializedS32() : mValue(0) {}
+ S32 mValue;
+ LLInitializedS32& operator++() { ++mValue; return *this; }
+ LLInitializedS32& operator--() { --mValue; return *this; }
+ };
+ typedef std::map<LLUUID, LLInitializedS32> update_map_t;
+
+ // Call these methods when there are category updates, but call
+ // them *before* the actual update so the method can do descendent
+ // accounting correctly.
+ void accountForUpdate(const LLCategoryUpdate& update);
+ void accountForUpdate(const update_list_t& updates);
+ void accountForUpdate(const update_map_t& updates);
+
+ // Return child status of category children. yes/no/maybe
+ EHasChildren categoryHasChildren(const LLUUID& cat_id) const;
+
+ // returns true iff category version is known and theoretical
+ // descendents == actual descendents.
+ bool isCategoryComplete(const LLUUID& cat_id) const;
+
+ // start and stop background breadth-first fetching of inventory contents
+ // this gets triggered when performing a filter-search
+ static void startBackgroundFetch(const LLUUID& cat_id = LLUUID::null); // start fetch process
+ static void stopBackgroundFetch(); // stop fetch process
+ static BOOL backgroundFetchActive();
+ static bool isEverythingFetched();
+ static void backgroundFetch(void*); // background fetch idle function
+
+protected:
+
+ // Internal methods which add inventory and make sure that all of
+ // the internal data structures are consistent. These methods
+ // should be passed pointers of newly created objects, and the
+ // instance will take over the memory management from there.
+ void addCategory(LLViewerInventoryCategory* category);
+ void addItem(LLViewerInventoryItem* item);
+
+ // Internal method which looks for a category with the specified
+ // preferred type. Returns LLUUID::null if not found
+ LLUUID findCatUUID(LLAssetType::EType preferred_type);
+
+ // Empty the entire contents
+ void empty();
+
+ // Given the current state of the inventory items, figure out the
+ // clone information. *FIX: This is sub-optimal, since we can
+ // insert this information snurgically, but this makes sure the
+ // implementation works before we worry about optimization.
+ //void recalculateCloneInformation();
+
+ // file import/export.
+ static bool loadFromFile(
+ const char* filename,
+ cat_array_t& categories,
+ item_array_t& items);
+ static bool saveToFile(
+ const char* filename,
+ const cat_array_t& categories,
+ const item_array_t& items);
+
+ // message handling functionality
+ //static void processUseCachedInventory(LLMessageSystem* msg, void**);
+ static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**);
+ static void processRemoveInventoryItem(LLMessageSystem* msg, void**);
+ static void processUpdateInventoryFolder(LLMessageSystem* msg, void**);
+ static void processRemoveInventoryFolder(LLMessageSystem* msg, void**);
+ //static void processExchangeCallingcard(LLMessageSystem* msg, void**);
+ //static void processAddCallingcard(LLMessageSystem* msg, void**);
+ //static void processDeclineCallingcard(LLMessageSystem* msg, void**);
+ static void processSaveAssetIntoInventory(LLMessageSystem* msg, void**);
+ static void processBulkUpdateInventory(LLMessageSystem* msg, void**);
+ static void processInventoryDescendents(LLMessageSystem* msg, void**);
+ static void processMoveInventoryItem(LLMessageSystem* msg, void**);
+ static void processFetchInventoryReply(LLMessageSystem* msg, void**);
+
+ bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting, bool highlight_new);
+
+protected:
+ // Varaibles used to track what has changed since the last notify.
+ U32 mModifyMask;
+ typedef std::set<LLUUID> changed_items_t;
+ changed_items_t mChangedItemIDs;
+
+ // Information for tracking the actual inventory. We index this
+ // information in a lot of different ways so we can access
+ // the inventory using several different identifiers.
+ // mInventory member data is the 'master' list of inventory, and
+ // mCategoryMap and mItemMap store uuid->object mappings.
+ typedef std::map<LLUUID, LLPointer<LLViewerInventoryCategory> > cat_map_t;
+ typedef std::map<LLUUID, LLPointer<LLViewerInventoryItem> > item_map_t;
+ //inv_map_t mInventory;
+ cat_map_t mCategoryMap;
+ item_map_t mItemMap;
+
+ // cache recent lookups
+ mutable LLPointer<LLViewerInventoryItem> mLastItem;
+
+ // This last set of indices is used to map parents to children.
+ //LLPtrSkipMap<const LLUUID, cat_array_t*> mParentChildCategoryTree;
+ //LLPtrSkipMap<const LLUUID, item_array_t*> mParentChildItemTree;
+ typedef std::map<LLUUID, cat_array_t*> parent_cat_map_t;
+ typedef std::map<LLUUID, item_array_t*> parent_item_map_t;
+ parent_cat_map_t mParentChildCategoryTree;
+ parent_item_map_t mParentChildItemTree;
+
+ typedef std::set<LLInventoryObserver*> observer_list_t;
+ observer_list_t mObservers;
+
+ // completing the fetch once per session should be sufficient
+ static BOOL sBackgroundFetchActive;
+ static BOOL sTimelyFetchPending;
+ static BOOL sAllFoldersFetched;
+ static BOOL sFullFetchStarted;
+ static S32 sNumFetchRetries;
+ static LLFrameTimer sFetchTimer;
+ static F32 sMinTimeBetweenFetches;
+ static F32 sMaxTimeBetweenFetches;
+
+public:
+ // *NOTE: DEBUG functionality
+ void dumpInventory();
+};
+
+// a special inventory model for the agent
+extern LLInventoryModel gInventory;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryCollectFunctor
+//
+// Base class for LLInventoryModel::collectDescendentsIf() method
+// which accepts an instance of one of these objects to use as the
+// function to determine if it should be added. Derive from this class
+// and override the () operator to return TRUE if you want to collect
+// the category or item passed in.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryCollectFunctor
+{
+public:
+ virtual ~LLInventoryCollectFunctor(){};
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLAssetIDMatches
+//
+// This functor finds inventory items pointing to the specified asset
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLViewerInventoryItem;
+
+class LLAssetIDMatches : public LLInventoryCollectFunctor
+{
+public:
+ LLAssetIDMatches(const LLUUID& asset_id) : mAssetID(asset_id) {}
+ virtual ~LLAssetIDMatches() {}
+ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+
+protected:
+ LLUUID mAssetID;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIsType
+//
+// Implementation of a LLInventoryCollectFunctor which returns TRUE if
+// the type is the type passed in during construction.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLIsType : public LLInventoryCollectFunctor
+{
+public:
+ LLIsType(LLAssetType::EType type) : mType(type) {}
+ virtual ~LLIsType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLAssetType::EType mType;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIsNotType
+//
+// Implementation of a LLInventoryCollectFunctor which returns FALSE if the
+// type is the type passed in during construction, otherwise false.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLIsNotType : public LLInventoryCollectFunctor
+{
+public:
+ LLIsNotType(LLAssetType::EType type) : mType(type) {}
+ virtual ~LLIsNotType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLAssetType::EType mType;
+};
+
+class LLIsTypeWithPermissions : public LLInventoryCollectFunctor
+{
+public:
+ LLIsTypeWithPermissions(LLAssetType::EType type, const PermissionBit perms, const LLUUID &agent_id, const LLUUID &group_id)
+ : mType(type), mPerm(perms), mAgentID(agent_id), mGroupID(group_id) {}
+ virtual ~LLIsTypeWithPermissions() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLAssetType::EType mType;
+ PermissionBit mPerm;
+ LLUUID mAgentID;
+ LLUUID mGroupID;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIsClone
+//
+// Implementation of a LLInventoryCollectFunctor which returns TRUE if
+// the object is a clone of the item passed in during
+// construction.
+//
+// *NOTE: Since clone information is determined based off of asset id
+// (or creator with calling cards), if the id is NULL, it has no
+// clones - even itself.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+//class LLIsClone : public LLInventoryCollectFunctor
+//{
+//public:
+// LLIsClone(LLViewerInventoryItem* item) : mItem(item) {}
+// virtual ~LLIsClone() {}
+// virtual bool operator()(LLViewerInventoryCategory* cat,
+// LLViewerInventoryItem* item);
+//protected:
+// LLPointer<LLViewerInventoryItem> mItem;
+//};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLBuddyCollector
+//
+// Simple class that collects calling cards that are not null, and not
+// the agent. Duplicates are possible.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLBuddyCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLBuddyCollector() {}
+ virtual ~LLBuddyCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLUniqueBuddyCollector
+//
+// Simple class that collects calling cards that are not null, and not
+// the agent. Duplicates are discarded.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLUniqueBuddyCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLUniqueBuddyCollector() {}
+ virtual ~LLUniqueBuddyCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+
+protected:
+ std::set<LLUUID> mSeen;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLParticularBuddyCollector
+//
+// Simple class that collects calling cards that match a particular uuid
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLParticularBuddyCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLParticularBuddyCollector(const LLUUID& id) : mBuddyID(id) {}
+ virtual ~LLParticularBuddyCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLUUID mBuddyID;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLNameCategoryCollector
+//
+// Collects categories based on case-insensitive match of prefix
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLNameCategoryCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLNameCategoryCollector(const char* name) : mName(name) {}
+ virtual ~LLNameCategoryCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ std::string mName;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryCompletionObserver
+//
+// Class which can be used as a base class for doing something when
+// when all observed items are locally complete. This class implements
+// the changed() method of LLInventoryObserver and declares a new
+// method named done() which is called when all watched items have
+// complete information in the inventory model.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryCompletionObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryCompletionObserver() {}
+ virtual void changed(U32 mask);
+
+ void watchItem(const LLUUID& id);
+
+protected:
+ virtual void done() = 0;
+
+ typedef std::vector<LLUUID> item_ref_t;
+ item_ref_t mComplete;
+ item_ref_t mIncomplete;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryFetchObserver
+//
+// This class is much like the LLInventoryCompletionObserver, except
+// that it handles all the the fetching necessary. Override the done()
+// method to do the thing you want.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryFetchObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryFetchObserver() {}
+ virtual void changed(U32 mask);
+
+ typedef std::vector<LLUUID> item_ref_t;
+
+ bool isEverythingComplete() const;
+ void fetchItems(const item_ref_t& ids);
+ virtual void done() = 0;
+
+protected:
+ item_ref_t mComplete;
+ item_ref_t mIncomplete;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryFetchDescendentsObserver
+//
+// This class is much like the LLInventoryCompletionObserver, except
+// that it handles fetching based on category. Override the done()
+// method to do the thing you want.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryFetchDescendentsObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryFetchDescendentsObserver() {}
+ virtual void changed(U32 mask);
+
+ typedef std::vector<LLUUID> folder_ref_t;
+ void fetchDescendents(const folder_ref_t& ids);
+ bool isEverythingComplete() const;
+ virtual void done() = 0;
+
+protected:
+ bool isComplete(LLViewerInventoryCategory* cat);
+ folder_ref_t mIncompleteFolders;
+ folder_ref_t mCompleteFolders;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryFetchComboObserver
+//
+// This class does an appropriate combination of fetch descendents and
+// item fetches based on completion of categories and items. Much like
+// the fetch and fetch descendents, this will call done() when everything
+// has arrived.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryFetchComboObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryFetchComboObserver() : mDone(false) {}
+ virtual void changed(U32 mask);
+
+ typedef std::vector<LLUUID> folder_ref_t;
+ typedef std::vector<LLUUID> item_ref_t;
+ void fetch(const folder_ref_t& folder_ids, const item_ref_t& item_ids);
+
+ virtual void done() = 0;
+
+protected:
+ bool mDone;
+ folder_ref_t mCompleteFolders;
+ folder_ref_t mIncompleteFolders;
+ item_ref_t mCompleteItems;
+ item_ref_t mIncompleteItems;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryExistenceObserver
+//
+// This class is used as a base class for doing somethign when all the
+// observed item ids exist in the inventory somewhere. You can derive
+// a class from this class and implement the done() method to do
+// something useful.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryExistenceObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryExistenceObserver() {}
+ virtual void changed(U32 mask);
+
+ void watchItem(const LLUUID& id);
+
+protected:
+ virtual void done() = 0;
+
+ typedef std::vector<LLUUID> item_ref_t;
+ item_ref_t mExist;
+ item_ref_t mMIA;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryTransactionObserver
+//
+// Class which can be used as a base class for doing something when an
+// inventory transaction completes.
+//
+// *NOTE: This class is not quite complete. Avoid using unless you fix up it's
+// functionality gaps.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryTransactionObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryTransactionObserver(const LLTransactionID& transaction_id);
+ virtual void changed(U32 mask);
+
+protected:
+ typedef std::vector<LLUUID> folder_ref_t;
+ typedef std::vector<LLUUID> item_ref_t;
+ virtual void done(const folder_ref_t& folders, const item_ref_t& items) = 0;
+
+ LLTransactionID mTransactionID;
+};
+
+
+#endif // LL_LLINVENTORYMODEL_H
diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp
new file mode 100644
index 0000000000..a9b5e6e124
--- /dev/null
+++ b/indra/newview/lljoystickbutton.cpp
@@ -0,0 +1,847 @@
+/**
+ * @file lljoystickbutton.cpp
+ * @brief LLJoystick class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lljoystickbutton.h"
+
+// Library includes
+#include "llcoord.h"
+#include "indra_constants.h"
+
+// Project includes
+#include "llui.h"
+#include "llagent.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llmoveview.h"
+
+#include "llglheaders.h"
+
+const F32 NUDGE_TIME = 0.25f; // in seconds
+const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
+
+//
+// Public Methods
+//
+LLJoystick::LLJoystick(
+ const LLString& name,
+ LLRect rect,
+ const LLString &default_image,
+ const LLString &selected_image,
+ EJoystickQuadrant initial_quadrant )
+ :
+ LLButton(name, rect, default_image, selected_image, NULL, NULL),
+ mInitialQuadrant(initial_quadrant),
+ mInitialOffset(0, 0),
+ mLastMouse(0, 0),
+ mFirstMouse(0, 0),
+ mVertSlopNear(0),
+ mVertSlopFar(0),
+ mHorizSlopNear(0),
+ mHorizSlopFar(0),
+ mHeldDown(FALSE),
+ mHeldDownTimer()
+{
+ mHeldDownCallback = &LLJoystick::onHeldDown;
+ mCallbackUserData = this;
+}
+
+
+void LLJoystick::updateSlop()
+{
+ mVertSlopNear = mRect.getHeight();
+ mVertSlopFar = mRect.getHeight() * 2;
+
+ mHorizSlopNear = mRect.getWidth();
+ mHorizSlopFar = mRect.getWidth() * 2;
+
+ // Compute initial mouse offset based on initial quadrant.
+ // Place the mouse evenly between the near and far zones.
+ switch (mInitialQuadrant)
+ {
+ case JQ_ORIGIN:
+ mInitialOffset.set(0, 0);
+ break;
+
+ case JQ_UP:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_DOWN:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_LEFT:
+ mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ case JQ_RIGHT:
+ mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ default:
+ llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
+ break;
+ }
+
+ return;
+}
+
+
+BOOL LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ //llinfos << "joystick mouse down " << x << ", " << y << llendl;
+
+ mLastMouse.set(x, y);
+ mFirstMouse.set(x, y);
+
+ mMouseDownTimer.reset();
+ return LLButton::handleMouseDown(x, y, mask);
+}
+
+
+BOOL LLJoystick::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // llinfos << "joystick mouse up " << x << ", " << y << llendl;
+
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ mLastMouse.set(x, y);
+ mHeldDown = FALSE;
+ onMouseUp();
+ }
+
+ return LLButton::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLJoystick::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ mLastMouse.set(x, y);
+ }
+
+ return LLButton::handleHover(x, y, mask);
+}
+
+F32 LLJoystick::getElapsedHeldDownTime()
+{
+ if( mHeldDown )
+ {
+ return mMouseDownTimer.getElapsedTimeF32();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+// static
+void LLJoystick::onHeldDown(void *userdata)
+{
+ LLJoystick *self = (LLJoystick *)userdata;
+
+ llassert( gViewerWindow->hasMouseCapture( self ) );
+
+ self->mHeldDown = TRUE;
+ self->onHeldDown();
+}
+
+EJoystickQuadrant LLJoystick::selectQuadrant(LLXMLNodePtr node)
+{
+
+ EJoystickQuadrant quadrant = JQ_RIGHT;
+
+ if (node->hasAttribute("quadrant"))
+ {
+ LLString quadrant_name;
+ node->getAttributeString("quadrant", quadrant_name);
+
+ quadrant = quadrantFromName(quadrant_name.c_str());
+ }
+ return quadrant;
+}
+
+
+LLString LLJoystick::nameFromQuadrant(EJoystickQuadrant quadrant)
+{
+ if (quadrant == JQ_ORIGIN) return LLString("origin");
+ else if (quadrant == JQ_UP) return LLString("up");
+ else if (quadrant == JQ_DOWN) return LLString("down");
+ else if (quadrant == JQ_LEFT) return LLString("left");
+ else if (quadrant == JQ_RIGHT) return LLString("right");
+ else return LLString();
+}
+
+
+EJoystickQuadrant LLJoystick::quadrantFromName(const LLString& sQuadrant)
+{
+ EJoystickQuadrant quadrant = JQ_RIGHT;
+
+ if (sQuadrant == "origin")
+ {
+ quadrant = JQ_ORIGIN;
+ }
+ else if (sQuadrant == "up")
+ {
+ quadrant = JQ_UP;
+ }
+ else if (sQuadrant == "down")
+ {
+ quadrant = JQ_DOWN;
+ }
+ else if (sQuadrant == "left")
+ {
+ quadrant = JQ_LEFT;
+ }
+ else if (sQuadrant == "right")
+ {
+ quadrant = JQ_RIGHT;
+ }
+
+ return quadrant;
+}
+
+
+LLXMLNodePtr LLJoystick::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign));
+ node->createChild("quadrant", TRUE)->setStringValue(nameFromQuadrant(mInitialQuadrant));
+
+ addImageAttributeToXML(node,mImageUnselectedName,mImageUnselectedID,"image_unselected");
+ addImageAttributeToXML(node,mImageSelectedName,mImageSelectedID,"image_selected");
+
+ node->createChild("scale_image", TRUE)->setBoolValue(mScaleImage);
+
+ return node;
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickAgentTurn
+//-------------------------------------------------------------------------------
+
+void LLJoystickAgentTurn::onHeldDown()
+{
+ F32 time = getElapsedHeldDownTime();
+ updateSlop();
+
+ //llinfos << "move forward/backward (and/or turn)" << llendl;
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ float m = (float) (dx)/abs(dy);
+
+ if (m > 1) {
+ m = 1;
+ }
+ else if (m < -1) {
+ m = -1;
+ }
+ gAgent.moveYaw(-LLFloaterMove::getYawRate(time)*m);
+
+
+ // handle forward/back movement
+ if (dy > mVertSlopFar)
+ {
+ // ...if mouse is forward of run region run forward
+ gAgent.moveAt(1);
+ }
+ else if (dy > mVertSlopNear)
+ {
+ if( time < NUDGE_TIME )
+ {
+ gAgent.moveAtNudge(1);
+ }
+ else
+ {
+ // ...else if mouse is forward of walk region walk forward
+ // JC 9/5/2002 - Always run / move quickly.
+ gAgent.moveAt(1);
+ }
+ }
+ else if (dy < -mVertSlopFar)
+ {
+ // ...else if mouse is behind run region run backward
+ gAgent.moveAt(-1);
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ if( time < NUDGE_TIME )
+ {
+ gAgent.moveAtNudge(-1);
+ }
+ else
+ {
+ // ...else if mouse is behind walk region walk backward
+ // JC 9/5/2002 - Always run / move quickly.
+ gAgent.moveAt(-1);
+ }
+ }
+}
+
+LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("button");
+ node->getAttributeString("name", name);
+
+ LLString image_unselected;
+ if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected);
+
+ LLString image_selected;
+ if (node->hasAttribute("image_selected")) node->getAttributeString("image_selected",image_selected);
+
+ EJoystickQuadrant quad = JQ_ORIGIN;
+ if (node->hasAttribute("quadrant")) quad = selectQuadrant(node);
+
+ LLJoystickAgentTurn *button = new LLJoystickAgentTurn(name,
+ LLRect(),
+ image_unselected,
+ image_selected,
+ quad);
+
+ if (node->hasAttribute("halign"))
+ {
+ LLFontGL::HAlign halign = selectFontHAlign(node);
+ button->setHAlign(halign);
+ }
+
+ if (node->hasAttribute("scale_image"))
+ {
+ BOOL needsScale = FALSE;
+ node->getAttributeBOOL("scale_image",needsScale);
+ button->setScaleImage( needsScale );
+ }
+
+ button->initFromXML(node, parent);
+
+ return button;
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickAgentSlide
+//-------------------------------------------------------------------------------
+
+void LLJoystickAgentSlide::onMouseUp()
+{
+ F32 time = getElapsedHeldDownTime();
+ if( time < NUDGE_TIME )
+ {
+ switch (mInitialQuadrant)
+ {
+ case JQ_LEFT:
+ gAgent.moveLeftNudge(1);
+ break;
+
+ case JQ_RIGHT:
+ gAgent.moveLeftNudge(-1);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void LLJoystickAgentSlide::onHeldDown()
+{
+ //llinfos << "slide left/right (and/or move forward/backward)" << llendl;
+
+ updateSlop();
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ // handle left-right sliding
+ if (dx > mHorizSlopNear)
+ {
+ gAgent.moveLeft(-1);
+ }
+ else if (dx < -mHorizSlopNear)
+ {
+ gAgent.moveLeft(1);
+ }
+
+ // handle forward/back movement
+ if (dy > mVertSlopFar)
+ {
+ // ...if mouse is forward of run region run forward
+ gAgent.moveAt(1);
+ }
+ else if (dy > mVertSlopNear)
+ {
+ // ...else if mouse is forward of walk region walk forward
+ gAgent.moveAtNudge(1);
+ }
+ else if (dy < -mVertSlopFar)
+ {
+ // ...else if mouse is behind run region run backward
+ gAgent.moveAt(-1);
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ // ...else if mouse is behind walk region walk backward
+ gAgent.moveAtNudge(-1);
+ }
+}
+
+
+// static
+LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("button");
+ node->getAttributeString("name", name);
+
+ LLString image_unselected;
+ if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected);
+
+ LLString image_selected;
+ if (node->hasAttribute("image_selected")) node->getAttributeString("image_selected",image_selected);
+
+
+ EJoystickQuadrant quad = JQ_ORIGIN;
+ if (node->hasAttribute("quadrant")) quad = selectQuadrant(node);
+
+ LLJoystickAgentSlide *button = new LLJoystickAgentSlide(name,
+ LLRect(),
+ image_unselected,
+ image_selected,
+ quad);
+
+ if (node->hasAttribute("halign"))
+ {
+ LLFontGL::HAlign halign = selectFontHAlign(node);
+ button->setHAlign(halign);
+ }
+
+ if (node->hasAttribute("scale_image"))
+ {
+ BOOL needsScale = FALSE;
+ node->getAttributeBOOL("scale_image",needsScale);
+ button->setScaleImage( needsScale );
+ }
+
+ button->initFromXML(node, parent);
+
+ return button;
+}
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickCameraRotate
+//-------------------------------------------------------------------------------
+
+LLJoystickCameraRotate::LLJoystickCameraRotate(const LLString& name, LLRect rect, const LLString &out_img, const LLString &in_img)
+ :
+ LLJoystick(name, rect, out_img, in_img, JQ_ORIGIN),
+ mInLeft( FALSE ),
+ mInTop( FALSE ),
+ mInRight( FALSE ),
+ mInBottom( FALSE )
+{ }
+
+
+void LLJoystickCameraRotate::updateSlop()
+{
+ // do the initial offset calculation based on mousedown location
+
+ // small fixed slop region
+ mVertSlopNear = 16;
+ mVertSlopFar = 32;
+
+ mHorizSlopNear = 16;
+ mHorizSlopFar = 32;
+
+ return;
+}
+
+
+BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ updateSlop();
+
+ // Set initial offset based on initial click location
+ S32 horiz_center = mRect.getWidth() / 2;
+ S32 vert_center = mRect.getHeight() / 2;
+
+ S32 dx = x - horiz_center;
+ S32 dy = y - vert_center;
+
+ if (dy > dx && dy > -dx)
+ {
+ // top
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
+ mInitialQuadrant = JQ_UP;
+ }
+ else if (dy > dx && dy <= -dx)
+ {
+ // left
+ mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ mInitialQuadrant = JQ_LEFT;
+ }
+ else if (dy <= dx && dy <= -dx)
+ {
+ // bottom
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
+ mInitialQuadrant = JQ_DOWN;
+ }
+ else
+ {
+ // right
+ mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ mInitialQuadrant = JQ_RIGHT;
+ }
+
+ return LLJoystick::handleMouseDown(x, y, mask);
+}
+
+
+void LLJoystickCameraRotate::onHeldDown()
+{
+ updateSlop();
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ // left-right rotation
+ if (dx > mHorizSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitLeftKey(getOrbitRate());
+ }
+ else if (dx < -mHorizSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitRightKey(getOrbitRate());
+ }
+
+ // over/under rotation
+ if (dy > mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitUpKey(getOrbitRate());
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitDownKey(getOrbitRate());
+ }
+}
+
+F32 LLJoystickCameraRotate::getOrbitRate()
+{
+ F32 time = getElapsedHeldDownTime();
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
+ //llinfos << rate << llendl;
+ return rate;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+
+// Only used for drawing
+void LLJoystickCameraRotate::setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom )
+{
+ mInLeft = left;
+ mInTop = top;
+ mInRight = right;
+ mInBottom = bottom;
+}
+
+void LLJoystickCameraRotate::draw()
+{
+
+ if( getVisible() )
+ {
+ LLGLSUIDefault gls_ui;
+
+ gl_draw_image( 0, 0, mImageUnselected );
+
+ if( mInTop )
+ {
+ drawRotatedImage( mImageSelected, 0 );
+ }
+
+ if( mInRight )
+ {
+ drawRotatedImage( mImageSelected, 1 );
+ }
+
+ if( mInBottom )
+ {
+ drawRotatedImage( mImageSelected, 2 );
+ }
+
+ if( mInLeft )
+ {
+ drawRotatedImage( mImageSelected, 3 );
+ }
+
+ if (sDebugRects)
+ {
+ drawDebugRect();
+ }
+ }
+}
+
+// Draws image rotated by multiples of 90 degrees
+void LLJoystickCameraRotate::drawRotatedImage( LLImageGL* image, S32 rotations )
+{
+ S32 width = image->getWidth();
+ S32 height = image->getHeight();
+
+ F32 uv[][2] =
+ {
+ { 1.f, 1.f },
+ { 0.f, 1.f },
+ { 0.f, 0.f },
+ { 1.f, 0.f }
+ };
+
+ image->bind();
+
+ glColor4fv(UI_VERTEX_COLOR.mV);
+
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2fv( uv[ (rotations + 0) % 4]);
+ glVertex2i(width, height );
+
+ glTexCoord2fv( uv[ (rotations + 1) % 4]);
+ glVertex2i(0, height );
+
+ glTexCoord2fv( uv[ (rotations + 2) % 4]);
+ glVertex2i(0, 0);
+
+ glTexCoord2fv( uv[ (rotations + 3) % 4]);
+ glVertex2i(width, 0);
+ }
+ glEnd();
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickCameraTrack
+//-------------------------------------------------------------------------------
+
+
+void LLJoystickCameraTrack::onHeldDown()
+{
+ updateSlop();
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ if (dx > mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanRightKey(getOrbitRate());
+ }
+ else if (dx < -mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanLeftKey(getOrbitRate());
+ }
+
+ // over/under rotation
+ if (dy > mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanUpKey(getOrbitRate());
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanDownKey(getOrbitRate());
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickCameraZoom
+//-------------------------------------------------------------------------------
+
+LLJoystickCameraZoom::LLJoystickCameraZoom(const LLString& name, LLRect rect, const LLString &out_img, const LLString &plus_in_img, const LLString &minus_in_img)
+ :
+ LLJoystick(name, rect, out_img, "", JQ_ORIGIN),
+ mInTop( FALSE ),
+ mInBottom( FALSE )
+{
+ mPlusInImage = gImageList.getImage(LLUI::findAssetUUIDByName(plus_in_img), MIPMAP_FALSE, TRUE);
+ mMinusInImage = gImageList.getImage(LLUI::findAssetUUIDByName(minus_in_img), MIPMAP_FALSE, TRUE);
+}
+
+
+BOOL LLJoystickCameraZoom::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLJoystick::handleMouseDown(x, y, mask);
+
+ if( handled )
+ {
+ if (mFirstMouse.mY > mRect.getHeight() / 2)
+ {
+ mInitialQuadrant = JQ_UP;
+ }
+ else
+ {
+ mInitialQuadrant = JQ_DOWN;
+ }
+ }
+ return handled;
+}
+
+
+void LLJoystickCameraZoom::onHeldDown()
+{
+ updateSlop();
+
+ const F32 FAST_RATE = 2.5f; // two and a half times the normal rate
+
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ if (dy > mVertSlopFar)
+ {
+ // Zoom in fast
+ gAgent.unlockView();
+ gAgent.setOrbitInKey(FAST_RATE);
+ }
+ else if (dy > mVertSlopNear)
+ {
+ // Zoom in slow
+ gAgent.unlockView();
+ gAgent.setOrbitInKey(getOrbitRate());
+ }
+ else if (dy < -mVertSlopFar)
+ {
+ // Zoom out fast
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey(FAST_RATE);
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ // Zoom out slow
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey(getOrbitRate());
+ }
+}
+
+// Only used for drawing
+void LLJoystickCameraZoom::setToggleState( BOOL top, BOOL bottom )
+{
+ mInTop = top;
+ mInBottom = bottom;
+}
+
+void LLJoystickCameraZoom::draw()
+{
+ if( getVisible() )
+ {
+ if( mInTop )
+ {
+ gl_draw_image( 0, 0, mPlusInImage );
+ }
+ else
+ if( mInBottom )
+ {
+ gl_draw_image( 0, 0, mMinusInImage );
+ }
+ else
+ {
+ gl_draw_image( 0, 0, mImageUnselected );
+ }
+
+ if (sDebugRects)
+ {
+ drawDebugRect();
+ }
+ }
+}
+
+void LLJoystickCameraZoom::updateSlop()
+{
+ mVertSlopNear = mRect.getHeight() / 4;
+ mVertSlopFar = mRect.getHeight() / 2;
+
+ mHorizSlopNear = mRect.getWidth() / 4;
+ mHorizSlopFar = mRect.getWidth() / 2;
+
+ // Compute initial mouse offset based on initial quadrant.
+ // Place the mouse evenly between the near and far zones.
+ switch (mInitialQuadrant)
+ {
+ case JQ_ORIGIN:
+ mInitialOffset.set(0, 0);
+ break;
+
+ case JQ_UP:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_DOWN:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_LEFT:
+ mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ case JQ_RIGHT:
+ mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ default:
+ llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
+ break;
+ }
+
+ return;
+}
+
+
+F32 LLJoystickCameraZoom::getOrbitRate()
+{
+ F32 time = getElapsedHeldDownTime();
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
+// llinfos << "rate " << rate << " time " << time << llendl;
+ return rate;
+ }
+ else
+ {
+ return 1;
+ }
+}
diff --git a/indra/newview/lljoystickbutton.h b/indra/newview/lljoystickbutton.h
new file mode 100644
index 0000000000..a9a8acdfaf
--- /dev/null
+++ b/indra/newview/lljoystickbutton.h
@@ -0,0 +1,168 @@
+/**
+ * @file lljoystickbutton.h
+ * @brief LLJoystick class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOYSTICKBUTTON_H
+#define LL_LLJOYSTICKBUTTON_H
+
+#include "llbutton.h"
+#include "llcoord.h"
+
+typedef enum e_joystick_quadrant
+{
+ JQ_ORIGIN,
+ JQ_UP,
+ JQ_DOWN,
+ JQ_LEFT,
+ JQ_RIGHT
+} EJoystickQuadrant;
+
+class LLJoystick
+: public LLButton
+{
+public:
+ LLJoystick(const LLString& name, LLRect rect, const LLString &default_image, const LLString &selected_image, EJoystickQuadrant initial);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual void onMouseUp() {}
+ virtual void onHeldDown() = 0;
+ F32 getElapsedHeldDownTime();
+
+ static void onHeldDown(void *userdata); // called by llbutton callback handler
+ void setInitialQuadrant(EJoystickQuadrant initial) { mInitialQuadrant = initial; };
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLString nameFromQuadrant(const EJoystickQuadrant quadrant);
+ static EJoystickQuadrant quadrantFromName(const LLString& name);
+ static EJoystickQuadrant selectQuadrant(LLXMLNodePtr node);
+
+
+protected:
+ virtual void updateSlop(); // recompute slop margins
+
+protected:
+ EJoystickQuadrant mInitialQuadrant; // mousedown = click in this quadrant
+ LLCoordGL mInitialOffset; // pretend mouse started here
+ LLCoordGL mLastMouse; // where was mouse on last hover event
+ LLCoordGL mFirstMouse; // when mouse clicked, where was it
+ S32 mVertSlopNear; // where the slop regions end
+ S32 mVertSlopFar; // where the slop regions end
+ S32 mHorizSlopNear; // where the slop regions end
+ S32 mHorizSlopFar; // where the slop regions end
+ BOOL mHeldDown;
+ LLFrameTimer mHeldDownTimer;
+};
+
+
+// Turn agent left and right, move forward and back
+class LLJoystickAgentTurn
+: public LLJoystick
+{
+public:
+ LLJoystickAgentTurn(const LLString& name, LLRect rect, const LLString &default_image, const LLString &selected_image, EJoystickQuadrant initial)
+ : LLJoystick(name, rect, default_image, selected_image, initial)
+ { }
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_JOYSTICK_TURN; }
+ virtual LLString getWidgetTag() const { return LL_JOYSTICK_TURN; }
+
+ virtual void onHeldDown();
+
+ static LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+};
+
+
+// Slide left and right, move forward and back
+class LLJoystickAgentSlide
+: public LLJoystick
+{
+public:
+ LLJoystickAgentSlide(const LLString& name, LLRect rect, const LLString &default_image, const LLString &selected_image, EJoystickQuadrant initial)
+ : LLJoystick(name, rect, default_image, selected_image, initial)
+ { }
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_JOYSTICK_SLIDE; }
+ virtual LLString getWidgetTag() const { return LL_JOYSTICK_SLIDE; }
+
+
+ virtual void onHeldDown();
+ virtual void onMouseUp();
+
+ static LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+};
+
+
+// Rotate camera around the focus point
+class LLJoystickCameraRotate
+: public LLJoystick
+{
+public:
+ LLJoystickCameraRotate(const LLString& name, LLRect rect, const LLString &out_img, const LLString &in_img);
+
+ virtual void setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual void onHeldDown();
+ virtual void draw();
+
+protected:
+ F32 getOrbitRate();
+ virtual void updateSlop();
+ void drawRotatedImage( LLImageGL* image, S32 rotations );
+
+protected:
+ BOOL mInLeft;
+ BOOL mInTop;
+ BOOL mInRight;
+ BOOL mInBottom;
+};
+
+
+// Track the camera focus point forward/backward and side to side
+class LLJoystickCameraTrack
+: public LLJoystickCameraRotate
+{
+public:
+ LLJoystickCameraTrack(const LLString& name, LLRect rect, const LLString &out_img, const LLString &in_img)
+ : LLJoystickCameraRotate(name, rect, out_img, in_img)
+ { }
+
+ virtual void onHeldDown();
+};
+
+
+// Zoom the camera in and out
+class LLJoystickCameraZoom
+: public LLJoystick
+{
+public:
+ LLJoystickCameraZoom(const LLString& name, LLRect rect, const LLString &out_img, const LLString &plus_in_img, const LLString &minus_in_img);
+
+ virtual void setToggleState( BOOL top, BOOL bottom );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual void onHeldDown();
+ virtual void draw();
+
+protected:
+ virtual void updateSlop();
+ F32 getOrbitRate();
+
+protected:
+ BOOL mInTop;
+ BOOL mInBottom;
+ LLPointer<LLViewerImage> mPlusInImage;
+ LLPointer<LLViewerImage> mMinusInImage;
+};
+
+
+
+#endif // LL_LLJOYSTICKBUTTON_H
diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp
new file mode 100644
index 0000000000..0fb32c2dc3
--- /dev/null
+++ b/indra/newview/lllandmarklist.cpp
@@ -0,0 +1,116 @@
+/**
+ * @file lllandmarklist.cpp
+ * @brief Landmark asset list class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lllandmarklist.h"
+
+#include "message.h"
+#include "llassetstorage.h"
+
+#include "llagent.h"
+#include "llnotify.h"
+#include "llvfile.h"
+#include "llviewerstats.h"
+
+// Globals
+LLLandmarkList gLandmarkList;
+
+
+////////////////////////////////////////////////////////////////////////////
+// LLLandmarkList
+
+LLLandmarkList::~LLLandmarkList()
+{
+ std::for_each(mList.begin(), mList.end(), DeletePairedPointer());
+}
+
+LLLandmark* LLLandmarkList::getAsset( const LLUUID& asset_uuid )
+{
+ LLLandmark* landmark = get_ptr_in_map(mList, asset_uuid);
+ if(landmark)
+ {
+ return landmark;
+ }
+ else
+ {
+ if ( gLandmarkList.mBadList.find(asset_uuid) == gLandmarkList.mBadList.end() )
+ {
+ gAssetStorage->getAssetData(
+ asset_uuid,
+ LLAssetType::AT_LANDMARK,
+ LLLandmarkList::processGetAssetReply,
+ NULL);
+ }
+ }
+ return NULL;
+}
+
+// static
+void LLLandmarkList::processGetAssetReply(
+ LLVFS *vfs,
+ const LLUUID& uuid,
+ LLAssetType::EType type,
+ void* user_data,
+ S32 status)
+{
+ if( status == 0 )
+ {
+ LLVFile file(vfs, uuid, type);
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[ file_length + 1 ];
+ file.read( (U8*)buffer, file_length);
+ buffer[ file_length ] = 0;
+
+ LLLandmark* landmark = LLLandmark::constructFromString(buffer);
+ if (landmark)
+ {
+ LLVector3d pos;
+ if(!landmark->getGlobalPos(pos))
+ {
+ LLUUID region_id;
+ if(landmark->getRegionID(region_id))
+ {
+ LLLandmark::requestRegionHandle(
+ gMessageSystem,
+ gAgent.getRegionHost(),
+ region_id,
+ NULL);
+ }
+ }
+ gLandmarkList.mList[ uuid ] = landmark;
+ }
+
+ delete[] buffer;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status )
+ {
+ LLNotifyBox::showXml("LandmarkMissing");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadLandmark");
+ }
+
+ gLandmarkList.mBadList.insert(uuid);
+ }
+
+}
+
+BOOL LLLandmarkList::assetExists(const LLUUID& asset_uuid)
+{
+ return mList.count(asset_uuid) != 0 || mBadList.count(asset_uuid) != 0;
+}
diff --git a/indra/newview/lllandmarklist.h b/indra/newview/lllandmarklist.h
new file mode 100644
index 0000000000..15e900b601
--- /dev/null
+++ b/indra/newview/lllandmarklist.h
@@ -0,0 +1,51 @@
+/**
+ * @file lllandmarklist.h
+ * @brief Landmark asset list class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLANDMARKLIST_H
+#define LL_LLLANDMARKLIST_H
+
+#include <map>
+#include "lllandmark.h"
+#include "lluuid.h"
+#include "llassetstorage.h"
+
+class LLMessageSystem;
+class LLLineEditor;
+class LLInventoryItem;
+
+class LLLandmarkList
+{
+public:
+ LLLandmarkList() {}
+ ~LLLandmarkList();
+
+ //S32 getLength() { return mList.getLength(); }
+ //const LLLandmark* getFirst() { return mList.getFirstData(); }
+ //const LLLandmark* getNext() { return mList.getNextData(); }
+
+ BOOL assetExists(const LLUUID& asset_uuid);
+ LLLandmark* getAsset(const LLUUID& asset_uuid);
+ static void processGetAssetReply(
+ LLVFS *vfs,
+ const LLUUID& uuid,
+ LLAssetType::EType type,
+ void* user_data,
+ S32 status);
+
+protected:
+ typedef std::map<LLUUID, LLLandmark*> landmark_list_t;
+ landmark_list_t mList;
+
+ typedef std::set<LLUUID> landmark_bad_list_t;
+ landmark_bad_list_t mBadList;
+};
+
+
+extern LLLandmarkList gLandmarkList;
+
+#endif // LL_LLLANDMARKLIST_H
diff --git a/indra/newview/lllightconstants.h b/indra/newview/lllightconstants.h
new file mode 100644
index 0000000000..01dbc7d1cc
--- /dev/null
+++ b/indra/newview/lllightconstants.h
@@ -0,0 +1,15 @@
+/**
+ * @file lllightconstants.h
+ * @brief Constants for light functions
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLIGHTCONSTANTS_H
+#define LL_LLLIGHTCONSTANTS_H
+
+//const F32 LIGHT_MAX_RADIUS = 20.f; // Maximum range of light
+const F32 LIGHT_FADE_TIME = 0.2f;
+
+#endif // LL_LLLIGHTCONSTANTS_H
diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp
new file mode 100644
index 0000000000..32f0032b0b
--- /dev/null
+++ b/indra/newview/lllogchat.cpp
@@ -0,0 +1,82 @@
+/**
+ * @file lllogchat.cpp
+ * @brief LLLogChat class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lllogchat.h"
+
+const S32 LOG_RECALL_SIZE = 2048;
+
+//static
+LLString LLLogChat::makeLogFileName(LLString filename)
+{
+
+ filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename.c_str());
+ filename += ".txt";
+ return filename;
+}
+
+//static
+void LLLogChat::saveHistory(LLString filename, LLString line)
+{
+ FILE *fp = LLFile::fopen(LLLogChat::makeLogFileName(filename).c_str(), "a");
+ if (!fp)
+ {
+ llinfos << "Couldn't open chat history log!" << llendl;
+ }
+ else
+ {
+ fprintf(fp, "%s\n", line.c_str());
+
+ fclose (fp);
+ }
+}
+
+void LLLogChat::loadHistory(LLString filename , void (*callback)(LLString,void*), void* userdata)
+{
+ FILE *fptr = LLFile::fopen(makeLogFileName(filename).c_str(), "r");
+ if (!fptr)
+ {
+ return; //No previous conversation with this name.
+ }
+ else
+ {
+ char buffer[LOG_RECALL_SIZE];
+ char *bptr;
+ S32 len;
+ bool firstline=TRUE;
+
+ if ( fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END) )
+ { //File is smaller than recall size. Get it all.
+ firstline = FALSE;
+ if ( fseek(fptr, 0, SEEK_SET) )
+ {
+ fclose(fptr);
+ return;
+ }
+ }
+
+ while ( fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr) )
+ {
+ len = strlen(buffer) - 1;
+ for ( bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
+
+ if (!firstline)
+ {
+ callback(buffer,userdata);
+ }
+ else
+ {
+ firstline = FALSE;
+ }
+ }
+ callback("-- End of Log ---",userdata);
+
+ fclose(fptr);
+ }
+}
diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h
new file mode 100644
index 0000000000..ac2d710b64
--- /dev/null
+++ b/indra/newview/lllogchat.h
@@ -0,0 +1,21 @@
+/**
+ * @file lllogchat.h
+ * @brief LLFloaterChat class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLLOGCHAT_H
+#define LL_LLLOGCHAT_H
+
+class LLLogChat
+{
+public:
+ static LLString makeLogFileName(LLString(filename));
+ static void saveHistory(LLString filename, LLString line);
+ static void loadHistory(LLString filename, void (*callback)(LLString,void*),void* userdata);
+};
+
+#endif
diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp
new file mode 100644
index 0000000000..74c7ae6c18
--- /dev/null
+++ b/indra/newview/llmanip.cpp
@@ -0,0 +1,581 @@
+/**
+ * @file llmanip.cpp
+ * @brief LLManip class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmanip.h"
+
+#include "llmath.h"
+#include "v3math.h"
+//#include "llquaternion.h"
+#include "llgl.h"
+#include "llprimitive.h"
+#include "llview.h"
+#include "llviewerimagelist.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llfontgl.h"
+#include "llhudrender.h"
+#include "llselectmgr.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerjoint.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h" // for gWorldPointer
+#include "llresmgr.h"
+#include "viewer.h" // for gFPS
+#include "pipeline.h"
+#include "llglheaders.h"
+
+// Local constants...
+const S32 VERTICAL_OFFSET = 50;
+
+F32 LLManip::sHelpTextVisibleTime = 2.f;
+F32 LLManip::sHelpTextFadeTime = 2.f;
+S32 LLManip::sNumTimesHelpTextShown = 0;
+S32 LLManip::sMaxTimesShowHelpText = 5;
+F32 LLManip::sGridMaxSubdivisionLevel = 32.f;
+F32 LLManip::sGridMinSubdivisionLevel = 1.f;
+LLVector2 LLManip::sTickLabelSpacing(60.f, 25.f);
+
+
+//static
+void LLManip::rebuild(LLViewerObject* vobj)
+{
+ LLDrawable* drawablep = vobj->mDrawable;
+ if (drawablep && drawablep->getVOVolume())
+ {
+
+ gPipeline.markRebuild(drawablep,LLDrawable::REBUILD_VOLUME, TRUE);
+ //gPipeline.markMoved(drawablep, FALSE);
+ //gPipeline.updateMoveNormalAsync(vobj->mDrawable);
+
+ drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED
+ drawablep->updateMove();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// LLManip
+
+
+LLManip::LLManip( const LLString& name, LLToolComposite* composite )
+ :
+ LLTool( name, composite ),
+ mInSnapRegime(FALSE)
+{}
+
+
+void LLManip::getManipNormal(LLViewerObject* object, EManipPart manip, LLVector3 &normal)
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ if (manip >= LL_X_ARROW && manip <= LL_Z_ARROW)
+ {
+ LLVector3 arrow_axis;
+ getManipAxis(object, manip, arrow_axis);
+
+ LLVector3 cross = arrow_axis % gCamera->getAtAxis();
+ normal = cross % arrow_axis;
+ normal.normVec();
+ }
+ else if (manip >= LL_YZ_PLANE && manip <= LL_XY_PLANE)
+ {
+ switch (manip)
+ {
+ case LL_YZ_PLANE:
+ normal = LLVector3::x_axis;
+ break;
+ case LL_XZ_PLANE:
+ normal = LLVector3::y_axis;
+ break;
+ case LL_XY_PLANE:
+ normal = LLVector3::z_axis;
+ break;
+ default:
+ break;
+ }
+ normal.rotVec(grid_rotation);
+ }
+ else
+ {
+ normal.clearVec();
+ }
+}
+
+
+BOOL LLManip::getManipAxis(LLViewerObject* object, EManipPart manip, LLVector3 &axis)
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ if (manip == LL_X_ARROW)
+ {
+ axis = LLVector3::x_axis;
+ }
+ else if (manip == LL_Y_ARROW)
+ {
+ axis = LLVector3::y_axis;
+ }
+ else if (manip == LL_Z_ARROW)
+ {
+ axis = LLVector3::z_axis;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ axis.rotVec( grid_rotation );
+ return TRUE;
+}
+
+F32 LLManip::getSubdivisionLevel(const LLVector3 &reference_point, const LLVector3 &translate_axis, F32 grid_scale, S32 min_pixel_spacing)
+{
+ //update current snap subdivision level
+ LLVector3 cam_to_reference;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_to_reference = LLVector3(1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
+ }
+ else
+ {
+ cam_to_reference = reference_point - gCamera->getOrigin();
+ }
+ F32 current_range = cam_to_reference.normVec();
+
+ F32 projected_translation_axis_length = (translate_axis % cam_to_reference).magVec();
+ F32 subdivisions = llmax(projected_translation_axis_length * grid_scale / (current_range / gCamera->getPixelMeterRatio() * min_pixel_spacing), 0.f);
+ subdivisions = llclamp((F32)pow(2.f, llfloor(log(subdivisions) / log(2.f))), 1.f / 32.f, 32.f);
+
+ return subdivisions;
+}
+
+BOOL LLManip::handleHover(S32 x, S32 y, MASK mask)
+{
+ // We only handle the event if mousedown started with us
+ if( hasMouseCapture() )
+ {
+ if( gSelectMgr->isEmpty() )
+ {
+ // Somehow the object got deselected while we were dragging it.
+ // Release the mouse
+ setMouseCapture( FALSE );
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManip (active)" << llendl;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManip (inactive)" << llendl;
+ }
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ return TRUE;
+}
+
+
+BOOL LLManip::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ if( hasMouseCapture() )
+ {
+ handled = TRUE;
+ setMouseCapture( FALSE );
+ }
+ return handled;
+}
+
+void LLManip::updateGridSettings()
+{
+ sGridMaxSubdivisionLevel = gSavedSettings.getBOOL("GridSubUnit") ? (F32)gSavedSettings.getS32("GridSubdivision") : 1.f;
+}
+
+BOOL LLManip::getMousePointOnPlaneAgent(LLVector3& point, S32 x, S32 y, LLVector3 origin, LLVector3 normal)
+{
+ LLVector3d origin_double = gAgent.getPosGlobalFromAgent(origin);
+ LLVector3d global_point;
+ BOOL result = getMousePointOnPlaneGlobal(global_point, x, y, origin_double, normal);
+ point = gAgent.getPosAgentFromGlobal(global_point);
+ return result;
+}
+
+BOOL LLManip::getMousePointOnPlaneGlobal(LLVector3d& point, S32 x, S32 y, LLVector3d origin, LLVector3 normal)
+{
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ BOOL result = FALSE;
+ F32 mouse_x = ((F32)x / gViewerWindow->getWindowWidth() - 0.5f) * gCamera->getAspect() / gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 mouse_y = ((F32)y / gViewerWindow->getWindowHeight() - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+
+ LLVector3 origin_agent = gAgent.getPosAgentFromGlobal(origin);
+ LLVector3 mouse_pos = LLVector3(0.f, -mouse_x, mouse_y);
+ if (llabs(normal.mV[VX]) < 0.001f)
+ {
+ // use largish value that should be outside HUD manipulation range
+ mouse_pos.mV[VX] = 10.f;
+ }
+ else
+ {
+ mouse_pos.mV[VX] = (normal * (origin_agent - mouse_pos))
+ / (normal.mV[VX]);
+ result = TRUE;
+ }
+
+ point = gAgent.getPosGlobalFromAgent(mouse_pos);
+ return result;
+ }
+ else
+ {
+ return gViewerWindow->mousePointOnPlaneGlobal(
+ point, x, y, origin, normal );
+ }
+
+ //return FALSE;
+}
+
+// Given the line defined by mouse cursor (a1 + a_param*(a2-a1)) and the line defined by b1 + b_param*(b2-b1),
+// returns a_param and b_param for the points where lines are closest to each other.
+// Returns false if the two lines are parallel.
+BOOL LLManip::nearestPointOnLineFromMouse( S32 x, S32 y, const LLVector3& b1, const LLVector3& b2, F32 &a_param, F32 &b_param ) const
+{
+ LLVector3 a1;
+ LLVector3 a2;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) * gCamera->getAspect() / gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 mouse_y = (((F32)y / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+ a1 = LLVector3(llmin(b1.mV[VX] - 0.1f, b2.mV[VX] - 0.1f, 0.f), -mouse_x, mouse_y);
+ a2 = a1 + LLVector3(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ a1 = gAgent.getCameraPositionAgent();
+ a2 = gAgent.getCameraPositionAgent() + LLVector3(gViewerWindow->mouseDirectionGlobal(x, y));
+ }
+
+ BOOL parallel = TRUE;
+ LLVector3 a = a2 - a1;
+ LLVector3 b = b2 - b1;
+
+ LLVector3 normal;
+ F32 dist, denom;
+ normal = (b % a) % b; // normal to plane (P) through b and (shortest line between a and b)
+ normal.normVec();
+ dist = b1 * normal; // distance from origin to P
+
+ denom = normal * a;
+ if( (denom < -F_APPROXIMATELY_ZERO) || (F_APPROXIMATELY_ZERO < denom) )
+ {
+ a_param = (dist - normal * a1) / denom;
+ parallel = FALSE;
+ }
+
+ normal = (a % b) % a; // normal to plane (P) through a and (shortest line between a and b)
+ normal.normVec();
+ dist = a1 * normal; // distance from origin to P
+ denom = normal * b;
+ if( (denom < -F_APPROXIMATELY_ZERO) || (F_APPROXIMATELY_ZERO < denom) )
+ {
+ b_param = (dist - normal * b1) / denom;
+ parallel = FALSE;
+ }
+
+ return parallel;
+}
+
+LLVector3 LLManip::getSavedPivotPoint() const
+{
+ return gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+}
+
+LLVector3 LLManip::getPivotPoint() const
+{
+ if (gSelectMgr->getFirstObject() && gSelectMgr->getObjectCount() == 1 && gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ return gSelectMgr->getFirstObject()->getPivotPositionAgent();
+ }
+ return gSelectMgr->getBBoxOfSelection().getCenterAgent();
+}
+
+
+void LLManip::renderGuidelines(BOOL draw_x, BOOL draw_y, BOOL draw_z)
+{
+ LLVector3 grid_origin;
+ LLQuaternion grid_rot;
+ LLVector3 grid_scale;
+ gSelectMgr->getGrid(grid_origin, grid_rot, grid_scale);
+
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if (!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ if (!object)
+ {
+ return;
+ }
+ }
+
+ //LLVector3 center_agent = gSelectMgr->getBBoxOfSelection().getCenterAgent();
+ LLVector3 center_agent = getPivotPoint();
+
+ glPushMatrix();
+ {
+ glTranslatef(center_agent.mV[VX], center_agent.mV[VY], center_agent.mV[VZ]);
+
+ F32 angle_radians, x, y, z;
+
+ grid_rot.getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ F32 region_size = gWorldPointer->getRegionWidthInMeters();
+
+ const F32 LINE_ALPHA = 0.33f;
+
+ LLGLSNoTexture gls_no_texture;
+ LLUI::setLineWidth(1.5f);
+
+ if (draw_x)
+ {
+ glColor4f(1.f, 0.f, 0.f, LINE_ALPHA);
+ glBegin(GL_LINES);
+ glVertex3f( -region_size, 0.f, 0.f );
+ glVertex3f( region_size, 0.f, 0.f );
+ glEnd();
+ }
+
+ if (draw_y)
+ {
+ glColor4f(0.f, 1.f, 0.f, LINE_ALPHA);
+ glBegin(GL_LINES);
+ glVertex3f( 0.f, -region_size, 0.f );
+ glVertex3f( 0.f, region_size, 0.f );
+ glEnd();
+ }
+
+ if (draw_z)
+ {
+ glColor4f(0.f, 0.f, 1.f, LINE_ALPHA);
+ glBegin(GL_LINES);
+ glVertex3f( 0.f, 0.f, -region_size );
+ glVertex3f( 0.f, 0.f, region_size );
+ glEnd();
+ }
+ LLUI::setLineWidth(1.0f);
+ }
+ glPopMatrix();
+}
+
+void LLManip::renderXYZ(const LLVector3 &vec)
+{
+ const S32 PAD = 10;
+ char feedback_string[128];
+ LLVector3 camera_pos = gCamera->getOrigin() + gCamera->getAtAxis();
+ S32 vertical_offset = gViewerWindow->getWindowHeight() / 2 - VERTICAL_OFFSET;
+ S32 window_center_x = gViewerWindow->getWindowWidth() / 2;
+ S32 window_center_y = gViewerWindow->getWindowHeight() / 2;
+
+ LLUUID image_id;
+ image_id.set(gViewerArt.getString("rounded_square.tga"));
+ LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ glPushMatrix();
+ {
+ gViewerWindow->setup2DRender();
+ const LLVector2& display_scale = gViewerWindow->getDisplayScale();
+ glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+ glColor4f(0.f, 0.f, 0.f, 0.7f);
+
+ gl_draw_scaled_image_with_border(window_center_x - 115,
+ window_center_y + vertical_offset - PAD,
+ 16,
+ 16,
+ 235,
+ PAD * 2 + 10,
+ imagep,
+ LLColor4(0.f, 0.f, 0.f, 0.7f) );
+ }
+ glPopMatrix();
+
+ gViewerWindow->setup3DRender();
+
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+ LLGLDepthTest gls_depth(GL_FALSE);
+ LLGLEnable tex(GL_TEXTURE_2D);
+ // render drop shadowed text
+ sprintf(feedback_string, "X: %.3f", vec.mV[VX]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -102.f + 1.f, (F32)vertical_offset - 1.f, LLColor4::black, FALSE);
+
+ sprintf(feedback_string, "Y: %.3f", vec.mV[VY]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -27.f + 1.f, (F32)vertical_offset - 1.f, LLColor4::black, FALSE);
+
+ sprintf(feedback_string, "Z: %.3f", vec.mV[VZ]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, 48.f + 1.f, (F32)vertical_offset - 1.f, LLColor4::black, FALSE);
+
+ // render text on top
+ sprintf(feedback_string, "X: %.3f", vec.mV[VX]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -102.f, (F32)vertical_offset, LLColor4(1.f, 0.5f, 0.5f, 1.f), FALSE);
+
+ glColor3f(0.5f, 1.f, 0.5f);
+ sprintf(feedback_string, "Y: %.3f", vec.mV[VY]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -27.f, (F32)vertical_offset, LLColor4(0.5f, 1.f, 0.5f, 1.f), FALSE);
+
+ glColor3f(0.5f, 0.5f, 1.f);
+ sprintf(feedback_string, "Z: %.3f", vec.mV[VZ]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, 48.f, (F32)vertical_offset, LLColor4(0.5f, 0.5f, 1.f, 1.f), FALSE);
+ }
+}
+
+void LLManip::renderTickText(const LLVector3& pos, const char* text, const LLColor4 &color)
+{
+ const LLFontGL* big_fontp = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ BOOL hud_selection = gSelectMgr->getSelectType() == SELECT_TYPE_HUD;
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ LLVector3 render_pos = pos;
+ if (hud_selection)
+ {
+ F32 zoom_amt = gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 inv_zoom_amt = 1.f / zoom_amt;
+ // scale text back up to counter-act zoom level
+ render_pos = pos * zoom_amt;
+ glScalef(inv_zoom_amt, inv_zoom_amt, inv_zoom_amt);
+ }
+
+ // render shadow first
+ LLColor4 shadow_color = LLColor4::black;
+ LLGLEnable tex(GL_TEXTURE_2D);
+ shadow_color.mV[VALPHA] = color.mV[VALPHA] * 0.5f;
+ gViewerWindow->setupViewport(1, -1);
+ hud_render_utf8text(text, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(text), 3.f, shadow_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ gViewerWindow->setupViewport();
+ hud_render_utf8text(text, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(text), 3.f, color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+
+ glPopMatrix();
+}
+
+void LLManip::renderTickValue(const LLVector3& pos, F32 value, const char* suffix, const LLColor4 &color)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ const LLFontGL* big_fontp = gResMgr->getRes( LLFONT_SANSSERIF );
+ const LLFontGL* small_fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+
+ char val_string[128];
+ char fraction_string[128];
+ F32 val_to_print = llround(value, 0.001f);
+ S32 fractional_portion = llround(fmodf(llabs(val_to_print), 1.f) * 100.f);
+ if (val_to_print < 0.f)
+ {
+ if (fractional_portion == 0)
+ {
+ sprintf(val_string, "-%d%s", lltrunc(llabs(val_to_print)), suffix);
+ }
+ else
+ {
+ sprintf(val_string, "-%d", lltrunc(llabs(val_to_print)));
+ }
+ }
+ else
+ {
+ if (fractional_portion == 0)
+ {
+ sprintf(val_string, "%d%s", lltrunc(llabs(val_to_print)), suffix);
+ }
+ else
+ {
+ sprintf(val_string, "%d", lltrunc(val_to_print));
+ }
+ }
+
+ BOOL hud_selection = gSelectMgr->getSelectType() == SELECT_TYPE_HUD;
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ LLVector3 render_pos = pos;
+ if (hud_selection)
+ {
+ F32 zoom_amt = gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 inv_zoom_amt = 1.f / zoom_amt;
+ // scale text back up to counter-act zoom level
+ render_pos = pos * zoom_amt;
+ glScalef(inv_zoom_amt, inv_zoom_amt, inv_zoom_amt);
+ }
+
+ LLColor4 shadow_color = LLColor4::black;
+ shadow_color.mV[VALPHA] = color.mV[VALPHA] * 0.5f;
+
+ LLGLEnable tex(GL_TEXTURE_2D);
+ if (fractional_portion != 0)
+ {
+ sprintf(fraction_string, "%c%d%s", gResMgr->getDecimalPoint(), fractional_portion, suffix);
+
+ gViewerWindow->setupViewport(1, -1);
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -1.f * big_fontp->getWidthF32(val_string), 3.f, shadow_color, hud_selection);
+ hud_render_utf8text(fraction_string, render_pos, *small_fontp, LLFontGL::NORMAL, 1.f, 3.f, shadow_color, hud_selection);
+
+ gViewerWindow->setupViewport();
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -1.f * big_fontp->getWidthF32(val_string), 3.f, color, hud_selection);
+ hud_render_utf8text(fraction_string, render_pos, *small_fontp, LLFontGL::NORMAL, 1.f, 3.f, color, hud_selection);
+ }
+ else
+ {
+ gViewerWindow->setupViewport(1, -1);
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(val_string), 3.f, shadow_color, hud_selection);
+ gViewerWindow->setupViewport();
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(val_string), 3.f, color, hud_selection);
+ }
+ glPopMatrix();
+}
+
+LLColor4 LLManip::setupSnapGuideRenderPass(S32 pass)
+{
+ static LLColor4 grid_color_fg = gColors.getColor("GridlineColor");
+ static LLColor4 grid_color_bg = gColors.getColor("GridlineBGColor");
+ static LLColor4 grid_color_shadow = gColors.getColor("GridlineShadowColor");
+
+ LLColor4 line_color;
+ F32 line_alpha = gSavedSettings.getF32("GridOpacity");
+
+ switch(pass)
+ {
+ case 0:
+ // shadow
+ gViewerWindow->setupViewport(1, -1);
+ line_color = grid_color_shadow;
+ line_color.mV[VALPHA] *= line_alpha;
+ LLUI::setLineWidth(2.f);
+ break;
+ case 1:
+ // hidden lines
+ gViewerWindow->setupViewport();
+ line_color = grid_color_bg;
+ line_color.mV[VALPHA] *= line_alpha;
+ LLUI::setLineWidth(1.f);
+ break;
+ case 2:
+ // visible lines
+ line_color = grid_color_fg;
+ line_color.mV[VALPHA] *= line_alpha;
+ break;
+ }
+
+ return line_color;
+}
diff --git a/indra/newview/llmanip.h b/indra/newview/llmanip.h
new file mode 100644
index 0000000000..b1c2474166
--- /dev/null
+++ b/indra/newview/llmanip.h
@@ -0,0 +1,137 @@
+/**
+ * @file llmanip.h
+ * @brief LLManip class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MANIP_H
+#define LL_MANIP_H
+
+#include "lltool.h"
+//#include "v3math.h"
+
+class LLView;
+class LLTextBox;
+class LLViewerObject;
+class LLToolComposite;
+class LLVector3;
+
+const S32 MIN_DIVISION_PIXEL_WIDTH = 9;
+
+class LLManip : public LLTool
+{
+public:
+ typedef enum e_manip_part
+ {
+ LL_NO_PART = 0,
+
+ // Translation
+ LL_X_ARROW,
+ LL_Y_ARROW,
+ LL_Z_ARROW,
+
+ LL_YZ_PLANE,
+ LL_XZ_PLANE,
+ LL_XY_PLANE,
+
+ // Scale
+ LL_CORNER_NNN,
+ LL_CORNER_NNP,
+ LL_CORNER_NPN,
+ LL_CORNER_NPP,
+ LL_CORNER_PNN,
+ LL_CORNER_PNP,
+ LL_CORNER_PPN,
+ LL_CORNER_PPP,
+
+ // Faces
+ LL_FACE_POSZ,
+ LL_FACE_POSX,
+ LL_FACE_POSY,
+ LL_FACE_NEGX,
+ LL_FACE_NEGY,
+ LL_FACE_NEGZ,
+
+ // Edges
+ LL_EDGE_NEGX_NEGY,
+ LL_EDGE_NEGX_POSY,
+ LL_EDGE_POSX_NEGY,
+ LL_EDGE_POSX_POSY,
+
+ LL_EDGE_NEGY_NEGZ,
+ LL_EDGE_NEGY_POSZ,
+ LL_EDGE_POSY_NEGZ,
+ LL_EDGE_POSY_POSZ,
+
+ LL_EDGE_NEGZ_NEGX,
+ LL_EDGE_NEGZ_POSX,
+ LL_EDGE_POSZ_NEGX,
+ LL_EDGE_POSZ_POSX,
+
+ // Rotation Manip
+ LL_ROT_GENERAL,
+ LL_ROT_X,
+ LL_ROT_Y,
+ LL_ROT_Z,
+ LL_ROT_ROLL
+ } EManipPart;
+
+ // For use in loops and range checking.
+ typedef enum e_select_part_ranges
+ {
+ LL_ARROW_MIN = LL_X_ARROW,
+ LL_ARROW_MAX = LL_Z_ARROW,
+
+ LL_CORNER_MIN = LL_CORNER_NNN,
+ LL_CORNER_MAX = LL_CORNER_PPP,
+
+ LL_FACE_MIN = LL_FACE_POSZ,
+ LL_FACE_MAX = LL_FACE_NEGZ,
+
+ LL_EDGE_MIN = LL_EDGE_NEGX_NEGY,
+ LL_EDGE_MAX = LL_EDGE_POSZ_POSX
+ } EManipPartRanges;
+public:
+ static void rebuild(LLViewerObject* vobj);
+
+ LLManip( const LLString& name, LLToolComposite* composite );
+
+ virtual BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask) = 0;
+ void renderGuidelines(BOOL draw_x = TRUE, BOOL draw_y = TRUE, BOOL draw_z = TRUE);
+ static void renderXYZ(const LLVector3 &vec);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0;
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual EManipPart getHighlightedPart() { return LL_NO_PART; }
+ virtual void highlightManipulators(S32 x, S32 y) {};
+protected:
+ LLVector3 getSavedPivotPoint() const;
+ LLVector3 getPivotPoint() const;
+ void getManipNormal(LLViewerObject* object, EManipPart manip, LLVector3 &normal);
+ BOOL getManipAxis(LLViewerObject* object, EManipPart manip, LLVector3 &axis);
+ F32 getSubdivisionLevel(const LLVector3 &reference_point, const LLVector3 &translate_axis, F32 grid_scale, S32 min_pixel_spacing = MIN_DIVISION_PIXEL_WIDTH);
+ void renderTickValue(const LLVector3& pos, F32 value, const char* suffix, const LLColor4 &color);
+ void renderTickText(const LLVector3& pos, const char* suffix, const LLColor4 &color);
+ void updateGridSettings();
+ BOOL getMousePointOnPlaneGlobal(LLVector3d& point, S32 x, S32 y, LLVector3d origin, LLVector3 normal);
+ BOOL getMousePointOnPlaneAgent(LLVector3& point, S32 x, S32 y, LLVector3 origin, LLVector3 normal);
+ BOOL nearestPointOnLineFromMouse( S32 x, S32 y, const LLVector3& b1, const LLVector3& b2, F32 &a_param, F32 &b_param ) const;
+ LLColor4 setupSnapGuideRenderPass(S32 pass);
+protected:
+ LLFrameTimer mHelpTextTimer;
+ BOOL mInSnapRegime;
+
+ static F32 sHelpTextVisibleTime;
+ static F32 sHelpTextFadeTime;
+ static S32 sNumTimesHelpTextShown;
+ static S32 sMaxTimesShowHelpText;
+ static F32 sGridMaxSubdivisionLevel;
+ static F32 sGridMinSubdivisionLevel;
+ static LLVector2 sTickLabelSpacing;
+};
+
+
+#endif
diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp
new file mode 100644
index 0000000000..a9508753c5
--- /dev/null
+++ b/indra/newview/llmaniprotate.cpp
@@ -0,0 +1,1876 @@
+/**
+ * @file llmaniprotate.cpp
+ * @brief LLManipRotate class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmaniprotate.h"
+
+// library includes
+#include "llmath.h"
+#include "llgl.h"
+#include "v4color.h"
+#include "llprimitive.h"
+#include "llview.h"
+#include "llfontgl.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llbox.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llhoverview.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llvoavatar.h"
+#include "llviewborder.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "lldrawable.h"
+#include "llglheaders.h"
+
+const F32 RADIUS_PIXELS = 100.f; // size in screen space
+const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS;
+const F32 WIDTH_PIXELS = 8;
+const S32 CIRCLE_STEPS = 100;
+const F32 DELTA = F_TWO_PI / CIRCLE_STEPS;
+const F32 SIN_DELTA = sin( DELTA );
+const F32 COS_DELTA = cos( DELTA );
+const F32 MAX_MANIP_SELECT_DISTANCE = 100.f;
+const F32 SNAP_ANGLE_INCREMENT = 5.625f;
+const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT;
+const F32 SNAP_GUIDE_RADIUS_1 = 2.8f;
+const F32 SNAP_GUIDE_RADIUS_2 = 2.4f;
+const F32 SNAP_GUIDE_RADIUS_3 = 2.2f;
+const F32 SNAP_GUIDE_RADIUS_4 = 2.1f;
+const F32 SNAP_GUIDE_RADIUS_5 = 2.05f;
+const F32 SNAP_GUIDE_INNER_RADIUS = 2.f;
+const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD );
+const F32 SELECTED_MANIPULATOR_SCALE = 1.05f;
+const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
+
+extern void handle_reset_rotation(void*); // in LLViewerWindow
+extern void handle_first_tool(void*);
+
+LLManipRotate::LLManipRotate( LLToolComposite* composite )
+: LLManip( "Rotate", composite ),
+ mRotationCenter(),
+ mCenterScreen(),
+ mRotation(),
+ mMouseDown(),
+ mMouseCur(),
+ mRadiusMeters(0.f),
+ mCenterToCam(),
+ mCenterToCamNorm(),
+ mCenterToCamMag(0.f),
+ mCenterToProfilePlane(),
+ mCenterToProfilePlaneMag(0.f),
+ mManipPart( LL_NO_PART ),
+ mSendUpdateOnMouseUp( FALSE ),
+ mSmoothRotate( FALSE ),
+ mCamEdgeOn(FALSE),
+ mManipulatorScales(1.f, 1.f, 1.f, 1.f)
+{ }
+
+void LLManipRotate::handleSelect()
+{
+ //FIXME: put this in mouseDown?
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ gFloaterTools->setStatusText("Drag colored bands to rotate object");
+}
+
+void LLManipRotate::handleDeselect()
+{
+ mHighlightedPart = LL_NO_PART;
+ mManipPart = LL_NO_PART;
+
+ gFloaterTools->setStatusText("");
+}
+
+void LLManipRotate::render()
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLEnable gl_blend(GL_BLEND);
+ LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
+
+ // You can rotate if you can move
+ LLViewerObject* first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+ if( !first_object )
+ {
+ return;
+ }
+
+ if( !updateVisiblity() )
+ {
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+
+
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ LLColor4 highlight_outside( 1.f, 1.f, 0.f, 1.f );
+ LLColor4 highlight_inside( 0.7f, 0.7f, 0.f, 0.5f );
+ F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
+
+ glPushMatrix();
+ {
+ // are we in the middle of a constrained drag?
+ if (mManipPart >= LL_ROT_X && mManipPart <= LL_ROT_Z)
+ {
+ renderSnapGuides();
+ }
+ else
+ {
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLDepthTest gls_depth(GL_FALSE);
+ glPushMatrix();
+ {
+ // Draw "sphere" (intersection of sphere with tangent cone that has apex at camera)
+ glTranslatef( mCenterToProfilePlane.mV[VX], mCenterToProfilePlane.mV[VY], mCenterToProfilePlane.mV[VZ] );
+ glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
+
+ // Inverse change of basis vectors
+ LLVector3 forward = mCenterToCamNorm;
+ LLVector3 left = gAgent.getUpAxis() % forward;
+ left.normVec();
+ LLVector3 up = forward % left;
+
+ LLVector4 a(-forward);
+ a.mV[3] = 0;
+ LLVector4 b(up);
+ b.mV[3] = 0;
+ LLVector4 c(left);
+ c.mV[3] = 0;
+ LLMatrix4 mat;
+ mat.initRows(a, b, c, LLVector4(0.f, 0.f, 0.f, 1.f));
+
+ glMultMatrixf( &mat.mMatrix[0][0] );
+
+ glRotatef( -90, 0.f, 1.f, 0.f);
+ LLColor4 color;
+ if (mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
+ {
+ color.setVec(0.8f, 0.8f, 0.8f, 0.8f);
+ glScalef(mManipulatorScales.mV[VW], mManipulatorScales.mV[VW], mManipulatorScales.mV[VW]);
+ }
+ else
+ {
+ color.setVec( 0.7f, 0.7f, 0.7f, 0.6f );
+ }
+ gl_washer_2d(mRadiusMeters + width_meters, mRadiusMeters, CIRCLE_STEPS, color, color);
+
+
+ if (mManipPart == LL_NO_PART)
+ {
+ glColor4f( 0.7f, 0.7f, 0.7f, 0.3f );
+ gl_circle_2d( 0, 0, mRadiusMeters, CIRCLE_STEPS, TRUE );
+ }
+
+ GLdouble plane_eqn[] = { 0, 0, 1, 0 };
+ glClipPlane( GL_CLIP_PLANE0, plane_eqn );
+ }
+ glPopMatrix();
+ }
+
+ glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
+
+ LLQuaternion rot;
+ F32 angle_radians, x, y, z;
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+
+ if (mManipPart == LL_ROT_Z)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glPushMatrix();
+ {
+ // selected part
+ glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
+ renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f) , LLColor4( 0.f, 0.f, 1.f, 0.3f ));
+ }
+ glPopMatrix();
+ }
+ else if (mManipPart == LL_ROT_Y)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 1.f, 0.f, 0.f );
+ glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
+ renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f), LLColor4( 0.f, 1.f, 0.f, 0.3f));
+ }
+ glPopMatrix();
+ }
+ else if (mManipPart == LL_ROT_X)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 0.f, 1.f, 0.f );
+ glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
+ renderActiveRing( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f), LLColor4( 1.f, 0.f, 0.f, 0.3f));
+ }
+ glPopMatrix();
+ }
+ else if (mManipPart == LL_ROT_ROLL)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ else if (mManipPart == LL_NO_PART)
+ {
+ if (mHighlightedPart == LL_NO_PART)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLEnable clip_plane0(GL_CLIP_PLANE0);
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ // First pass: centers. Second pass: sides.
+ for( S32 i=0; i<2; i++ )
+ {
+ glPushMatrix();
+ {
+ if (mHighlightedPart == LL_ROT_Z)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
+ // hovering over part
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f ), LLColor4( 0.f, 0.f, 1.f, 0.5f ), CIRCLE_STEPS, i);
+ }
+ else
+ {
+ // default
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 0.8f, 0.8f ), LLColor4( 0.f, 0.f, 0.8f, 0.4f ), CIRCLE_STEPS, i);
+ }
+ }
+ glPopMatrix();
+
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 1.f, 0.f, 0.f );
+ if (mHighlightedPart == LL_ROT_Y)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
+ // hovering over part
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f ), LLColor4( 0.f, 1.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
+ }
+ else
+ {
+ // default
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.8f, 0.f, 0.8f ), LLColor4( 0.f, 0.8f, 0.f, 0.4f ), CIRCLE_STEPS, i);
+ }
+ }
+ glPopMatrix();
+
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 0.f, 1.f, 0.f );
+ if (mHighlightedPart == LL_ROT_X)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
+
+ // hovering over part
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f ), LLColor4( 1.f, 0.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
+ }
+ else
+ {
+ // default
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.8f, 0.f, 0.f, 0.8f ), LLColor4( 0.8f, 0.f, 0.f, 0.4f ), CIRCLE_STEPS, i);
+ }
+ }
+ glPopMatrix();
+
+ if (mHighlightedPart == LL_ROT_ROLL)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ }
+ }
+ }
+ glPopMatrix();
+ glPopMatrix();
+
+ LLVector3 euler_angles;
+ LLQuaternion object_rot = first_object->getRotationEdit();
+ object_rot.getEulerAngles(&(euler_angles.mV[VX]), &(euler_angles.mV[VY]), &(euler_angles.mV[VZ]));
+ euler_angles *= RAD_TO_DEG;
+ euler_angles.mV[VX] = llround(fmodf(euler_angles.mV[VX] + 360.f, 360.f), 0.05f);
+ euler_angles.mV[VY] = llround(fmodf(euler_angles.mV[VY] + 360.f, 360.f), 0.05f);
+ euler_angles.mV[VZ] = llround(fmodf(euler_angles.mV[VZ] + 360.f, 360.f), 0.05f);
+
+ renderXYZ(euler_angles);
+}
+
+BOOL LLManipRotate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ LLViewerObject* first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+ if( first_object )
+ {
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if( hit_obj && mHighlightedPart != LL_NO_PART )
+ {
+ handled = handleMouseDownOnPart( x, y, mask );
+ }
+ }
+
+ return handled;
+}
+
+// Assumes that one of the parts of the manipulator was hit.
+BOOL LLManipRotate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
+{
+ BOOL can_rotate = gSelectMgr->getObjectCount() != 0;
+ for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
+ objectp;
+ objectp = gSelectMgr->getNextObject())
+ {
+ can_rotate = can_rotate && objectp->permMove() && (objectp->permModify() || gSavedSettings.getBOOL("SelectLinkedSet"));
+ }
+
+ if (!can_rotate)
+ {
+ return FALSE;
+ }
+
+ highlightManipulators(x, y);
+ S32 hit_part = mHighlightedPart;
+ // we just started a drag, so save initial object positions
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_ROTATE);
+
+ // save selection center
+ mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() ); //gSelectMgr->getSelectionCenterGlobal();
+
+ mManipPart = (EManipPart)hit_part;
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ if( mManipPart == LL_ROT_GENERAL)
+ {
+ mMouseDown = intersectMouseWithSphere( x, y, center, mRadiusMeters);
+ }
+ else
+ {
+ // Project onto the plane of the ring
+ LLVector3 axis = getConstraintAxis();
+
+ F32 axis_onto_cam = llabs( axis * mCenterToCamNorm );
+ const F32 AXIS_ONTO_CAM_TOL = cos( 85.f * DEG_TO_RAD );
+ if( axis_onto_cam < AXIS_ONTO_CAM_TOL )
+ {
+ LLVector3 up_from_axis = mCenterToCamNorm % axis;
+ up_from_axis.normVec();
+ LLVector3 cur_intersection;
+ getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
+ cur_intersection -= center;
+ mMouseDown = projected_vec(cur_intersection, up_from_axis);
+ F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
+ F32 mouse_dist_sqrd = mMouseDown.magVecSquared();
+ if (mouse_dist_sqrd > 0.0001f)
+ {
+ mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
+ mouse_dist_sqrd);
+ }
+ LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, axis);
+ mMouseDown += mouse_depth * projected_center_to_cam;
+
+ }
+ else
+ {
+ mMouseDown = findNearestPointOnRing( x, y, center, axis ) - center;
+ mMouseDown.normVec();
+ }
+ }
+
+ mMouseCur = mMouseDown;
+
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ setMouseCapture( TRUE );
+ gSelectMgr->enableSilhouette(FALSE);
+ return TRUE;
+}
+
+
+LLVector3 LLManipRotate::findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis )
+{
+ // Project the delta onto the ring and rescale it by the radius so that it's _on_ the ring.
+ LLVector3 proj_onto_ring;
+ getMousePointOnPlaneAgent(proj_onto_ring, x, y, center, axis);
+ proj_onto_ring -= center;
+ proj_onto_ring.normVec();
+
+ return center + proj_onto_ring * mRadiusMeters;
+}
+
+BOOL LLManipRotate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // first, perform normal processing in case this was a quick-click
+ handleHover(x, y, mask);
+
+ mManipPart = LL_NO_PART;
+
+ // Might have missed last update due to timing.
+ if (mSendUpdateOnMouseUp)
+ {
+ gSelectMgr->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION );
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ gSelectMgr->enableSilhouette(TRUE);
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+
+ gSelectMgr->updateSelectionCenter();
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+
+ return LLManip::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLManipRotate::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if( gSelectMgr->isEmpty() )
+ {
+ // Somehow the object got deselected while we were dragging it.
+ setMouseCapture( FALSE );
+ }
+ else
+ {
+ drag(x, y);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (active)" << llendl;
+ }
+ else
+ {
+ highlightManipulators(x, y);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (inactive)" << llendl;
+ }
+
+ gViewerWindow->setCursor(UI_CURSOR_TOOLROTATE);
+ return TRUE;
+}
+
+
+LLVector3 LLManipRotate::projectToSphere( F32 x, F32 y, BOOL* on_sphere )
+{
+ F32 z = 0.f;
+ F32 dist_squared = x*x + y*y;
+
+ *on_sphere = dist_squared <= SQ_RADIUS;
+ if( *on_sphere )
+ {
+ z = sqrt(SQ_RADIUS - dist_squared);
+ }
+ return LLVector3( x, y, z );
+}
+
+extern U32 gFrameCount;
+
+// Freeform rotation
+void LLManipRotate::drag( S32 x, S32 y )
+{
+ static LLTimer update_timer;
+ F32 elapsed_time = update_timer.getElapsedTimeF32();
+ const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
+ BOOL send_rotation_update = FALSE;
+ BOOL send_position_update = FALSE;
+
+ if( !updateVisiblity() )
+ {
+ return;
+ }
+
+ if( mManipPart == LL_ROT_GENERAL )
+ {
+ mRotation = dragUnconstrained(x, y);
+ }
+ else
+ {
+ mRotation = dragConstrained(x, y);
+ }
+
+ BOOL damped = mSmoothRotate;
+ mSmoothRotate = FALSE;
+
+ LLViewerObject* object;
+ LLSelectNode* selectNode;
+ BOOL using_linked_selection = gSavedSettings.getBOOL("SelectLinkedSet");
+
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode != NULL; selectNode = gSelectMgr->getNextNode() )
+ {
+ object = selectNode->getObject();
+
+ // have permission to move and object is root of selection or individually selected
+ if (object->permMove() && (object->isRootEdit() || selectNode->mIndividualSelection))
+ {
+ if (!object->isRootEdit())
+ {
+ // child objects should not update if parent is selected
+ LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
+ if (editable_root->isSelected())
+ {
+ // we will be moved properly by our parent, so skip
+ continue;
+ }
+ }
+
+ LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
+ std::vector<LLVector3> child_positions;
+ std::vector<LLQuaternion> child_rotations;
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ for (U32 i = 0; i < object->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = object->mChildList[i];
+ child_positions.push_back(childp->getPositionEdit());
+ child_rotations.push_back(childp->getRotationEdit());
+ }
+ }
+
+ if (object->getParent() && object->mDrawable.notNull())
+ {
+ LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
+ invParentRotation.transQuat();
+
+ object->setRotation(new_rot * invParentRotation, damped);
+ rebuild(object);
+ }
+ else
+ {
+ object->setRotation(new_rot, damped);
+ rebuild(object);
+ }
+
+ // don't send updates all the time for sub-objects
+ if (using_linked_selection && object->getRenderRotation() != new_rot)
+ {
+ send_rotation_update = TRUE;
+ }
+
+ // for individually selected roots, we need to counterrotate all the children
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ //RN: must do non-damped updates on these objects so relative rotation appears constant
+ // instead of having two competing slerps making the child objects appear to "wobble"
+ for (U32 i = 0; i < object->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = object->mChildList[i];
+ LLVector3 child_offset = ((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit()) - childp->getPosition();
+ if (!childp->isSelected() && childp->mDrawable.notNull())
+ {
+ childp->setRotation(child_rotations[i] * ~object->getRotationEdit());
+ childp->setPosition((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit());
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ }
+
+ // update positions
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode != NULL; selectNode = gSelectMgr->getNextNode() )
+ {
+ object = selectNode->getObject();
+
+ // to avoid cumulative position changes we calculate the objects new position using its saved position
+ if (object && object->permMove())
+ {
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ LLVector3 old_position;
+ LLVector3 new_position;
+
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // need to work in drawable space to handle selected items from multiple attachments
+ // (which have no shared frame of reference other than their render positions)
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition();
+ }
+ else
+ {
+ new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal );
+ old_position = object->getPositionAgent();
+ }
+
+ new_position = (new_position - center) * mRotation; // new relative rotated position
+ new_position += center;
+
+ if (object->isRootEdit() && !object->isAttachment())
+ {
+ LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position);
+ new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
+ new_position = gAgent.getPosAgentFromGlobal(new_pos_global);
+ }
+
+ // for individually selected child objects
+ if (!object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ LLViewerObject* parentp = (LLViewerObject*)object->getParent();
+ if (!parentp->isSelected())
+ {
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // find position relative to render position of parent
+ object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
+ rebuild(object);
+ }
+ else
+ {
+ object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
+ rebuild(object);
+ }
+ }
+ }
+ else if (object->isRootEdit())
+ {
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation());
+ rebuild(object);
+ }
+ else
+ {
+ object->setPositionAgent(new_position);
+ rebuild(object);
+ }
+ }
+
+ if (using_linked_selection && object->getPositionAgent() != new_position)
+ {
+ send_position_update = TRUE;
+ }
+
+ // for individually selected roots, we need to counter-translate all unselected children
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ // only offset by parent's translation as we've already countered parent's rotation
+ LLVector3 child_offset;
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ LLXform* attachment_point_xform = object->mDrawable->getXform()->getParent();
+ LLQuaternion parent_rotation = object->getRotation() * attachment_point_xform->getWorldRotation();
+ child_offset = LLVector3(old_position - new_position) * ~parent_rotation;
+ }
+ else
+ {
+ child_offset = LLVector3(old_position - new_position) * ~object->getRenderRotation();
+ }
+
+ rebuild(object);
+ for (U32 i = 0; i < object->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = object->mChildList[i];
+ if (!childp->isSelected() && childp->mDrawable.notNull())
+ {
+ childp->setPosition(childp->getPosition() + child_offset);
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ }
+
+ if ((send_position_update || send_rotation_update) && (elapsed_time > UPDATE_DELAY))
+ {
+ U32 flag = UPD_NONE;
+ if (send_rotation_update)
+ {
+ flag |= UPD_ROTATION;
+ }
+ if (send_position_update)
+ {
+ flag |= UPD_POSITION;
+ }
+
+ gSelectMgr->sendMultipleUpdate( flag );
+ update_timer.reset();
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ else
+ {
+ mSendUpdateOnMouseUp = TRUE;
+ }
+
+ gSelectMgr->updateSelectionCenter();
+
+ // RN: just clear focus so camera doesn't follow spurious object updates
+ gAgent.clearFocusObject();
+ dialog_refresh_all();
+}
+
+void LLManipRotate::renderActiveRing( F32 radius, F32 width, const LLColor4& front_color, const LLColor4& back_color)
+{
+ LLGLEnable cull_face(GL_CULL_FACE);
+ {
+ gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, FALSE);
+ gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, TRUE);
+ }
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+ gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, FALSE);
+ gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, TRUE);
+ }
+}
+
+void LLManipRotate::renderSnapGuides()
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+ LLVector3 constraint_axis = getConstraintAxis();
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ if (!gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ return;
+ }
+
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+ LLVector3 cam_at_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_at_axis.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_at_axis = center - gAgent.getCameraPositionAgent();
+ cam_at_axis.normVec();
+ }
+
+ LLVector3 world_snap_axis;
+ LLVector3 test_axis = constraint_axis;
+
+ BOOL constrain_to_ref_object = FALSE;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ test_axis = test_axis * ~grid_rotation;
+ }
+ else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
+ {
+ test_axis = test_axis * ~grid_rotation;
+ constrain_to_ref_object = TRUE;
+ }
+
+ test_axis.abs();
+
+ // find closest global/reference axis to local constraint axis;
+ if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
+ {
+ world_snap_axis = LLVector3::y_axis;
+ }
+ else if (test_axis.mV[VY] > test_axis.mV[VZ])
+ {
+ world_snap_axis = LLVector3::z_axis;
+ }
+ else
+ {
+ world_snap_axis = LLVector3::x_axis;
+ }
+
+ LLVector3 projected_snap_axis = world_snap_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ projected_snap_axis = projected_snap_axis * grid_rotation;
+ }
+ else if (constrain_to_ref_object)
+ {
+ projected_snap_axis = projected_snap_axis * grid_rotation;
+ }
+
+ // project world snap axis onto constraint plane
+ projected_snap_axis -= projected_vec(projected_snap_axis, constraint_axis);
+ projected_snap_axis.normVec();
+
+ S32 num_rings = mCamEdgeOn ? 2 : 1;
+ for (S32 ring_num = 0; ring_num < num_rings; ring_num++)
+ {
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ if (mCamEdgeOn)
+ {
+ // draw two opposing rings
+ if (ring_num == 0)
+ {
+ center += constraint_axis * mRadiusMeters * 0.5f;
+ }
+ else
+ {
+ center -= constraint_axis * mRadiusMeters * 0.5f;
+ }
+ }
+
+ LLGLDepthTest gls_depth(GL_FALSE);
+ for (S32 pass = 0; pass < 3; pass++)
+ {
+ // render snap guide ring
+ glPushMatrix();
+
+ LLQuaternion snap_guide_rot;
+ F32 angle_radians, x, y, z;
+ snap_guide_rot.shortestArc(LLVector3::z_axis, getConstraintAxis());
+ snap_guide_rot.getAngleAxis(&angle_radians, &x, &y, &z);
+ glTranslatef(center.mV[VX], center.mV[VY], center.mV[VZ]);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ LLColor4 line_color = setupSnapGuideRenderPass(pass);
+
+ glColor4fv(line_color.mV);
+
+ if (mCamEdgeOn)
+ {
+ // render an arc
+ LLVector3 edge_normal = cam_at_axis % constraint_axis;
+ edge_normal.normVec();
+ LLVector3 x_axis_snap = LLVector3::x_axis * snap_guide_rot;
+ LLVector3 y_axis_snap = LLVector3::y_axis * snap_guide_rot;
+
+ F32 end_angle = atan2(y_axis_snap * edge_normal, x_axis_snap * edge_normal);
+ //F32 start_angle = angle_between((-1.f * LLVector3::x_axis) * snap_guide_rot, edge_normal);
+ F32 start_angle = end_angle - F_PI;
+ gl_arc_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE, start_angle, end_angle);
+ }
+ else
+ {
+ gl_circle_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE);
+ }
+ glPopMatrix();
+
+ for (S32 i = 0; i < 64; i++)
+ {
+ BOOL render_text = TRUE;
+ F32 deg = 5.625f * (F32)i;
+ LLVector3 inner_point;
+ LLVector3 outer_point;
+ LLVector3 text_point;
+ LLQuaternion rot(deg * DEG_TO_RAD, constraint_axis);
+ glBegin(GL_LINES);
+ {
+ inner_point = (projected_snap_axis * mRadiusMeters * SNAP_GUIDE_INNER_RADIUS * rot) + center;
+ F32 tick_length = 0.f;
+ if (i % 16 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_1 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else if (i % 8 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_2 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else if (i % 4 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_3 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else if (i % 2 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_4 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_5 - SNAP_GUIDE_INNER_RADIUS);
+ }
+
+ if (mCamEdgeOn)
+ {
+ // don't draw ticks that are on back side of circle
+ F32 dot = cam_at_axis * (projected_snap_axis * rot);
+ if (dot > 0.f)
+ {
+ outer_point = inner_point;
+ render_text = FALSE;
+ }
+ else
+ {
+ if (ring_num == 0)
+ {
+ outer_point = inner_point + (constraint_axis * tick_length) * rot;
+ }
+ else
+ {
+ outer_point = inner_point - (constraint_axis * tick_length) * rot;
+ }
+ }
+ }
+ else
+ {
+ outer_point = inner_point + (projected_snap_axis * tick_length) * rot;
+ }
+
+ text_point = outer_point + (projected_snap_axis * mRadiusMeters * 0.1f) * rot;
+
+ glVertex3fv(inner_point.mV);
+ glVertex3fv(outer_point.mV);
+ }
+ glEnd();
+
+ //RN: text rendering does own shadow pass, so only render once
+ if (pass == 1 && render_text && i % 16 == 0)
+ {
+ if (world_snap_axis.mV[VX])
+ {
+ if (i == 0)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
+ }
+ else if (i == 16)
+ {
+ if (constraint_axis.mV[VZ] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
+ }
+ }
+ else if (i == 32)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
+ }
+ else
+ {
+ if (constraint_axis.mV[VZ] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
+ }
+ }
+ }
+ else if (world_snap_axis.mV[VY])
+ {
+ if (i == 0)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
+ }
+ else if (i == 16)
+ {
+ if (constraint_axis.mV[VX] > 0.f)
+ {
+ renderTickText(text_point, "Up", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, "Down", LLColor4::white);
+ }
+ }
+ else if (i == 32)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
+ }
+ else
+ {
+ if (constraint_axis.mV[VX] > 0.f)
+ {
+ renderTickText(text_point, "Down", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, "Up", LLColor4::white);
+ }
+ }
+ }
+ else if (world_snap_axis.mV[VZ])
+ {
+ if (i == 0)
+ {
+ renderTickText(text_point, "Up", LLColor4::white);
+ }
+ else if (i == 16)
+ {
+ if (constraint_axis.mV[VY] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
+ }
+ }
+ else if (i == 32)
+ {
+ renderTickText(text_point, "Down", LLColor4::white);
+ }
+ else
+ {
+ if (constraint_axis.mV[VY] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
+ }
+ }
+ }
+ }
+ glColor4fv(line_color.mV);
+ }
+
+ // now render projected object axis
+ if (mInSnapRegime)
+ {
+ LLVector3 object_axis;
+ getObjectAxisClosestToMouse(object_axis);
+
+ // project onto constraint plane
+ LLSelectNode* first_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ object_axis = object_axis * first_node->getObject()->getRenderRotation();
+ object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
+ object_axis.normVec();
+ object_axis = object_axis * SNAP_GUIDE_INNER_RADIUS * mRadiusMeters + center;
+ LLVector3 line_start = center;
+
+ glBegin(GL_LINES);
+ {
+ glVertex3fv(line_start.mV);
+ glVertex3fv(object_axis.mV);
+ }
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
+ arrow_span.normVec();
+
+ arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
+ arrow_dir.normVec();
+ if (ring_num == 1)
+ {
+ arrow_dir *= -1.f;
+ }
+ glVertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
+ }
+ glEnd();
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv(line_start.mV);
+ glVertex3fv(object_axis.mV);
+ }
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
+ arrow_span.normVec();
+
+ arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
+ arrow_dir.normVec();
+ if (ring_num == 1)
+ {
+ arrow_dir *= -1.f;
+ }
+
+ glVertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
+ }
+ glEnd();
+ }
+ }
+ }
+ }
+}
+
+// Returns TRUE if center of sphere is visible. Also sets a bunch of member variables that are used later (e.g. mCenterToCam)
+BOOL LLManipRotate::updateVisiblity()
+{
+ // Don't want to recalculate the center of the selection during a drag.
+ // Due to packet delays, sometimes half the objects in the selection have their
+ // new position and half have their old one. This creates subtle errors in the
+ // computed center position for that frame. Unfortunately, these errors
+ // accumulate. The result is objects seem to "fly apart" during rotations.
+ // JC - 03.26.2002
+ if (!hasMouseCapture())
+ {
+ mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() );//gSelectMgr->getSelectionCenterGlobal();
+ }
+
+ BOOL visible = FALSE;
+
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mCenterToCam = LLVector3(-1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
+ mCenterToCamNorm = mCenterToCam;
+ mCenterToCamMag = mCenterToCamNorm.normVec();
+
+ mRadiusMeters = RADIUS_PIXELS / (F32) gCamera->getViewHeightInPixels();
+ mRadiusMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
+
+ mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
+ mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
+
+ mCenterScreen.set((S32)((0.5f - mRotationCenter.mdV[VY]) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowWidth()),
+ (S32)((mRotationCenter.mdV[VZ] + 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowHeight()));
+ visible = TRUE;
+ }
+ else
+ {
+ visible = gCamera->projectPosAgentToScreen(center, mCenterScreen );
+ if( visible )
+ {
+ mCenterToCam = gAgent.getCameraPositionAgent() - center;
+ mCenterToCamNorm = mCenterToCam;
+ mCenterToCamMag = mCenterToCamNorm.normVec();
+ LLVector3 cameraAtAxis = gCamera->getAtAxis();
+ cameraAtAxis.normVec();
+
+ F32 z_dist = -1.f * (mCenterToCam * cameraAtAxis);
+
+ // Don't drag manip if object too far away
+ if (gSavedSettings.getBOOL("LimitSelectDistance"))
+ {
+ F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
+ if (dist_vec(gAgent.getPositionAgent(), center) > max_select_distance)
+ {
+ visible = FALSE;
+ }
+ }
+
+ if (mCenterToCamMag > 0.001f)
+ {
+ F32 fraction_of_fov = RADIUS_PIXELS / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ mRadiusMeters = z_dist * tan(apparent_angle);
+
+ mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
+ mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
+ }
+ else
+ {
+ visible = FALSE;
+ }
+ }
+ }
+
+ mCamEdgeOn = FALSE;
+ F32 axis_onto_cam = mManipPart >= LL_ROT_X ? llabs( getConstraintAxis() * mCenterToCamNorm ) : 0.f;
+ if( axis_onto_cam < AXIS_ONTO_CAM_TOLERANCE )
+ {
+ mCamEdgeOn = TRUE;
+ }
+
+ return visible;
+}
+
+LLQuaternion LLManipRotate::dragUnconstrained( S32 x, S32 y )
+{
+ LLVector3 cam = gAgent.getCameraPositionAgent();
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ mMouseCur = intersectMouseWithSphere( x, y, center, mRadiusMeters);
+
+ F32 delta_x = (F32)(mCenterScreen.mX - x);
+ F32 delta_y = (F32)(mCenterScreen.mY - y);
+
+ F32 dist_from_sphere_center = sqrt(delta_x * delta_x + delta_y * delta_y);
+
+ LLVector3 axis = mMouseDown % mMouseCur;
+ axis.normVec();
+ F32 angle = acos(mMouseDown * mMouseCur);
+ LLQuaternion sphere_rot( angle, axis );
+
+ if (is_approx_zero(1.f - mMouseDown * mMouseCur))
+ {
+ return LLQuaternion::DEFAULT;
+ }
+ else if (dist_from_sphere_center < RADIUS_PIXELS)
+ {
+ return sphere_rot;
+ }
+ else
+ {
+ LLVector3 intersection;
+ getMousePointOnPlaneAgent( intersection, x, y, center + mCenterToProfilePlane, mCenterToCamNorm );
+
+ // amount dragging in sphere from center to periphery would rotate object
+ F32 in_sphere_angle = F_PI_BY_TWO;
+ F32 dist_to_tangent_point = mRadiusMeters;
+ if( !is_approx_zero( mCenterToProfilePlaneMag ) )
+ {
+ dist_to_tangent_point = sqrt( mRadiusMeters * mRadiusMeters - mCenterToProfilePlaneMag * mCenterToProfilePlaneMag );
+ in_sphere_angle = atan2( dist_to_tangent_point, mCenterToProfilePlaneMag );
+ }
+
+ LLVector3 profile_center_to_intersection = intersection - (center + mCenterToProfilePlane);
+ F32 dist_to_intersection = profile_center_to_intersection.normVec();
+ F32 angle = (-1.f + dist_to_intersection / dist_to_tangent_point) * in_sphere_angle;
+
+ LLVector3 axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ axis = LLVector3(-1.f, 0.f, 0.f) % profile_center_to_intersection;
+ }
+ else
+ {
+ axis = (cam - center) % profile_center_to_intersection;
+ axis.normVec();
+ }
+ return sphere_rot * LLQuaternion( angle, axis );
+ }
+}
+
+LLVector3 LLManipRotate::getConstraintAxis()
+{
+ LLVector3 axis;
+ if( LL_ROT_ROLL == mManipPart )
+ {
+ axis = mCenterToCamNorm;
+ }
+ else
+ {
+ S32 axis_dir = mManipPart - LL_ROT_X;
+ if ((axis_dir >= 0) && (axis_dir < 3))
+ {
+ axis.mV[axis_dir] = 1.f;
+ }
+ else
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llerrs << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
+#else
+ llwarns << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
+#endif
+ axis.mV[0] = 1.f;
+ }
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLSelectNode* first_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ if (first_node)
+ {
+ //FIXME: get agent local attachment grid working
+ // Put rotation into frame of first selected root object
+ axis = axis * grid_rotation;
+ }
+ }
+
+ return axis;
+}
+
+LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y )
+{
+ LLSelectNode* first_object_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ LLVector3 constraint_axis = getConstraintAxis();
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ F32 angle = 0.f;
+
+ // build snap axes
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 axis1;
+ LLVector3 axis2;
+
+ LLVector3 test_axis = constraint_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ test_axis = test_axis * ~grid_rotation;
+ }
+ else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
+ {
+ test_axis = test_axis * ~grid_rotation;
+ }
+ test_axis.abs();
+
+ // find closest global axis to constraint axis;
+ if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
+ {
+ axis1 = LLVector3::y_axis;
+ }
+ else if (test_axis.mV[VY] > test_axis.mV[VZ])
+ {
+ axis1 = LLVector3::z_axis;
+ }
+ else
+ {
+ axis1 = LLVector3::x_axis;
+ }
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ axis1 = axis1 * grid_rotation;
+ }
+ else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
+ {
+ axis1 = axis1 * grid_rotation;
+ }
+
+ //project axis onto constraint plane
+ axis1 -= (axis1 * constraint_axis) * constraint_axis;
+ axis1.normVec();
+
+ // calculate third and final axis
+ axis2 = constraint_axis % axis1;
+
+ //F32 axis_onto_cam = llabs( constraint_axis * mCenterToCamNorm );
+ if( mCamEdgeOn )
+ {
+ // We're looking at the ring edge-on.
+ LLVector3 snap_plane_center = (center + (constraint_axis * mRadiusMeters * 0.5f));
+ LLVector3 cam_to_snap_plane;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
+ cam_to_snap_plane.normVec();
+ }
+
+ LLVector3 projected_mouse;
+ BOOL hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
+ projected_mouse -= snap_plane_center;
+
+ S32 snap_plane = 0;
+
+ F32 dot = cam_to_snap_plane * constraint_axis;
+ if (llabs(dot) < 0.01f)
+ {
+ // looking at ring edge on, project onto view plane and check if mouse is past ring
+ getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
+ projected_mouse -= snap_plane_center;
+ dot = projected_mouse * constraint_axis;
+ if (projected_mouse * constraint_axis > 0)
+ {
+ snap_plane = 1;
+ }
+ projected_mouse -= dot * constraint_axis;
+ }
+ else if (dot > 0.f)
+ {
+ // look for mouse position outside and in front of snap circle
+ if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
+ {
+ snap_plane = 1;
+ }
+ }
+ else
+ {
+ // look for mouse position inside or in back of snap circle
+ if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
+ {
+ snap_plane = 1;
+ }
+ }
+
+ if (snap_plane == 0)
+ {
+ // try other plane
+ snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f));
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
+ cam_to_snap_plane.normVec();
+ }
+
+ hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
+ projected_mouse -= snap_plane_center;
+
+ dot = cam_to_snap_plane * constraint_axis;
+ if (llabs(dot) < 0.01f)
+ {
+ // looking at ring edge on, project onto view plane and check if mouse is past ring
+ getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
+ projected_mouse -= snap_plane_center;
+ dot = projected_mouse * constraint_axis;
+ if (projected_mouse * constraint_axis < 0)
+ {
+ snap_plane = 2;
+ }
+ projected_mouse -= dot * constraint_axis;
+ }
+ else if (dot < 0.f)
+ {
+ // look for mouse position outside and in front of snap circle
+ if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
+ {
+ snap_plane = 2;
+ }
+ }
+ else
+ {
+ // look for mouse position inside or in back of snap circle
+ if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
+ {
+ snap_plane = 2;
+ }
+ }
+ }
+
+ if (snap_plane > 0)
+ {
+ LLVector3 cam_at_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_at_axis.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_at_axis = snap_plane_center - gAgent.getCameraPositionAgent();
+ cam_at_axis.normVec();
+ }
+
+ // first, project mouse onto screen plane at point tangent to rotation radius.
+ getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis);
+ // project that point onto rotation plane
+ projected_mouse -= snap_plane_center;
+ projected_mouse -= projected_vec(projected_mouse, constraint_axis);
+
+ F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec());
+ F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
+ if (llabs(mouse_lateral_dist) > 0.01f)
+ {
+ mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
+ (mouse_lateral_dist * mouse_lateral_dist));
+ }
+ LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis);
+ projected_mouse -= mouse_depth * projected_camera_at;
+
+ if (!mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = TRUE;
+ // 0 to 360 deg
+ F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
+
+ F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
+ //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
+
+ LLVector3 object_axis;
+ getObjectAxisClosestToMouse(object_axis);
+ object_axis = object_axis * first_object_node->mSavedRotation;
+
+ // project onto constraint plane
+ object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
+ object_axis.normVec();
+
+ if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
+ {
+ F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
+ angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ else
+ {
+ angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ return LLQuaternion( -angle, constraint_axis );
+ }
+ else
+ {
+ if (mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = FALSE;
+
+ LLVector3 up_from_axis = mCenterToCamNorm % constraint_axis;
+ up_from_axis.normVec();
+ LLVector3 cur_intersection;
+ getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
+ cur_intersection -= center;
+ mMouseCur = projected_vec(cur_intersection, up_from_axis);
+ F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
+ F32 mouse_dist_sqrd = mMouseCur.magVecSquared();
+ if (mouse_dist_sqrd > 0.0001f)
+ {
+ mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
+ mouse_dist_sqrd);
+ }
+ LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, constraint_axis);
+ mMouseCur += mouse_depth * projected_center_to_cam;
+
+ F32 dist = (cur_intersection * up_from_axis) - (mMouseDown * up_from_axis);
+ angle = dist / (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * -F_PI_BY_TWO;
+ }
+ }
+ else
+ {
+ LLVector3 projected_mouse;
+ getMousePointOnPlaneAgent(projected_mouse, x, y, center, constraint_axis);
+ projected_mouse -= center;
+ mMouseCur = projected_mouse;
+ mMouseCur.normVec();
+
+ if (!first_object_node)
+ {
+ return LLQuaternion::DEFAULT;
+ }
+
+ if (gSavedSettings.getBOOL("SnapEnabled") && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters)
+ {
+ if (!mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = TRUE;
+ // 0 to 360 deg
+ F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
+
+ F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
+ //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
+
+ LLVector3 object_axis;
+ getObjectAxisClosestToMouse(object_axis);
+ object_axis = object_axis * first_object_node->mSavedRotation;
+
+ // project onto constraint plane
+ object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
+ object_axis.normVec();
+
+ if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
+ {
+ F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
+ angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ else
+ {
+ angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ return LLQuaternion( -angle, constraint_axis );
+ }
+ else
+ {
+ if (mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = FALSE;
+ }
+
+ angle = acos(mMouseCur * mMouseDown);
+
+ F32 dir = (mMouseDown % mMouseCur) * constraint_axis; // cross product
+ if( dir < 0.f )
+ {
+ angle *= -1.f;
+ }
+ }
+
+ F32 rot_step = gSavedSettings.getF32("RotationStep");
+ F32 step_size = DEG_TO_RAD * rot_step;
+ angle -= fmod(angle, step_size);
+
+ return LLQuaternion( angle, constraint_axis );
+}
+
+
+
+LLVector3 LLManipRotate::intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius)
+{
+ LLVector3 ray_pt;
+ LLVector3 ray_dir;
+ mouseToRay( x, y, &ray_pt, &ray_dir);
+ return intersectRayWithSphere( ray_pt, ray_dir, sphere_center, sphere_radius );
+}
+
+LLVector3 LLManipRotate::intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius)
+{
+ LLVector3 ray_pt_to_center = sphere_center - ray_pt;
+ F32 center_distance = ray_pt_to_center.normVec();
+
+ F32 dot = ray_dir * ray_pt_to_center;
+
+ if (dot == 0.f)
+ {
+ return LLVector3::zero;
+ }
+
+ // point which ray hits plane centered on sphere origin, facing ray origin
+ LLVector3 intersection_sphere_plane = ray_pt + (ray_dir * center_distance / dot);
+ // vector from sphere origin to the point, normalized to sphere radius
+ LLVector3 sphere_center_to_intersection = (intersection_sphere_plane - sphere_center) / sphere_radius;
+
+ F32 dist_squared = sphere_center_to_intersection.magVecSquared();
+ LLVector3 result;
+
+ if (dist_squared > 1.f)
+ {
+ result = sphere_center_to_intersection;
+ result.normVec();
+ }
+ else
+ {
+ result = sphere_center_to_intersection - ray_dir * sqrt(1.f - dist_squared);
+ }
+
+ return result;
+}
+
+// Utility function. Should probably be moved to another class.
+void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir )
+{
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 mouse_y = ((((F32)y) / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+
+ *ray_pt = LLVector3(-1.f, -mouse_x, mouse_y);
+ *ray_dir = LLVector3(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ *ray_pt = gAgent.getCameraPositionAgent();
+ gCamera->projectScreenToPosAgent(x, y, ray_dir);
+ *ray_dir -= *ray_pt;
+ ray_dir->normVec();
+ }
+}
+
+void LLManipRotate::highlightManipulators( S32 x, S32 y )
+{
+ mHighlightedPart = LL_NO_PART;
+
+ //LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLViewerObject *first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+
+ if (!first_object)
+ {
+ return;
+ }
+
+ LLQuaternion object_rot = first_object->getRenderRotation();
+ LLVector3 rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
+ LLVector3 mouse_dir_x;
+ LLVector3 mouse_dir_y;
+ LLVector3 mouse_dir_z;
+ LLVector3 intersection_roll;
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 rot_x_axis = LLVector3::x_axis * grid_rotation;
+ LLVector3 rot_y_axis = LLVector3::y_axis * grid_rotation;
+ LLVector3 rot_z_axis = LLVector3::z_axis * grid_rotation;
+
+ F32 proj_rot_x_axis = llabs(rot_x_axis * mCenterToCamNorm);
+ F32 proj_rot_y_axis = llabs(rot_y_axis * mCenterToCamNorm);
+ F32 proj_rot_z_axis = llabs(rot_z_axis * mCenterToCamNorm);
+
+ F32 min_select_distance = 0.f;
+ F32 cur_select_distance = 0.f;
+
+ // test x
+ getMousePointOnPlaneAgent(mouse_dir_x, x, y, rotation_center, rot_x_axis);
+ mouse_dir_x -= rotation_center;
+ // push intersection point out when working at obtuse angle to make ring easier to hit
+ mouse_dir_x *= 1.f + (1.f - llabs(rot_x_axis * mCenterToCamNorm)) * 0.1f;
+
+ // test y
+ getMousePointOnPlaneAgent(mouse_dir_y, x, y, rotation_center, rot_y_axis);
+ mouse_dir_y -= rotation_center;
+ mouse_dir_y *= 1.f + (1.f - llabs(rot_y_axis * mCenterToCamNorm)) * 0.1f;
+
+ // test z
+ getMousePointOnPlaneAgent(mouse_dir_z, x, y, rotation_center, rot_z_axis);
+ mouse_dir_z -= rotation_center;
+ mouse_dir_z *= 1.f + (1.f - llabs(rot_z_axis * mCenterToCamNorm)) * 0.1f;
+
+ // test roll
+ getMousePointOnPlaneAgent(intersection_roll, x, y, rotation_center, mCenterToCamNorm);
+ intersection_roll -= rotation_center;
+
+ F32 dist_x = mouse_dir_x.normVec();
+ F32 dist_y = mouse_dir_y.normVec();
+ F32 dist_z = mouse_dir_z.normVec();
+
+ F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWindowHeight();
+
+ if (llabs(dist_x - mRadiusMeters) * llmax(0.05f, proj_rot_x_axis) < distance_threshold)
+ {
+ // selected x
+ cur_select_distance = dist_x * mouse_dir_x * mCenterToCamNorm;
+ if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
+ {
+ min_select_distance = cur_select_distance;
+ mHighlightedPart = LL_ROT_X;
+ }
+ }
+ if (llabs(dist_y - mRadiusMeters) * llmax(0.05f, proj_rot_y_axis) < distance_threshold)
+ {
+ // selected y
+ cur_select_distance = dist_y * mouse_dir_y * mCenterToCamNorm;
+ if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
+ {
+ min_select_distance = cur_select_distance;
+ mHighlightedPart = LL_ROT_Y;
+ }
+ }
+ if (llabs(dist_z - mRadiusMeters) * llmax(0.05f, proj_rot_z_axis) < distance_threshold)
+ {
+ // selected z
+ cur_select_distance = dist_z * mouse_dir_z * mCenterToCamNorm;
+ if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
+ {
+ min_select_distance = cur_select_distance;
+ mHighlightedPart = LL_ROT_Z;
+ }
+ }
+
+ // test for edge-on intersections
+ if (proj_rot_x_axis < 0.05f)
+ {
+ if ((proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_x_axis) < distance_threshold) && dist_y < mRadiusMeters) ||
+ (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_x_axis) < distance_threshold) && dist_z < mRadiusMeters))
+ {
+ mHighlightedPart = LL_ROT_X;
+ }
+ }
+
+ if (proj_rot_y_axis < 0.05f)
+ {
+ if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_y_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
+ (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_y_axis) < distance_threshold) && dist_z < mRadiusMeters))
+ {
+ mHighlightedPart = LL_ROT_Y;
+ }
+ }
+
+ if (proj_rot_z_axis < 0.05f)
+ {
+ if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_z_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
+ (proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_z_axis) < distance_threshold) && dist_y < mRadiusMeters))
+ {
+ mHighlightedPart = LL_ROT_Z;
+ }
+ }
+
+ // test for roll
+ if (mHighlightedPart == LL_NO_PART)
+ {
+ F32 roll_distance = intersection_roll.magVec();
+ F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
+
+ // use larger distance threshold for roll as it is checked only if something else wasn't highlighted
+ if (llabs(roll_distance - (mRadiusMeters + (width_meters * 2.f))) < distance_threshold * 2.f)
+ {
+ mHighlightedPart = LL_ROT_ROLL;
+ }
+ else if (roll_distance < mRadiusMeters)
+ {
+ mHighlightedPart = LL_ROT_GENERAL;
+ }
+ }
+}
+
+S32 LLManipRotate::getObjectAxisClosestToMouse(LLVector3& object_axis)
+{
+ LLSelectNode* first_object_node = gSelectMgr->getFirstMoveableNode(TRUE);
+
+ if (!first_object_node)
+ {
+ object_axis.clearVec();
+ return -1;
+ }
+
+ LLQuaternion obj_rotation = first_object_node->mSavedRotation;
+ LLVector3 mouse_down_object = mMouseDown * ~obj_rotation;
+ LLVector3 mouse_down_abs = mouse_down_object;
+ mouse_down_abs.abs();
+
+ S32 axis_index = 0;
+ if (mouse_down_abs.mV[VX] > mouse_down_abs.mV[VY] && mouse_down_abs.mV[VX] > mouse_down_abs.mV[VZ])
+ {
+ if (mouse_down_object.mV[VX] > 0.f)
+ {
+ object_axis = LLVector3::x_axis;
+ }
+ else
+ {
+ object_axis = LLVector3::x_axis_neg;
+ }
+ axis_index = VX;
+ }
+ else if (mouse_down_abs.mV[VY] > mouse_down_abs.mV[VZ])
+ {
+ if (mouse_down_object.mV[VY] > 0.f)
+ {
+ object_axis = LLVector3::y_axis;
+ }
+ else
+ {
+ object_axis = LLVector3::y_axis_neg;
+ }
+ axis_index = VY;
+ }
+ else
+ {
+ if (mouse_down_object.mV[VZ] > 0.f)
+ {
+ object_axis = LLVector3::z_axis;
+ }
+ else
+ {
+ object_axis = LLVector3::z_axis_neg;
+ }
+ axis_index = VZ;
+ }
+
+ return axis_index;
+}
diff --git a/indra/newview/llmaniprotate.h b/indra/newview/llmaniprotate.h
new file mode 100644
index 0000000000..5af6b6be92
--- /dev/null
+++ b/indra/newview/llmaniprotate.h
@@ -0,0 +1,100 @@
+/**
+ * @file llmaniprotate.h
+ * @brief LLManipRotate class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMANIPROTATE_H
+#define LL_LLMANIPROTATE_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "llregionposition.h"
+#include "llmanip.h"
+#include "llviewerobject.h"
+
+class LLToolComposite;
+class LLColor4;
+
+class LLManipRotate : public LLManip
+{
+public:
+ class ManipulatorHandle
+ {
+ public:
+ LLVector3 mAxisU;
+ LLVector3 mAxisV;
+ U32 mManipID;
+
+ ManipulatorHandle(LLVector3 axis_u, LLVector3 axis_v, U32 id) : mAxisU(axis_u), mAxisV(axis_v), mManipID(id){}
+ };
+
+ LLManipRotate( LLToolComposite* composite );
+
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual void render();
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask);
+ virtual void highlightManipulators(S32 x, S32 y);
+ EManipPart getHighlightedPart() { return mHighlightedPart; }
+private:
+ void updateHoverView();
+
+ void drag( S32 x, S32 y );
+ LLVector3 projectToSphere( F32 x, F32 y, BOOL* on_sphere );
+
+ void renderSnapGuides();
+ void renderActiveRing(F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color);
+
+ BOOL updateVisiblity();
+ LLVector3 findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis );
+
+ LLQuaternion dragUnconstrained( S32 x, S32 y );
+ LLQuaternion dragConstrained( S32 x, S32 y );
+ LLVector3 getConstraintAxis();
+ S32 getObjectAxisClosestToMouse(LLVector3& axis);
+
+ // Utility functions
+ static void mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir );
+ static LLVector3 intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius );
+ static LLVector3 intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius);
+
+private:
+ LLVector3d mRotationCenter;
+ LLCoordGL mCenterScreen;
+// S32 mLastHoverMouseX; // used to suppress hover if mouse doesn't move
+// S32 mLastHoverMouseY;
+ LLQuaternion mRotation;
+
+ LLVector3 mMouseDown;
+ LLVector3 mMouseCur;
+ F32 mRadiusMeters;
+
+ LLVector3 mCenterToCam;
+ LLVector3 mCenterToCamNorm;
+ F32 mCenterToCamMag;
+ LLVector3 mCenterToProfilePlane;
+ F32 mCenterToProfilePlaneMag;
+
+ EManipPart mManipPart;
+
+ BOOL mSendUpdateOnMouseUp;
+ EManipPart mHighlightedPart;
+
+ BOOL mSmoothRotate;
+ BOOL mCamEdgeOn;
+
+ LLVector4 mManipulatorVertices[6];
+ LLVector4 mManipulatorScales;
+};
+
+#endif // LL_LLMANIPROTATE_H
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp
new file mode 100644
index 0000000000..aa3749b539
--- /dev/null
+++ b/indra/newview/llmanipscale.cpp
@@ -0,0 +1,2024 @@
+/**
+ * @file llmanipscale.cpp
+ * @brief LLManipScale class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmanipscale.h"
+
+// library includes
+#include "llmath.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llgl.h"
+#include "v4color.h"
+#include "llprimitive.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbox.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llfloatertools.h"
+#include "llglheaders.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llhudrender.h"
+#include "llworld.h"
+#include "v2math.h"
+#include "llvoavatar.h"
+
+
+const F32 MAX_MANIP_SELECT_DISTANCE_SQUARED = 11.f * 11.f;
+const F32 SNAP_GUIDE_SCREEN_OFFSET = 0.05f;
+const F32 SNAP_GUIDE_SCREEN_LENGTH = 0.7f;
+const F32 SELECTED_MANIPULATOR_SCALE = 1.2f;
+const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
+const S32 NUM_MANIPULATORS = 14;
+
+const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] =
+{
+ LLManip::LL_CORNER_NNN,
+ LLManip::LL_CORNER_NNP,
+ LLManip::LL_CORNER_NPN,
+ LLManip::LL_CORNER_NPP,
+ LLManip::LL_CORNER_PNN,
+ LLManip::LL_CORNER_PNP,
+ LLManip::LL_CORNER_PPN,
+ LLManip::LL_CORNER_PPP,
+ LLManip::LL_FACE_POSZ,
+ LLManip::LL_FACE_POSX,
+ LLManip::LL_FACE_POSY,
+ LLManip::LL_FACE_NEGX,
+ LLManip::LL_FACE_NEGY,
+ LLManip::LL_FACE_NEGZ
+};
+
+
+
+// static
+void LLManipScale::setUniform(BOOL b)
+{
+ gSavedSettings.setBOOL("ScaleUniform", b);
+}
+
+// static
+void LLManipScale::setShowAxes(BOOL b)
+{
+ gSavedSettings.setBOOL("ScaleShowAxes", b);
+}
+
+// static
+void LLManipScale::setStretchTextures(BOOL b)
+{
+ gSavedSettings.setBOOL("ScaleStretchTextures", b);
+}
+
+// static
+BOOL LLManipScale::getUniform()
+{
+ return gSavedSettings.getBOOL("ScaleUniform");
+}
+
+// static
+BOOL LLManipScale::getShowAxes()
+{
+ return gSavedSettings.getBOOL("ScaleShowAxes");
+}
+
+// static
+BOOL LLManipScale::getStretchTextures()
+{
+ return gSavedSettings.getBOOL("ScaleStretchTextures");
+}
+
+inline void LLManipScale::conditionalHighlight( U32 part, const LLColor4* highlight, const LLColor4* normal )
+{
+ LLColor4 default_highlight( 1.f, 1.f, 1.f, 1.f );
+ LLColor4 default_normal( 0.7f, 0.7f, 0.7f, 0.6f );
+ LLColor4 invisible(0.f, 0.f, 0.f, 0.f);
+ F32 manipulator_scale = 1.f;
+
+ for (S32 i = 0; i < NUM_MANIPULATORS; i++)
+ {
+ if((U32)MANIPULATOR_IDS[i] == part)
+ {
+ manipulator_scale = mManipulatorScales[i];
+ break;
+ }
+ }
+
+ mScaledBoxHandleSize = mBoxHandleSize * manipulator_scale;
+ if (mManipPart != (S32)LL_NO_PART && mManipPart != (S32)part)
+ {
+ glColor4fv( invisible.mV );
+ }
+ else if( mHighlightedPart == (S32)part )
+ {
+ glColor4fv( highlight ? highlight->mV : default_highlight.mV );
+ }
+ else
+ {
+ glColor4fv( normal ? normal->mV : default_normal.mV );
+ }
+}
+
+void LLManipScale::handleSelect()
+{
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ updateSnapGuides(bbox);
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ gFloaterTools->setStatusText("Click and drag to stretch selected side");
+}
+
+void LLManipScale::handleDeselect()
+{
+ mHighlightedPart = LL_NO_PART;
+ mManipPart = LL_NO_PART;
+ gFloaterTools->setStatusText("");
+}
+
+BOOL sort_manip_by_z(LLManipScale::ManipulatorHandle *new_manip, LLManipScale::ManipulatorHandle *test_manip)
+{
+ return ((new_manip->mType < test_manip->mType) || (new_manip->mPosition.mV[VZ] < test_manip->mPosition.mV[VZ]));
+}
+
+LLManipScale::LLManipScale( LLToolComposite* composite )
+ :
+ LLManip( "Scale", composite ),
+ mBoxHandleSize( 1.f ),
+ mScaledBoxHandleSize( 1.f ),
+ mManipPart( LL_NO_PART ),
+ mHighlightedPart( LL_NO_PART ),
+ mLastMouseX( -1 ),
+ mLastMouseY( -1 ),
+ mSendUpdateOnMouseUp( FALSE ),
+ mLastUpdateFlags( 0 ),
+ mScaleSnapUnit1(1.f),
+ mScaleSnapUnit2(1.f),
+ mSnapRegimeOffset(0.f),
+ mSnapGuideLength(0.f),
+ mScaleSnapValue(0.f)
+{
+ mProjectedManipulators.setInsertBefore(sort_manip_by_z);
+ mManipulatorScales = new F32[NUM_MANIPULATORS];
+ for (S32 i = 0; i < NUM_MANIPULATORS; i++)
+ {
+ mManipulatorScales[i] = 1.f;
+ }
+}
+
+LLManipScale::~LLManipScale()
+{
+ delete []mManipulatorScales;
+}
+
+void LLManipScale::render()
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLEnable gl_blend(GL_BLEND);
+ LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
+
+ if( isSelectionScalable() )
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Calculate size of drag handles
+
+ const F32 BOX_HANDLE_BASE_SIZE = 50.0f; // box size in pixels = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR
+ const F32 BOX_HANDLE_BASE_FACTOR = 0.2f;
+
+ LLVector3 center_agent = gAgent.getPosAgentFromGlobal(gSelectMgr->getSelectionCenterGlobal());
+
+ F32 range;
+ F32 range_from_agent;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mBoxHandleSize = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR / (F32) gCamera->getViewHeightInPixels();
+ mBoxHandleSize /= gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ range = dist_vec(gAgent.getCameraPositionAgent(), center_agent);
+ range_from_agent = dist_vec(gAgent.getPositionAgent(), center_agent);
+
+ // Don't draw manip if object too far away
+ if (gSavedSettings.getBOOL("LimitSelectDistance"))
+ {
+ F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
+ if (range_from_agent > max_select_distance)
+ {
+ return;
+ }
+ }
+
+ if (range > 0.001f)
+ {
+ // range != zero
+ F32 fraction_of_fov = BOX_HANDLE_BASE_SIZE / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ mBoxHandleSize = range * tan(apparent_angle) * BOX_HANDLE_BASE_FACTOR;
+ }
+ else
+ {
+ // range == zero
+ mBoxHandleSize = BOX_HANDLE_BASE_FACTOR;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Draw bounding box
+
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLVector3 pos_agent = bbox.getPositionAgent();
+ LLQuaternion rot = bbox.getRotation();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ {
+ glTranslatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ]);
+
+ F32 angle_radians, x, y, z;
+ rot.getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+
+ {
+ LLGLEnable poly_offset(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset( -2.f, -2.f);
+
+ // JC - Band-aid until edge stretch working similar to side stretch
+ // in non-uniform.
+ // renderEdges( bbox );
+
+ renderCorners( bbox );
+ renderFaces( bbox );
+
+ if (mManipPart != LL_NO_PART)
+ {
+ renderGuidelinesPart( bbox );
+ }
+
+ glPolygonOffset( 0.f, 0.f);
+ }
+ }
+ glPopMatrix();
+
+ if (mManipPart != LL_NO_PART)
+ {
+ renderSnapGuides(bbox);
+ }
+ glPopMatrix();
+
+ renderXYZ(bbox.getExtentLocal());
+ }
+}
+
+BOOL LLManipScale::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if( hit_obj ||
+ (mHighlightedPart != LL_NO_PART) )
+ {
+ handled = handleMouseDownOnPart( x, y, mask );
+ }
+
+ return handled;
+}
+
+// Assumes that one of the arrows on an object was hit.
+BOOL LLManipScale::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
+{
+ BOOL can_scale = gSelectMgr->getObjectCount() != 0;
+ for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
+ objectp;
+ objectp = gSelectMgr->getNextObject())
+ {
+ can_scale = can_scale && objectp->permModify() && objectp->permMove() && !objectp->isSeat();
+ }
+
+ if (!can_scale)
+ {
+ return FALSE;
+ }
+
+ highlightManipulators(x, y);
+ S32 hit_part = mHighlightedPart;
+
+ gSelectMgr->enableSilhouette(FALSE);
+ mManipPart = (EManipPart)hit_part;
+
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLVector3 box_center_agent = bbox.getCenterAgent();
+ LLVector3 box_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ) );
+
+ updateSnapGuides(bbox);
+
+ mDragStartPointGlobal = gAgent.getPosGlobalFromAgent(box_corner_agent);
+ mDragStartCenterGlobal = gAgent.getPosGlobalFromAgent(box_center_agent);
+ LLVector3 far_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ) );
+ mDragFarHitGlobal = gAgent.getPosGlobalFromAgent(far_corner_agent);
+ mDragPointGlobal = mDragStartPointGlobal;
+
+ // we just started a drag, so save initial object positions, orientations, and scales
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE);
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ setMouseCapture( TRUE );
+
+ mHelpTextTimer.reset();
+ sNumTimesHelpTextShown++;
+ return TRUE;
+}
+
+
+BOOL LLManipScale::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // first, perform normal processing in case this was a quick-click
+ handleHover(x, y, mask);
+
+ gSelectMgr->enableSilhouette(TRUE);
+ mManipPart = LL_NO_PART;
+
+ // Might have missed last update due to UPDATE_DELAY timing
+ if (mSendUpdateOnMouseUp)
+ {
+ gSelectMgr->sendMultipleUpdate( mLastUpdateFlags );
+ }
+
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ return LLManip::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLManipScale::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if( gSelectMgr->isEmpty() )
+ {
+ // Somehow the object got deselected while we were dragging it.
+ setMouseCapture( FALSE );
+ }
+ else
+ {
+ drag( x, y );
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (active)" << llendl;
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ // not dragging...
+ highlightManipulators(x, y);
+ }
+
+ // Patch up textures, if possible.
+ gSelectMgr->adjustTexturesByScale(TRUE, getStretchTextures());
+
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLSCALE);
+ return TRUE;
+}
+
+void LLManipScale::highlightManipulators(S32 x, S32 y)
+{
+ mHighlightedPart = LL_NO_PART;
+
+ // If we have something selected, try to hit its manipulator handles.
+ // Don't do this with nothing selected, as it kills the framerate.
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+
+ if( isSelectionScalable() )
+ {
+ LLMatrix4 transform;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ LLVector4 translation(bbox.getPositionAgent());
+ transform.initRotTrans(bbox.getRotation(), translation);
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ transform *= cfr;
+ LLMatrix4 window_scale;
+ F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
+ window_scale.initAll(LLVector3(zoom_level / gCamera->getAspect(), zoom_level, 0.f),
+ LLQuaternion::DEFAULT,
+ LLVector3::zero);
+ transform *= window_scale;
+ }
+ else
+ {
+ LLMatrix4 projMatrix = gCamera->getProjection();
+ LLMatrix4 modelView = gCamera->getModelview();
+ transform.initAll(LLVector3(1.f, 1.f, 1.f), bbox.getRotation(), bbox.getPositionAgent());
+
+ transform *= modelView;
+ transform *= projMatrix;
+ }
+
+ LLVector3 min = bbox.getMinLocal();
+ LLVector3 max = bbox.getMaxLocal();
+ LLVector3 ctr = bbox.getCenterLocal();
+
+ mProjectedManipulators.deleteAllData();
+
+ S32 numManips = 0;
+ // corners
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
+
+ // 1-D highlights are applicable iff one object is selected
+ if( gSelectMgr->getObjectCount() == 1 )
+ {
+ // face centers
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], max.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], min.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], min.mV[VZ], 1.f);
+ }
+
+ for (S32 i = 0; i < numManips; i++)
+ {
+ LLVector4 projectedVertex = mManipulatorVertices[i] * transform;
+ projectedVertex = projectedVertex * (1.f / projectedVertex.mV[VW]);
+
+ ManipulatorHandle* projManipulator = new ManipulatorHandle(LLVector3(projectedVertex.mV[VX], projectedVertex.mV[VY],
+ projectedVertex.mV[VZ]), MANIPULATOR_IDS[i], (i < 7) ? SCALE_MANIP_CORNER : SCALE_MANIP_FACE);
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ F32 half_width = (F32)gViewerWindow->getWindowWidth() / 2.f;
+ F32 half_height = (F32)gViewerWindow->getWindowHeight() / 2.f;
+ LLVector2 manip2d;
+ LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
+ LLVector2 delta;
+
+ mHighlightedPart = LL_NO_PART;
+
+ for (ManipulatorHandle* manipulator = mProjectedManipulators.getFirstData();
+ manipulator;
+ manipulator = mProjectedManipulators.getNextData())
+ {
+ manip2d.setVec(manipulator->mPosition.mV[VX] * half_width, manipulator->mPosition.mV[VY] * half_height);
+
+ delta = manip2d - mousePos;
+ if (delta.magVecSquared() < MAX_MANIP_SELECT_DISTANCE_SQUARED)
+ {
+ mHighlightedPart = manipulator->mManipID;
+
+ //llinfos << "Tried: " << mHighlightedPart << llendl;
+ break;
+ }
+ }
+ }
+
+ for (S32 i = 0; i < NUM_MANIPULATORS; i++)
+ {
+ if (mHighlightedPart == MANIPULATOR_IDS[i])
+ {
+ mManipulatorScales[i] = lerp(mManipulatorScales[i], SELECTED_MANIPULATOR_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ else
+ {
+ mManipulatorScales[i] = lerp(mManipulatorScales[i], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (inactive)" << llendl;
+}
+
+
+void LLManipScale::renderFaces( const LLBBox& bbox )
+{
+ // Don't bother to render the drag handles for 1-D scaling if
+ // more than one object is selected or if it is an attachment
+ if ( gSelectMgr->getObjectCount() > 1 )
+ {
+ return;
+ }
+
+ // This is a flattened representation of the box as render here
+ // .
+ // (+++) (++-) /|\t
+ // +------------+ | (texture coordinates)
+ // | | |
+ // | 1 | (*) --->s
+ // | +X |
+ // | |
+ // (+++) (+-+)| |(+--) (++-) (+++)
+ // +------------+------------+------------+------------+
+ // |0 3|3 7|7 4|4 0|
+ // | 0 | 4 | 5 | 2 |
+ // | +Z | -Y | -Z | +Y |
+ // | | | | |
+ // |1 2|2 6|6 5|5 1|
+ // +------------+------------+------------+------------+
+ // (-++) (--+)| |(---) (-+-) (-++)
+ // | 3 |
+ // | -X |
+ // | |
+ // | |
+ // +------------+
+ // (-++) (-+-)
+
+ LLColor4 highlight_color( 1.f, 1.f, 1.f, 0.5f);
+ LLColor4 normal_color( 1.f, 1.f, 1.f, 0.3f);
+
+ LLColor4 x_highlight_color( 1.f, 0.2f, 0.2f, 1.0f);
+ LLColor4 x_normal_color( 0.6f, 0.f, 0.f, 0.4f);
+
+ LLColor4 y_highlight_color( 0.2f, 1.f, 0.2f, 1.0f);
+ LLColor4 y_normal_color( 0.f, 0.6f, 0.f, 0.4f);
+
+ LLColor4 z_highlight_color( 0.2f, 0.2f, 1.f, 1.0f);
+ LLColor4 z_normal_color( 0.f, 0.f, 0.6f, 0.4f);
+
+ LLColor4 default_normal_color( 0.7f, 0.7f, 0.7f, 0.15f );
+
+ const LLVector3& min = bbox.getMinLocal();
+ const LLVector3& max = bbox.getMaxLocal();
+ LLVector3 ctr = bbox.getCenterLocal();
+
+ if (mManipPart == LL_NO_PART)
+ {
+ glColor4fv( default_normal_color.mV );
+ glBegin(GL_QUADS);
+ {
+ // Face 0
+ glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
+
+ // Face 1
+ glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
+
+ // Face 2
+ glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
+
+ // Face 3
+ glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
+
+ // Face 4
+ glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
+
+ // Face 5
+ glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
+ }
+ glEnd();
+ }
+
+ // Find nearest vertex
+ LLVector3 orientWRTHead = bbox.agentToLocalBasis( bbox.getCenterAgent() - gAgent.getCameraPositionAgent() );
+ U32 nearest =
+ (orientWRTHead.mV[0] < 0.0f ? 1 : 0) +
+ (orientWRTHead.mV[1] < 0.0f ? 2 : 0) +
+ (orientWRTHead.mV[2] < 0.0f ? 4 : 0);
+
+ // opposite faces on Linden cubes:
+ // 0 & 5
+ // 1 & 3
+ // 2 & 4
+
+ // Table of order to draw faces, based on nearest vertex
+ static U32 face_list[8][6] = {
+ { 2,0,1, 4,5,3 }, // v6 F201 F453
+ { 2,0,3, 4,5,1 }, // v7 F203 F451
+ { 4,0,1, 2,5,3 }, // v5 F401 F253
+ { 4,0,3, 2,5,1 }, // v4 F403 F251
+ { 2,5,1, 4,0,3 }, // v2 F251 F403
+ { 2,5,3, 4,0,1 }, // v3 F253 F401
+ { 4,5,1, 2,0,3 }, // v1 F451 F203
+ { 4,5,3, 2,0,1 } // v0 F453 F201
+ };
+
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ for (S32 i = 0; i < 6; i++)
+ {
+ U32 face = face_list[nearest][i];
+ switch( face )
+ {
+ case 0:
+ conditionalHighlight( LL_FACE_POSZ, &z_highlight_color, &z_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], max.mV[VZ] ) );
+ break;
+
+ case 1:
+ conditionalHighlight( LL_FACE_POSX, &x_highlight_color, &x_normal_color );
+ renderAxisHandle( ctr, LLVector3( max.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 2:
+ conditionalHighlight( LL_FACE_POSY, &y_highlight_color, &y_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], max.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 3:
+ conditionalHighlight( LL_FACE_NEGX, &x_highlight_color, &x_normal_color );
+ renderAxisHandle( ctr, LLVector3( min.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 4:
+ conditionalHighlight( LL_FACE_NEGY, &y_highlight_color, &y_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], min.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 5:
+ conditionalHighlight( LL_FACE_NEGZ, &z_highlight_color, &z_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], min.mV[VZ] ) );
+ break;
+ }
+ }
+ }
+}
+
+void LLManipScale::renderEdges( const LLBBox& bbox )
+{
+ LLVector3 extent = bbox.getExtentLocal();
+ F32 edge_width = mBoxHandleSize * .6f;
+
+ for( U32 part = LL_EDGE_MIN; part <= LL_EDGE_MAX; part++ )
+ {
+ LLVector3 direction = edgeToUnitVector( part );
+ LLVector3 center_to_edge = unitVectorToLocalBBoxExtent( direction, bbox );
+
+ glPushMatrix();
+ {
+ glTranslatef( center_to_edge.mV[0], center_to_edge.mV[1], center_to_edge.mV[2] );
+ conditionalHighlight( part );
+ glScalef(
+ direction.mV[0] ? edge_width : extent.mV[VX],
+ direction.mV[1] ? edge_width : extent.mV[VY],
+ direction.mV[2] ? edge_width : extent.mV[VZ] );
+ gBox.render();
+ }
+ glPopMatrix();
+ }
+}
+
+
+void LLManipScale::renderCorners( const LLBBox& bbox )
+{
+ U32 part = LL_CORNER_NNN;
+
+ F32 x_offset = bbox.getMinLocal().mV[VX];
+ for( S32 i=0; i < 2; i++ )
+ {
+ F32 y_offset = bbox.getMinLocal().mV[VY];
+ for( S32 j=0; j < 2; j++ )
+ {
+ F32 z_offset = bbox.getMinLocal().mV[VZ];
+ for( S32 k=0; k < 2; k++ )
+ {
+ conditionalHighlight( part );
+ renderBoxHandle( x_offset, y_offset, z_offset );
+ part++;
+
+ z_offset = bbox.getMaxLocal().mV[VZ];
+
+ }
+ y_offset = bbox.getMaxLocal().mV[VY];
+ }
+ x_offset = bbox.getMaxLocal().mV[VX];
+ }
+}
+
+
+void LLManipScale::renderBoxHandle( F32 x, F32 y, F32 z )
+{
+ LLGLDisable gls_tex(GL_TEXTURE_2D);
+
+ glPushMatrix();
+ {
+ glTranslatef( x, y, z );
+ glScalef( mScaledBoxHandleSize, mScaledBoxHandleSize, mScaledBoxHandleSize );
+ gBox.render();
+ }
+ glPopMatrix();
+}
+
+
+void LLManipScale::renderAxisHandle( const LLVector3& start, const LLVector3& end )
+{
+ if( getShowAxes() )
+ {
+ // Draws a single "jacks" style handle: a long, retangular box from start to end.
+ LLVector3 offset_start = end - start;
+ offset_start.normVec();
+ offset_start = start + mBoxHandleSize * offset_start;
+
+ LLVector3 delta = end - offset_start;
+ LLVector3 pos = offset_start + 0.5f * delta;
+
+ glPushMatrix();
+ {
+ glTranslatef( pos.mV[VX], pos.mV[VY], pos.mV[VZ] );
+ glScalef(
+ mBoxHandleSize + llabs(delta.mV[VX]),
+ mBoxHandleSize + llabs(delta.mV[VY]),
+ mBoxHandleSize + llabs(delta.mV[VZ]));
+ gBox.render();
+ }
+ glPopMatrix();
+ }
+ else
+ {
+ renderBoxHandle( end.mV[VX], end.mV[VY], end.mV[VZ] );
+ }
+}
+
+
+void LLManipScale::drag( S32 x, S32 y )
+{
+ if( (LL_FACE_MIN <= (S32)mManipPart)
+ && ((S32)mManipPart <= LL_FACE_MAX) )
+ {
+ dragFace( x, y );
+ }
+ else
+ if( (LL_CORNER_MIN <= (S32)mManipPart)
+ && ((S32)mManipPart <= LL_CORNER_MAX) )
+ {
+ dragCorner( x, y );
+ }
+
+ //gAgent.setObjectTracking(FALSE);
+ gAgent.clearFocusObject();
+}
+
+// Scale around the
+void LLManipScale::dragCorner( S32 x, S32 y )
+{
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+
+ // Suppress scale if mouse hasn't moved.
+ if (x == mLastMouseX && y == mLastMouseY)
+ {
+ if (mSendUpdateOnMouseUp)
+ {
+ sendUpdates(TRUE,TRUE,TRUE);
+ }
+ return;
+ }
+
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ LLVector3d drag_start_point_global = mDragStartPointGlobal;
+ LLVector3d drag_start_center_global = mDragStartCenterGlobal;
+ LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
+ LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
+
+ LLVector3d drag_start_dir_d;
+ drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
+ LLVector3 drag_start_dir_f;
+ drag_start_dir_f.setVec(drag_start_dir_d);
+
+ F32 s = 0;
+ F32 t = 0;
+
+ nearestPointOnLineFromMouse(x, y,
+ drag_start_center_agent,
+ drag_start_point_agent,
+ s, t );
+
+ F32 drag_start_dist = dist_vec(drag_start_point_agent, drag_start_center_agent);
+
+ if( s <= 0 ) // we only care about intersections in front of the camera
+ {
+ return;
+ }
+
+ LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
+
+ F32 scale_factor = t;
+
+ BOOL uniform = LLManipScale::getUniform();
+
+ if( !uniform )
+ {
+ scale_factor = 0.5f + (scale_factor * 0.5f);
+ }
+
+ // check for snapping
+ LLVector3 drag_center_agent = gAgent.getPosAgentFromGlobal(drag_point_global);
+ LLVector3 mouse_on_plane1;
+ getMousePointOnPlaneAgent(mouse_on_plane1, x, y, drag_center_agent, mScalePlaneNormal1);
+ LLVector3 mouse_on_plane2;
+ getMousePointOnPlaneAgent(mouse_on_plane2, x, y, drag_center_agent, mScalePlaneNormal2);
+ LLVector3 mouse_dir_1 = mouse_on_plane1 - mScaleCenter;
+ LLVector3 mouse_dir_2 = mouse_on_plane2 - mScaleCenter;
+ LLVector3 mouse_to_scale_line_1 = mouse_dir_1 - projected_vec(mouse_dir_1, mScaleDir);
+ LLVector3 mouse_to_scale_line_2 = mouse_dir_2 - projected_vec(mouse_dir_2, mScaleDir);
+ LLVector3 mouse_to_scale_line_dir_1 = mouse_to_scale_line_1;
+ mouse_to_scale_line_dir_1.normVec();
+ if (mouse_to_scale_line_dir_1 * mSnapGuideDir1 < 0.f)
+ {
+ // need to keep sign of mouse offset wrt to snap guide direction
+ mouse_to_scale_line_dir_1 *= -1.f;
+ }
+ LLVector3 mouse_to_scale_line_dir_2 = mouse_to_scale_line_2;
+ mouse_to_scale_line_dir_2.normVec();
+ if (mouse_to_scale_line_dir_2 * mSnapGuideDir2 < 0.f)
+ {
+ // need to keep sign of mouse offset wrt to snap guide direction
+ mouse_to_scale_line_dir_2 *= -1.f;
+ }
+
+ F32 snap_dir_dot_mouse_offset1 = mSnapGuideDir1 * mouse_to_scale_line_dir_1;
+ F32 snap_dir_dot_mouse_offset2 = mSnapGuideDir2 * mouse_to_scale_line_dir_2;
+
+ F32 dist_from_scale_line_1 = mouse_to_scale_line_1 * mouse_to_scale_line_dir_1;
+ F32 dist_from_scale_line_2 = mouse_to_scale_line_2 * mouse_to_scale_line_dir_2;
+
+ F32 max_scale = partToMaxScale(mManipPart, bbox);
+ F32 min_scale = partToMinScale(mManipPart, bbox);
+
+ BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
+ if (snap_enabled && dist_from_scale_line_1 > mSnapRegimeOffset * snap_dir_dot_mouse_offset1)
+ {
+ mInSnapRegime = TRUE;
+ LLVector3 projected_drag_pos = mouse_on_plane1 - (dist_from_scale_line_1 / snap_dir_dot_mouse_offset1) * mSnapGuideDir1;
+ F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+ F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
+ F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
+
+ mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
+
+ scale_factor = mScaleSnapValue / drag_start_dist;
+ if( !uniform )
+ {
+ scale_factor *= 0.5f;
+ }
+ }
+ else if (snap_enabled && dist_from_scale_line_2 > mSnapRegimeOffset * snap_dir_dot_mouse_offset2)
+ {
+ mInSnapRegime = TRUE;
+ LLVector3 projected_drag_pos = mouse_on_plane2 - (dist_from_scale_line_2 / snap_dir_dot_mouse_offset2) * mSnapGuideDir2;
+ F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+ F32 snap_dist = mScaleSnapUnit2 / (2.f * cur_subdivisions);
+ F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit2 / cur_subdivisions);
+
+ mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
+
+ scale_factor = mScaleSnapValue / drag_start_dist;
+ if( !uniform )
+ {
+ scale_factor *= 0.5f;
+ }
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+
+ F32 max_scale_factor = MAX_OBJECT_SCALE / MIN_OBJECT_SCALE;
+ F32 min_scale_factor = MIN_OBJECT_SCALE / MAX_OBJECT_SCALE;
+
+ // find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
+ LLSelectNode* selectNode;
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject* cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() )
+ {
+ const LLVector3& scale = selectNode->mSavedScale;
+
+ F32 cur_max_scale_factor = llmin( MAX_OBJECT_SCALE / scale.mV[VX], MAX_OBJECT_SCALE / scale.mV[VY], MAX_OBJECT_SCALE / scale.mV[VZ] );
+ max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
+
+ F32 cur_min_scale_factor = llmax( MIN_OBJECT_SCALE / scale.mV[VX], MIN_OBJECT_SCALE / scale.mV[VY], MIN_OBJECT_SCALE / scale.mV[VZ] );
+ min_scale_factor = llmax( min_scale_factor, cur_min_scale_factor );
+ }
+ }
+
+ scale_factor = llclamp( scale_factor, min_scale_factor, max_scale_factor );
+
+ LLVector3d drag_global = uniform ? mDragStartCenterGlobal : mDragFarHitGlobal;
+
+ // do the root objects i.e. (TRUE == cur->isRootEdit())
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject* cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() && cur->isRootEdit() )
+ {
+ const LLVector3& scale = selectNode->mSavedScale;
+ cur->setScale( scale_factor * scale );
+
+ LLVector3 delta_pos;
+ LLVector3 original_pos = cur->getPositionEdit();
+ LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
+ if (!cur->isAttachment())
+ {
+ new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
+ }
+ cur->setPositionAbsoluteGlobal( new_pos_global );
+ rebuild(cur);
+
+ delta_pos = cur->getPositionEdit() - original_pos;
+
+ if (selectNode->mIndividualSelection)
+ {
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = cur->mChildList[child_num];
+
+ if (cur->isAttachment())
+ {
+ LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
+ childp->setPosition(child_pos);
+ }
+ else
+ {
+ LLVector3d child_pos_delta(delta_pos);
+ // RN: this updates drawable position instantly
+ childp->setPositionAbsoluteGlobal(childp->getPositionGlobal() - child_pos_delta);
+ }
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ // do the child objects i.e. (FALSE == cur->isRootEdit())
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject*cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() && !cur->isRootEdit() )
+ {
+ const LLVector3& scale = selectNode->mSavedScale;
+ cur->setScale( scale_factor * scale, FALSE );
+
+ if (!selectNode->mIndividualSelection)
+ {
+ cur->setPosition(selectNode->mSavedPositionLocal * scale_factor);
+ continue;
+ }
+
+ LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
+ cur->setPositionAbsoluteGlobal( new_pos_global );
+ rebuild(cur);
+ }
+ }
+
+
+
+ mDragPointGlobal = drag_point_global;
+
+ sendUpdates( TRUE, TRUE, TRUE );
+}
+
+
+void LLManipScale::dragFace( S32 x, S32 y )
+{
+ // Suppress scale if mouse hasn't moved.
+ if (x == mLastMouseX && y == mLastMouseY)
+ {
+ if (mSendUpdateOnMouseUp)
+ {
+ sendUpdates(TRUE,TRUE,FALSE);
+ }
+ return;
+ }
+
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ LLVector3d drag_start_point_global = mDragStartPointGlobal;
+ LLVector3d drag_start_center_global = mDragStartCenterGlobal;
+ LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
+ LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
+
+ LLVector3d drag_start_dir_d;
+ drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
+ LLVector3 drag_start_dir_f;
+ drag_start_dir_f.setVec(drag_start_dir_d);
+
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+
+ F32 s = 0;
+ F32 t = 0;
+
+ nearestPointOnLineFromMouse(x,
+ y,
+ drag_start_center_agent,
+ drag_start_point_agent,
+ s, t );
+
+ if( s <= 0 ) // we only care about intersections in front of the camera
+ {
+ return;
+ }
+
+ LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
+ LLVector3 part_dir_local = partToUnitVector( mManipPart );
+
+ // check for snapping
+ LLVector3 mouse_on_plane;
+ getMousePointOnPlaneAgent(mouse_on_plane, x, y, mScaleCenter, mScalePlaneNormal1 );
+
+ LLVector3 mouse_on_scale_line = mScaleCenter + projected_vec(mouse_on_plane - mScaleCenter, mScaleDir);
+ LLVector3 drag_delta(mouse_on_scale_line - drag_start_point_agent);
+ F32 max_drag_dist = partToMaxScale(mManipPart, bbox);
+ F32 min_drag_dist = partToMinScale(mManipPart, bbox);
+
+ BOOL uniform = LLManipScale::getUniform();
+ if( uniform )
+ {
+ drag_delta *= 2.f;
+ }
+
+ LLVector3 scale_center_to_mouse = mouse_on_plane - mScaleCenter;
+ F32 dist_from_scale_line = dist_vec(scale_center_to_mouse, (mouse_on_scale_line - mScaleCenter));
+ F32 dist_along_scale_line = scale_center_to_mouse * mScaleDir;
+
+ BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
+
+ if (snap_enabled && dist_from_scale_line > mSnapRegimeOffset)
+ {
+ mInSnapRegime = TRUE;
+
+ if (dist_along_scale_line > max_drag_dist)
+ {
+ mScaleSnapValue = max_drag_dist;
+
+ LLVector3 clamp_point = mScaleCenter + max_drag_dist * mScaleDir;
+ drag_delta.setVec(clamp_point - drag_start_point_agent);
+ }
+ else if (dist_along_scale_line < min_drag_dist)
+ {
+ mScaleSnapValue = min_drag_dist;
+
+ LLVector3 clamp_point = mScaleCenter + min_drag_dist * mScaleDir;
+ drag_delta.setVec(clamp_point - drag_start_point_agent);
+ }
+ else
+ {
+ F32 drag_dist = scale_center_to_mouse * mScaleDir;
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(mScaleCenter + mScaleDir * drag_dist, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+ F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
+ F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
+ relative_snap_dist -= snap_dist;
+
+ //make sure that values that the scale is "snapped to"
+ //do not exceed/go under the applicable max/mins
+ //this causes the box to shift displacements ever so slightly
+ //although the "snap value" should go down to 0
+ //see Jira 1027
+ relative_snap_dist = llclamp(relative_snap_dist,
+ drag_dist - max_drag_dist,
+ drag_dist - min_drag_dist);
+
+ mScaleSnapValue = drag_dist - relative_snap_dist;
+
+ if (llabs(relative_snap_dist) < snap_dist)
+ {
+ LLVector3 drag_correction = relative_snap_dist * mScaleDir;
+ if (uniform)
+ {
+ drag_correction *= 2.f;
+ }
+
+ drag_delta -= drag_correction;
+ }
+ }
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+
+ BOOL send_scale_update = FALSE;
+ BOOL send_position_update = FALSE;
+
+ LLVector3 dir_agent;
+ if( part_dir_local.mV[VX] )
+ {
+ dir_agent = bbox.localToAgentBasis( LLVector3::x_axis );
+ }
+ else if( part_dir_local.mV[VY] )
+ {
+ dir_agent = bbox.localToAgentBasis( LLVector3::y_axis );
+ }
+ else if( part_dir_local.mV[VZ] )
+ {
+ dir_agent = bbox.localToAgentBasis( LLVector3::z_axis );
+ }
+ stretchFace(
+ projected_vec(drag_start_dir_f, dir_agent) + drag_start_center_agent,
+ projected_vec(drag_delta, dir_agent));
+ send_position_update = TRUE;
+ send_scale_update = TRUE;
+
+ mDragPointGlobal = drag_point_global;
+
+ sendUpdates( send_position_update, send_scale_update );
+}
+
+void LLManipScale::sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner )
+{
+ // Throttle updates to 10 per second.
+ static LLTimer update_timer;
+ F32 elapsed_time = update_timer.getElapsedTimeF32();
+ const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
+
+ if( send_scale_update || send_position_update )
+ {
+ U32 update_flags = UPD_NONE;
+ if (send_position_update) update_flags |= UPD_POSITION;
+ if (send_scale_update) update_flags |= UPD_SCALE;
+
+// BOOL send_type = SEND_INDIVIDUALS;
+ if (corner)
+ {
+ update_flags |= UPD_UNIFORM;
+ }
+ // keep this up to date for sendonmouseup
+ mLastUpdateFlags = update_flags;
+
+ // enforce minimum update delay and don't stream updates on sub-object selections
+ if( elapsed_time > UPDATE_DELAY && gSavedSettings.getBOOL("SelectLinkedSet") )
+ {
+ gSelectMgr->sendMultipleUpdate( update_flags );
+ update_timer.reset();
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ else
+ {
+ mSendUpdateOnMouseUp = TRUE;
+ }
+
+ gSelectMgr->updateSelectionCenter();
+ dialog_refresh_all();
+ }
+}
+
+// Rescales in a single dimension. Either uniform (standard) or one-sided (scale plus translation)
+// depending on mUniform. Handles multiple selection and objects that are not aligned to the bounding box.
+void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVector3& drag_delta_agent )
+{
+ LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(mDragStartCenterGlobal);
+
+ LLSelectNode *selectNode;
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject*cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() )
+ {
+ LLBBox cur_bbox = cur->getBoundingBoxAgent();
+ LLVector3 start_local = cur_bbox.agentToLocal( drag_start_agent );
+ LLVector3 end_local = cur_bbox.agentToLocal( drag_start_agent + drag_delta_agent);
+ LLVector3 start_center_local = cur_bbox.agentToLocal( drag_start_center_agent );
+ LLVector3 axis = nearestAxis( start_local - start_center_local );
+ S32 axis_index = axis.mV[0] ? 0 : (axis.mV[1] ? 1 : 2 );
+
+ LLVector3 delta_local = end_local - start_local;
+ F32 delta_local_mag = delta_local.magVec();
+ LLVector3 dir_local;
+ if (delta_local_mag == 0.f)
+ {
+ dir_local = axis;
+ }
+ else
+ {
+ dir_local = delta_local / delta_local_mag; // normalized delta_local
+ }
+
+ F32 denom = axis * dir_local;
+ F32 desired_delta_size = is_approx_zero(denom) ? 0.f : (delta_local_mag / denom); // in meters
+ F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_OBJECT_SCALE, MAX_OBJECT_SCALE);
+ // propagate scale constraint back to position offset
+ desired_delta_size = desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
+
+ LLVector3 scale = cur->getScale();
+ scale.mV[axis_index] = desired_scale;
+ cur->setScale(scale, FALSE);
+ rebuild(cur);
+ LLVector3 delta_pos;
+ if( !getUniform() )
+ {
+ LLVector3 delta_pos_local = axis * (0.5f * desired_delta_size);
+ LLVector3d delta_pos_global;
+ delta_pos_global.setVec(cur_bbox.localToAgent( delta_pos_local ) - cur_bbox.getCenterAgent());
+ LLVector3 cur_pos = cur->getPositionEdit();
+
+ if (cur->isRootEdit() && !cur->isAttachment())
+ {
+ LLVector3d new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, selectNode->mSavedPositionGlobal + delta_pos_global);
+ cur->setPositionGlobal( new_pos_global );
+ }
+ else
+ {
+ LLXform* parent_xform = cur->mDrawable->getXform()->getParent();
+ LLVector3 new_pos_local;
+ // this works in attachment point space using world space delta
+ if (parent_xform)
+ {
+ new_pos_local = selectNode->mSavedPositionLocal + (LLVector3(delta_pos_global) * ~parent_xform->getWorldRotation());
+ }
+ else
+ {
+ new_pos_local = selectNode->mSavedPositionLocal + LLVector3(delta_pos_global);
+ }
+ cur->setPosition(new_pos_local);
+ }
+ delta_pos = cur->getPositionEdit() - cur_pos;
+ }
+ if (cur->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = cur->mChildList[child_num];
+ if (!getUniform())
+ {
+ LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
+ childp->setPosition(child_pos);
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void LLManipScale::renderGuidelinesPart( const LLBBox& bbox )
+{
+ LLVector3 guideline_start = bbox.getCenterLocal();
+
+ LLVector3 guideline_end = unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox );
+
+ if (!getUniform())
+ {
+ guideline_start = unitVectorToLocalBBoxExtent( -partToUnitVector( mManipPart ), bbox );
+ }
+
+ guideline_end -= guideline_start;
+ guideline_end.normVec();
+ guideline_end *= gWorldPointer->getRegionWidthInMeters();
+ guideline_end += guideline_start;
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE);
+ gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.5f) );
+ }
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+ gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.25f) );
+ }
+}
+
+void LLManipScale::updateSnapGuides(const LLBBox& bbox)
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 box_corner_agent = bbox.localToAgent(unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ));
+ mScaleCenter = getUniform() ? bbox.getCenterAgent() : bbox.localToAgent(unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ));
+ mScaleDir = box_corner_agent - mScaleCenter;
+ mScaleDir.normVec();
+
+ if(gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mSnapRegimeOffset = SNAP_GUIDE_SCREEN_OFFSET / gAgent.getAvatarObject()->mHUDCurZoom;
+
+ }
+ else
+ {
+ F32 object_distance = dist_vec(mScaleCenter, gCamera->getOrigin());
+ mSnapRegimeOffset = (SNAP_GUIDE_SCREEN_OFFSET * gViewerWindow->getWindowWidth() * object_distance) / gCamera->getPixelMeterRatio();
+ }
+ LLVector3 cam_at_axis;
+ F32 snap_guide_length;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_at_axis.setVec(1.f, 0.f, 0.f);
+ snap_guide_length = SNAP_GUIDE_SCREEN_LENGTH / gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ cam_at_axis = gCamera->getAtAxis();
+ F32 manipulator_distance = dist_vec(box_corner_agent, gCamera->getOrigin());
+ snap_guide_length = (SNAP_GUIDE_SCREEN_LENGTH * gViewerWindow->getWindowWidth() * manipulator_distance) / gCamera->getPixelMeterRatio();
+ }
+
+ mSnapGuideLength = snap_guide_length / llmax(0.1f, (llmin(mSnapGuideDir1 * cam_at_axis, mSnapGuideDir2 * cam_at_axis)));
+
+ LLVector3 off_axis_dir = mScaleDir % cam_at_axis;
+ off_axis_dir.normVec();
+
+ if( (LL_FACE_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_FACE_MAX) )
+ {
+ LLVector3 object_scale = bbox.getMaxLocal();
+ object_scale.scaleVec(off_axis_dir * ~bbox.getRotation());
+ object_scale.abs();
+ if (object_scale.mV[VX] > object_scale.mV[VY] && object_scale.mV[VX] > object_scale.mV[VZ])
+ {
+ mSnapGuideDir1 = LLVector3::x_axis * bbox.getRotation();
+ }
+ else if (object_scale.mV[VY] > object_scale.mV[VZ])
+ {
+ mSnapGuideDir1 = LLVector3::y_axis * bbox.getRotation();
+ }
+ else
+ {
+ mSnapGuideDir1 = LLVector3::z_axis * bbox.getRotation();
+ }
+
+ LLVector3 scale_snap = grid_scale;
+ mScaleSnapUnit1 = scale_snap.scaleVec(partToUnitVector( mManipPart )).magVec();
+ mScaleSnapUnit2 = mScaleSnapUnit1;
+ mSnapGuideDir1 *= mSnapGuideDir1 * gCamera->getUpAxis() > 0.f ? 1.f : -1.f;
+ mSnapGuideDir2 = mSnapGuideDir1 * -1.f;
+ mSnapDir1 = mScaleDir;
+ mSnapDir2 = mScaleDir;
+ }
+ else if( (LL_CORNER_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_CORNER_MAX) )
+ {
+ LLVector3 local_scale_dir = partToUnitVector( mManipPart );
+ LLVector3 local_camera_dir;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ local_camera_dir = LLVector3(-1.f, 0.f, 0.f) * ~bbox.getRotation();
+ }
+ else
+ {
+ local_camera_dir = (gCamera->getOrigin() - bbox.getCenterAgent()) * ~bbox.getRotation();
+ local_camera_dir.normVec();
+ }
+ local_scale_dir -= projected_vec(local_scale_dir, local_camera_dir);
+ local_scale_dir.normVec();
+ LLVector3 x_axis_proj_camera = LLVector3::x_axis - projected_vec(LLVector3::x_axis, local_camera_dir);
+ x_axis_proj_camera.normVec();
+ LLVector3 y_axis_proj_camera = LLVector3::y_axis - projected_vec(LLVector3::y_axis, local_camera_dir);
+ y_axis_proj_camera.normVec();
+ LLVector3 z_axis_proj_camera = LLVector3::z_axis - projected_vec(LLVector3::z_axis, local_camera_dir);
+ z_axis_proj_camera.normVec();
+ F32 x_axis_proj = llabs(local_scale_dir * x_axis_proj_camera);
+ F32 y_axis_proj = llabs(local_scale_dir * y_axis_proj_camera);
+ F32 z_axis_proj = llabs(local_scale_dir * z_axis_proj_camera);
+
+ if (x_axis_proj > y_axis_proj && x_axis_proj > z_axis_proj)
+ {
+ mSnapGuideDir1 = LLVector3::y_axis;
+ mScaleSnapUnit2 = grid_scale.mV[VY];
+ mSnapGuideDir2 = LLVector3::z_axis;
+ mScaleSnapUnit1 = grid_scale.mV[VZ];
+ }
+ else if (y_axis_proj > z_axis_proj)
+ {
+ mSnapGuideDir1 = LLVector3::x_axis;
+ mScaleSnapUnit2 = grid_scale.mV[VX];
+ mSnapGuideDir2 = LLVector3::z_axis;
+ mScaleSnapUnit1 = grid_scale.mV[VZ];
+ }
+ else
+ {
+ mSnapGuideDir1 = LLVector3::x_axis;
+ mScaleSnapUnit2 = grid_scale.mV[VX];
+ mSnapGuideDir2 = LLVector3::y_axis;
+ mScaleSnapUnit1 = grid_scale.mV[VY];
+ }
+
+ LLVector3 snap_guide_flip(1.f, 1.f, 1.f);
+ switch (mManipPart)
+ {
+ case LL_CORNER_NNN:
+ break;
+ case LL_CORNER_NNP:
+ snap_guide_flip.setVec(1.f, 1.f, -1.f);
+ break;
+ case LL_CORNER_NPN:
+ snap_guide_flip.setVec(1.f, -1.f, 1.f);
+ break;
+ case LL_CORNER_NPP:
+ snap_guide_flip.setVec(1.f, -1.f, -1.f);
+ break;
+ case LL_CORNER_PNN:
+ snap_guide_flip.setVec(-1.f, 1.f, 1.f);
+ break;
+ case LL_CORNER_PNP:
+ snap_guide_flip.setVec(-1.f, 1.f, -1.f);
+ break;
+ case LL_CORNER_PPN:
+ snap_guide_flip.setVec(-1.f, -1.f, 1.f);
+ break;
+ case LL_CORNER_PPP:
+ snap_guide_flip.setVec(-1.f, -1.f, -1.f);
+ break;
+ default:
+ break;
+ }
+ mSnapGuideDir1.scaleVec(snap_guide_flip);
+ mSnapGuideDir2.scaleVec(snap_guide_flip);
+ mSnapGuideDir1.rotVec(bbox.getRotation());
+ mSnapGuideDir2.rotVec(bbox.getRotation());
+ mSnapDir1 = -1.f * mSnapGuideDir2;
+ mSnapDir2 = -1.f * mSnapGuideDir1;
+ }
+
+ mScalePlaneNormal1 = mSnapGuideDir1 % mScaleDir;
+ mScalePlaneNormal1.normVec();
+
+ mScalePlaneNormal2 = mSnapGuideDir2 % mScaleDir;
+ mScalePlaneNormal2.normVec();
+
+ mScaleSnapUnit1 = mScaleSnapUnit1 / (mSnapDir1 * mScaleDir);
+ mScaleSnapUnit2 = mScaleSnapUnit2 / (mSnapDir2 * mScaleDir);
+}
+
+void LLManipScale::renderSnapGuides(const LLBBox& bbox)
+{
+ if (!gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ return;
+ }
+
+ F32 max_subdivisions = sGridMaxSubdivisionLevel;
+ F32 grid_alpha = gSavedSettings.getF32("GridOpacity");
+
+ F32 max_point_on_scale_line = partToMaxScale(mManipPart, bbox);
+ LLVector3 drag_point = gAgent.getPosAgentFromGlobal(mDragPointGlobal);
+
+ updateGridSettings();
+
+ S32 pass;
+ for (pass = 0; pass < 3; pass++)
+ {
+ LLColor4 tick_color = setupSnapGuideRenderPass(pass);
+
+ glBegin(GL_LINES);
+ LLVector3 line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir1 * mSnapRegimeOffset);
+ LLVector3 line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
+ LLVector3 line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
+
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
+ glVertex3fv(line_start.mV);
+ glColor4fv(tick_color.mV);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
+ glVertex3fv(line_end.mV);
+
+ line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir2 * mSnapRegimeOffset);
+ line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
+ line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
+ glVertex3fv(line_start.mV);
+ glColor4fv(tick_color.mV);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
+ glVertex3fv(line_end.mV);
+ glEnd();
+ }
+
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ F32 dist_grid_axis = (drag_point - mScaleCenter) * mScaleDir;
+ // find distance to nearest smallest grid unit
+ F32 grid_offset1 = fmodf(dist_grid_axis, mScaleSnapUnit1 / max_subdivisions);
+ F32 grid_offset2 = fmodf(dist_grid_axis, mScaleSnapUnit2 / max_subdivisions);
+
+ // how many smallest grid units are we away from largest grid scale?
+ S32 sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 / sGridMinSubdivisionLevel) / (mScaleSnapUnit1 / max_subdivisions));
+ S32 sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 / sGridMinSubdivisionLevel) / (mScaleSnapUnit2 / max_subdivisions));
+
+ S32 num_ticks_per_side1 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit1 / max_subdivisions)));
+ S32 num_ticks_per_side2 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit2 / max_subdivisions)));
+ F32 dist_scale_units_1 = dist_grid_axis / (mScaleSnapUnit1 / max_subdivisions);
+ F32 dist_scale_units_2 = dist_grid_axis / (mScaleSnapUnit2 / max_subdivisions);
+ S32 ticks_from_scale_center_1 = lltrunc(dist_scale_units_1);
+ S32 ticks_from_scale_center_2 = lltrunc(dist_scale_units_2);
+ S32 max_ticks1 = llceil(max_point_on_scale_line / (mScaleSnapUnit1 / max_subdivisions) - dist_scale_units_1);
+ S32 max_ticks2 = llceil(max_point_on_scale_line / (mScaleSnapUnit2 / max_subdivisions) - dist_scale_units_2);
+ S32 start_tick = 0;
+ S32 stop_tick = 0;
+
+ if (mInSnapRegime)
+ {
+ // draw snap guide line
+ glBegin(GL_LINES);
+ LLVector3 snap_line_center = mScaleCenter + (mScaleSnapValue * mScaleDir);
+
+ LLVector3 snap_line_start = snap_line_center + (mSnapGuideDir1 * mSnapRegimeOffset);
+ LLVector3 snap_line_end = snap_line_center + (mSnapGuideDir2 * mSnapRegimeOffset);
+
+ glColor4f(1.f, 1.f, 1.f, grid_alpha);
+ glVertex3fv(snap_line_start.mV);
+ glVertex3fv(snap_line_center.mV);
+ glVertex3fv(snap_line_center.mV);
+ glVertex3fv(snap_line_end.mV);
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ //gGLSNoCullFaces.set();
+ glColor4f(1.f, 1.f, 1.f, grid_alpha);
+
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = mScaleDir;
+
+ arrow_dir = snap_line_start - snap_line_center;
+ arrow_dir.normVec();
+ glVertex3fv((snap_line_start + arrow_dir * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_start + arrow_span * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_start - arrow_span * mBoxHandleSize).mV);
+
+ arrow_dir = snap_line_end - snap_line_center;
+ arrow_dir.normVec();
+ glVertex3fv((snap_line_end + arrow_dir * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_end + arrow_span * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_end - arrow_span * mBoxHandleSize).mV);
+ }
+ glEnd();
+ }
+
+ LLVector2 screen_translate_axis(llabs(mScaleDir * gCamera->getLeftAxis()), llabs(mScaleDir * gCamera->getUpAxis()));
+ screen_translate_axis.normVec();
+
+ S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
+
+ for (pass = 0; pass < 3; pass++)
+ {
+ LLColor4 tick_color = setupSnapGuideRenderPass(pass);
+
+ start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
+ stop_tick = llmin(max_ticks1, num_ticks_per_side1);
+
+ glBegin(GL_LINES);
+ // draw first row of ticks
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side1)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ if (fmodf((F32)(i + sub_div_offset_1), (max_subdivisions / cur_subdivisions)) != 0.f)
+ {
+ continue;
+ }
+
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset_1), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
+ LLVector3 tick_start = tick_pos + (mSnapGuideDir1 * mSnapRegimeOffset);
+ LLVector3 tick_end = tick_start + (mSnapGuideDir1 * mSnapRegimeOffset * tick_scale);
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+ }
+
+ // draw opposite row of ticks
+ start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
+ stop_tick = llmin(max_ticks2, num_ticks_per_side2);
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side2)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ if (fmodf((F32)(i + sub_div_offset_2), (max_subdivisions / cur_subdivisions)) != 0.f)
+ {
+ continue;
+ }
+
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset_2), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
+ LLVector3 tick_start = tick_pos + (mSnapGuideDir2 * mSnapRegimeOffset);
+ LLVector3 tick_end = tick_start + (mSnapGuideDir2 * mSnapRegimeOffset * tick_scale);
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+ }
+ glEnd();
+ }
+
+ // render tick labels
+ start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
+ stop_tick = llmin(max_ticks1, num_ticks_per_side1);
+
+ F32 grid_resolution = gSelectMgr->getSelectType() == SELECT_TYPE_HUD ? 0.25f : llmax(gSavedSettings.getF32("GridResolution"), 0.001f);
+ S32 label_sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 * 32.f) / (mScaleSnapUnit1 / max_subdivisions));
+ S32 label_sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 * 32.f) / (mScaleSnapUnit2 / max_subdivisions));
+
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 tick_scale = 1.f;
+ F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side1)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
+
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + label_sub_div_offset_1), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ if (fmodf((F32)(i + label_sub_div_offset_1), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1, tick_label_spacing)))) == 0.f)
+ {
+ LLVector3 text_origin = tick_pos +
+ (mSnapGuideDir1 * mSnapRegimeOffset * (1.f + tick_scale));
+
+ EGridMode grid_mode = gSelectMgr->getGridMode();
+ F32 tick_val;
+ if (grid_mode == GRID_MODE_WORLD)
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 / grid_resolution);
+ }
+ else
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 * 2.f);
+ }
+
+ if (getUniform())
+ {
+ tick_val *= 2.f;
+ }
+
+ F32 text_highlight = 0.8f;
+
+ if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
+ {
+ text_highlight = 1.f;
+ }
+
+ renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ }
+
+ // label ticks on opposite side
+ if (mScaleSnapUnit2 != mScaleSnapUnit1)
+ {
+ start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
+ stop_tick = llmin(max_ticks2, num_ticks_per_side2);
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 tick_scale = 1.f;
+ F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side2)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
+
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + label_sub_div_offset_2), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ if (fmodf((F32)(i + label_sub_div_offset_2), (max_subdivisions / llmin(max_subdivisions, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2, tick_label_spacing)))) == 0.f)
+ {
+ LLVector3 text_origin = tick_pos +
+ (mSnapGuideDir2 * mSnapRegimeOffset * (1.f + tick_scale));
+
+ EGridMode grid_mode = gSelectMgr->getGridMode();
+ F32 tick_val;
+ if (grid_mode == GRID_MODE_WORLD)
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 / grid_resolution);
+ }
+ else
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 * 2.f);
+ }
+
+ if (getUniform())
+ {
+ tick_val *= 2.f;
+ }
+
+ F32 text_highlight = 0.8f;
+
+ if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
+ {
+ text_highlight = 1.f;
+ }
+
+ renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ }
+ }
+
+
+ // render help text
+ if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
+ {
+ LLVector3 selection_center_start = gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+
+ LLVector3 offset_dir;
+ if (mSnapGuideDir1 * gCamera->getAtAxis() > mSnapGuideDir2 * gCamera->getAtAxis())
+ {
+ offset_dir = mSnapGuideDir2;
+ }
+ else
+ {
+ offset_dir = mSnapGuideDir1;
+ }
+
+ LLVector3 help_text_pos = selection_center_start + (mSnapRegimeOffset * 5.f * offset_dir);
+ const LLFontGL* big_fontp = LLFontGL::sSansSerif;
+
+ std::string help_text = "Move mouse cursor over ruler";
+ LLColor4 help_text_color = LLColor4::white;
+ help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, grid_alpha, 0.f);
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ help_text = "to snap to grid";
+ help_text_pos -= gCamera->getUpAxis() * mSnapRegimeOffset * 0.4f;
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ }
+ }
+ }
+}
+
+// Returns unit vector in direction of part of an origin-centered cube
+LLVector3 LLManipScale::partToUnitVector( S32 part ) const
+{
+ if( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) )
+ {
+ return faceToUnitVector( part );
+ }
+ else
+ if( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) )
+ {
+ return cornerToUnitVector( part );
+ }
+ else
+ if( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX ) )
+ {
+ return edgeToUnitVector( part );
+ }
+ return LLVector3();
+}
+
+
+// Returns unit vector in direction of face of an origin-centered cube
+LLVector3 LLManipScale::faceToUnitVector( S32 part ) const
+{
+ llassert( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) );
+ switch( part )
+ {
+ case LL_FACE_POSX:
+ return LLVector3( 1.f, 0.f, 0.f );
+
+ case LL_FACE_NEGX:
+ return LLVector3( -1.f, 0.f, 0.f );
+
+ case LL_FACE_POSY:
+ return LLVector3( 0.f, 1.f, 0.f );
+
+ case LL_FACE_NEGY:
+ return LLVector3( 0.f, -1.f, 0.f );
+
+ case LL_FACE_POSZ:
+ return LLVector3( 0.f, 0.f, 1.f );
+
+ case LL_FACE_NEGZ:
+ return LLVector3( 0.f, 0.f, -1.f );
+ }
+ return LLVector3();
+}
+
+
+// Returns unit vector in direction of corner of an origin-centered cube
+LLVector3 LLManipScale::cornerToUnitVector( S32 part ) const
+{
+ llassert( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) );
+ LLVector3 vec;
+ switch(part)
+ {
+ case LL_CORNER_NNN:
+ vec.setVec(-F_SQRT3, -F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_NNP:
+ vec.setVec(-F_SQRT3, -F_SQRT3, F_SQRT3);
+ break;
+ case LL_CORNER_NPN:
+ vec.setVec(-F_SQRT3, F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_NPP:
+ vec.setVec(-F_SQRT3, F_SQRT3, F_SQRT3);
+ break;
+ case LL_CORNER_PNN:
+ vec.setVec(F_SQRT3, -F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_PNP:
+ vec.setVec(F_SQRT3, -F_SQRT3, F_SQRT3);
+ break;
+ case LL_CORNER_PPN:
+ vec.setVec(F_SQRT3, F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_PPP:
+ vec.setVec(F_SQRT3, F_SQRT3, F_SQRT3);
+ break;
+ default:
+ vec.clearVec();
+ }
+
+ return vec;
+}
+
+// Returns unit vector in direction of edge of an origin-centered cube
+LLVector3 LLManipScale::edgeToUnitVector( S32 part ) const
+{
+ llassert( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX) );
+ //FIXME
+ part -= LL_EDGE_MIN;
+ S32 rotation = part >> 2; // Edge between which faces: 0 => XY, 1 => YZ, 2 => ZX
+ LLVector3 v;
+ v.mV[rotation] = (part & 1) ? F_SQRT2 : -F_SQRT2;
+ v.mV[(rotation+1) % 3] = (part & 2) ? F_SQRT2 : -F_SQRT2;
+ // v.mV[(rotation+2) % 3] defaults to 0.
+ return v;
+}
+
+// Non-linear scale of origin-centered unit cube to non-origin-centered, non-symetrical bounding box
+LLVector3 LLManipScale::unitVectorToLocalBBoxExtent( const LLVector3& v, const LLBBox& bbox ) const
+{
+ const LLVector3& min = bbox.getMinLocal();
+ const LLVector3& max = bbox.getMaxLocal();
+ LLVector3 ctr = bbox.getCenterLocal();
+
+ return LLVector3(
+ v.mV[0] ? (v.mV[0]>0 ? max.mV[0] : min.mV[0] ) : ctr.mV[0],
+ v.mV[1] ? (v.mV[1]>0 ? max.mV[1] : min.mV[1] ) : ctr.mV[1],
+ v.mV[2] ? (v.mV[2]>0 ? max.mV[2] : min.mV[2] ) : ctr.mV[2] );
+}
+
+// returns max allowable scale along a given stretch axis
+F32 LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const
+{
+ F32 max_scale_factor = 0.f;
+ LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
+ bbox_extents.abs();
+ F32 max_extent = 0.f;
+ for (U32 i = VX; i <= VZ; i++)
+ {
+ if (bbox_extents.mV[i] > max_extent)
+ {
+ max_extent = bbox_extents.mV[i];
+ }
+ }
+ max_scale_factor = bbox_extents.magVec() * MAX_OBJECT_SCALE / max_extent;
+
+ if (getUniform())
+ {
+ max_scale_factor *= 0.5f;
+ }
+
+ return max_scale_factor;
+}
+
+// returns min allowable scale along a given stretch axis
+F32 LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
+{
+ LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
+ bbox_extents.abs();
+ F32 min_extent = MAX_OBJECT_SCALE;
+ for (U32 i = VX; i <= VZ; i++)
+ {
+ if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent)
+ {
+ min_extent = bbox_extents.mV[i];
+ }
+ }
+ F32 min_scale_factor = bbox_extents.magVec() * MIN_OBJECT_SCALE / min_extent;
+
+ if (getUniform())
+ {
+ min_scale_factor *= 0.5f;
+ }
+
+ return min_scale_factor;
+}
+
+// Returns the axis aligned unit vector closest to v.
+LLVector3 LLManipScale::nearestAxis( const LLVector3& v ) const
+{
+ // Note: yes, this is a slow but easy implementation
+ // assumes v is normalized
+
+ F32 coords[][3] =
+ {
+ { 1.f, 0.f, 0.f },
+ { 0.f, 1.f, 0.f },
+ { 0.f, 0.f, 1.f },
+ {-1.f, 0.f, 0.f },
+ { 0.f,-1.f, 0.f },
+ { 0.f, 0.f,-1.f }
+ };
+
+ F32 cosine[6];
+ cosine[0] = v * LLVector3( coords[0] );
+ cosine[1] = v * LLVector3( coords[1] );
+ cosine[2] = v * LLVector3( coords[2] );
+ cosine[3] = -cosine[0];
+ cosine[4] = -cosine[1];
+ cosine[5] = -cosine[2];
+
+ F32 greatest_cos = cosine[0];
+ S32 greatest_index = 0;
+ for( S32 i=1; i<6; i++ )
+ {
+ if( greatest_cos < cosine[i] )
+ {
+ greatest_cos = cosine[i];
+ greatest_index = i;
+ }
+ }
+
+ return LLVector3( coords[greatest_index] );
+}
+
+BOOL LLManipScale::isSelectionScalable() const
+{
+ // An selection is scalable if you are allowed to both edit and move
+ // everything in it, and it does not have any sitting agents
+ BOOL scalable = gSelectMgr->getFirstObject() ? TRUE : FALSE;
+ for(LLViewerObject* cur = gSelectMgr->getFirstObject();
+ cur;
+ cur = gSelectMgr->getNextObject() )
+ {
+ if( !(cur->permModify() && cur->permMove())
+ || cur->isSeat())
+ {
+ scalable = FALSE;
+ break;
+ }
+ }
+ return scalable;
+}
diff --git a/indra/newview/llmanipscale.h b/indra/newview/llmanipscale.h
new file mode 100644
index 0000000000..0ba141cc01
--- /dev/null
+++ b/indra/newview/llmanipscale.h
@@ -0,0 +1,137 @@
+/**
+ * @file llmanipscale.h
+ * @brief LLManipScale class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MANIPSCALE_H
+#define LL_MANIPSCALE_H
+
+// llmanipscale.h
+//
+// copyright 2001-2002, linden research inc
+
+
+#include "lltool.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "llmanip.h"
+#include "llviewerobject.h"
+#include "llbbox.h"
+
+class LLToolComposite;
+class LLColor4;
+
+typedef enum e_scale_manipulator_type
+{
+ SCALE_MANIP_CORNER,
+ SCALE_MANIP_FACE
+} EScaleManipulatorType;
+
+
+class LLManipScale : public LLManip
+{
+public:
+ class ManipulatorHandle
+ {
+ public:
+ LLVector3 mPosition;
+ EManipPart mManipID;
+ EScaleManipulatorType mType;
+
+ ManipulatorHandle(LLVector3 pos, EManipPart id, EScaleManipulatorType type):mPosition(pos), mManipID(id), mType(type){}
+ };
+
+
+ LLManipScale( LLToolComposite* composite );
+ ~LLManipScale();
+
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual void render();
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask);
+ EManipPart getHighlightedPart() { return mHighlightedPart; }
+ virtual void highlightManipulators(S32 x, S32 y); // decided which manipulator, if any, should be highlighted by mouse hover
+
+ static void setUniform( BOOL b );
+ static BOOL getUniform();
+ static void setStretchTextures( BOOL b );
+ static BOOL getStretchTextures();
+ static void setShowAxes( BOOL b );
+ static BOOL getShowAxes();
+
+private:
+ void renderCorners( const LLBBox& local_bbox );
+ void renderFaces( const LLBBox& local_bbox );
+ void renderEdges( const LLBBox& local_bbox );
+ void renderBoxHandle( F32 x, F32 y, F32 z );
+ void renderAxisHandle( const LLVector3& start, const LLVector3& end );
+ void renderGuidelinesPart( const LLBBox& local_bbox );
+ void renderSnapGuides( const LLBBox& local_bbox );
+
+ void revert();
+
+ inline void conditionalHighlight( U32 part, const LLColor4* highlight = NULL, const LLColor4* normal = NULL );
+
+ void drag( S32 x, S32 y );
+ void dragFace( S32 x, S32 y );
+ void dragCorner( S32 x, S32 y );
+
+ void sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner = FALSE);
+
+ LLVector3 faceToUnitVector( S32 part ) const;
+ LLVector3 cornerToUnitVector( S32 part ) const;
+ LLVector3 edgeToUnitVector( S32 part ) const;
+ LLVector3 partToUnitVector( S32 part ) const;
+ LLVector3 unitVectorToLocalBBoxExtent( const LLVector3& v, const LLBBox& bbox ) const;
+ F32 partToMaxScale( S32 part, const LLBBox& bbox ) const;
+ F32 partToMinScale( S32 part, const LLBBox& bbox ) const;
+ LLVector3 nearestAxis( const LLVector3& v ) const;
+
+ BOOL isSelectionScalable() const;
+
+ void stretchFace( const LLVector3& drag_start_agent, const LLVector3& drag_delta_agent);
+
+ void adjustTextureRepeats(); // Adjusts texture coords based on mSavedScale and current scale, only works for boxes
+
+ void updateSnapGuides(const LLBBox& bbox);
+private:
+
+ F32 mBoxHandleSize; // The size of the handles at the corners of the bounding box
+ F32 mScaledBoxHandleSize; // handle size after scaling for selection feedback
+ EManipPart mManipPart;
+ LLVector3d mDragStartPointGlobal;
+ LLVector3d mDragStartCenterGlobal; // The center of the bounding box of all selected objects at time of drag start
+ LLVector3d mDragPointGlobal;
+ LLVector3d mDragFarHitGlobal;
+ EManipPart mHighlightedPart;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ BOOL mSendUpdateOnMouseUp;
+ U32 mLastUpdateFlags;
+ LLLinkedList<ManipulatorHandle> mProjectedManipulators;
+ LLVector4 mManipulatorVertices[14];
+ F32 mScaleSnapUnit1; // size of snap multiples for axis 1
+ F32 mScaleSnapUnit2; // size of snap multiples for axis 2
+ LLVector3 mScalePlaneNormal1; // normal of plane in which scale occurs that most faces camera
+ LLVector3 mScalePlaneNormal2; // normal of plane in which scale occurs that most faces camera
+ LLVector3 mSnapGuideDir1;
+ LLVector3 mSnapGuideDir2;
+ LLVector3 mSnapDir1;
+ LLVector3 mSnapDir2;
+ F32 mSnapRegimeOffset;
+ F32 mSnapGuideLength;
+ LLVector3 mScaleCenter;
+ LLVector3 mScaleDir;
+ F32 mScaleSnapValue;
+ BOOL mInSnapRegime;
+ F32* mManipulatorScales;
+};
+
+#endif // LL_MANIPSCALE_H
diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp
new file mode 100644
index 0000000000..3db23c06ca
--- /dev/null
+++ b/indra/newview/llmaniptranslate.cpp
@@ -0,0 +1,2126 @@
+/**
+ * @file llmaniptranslate.cpp
+ * @brief LLManipTranslate class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Positioning tool
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmaniptranslate.h"
+
+#include "llgl.h"
+
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbox.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llfloatertools.h"
+#include "llfontgl.h"
+#include "llglheaders.h"
+#include "llhudrender.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "llstatusbar.h"
+#include "lltoolmgr.h"
+#include "llviewercamera.h"
+#include "llviewerjoint.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llui.h"
+
+const S32 NUM_AXES = 3;
+const S32 MOUSE_DRAG_SLOP = 2; // pixels
+const F32 HANDLE_HIDE_ANGLE = 0.15f; // radians
+const F32 SELECTED_ARROW_SCALE = 1.3f;
+const F32 MANIPULATOR_HOTSPOT_START = 0.2f;
+const F32 MANIPULATOR_HOTSPOT_END = 1.2f;
+const F32 SNAP_GUIDE_SCREEN_SIZE = 0.7f;
+const F32 MIN_PLANE_MANIP_DOT_PRODUCT = 0.25f;
+const F32 PLANE_TICK_SIZE = 0.4f;
+const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
+const F32 SNAP_ARROW_SCALE = 0.7f;
+
+static GLuint sGridTex = 0;
+
+const LLManip::EManipPart MANIPULATOR_IDS[9] =
+{
+ LLManip::LL_X_ARROW,
+ LLManip::LL_Y_ARROW,
+ LLManip::LL_Z_ARROW,
+ LLManip::LL_X_ARROW,
+ LLManip::LL_Y_ARROW,
+ LLManip::LL_Z_ARROW,
+ LLManip::LL_YZ_PLANE,
+ LLManip::LL_XZ_PLANE,
+ LLManip::LL_XY_PLANE
+};
+
+const U32 ARROW_TO_AXIS[4] =
+{
+ VX,
+ VX,
+ VY,
+ VZ
+};
+
+BOOL sort_manip_by_end_z(LLManipTranslate::ManipulatorHandle *new_manip, LLManipTranslate::ManipulatorHandle *test_manip)
+{
+ return (new_manip->mEndPosition.mV[VZ] < test_manip->mEndPosition.mV[VZ]);
+}
+
+LLManipTranslate::LLManipTranslate( LLToolComposite* composite )
+: LLManip( "Move", composite ),
+ mLastHoverMouseX(-1),
+ mLastHoverMouseY(-1),
+ mSendUpdateOnMouseUp(FALSE),
+ mMouseOutsideSlop(FALSE),
+ mCopyMadeThisDrag(FALSE),
+ mMouseDownX(-1),
+ mMouseDownY(-1),
+ mAxisArrowLength(50),
+ mConeSize(0),
+ mArrowLengthMeters(0.f),
+ mPlaneManipOffsetMeters(0.f),
+ mManipPart(LL_NO_PART),
+ mUpdateTimer(),
+ mSnapOffsetMeters(0.f),
+ mArrowScales(1.f, 1.f, 1.f),
+ mPlaneScales(1.f, 1.f, 1.f),
+ mPlaneManipPositions(1.f, 1.f, 1.f, 1.f)
+{
+ mProjectedManipulators.setInsertBefore(sort_manip_by_end_z);
+
+ if (sGridTex == 0)
+ {
+ restoreGL();
+ }
+}
+
+//static
+void LLManipTranslate::restoreGL()
+{
+ //generate grid texture
+ U32 rez = 512;
+ U32 mip = 0;
+
+ GLuint* d = new GLuint[rez*rez];
+ LLGLEnable tex2d(GL_TEXTURE_2D);
+ glGenTextures(1, &sGridTex);
+ glBindTexture(GL_TEXTURE_2D, sGridTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ while (rez >= 1)
+ {
+ for (U32 i = 0; i < rez*rez; i++)
+ {
+ d[i] = 0x00FFFFFF;
+ }
+
+ U32 subcol = 0xFFFFFFFF;
+ if (rez >= 4)
+ { //large grain grid
+ for (U32 i = 0; i < rez; i++)
+ {
+ if (rez <= 16)
+ {
+ if (rez == 16)
+ {
+ subcol = 0xA0FFFFFF;
+ }
+ else if (rez == 8)
+ {
+ subcol = 0x80FFFFFF;
+ }
+ else
+ {
+ subcol = 0x40FFFFFF;
+ }
+ }
+ else
+ {
+ subcol = 0xFFFFFFFF;
+ }
+ d[i *rez+ 0 ] = subcol;
+ d[0 *rez+ i ] = subcol;
+ if (rez >= 32)
+ {
+ d[i *rez+ (rez-1)] = subcol;
+ d[(rez-1) *rez+ i ] = subcol;
+ }
+
+ if (rez >= 64)
+ {
+ subcol = 0xFFFFFFFF;
+
+ if (i > 0 && i < (rez-1))
+ {
+ d[i *rez+ 1 ] = subcol;
+ d[i *rez+ (rez-2)] = subcol;
+ d[1 *rez+ i ] = subcol;
+ d[(rez-2) *rez+ i ] = subcol;
+ }
+ }
+ }
+ }
+
+ subcol = 0x50A0A0A0;
+ if (rez >= 128)
+ { //small grain grid
+ for (U32 i = 8; i < rez; i+=8)
+ {
+ for (U32 j = 2; j < rez-2; j++)
+ {
+ d[i *rez+ j] = subcol;
+ d[j *rez+ i] = subcol;
+ }
+ }
+ }
+ if (rez >= 64)
+ { //medium grain grid
+ if (rez == 64)
+ {
+ subcol = 0x50A0A0A0;
+ }
+ else
+ {
+ subcol = 0xA0D0D0D0;
+ }
+
+ for (U32 i = 32; i < rez; i+=32)
+ {
+ U32 pi = i-1;
+ for (U32 j = 2; j < rez-2; j++)
+ {
+ d[i *rez+ j] = subcol;
+ d[j *rez+ i] = subcol;
+
+ if (rez > 128)
+ {
+ d[pi *rez+ j] = subcol;
+ d[j *rez+ pi] = subcol;
+ }
+ }
+ }
+ }
+#ifdef LL_WINDOWS
+ glTexImage2D(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, 0, GL_RGBA, GL_UNSIGNED_BYTE, d);
+#else
+ glTexImage2D(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, d);
+#endif
+ rez = rez >> 1;
+ mip++;
+ }
+ delete [] d;
+}
+
+
+LLManipTranslate::~LLManipTranslate()
+{
+ mProjectedManipulators.deleteAllData();
+}
+
+
+void LLManipTranslate::handleSelect()
+{
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ gFloaterTools->setStatusText("Drag to move, shift-drag to copy");
+}
+
+void LLManipTranslate::handleDeselect()
+{
+ mHighlightedPart = LL_NO_PART;
+ mManipPart = LL_NO_PART;
+ gFloaterTools->setStatusText("");
+}
+
+BOOL LLManipTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // didn't click in any UI object, so must have clicked in the world
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if( hit_obj &&
+ (mHighlightedPart == LL_X_ARROW ||
+ mHighlightedPart == LL_Y_ARROW ||
+ mHighlightedPart == LL_Z_ARROW ||
+ mHighlightedPart == LL_YZ_PLANE ||
+ mHighlightedPart == LL_XZ_PLANE ||
+ mHighlightedPart == LL_XY_PLANE ) )
+ {
+ handled = handleMouseDownOnPart( x, y, mask );
+ }
+
+ return handled;
+}
+
+// Assumes that one of the arrows on an object was hit.
+BOOL LLManipTranslate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
+{
+ BOOL can_move = gSelectMgr->getObjectCount() != 0;
+ for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
+ objectp;
+ objectp = gSelectMgr->getNextObject())
+ {
+ can_move = can_move && objectp->permMove() && (objectp->permModify() || gSavedSettings.getBOOL("SelectLinkedSet"));
+ }
+
+ if (!can_move)
+ {
+ return FALSE;
+ }
+
+ highlightManipulators(x, y);
+ S32 hit_part = mHighlightedPart;
+
+ if( (hit_part != LL_X_ARROW) &&
+ (hit_part != LL_Y_ARROW) &&
+ (hit_part != LL_Z_ARROW) &&
+ (hit_part != LL_YZ_PLANE) &&
+ (hit_part != LL_XZ_PLANE) &&
+ (hit_part != LL_XY_PLANE) )
+ {
+ return TRUE;
+ }
+
+ mHelpTextTimer.reset();
+ sNumTimesHelpTextShown++;
+
+ gSelectMgr->getGrid(mGridOrigin, mGridRotation, mGridScale);
+
+ gSelectMgr->enableSilhouette(FALSE);
+
+ // we just started a drag, so save initial object positions
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_MOVE);
+
+ mManipPart = (EManipPart)hit_part;
+ mMouseDownX = x;
+ mMouseDownY = y;
+ mMouseOutsideSlop = FALSE;
+
+ LLVector3 axis;
+
+ LLSelectNode *selectNode = gSelectMgr->getFirstMoveableNode(TRUE);
+
+ if (!selectNode)
+ {
+ // didn't find the object in our selection...oh well
+ llwarns << "Trying to translate an unselected object" << llendl;
+ return TRUE;
+ }
+
+ LLViewerObject *selected_object = selectNode->getObject();
+ if (!selected_object)
+ {
+ // somehow we lost the object!
+ llwarns << "Translate manip lost the object" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+
+ // Compute unit vectors for arrow hit and a plane through that vector
+ BOOL axis_exists = getManipAxis(selected_object, mManipPart, axis);
+ getManipNormal(selected_object, mManipPart, mManipNormal);
+
+ //LLVector3 select_center_agent = gAgent.getPosAgentFromGlobal(gSelectMgr->getSelectionCenterGlobal());
+ // TomY: The above should (?) be identical to the below
+ LLVector3 select_center_agent = getPivotPoint();
+ mSubdivisions = llclamp(getSubdivisionLevel(select_center_agent, axis_exists ? axis : LLVector3::z_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ // if we clicked on a planar manipulator, recenter mouse cursor
+ if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
+ {
+ LLCoordGL mouse_pos;
+ gCamera->projectPosAgentToScreen(select_center_agent, mouse_pos);
+
+ if (gSavedSettings.getBOOL("SnapToMouseCursor"))
+ {
+ LLUI::setCursorPositionScreen(mouse_pos.mX, mouse_pos.mY);
+ x = mouse_pos.mX;
+ y = mouse_pos.mY;
+ }
+ }
+
+ gSelectMgr->updateSelectionCenter();
+ LLVector3d object_start_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
+ getMousePointOnPlaneGlobal(mDragCursorStartGlobal, x, y, object_start_global, mManipNormal);
+ mDragSelectionStartGlobal = object_start_global;
+ mCopyMadeThisDrag = FALSE;
+
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ setMouseCapture( TRUE );
+
+ return TRUE;
+}
+
+BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
+{
+ // Translation tool only works if mouse button is down.
+ // Bail out if mouse not down.
+ if( !hasMouseCapture() )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (inactive)" << llendl;
+ // Always show cursor
+ // gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+
+ highlightManipulators(x, y);
+ return TRUE;
+ }
+
+ // Handle auto-rotation if necessary.
+ const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
+ const S32 ROTATE_H_MARGIN = gViewerWindow->getWindowWidth() / 20;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+ BOOL rotated = FALSE;
+
+ // ...build mode moves camera about focus point
+ if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ if (x < ROTATE_H_MARGIN)
+ {
+ gAgent.cameraOrbitAround(rotate_angle);
+ rotated = TRUE;
+ }
+ else if (x > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN)
+ {
+ gAgent.cameraOrbitAround(-rotate_angle);
+ rotated = TRUE;
+ }
+ }
+
+ // Suppress processing if mouse hasn't actually moved.
+ // This may cause problems if the camera moves outside of the
+ // rotation above.
+ if( x == mLastHoverMouseX && y == mLastHoverMouseY && !rotated)
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse unmoved)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+ mLastHoverMouseX = x;
+ mLastHoverMouseY = y;
+
+ // Suppress if mouse hasn't moved past the initial slop region
+ // Reset once we start moving
+ if( !mMouseOutsideSlop )
+ {
+ if (abs(mMouseDownX - x) < MOUSE_DRAG_SLOP && abs(mMouseDownY - y) < MOUSE_DRAG_SLOP )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse inside slop)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+ else
+ {
+ // ...just went outside the slop region
+ mMouseOutsideSlop = TRUE;
+ // If holding down shift, leave behind a copy.
+ if (mask == MASK_COPY)
+ {
+ // ...we're trying to make a copy
+ gSelectMgr->selectDuplicate(LLVector3::zero, FALSE);
+ mCopyMadeThisDrag = TRUE;
+
+ // When we make the copy, we don't want to do any other processing.
+ // If so, the object will also be moved, and the copy will be offset.
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (made copy)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ }
+ }
+ }
+
+ // Throttle updates to 10 per second.
+ BOOL send_update = FALSE;
+
+ LLVector3 axis_f;
+ LLVector3d axis_d;
+ LLViewerObject *object;
+
+ // pick the first object to constrain to grid w/ common origin
+ // this is so we don't screw up groups
+ LLSelectNode* selectNode = gSelectMgr->getFirstMoveableNode(TRUE);
+ if (!selectNode)
+ {
+ // somehow we lost the object!
+ llwarns << "Translate manip lost the object" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+
+ object = selectNode->getObject();
+ if (!object)
+ {
+ // somehow we lost the object!
+ llwarns << "Translate manip lost the object" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+
+ // Compute unit vectors for arrow hit and a plane through that vector
+ BOOL axis_exists = getManipAxis(object, mManipPart, axis_f); // TODO: move this
+
+ axis_d.setVec(axis_f);
+
+ gSelectMgr->updateSelectionCenter();
+ LLVector3d current_pos_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
+
+ mSubdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), axis_f, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ // Project the cursor onto that plane
+ LLVector3d relative_move;
+ getMousePointOnPlaneGlobal(relative_move, x, y, current_pos_global, mManipNormal);\
+ relative_move -= mDragCursorStartGlobal;
+
+ // You can't move more than some distance from your original mousedown point.
+ if (gSavedSettings.getBOOL("LimitDragDistance"))
+ {
+ F32 max_drag_distance = gSavedSettings.getF32("MaxDragDistance");
+
+ if (relative_move.magVecSquared() > max_drag_distance * max_drag_distance)
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (too far)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
+ return TRUE;
+ }
+ }
+
+ F64 axis_magnitude = relative_move * axis_d; // dot product
+ LLVector3d cursor_point_snap_line;
+
+ F64 off_axis_magnitude;
+
+ getMousePointOnPlaneGlobal(cursor_point_snap_line, x, y, current_pos_global, mSnapOffsetAxis % axis_f);
+ off_axis_magnitude = axis_exists ? llabs((cursor_point_snap_line - current_pos_global) * LLVector3d(mSnapOffsetAxis)) : 0.f;
+
+ if (gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ if (off_axis_magnitude > mSnapOffsetMeters)
+ {
+ mInSnapRegime = TRUE;
+ LLVector3 mouse_down_offset(mDragCursorStartGlobal - mDragSelectionStartGlobal);
+ LLVector3 cursor_snap_agent = gAgent.getPosAgentFromGlobal(cursor_point_snap_line);
+ if (!gSavedSettings.getBOOL("SnapToMouseCursor"))
+ {
+ cursor_snap_agent -= mouse_down_offset;
+ }
+
+ F32 cursor_grid_dist = (cursor_snap_agent - mGridOrigin) * axis_f;
+
+ F32 snap_dist = getMinGridScale() / (2.f * mSubdivisions);
+ F32 relative_snap_dist = fmodf(llabs(cursor_grid_dist) + snap_dist, getMinGridScale() / mSubdivisions);
+ if (relative_snap_dist < snap_dist * 2.f)
+ {
+ if (cursor_grid_dist > 0.f)
+ {
+ cursor_grid_dist -= relative_snap_dist - snap_dist;
+ }
+ else
+ {
+ cursor_grid_dist += relative_snap_dist - snap_dist;
+ }
+ }
+
+ F32 object_start_on_axis = (gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal) - mGridOrigin) * axis_f;
+ axis_magnitude = cursor_grid_dist - object_start_on_axis;
+ }
+ else if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
+ {
+ // subtract offset from object center
+ LLVector3d cursor_point_global;
+ getMousePointOnPlaneGlobal( cursor_point_global, x, y, current_pos_global, mManipNormal );
+ cursor_point_global -= (mDragCursorStartGlobal - mDragSelectionStartGlobal);
+
+ // snap to planar grid
+ LLVector3 cursor_point_agent = gAgent.getPosAgentFromGlobal(cursor_point_global);
+ LLVector3 camera_plane_projection = gCamera->getAtAxis();
+ camera_plane_projection -= projected_vec(camera_plane_projection, mManipNormal);
+ camera_plane_projection.normVec();
+ LLVector3 camera_projected_dir = camera_plane_projection;
+ camera_plane_projection.rotVec(~mGridRotation);
+ camera_plane_projection.scaleVec(mGridScale);
+ camera_plane_projection.abs();
+ F32 max_grid_scale;
+ if (camera_plane_projection.mV[VX] > camera_plane_projection.mV[VY] &&
+ camera_plane_projection.mV[VX] > camera_plane_projection.mV[VZ])
+ {
+ max_grid_scale = mGridScale.mV[VX];
+ }
+ else if (camera_plane_projection.mV[VY] > camera_plane_projection.mV[VZ])
+ {
+ max_grid_scale = mGridScale.mV[VY];
+ }
+ else
+ {
+ max_grid_scale = mGridScale.mV[VZ];
+ }
+
+ F32 num_subdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), camera_projected_dir, max_grid_scale), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ F32 grid_scale_a;
+ F32 grid_scale_b;
+ LLVector3 cursor_point_grid = (cursor_point_agent - mGridOrigin) * ~mGridRotation;
+
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ grid_scale_a = mGridScale.mV[VY] / num_subdivisions;
+ grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
+ cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
+ cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
+ break;
+ case LL_XZ_PLANE:
+ grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
+ grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
+ cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
+ cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
+ break;
+ case LL_XY_PLANE:
+ grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
+ grid_scale_b = mGridScale.mV[VY] / num_subdivisions;
+ cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
+ cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
+ break;
+ default:
+ break;
+ }
+ cursor_point_agent = (cursor_point_grid * mGridRotation) + mGridOrigin;
+ relative_move.setVec(cursor_point_agent - gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal));
+ mInSnapRegime = TRUE;
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+
+ // Clamp to arrow direction
+ //FIXME: does this apply anymore?
+ if (!axis_exists)
+ {
+ axis_magnitude = relative_move.normVec();
+ axis_d.setVec(relative_move);
+ axis_d.normVec();
+ axis_f.setVec(axis_d);
+ }
+
+ LLVector3d clamped_relative_move = axis_magnitude * axis_d; // scalar multiply
+ LLVector3 clamped_relative_move_f = (F32)axis_magnitude * axis_f; // scalar multiply
+
+ for(selectNode = gSelectMgr->getFirstNode();
+ selectNode;
+ selectNode = gSelectMgr->getNextNode() )
+ {
+ object = selectNode->getObject();
+
+ // Only apply motion to root objects and objects selected
+ // as "individual".
+ if (!object->isRootEdit() && !selectNode->mIndividualSelection)
+ {
+ continue;
+ }
+
+ if (!object->isRootEdit())
+ {
+ // child objects should not update if parent is selected
+ LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
+ if (editable_root->isSelected())
+ {
+ // we will be moved properly by our parent, so skip
+ continue;
+ }
+ }
+
+ if (object->permMove())
+ {
+ // handle attachments in local space
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // calculate local version of relative move
+ LLQuaternion objWorldRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
+ objWorldRotation.transQuat();
+
+ LLVector3 old_position_local = object->getPosition();
+ LLVector3 new_position_local = selectNode->mSavedPositionLocal + (clamped_relative_move_f * objWorldRotation);
+
+ // move and clamp root object first, before adjusting children
+ if (new_position_local != old_position_local)
+ {
+ send_update = TRUE;
+ }
+ //RN: I forget, but we need to do this because of snapping which doesn't often result
+ // in position changes even when the mouse moves
+ object->setPosition(new_position_local);
+ rebuild(object);
+ gAgent.getAvatarObject()->clampAttachmentPositions();
+ new_position_local = object->getPosition();
+
+ if (selectNode->mIndividualSelection)
+ {
+ send_update = FALSE;
+ LLVector3 child_offset = (old_position_local - new_position_local) * ~object->getRotation();
+
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+
+ if (!childp->isSelected())
+ {
+ childp->setPosition(childp->getPosition() + child_offset);
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ else
+ {
+ // compute new position to send to simulators, but don't set it yet.
+ // We need the old position to know which simulator to send the move message to.
+ LLVector3d new_position_global = selectNode->mSavedPositionGlobal + clamped_relative_move;
+
+ // Don't let object centers go too far underground
+ F64 min_height = gWorldp->getMinAllowedZ(object);
+ if (new_position_global.mdV[VZ] < min_height)
+ {
+ new_position_global.mdV[VZ] = min_height;
+ }
+
+ // For safety, cap heights where objects can be dragged
+ if (new_position_global.mdV[VZ] > MAX_OBJECT_Z)
+ {
+ new_position_global.mdV[VZ] = MAX_OBJECT_Z;
+ }
+
+ // Grass is always drawn on the ground, so clamp its position to the ground
+ if (object->getPCode() == LL_PCODE_LEGACY_GRASS)
+ {
+ new_position_global.mdV[VZ] = gWorldp->resolveLandHeightGlobal(new_position_global) + 1.f;
+ }
+
+ if (object->isRootEdit())
+ {
+ new_position_global = gWorldp->clipToVisibleRegions(object->getPositionGlobal(), new_position_global);
+ }
+
+ // PR: Only update if changed
+ LLVector3d old_position_global = object->getPositionGlobal();
+ LLVector3 old_position_agent = object->getPositionAgent();
+ LLVector3 new_position_agent = gAgent.getPosAgentFromGlobal(new_position_global);
+ if (object->isRootEdit())
+ {
+ // finally, move parent object after children have calculated new offsets
+ object->setPositionAgent(new_position_agent);
+ rebuild(object);
+ }
+ else
+ {
+ LLViewerObject* root_object = object->getRootEdit();
+ new_position_agent -= root_object->getPositionAgent();
+ new_position_agent = new_position_agent * ~root_object->getRotation();
+ object->setPositionParent(new_position_agent, FALSE);
+ rebuild(object);
+ }
+
+ if (selectNode->mIndividualSelection)
+ {
+ LLVector3 parent_offset = (new_position_agent - old_position_agent) * ~object->getRotation();
+
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+ if (!childp->isSelected())
+ {
+ childp->setPosition(childp->getPosition() - parent_offset);
+ rebuild(childp);
+ }
+ }
+ send_update = FALSE;
+ }
+ else if (old_position_global != new_position_global)
+ {
+ send_update = TRUE;
+ }
+ }
+ }
+ }
+
+ // Handle throttling to 10 updates per second.
+ F32 elapsed_time = mUpdateTimer.getElapsedTimeF32();
+ const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
+ if (send_update && (elapsed_time > UPDATE_DELAY))
+ {
+ gSelectMgr->sendMultipleUpdate(UPD_POSITION);
+ mUpdateTimer.reset();
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ else
+ {
+ // ...suppressed update
+ mSendUpdateOnMouseUp = TRUE;
+ }
+
+ gSelectMgr->updateSelectionCenter();
+ gAgent.clearFocusObject();
+ //gAgent.setObjectTracking(FALSE);
+ dialog_refresh_all(); // ??? is this necessary?
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (active)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+}
+
+void LLManipTranslate::highlightManipulators(S32 x, S32 y)
+{
+ mHighlightedPart = LL_NO_PART;
+
+ if (!gSelectMgr->getObjectCount())
+ {
+ return;
+ }
+
+ //LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLMatrix4 projMatrix = gCamera->getProjection();
+ LLMatrix4 modelView = gCamera->getModelview();
+
+ LLVector3 object_position = getPivotPoint();
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 relative_camera_dir;
+
+ LLMatrix4 transform;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ relative_camera_dir = LLVector3(1.f, 0.f, 0.f) * ~grid_rotation;
+ LLVector4 translation(object_position);
+ transform.initRotTrans(grid_rotation, translation);
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ transform *= cfr;
+ LLMatrix4 window_scale;
+ F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
+ window_scale.initAll(LLVector3(zoom_level / gCamera->getAspect(), zoom_level, 0.f),
+ LLQuaternion::DEFAULT,
+ LLVector3::zero);
+ transform *= window_scale;
+ }
+ else
+ {
+ relative_camera_dir = (object_position - gCamera->getOrigin()) * ~grid_rotation;
+ relative_camera_dir.normVec();
+
+ transform.initRotTrans(grid_rotation, LLVector4(object_position));
+ transform *= modelView;
+ transform *= projMatrix;
+ }
+
+ mProjectedManipulators.deleteAllData();
+
+ S32 numManips = 0;
+
+ // edges
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 1.f);
+
+ S32 num_arrow_manips = numManips;
+
+ // planar manipulators
+ BOOL planar_manip_yz_visible = FALSE;
+ BOOL planar_manip_xz_visible = FALSE;
+ BOOL planar_manip_xy_visible = FALSE;
+
+ mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ if (llabs(relative_camera_dir.mV[VX]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ planar_manip_yz_visible = TRUE;
+ }
+
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ if (llabs(relative_camera_dir.mV[VY]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ planar_manip_xz_visible = TRUE;
+ }
+
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ if (llabs(relative_camera_dir.mV[VZ]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ planar_manip_xy_visible = TRUE;
+ }
+
+ for (S32 i = 0; i < num_arrow_manips; i+= 2)
+ {
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 10.f); // 10 pixel hotspot for arrows
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ if (planar_manip_yz_visible)
+ {
+ S32 i = num_arrow_manips;
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 20.f); // 20 pixels for planar manipulators
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ if (planar_manip_xz_visible)
+ {
+ S32 i = num_arrow_manips + 2;
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 20.f); // 20 pixels for planar manipulators
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ if (planar_manip_xy_visible)
+ {
+ S32 i = num_arrow_manips + 4;
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 20.f); // 20 pixels for planar manipulators
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ LLVector2 manip_start_2d;
+ LLVector2 manip_end_2d;
+ LLVector2 manip_dir;
+ F32 half_width = gViewerWindow->getWindowWidth() / 2.f;
+ F32 half_height = gViewerWindow->getWindowHeight() / 2.f;
+ LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
+ LLVector2 mouse_delta;
+
+ for (ManipulatorHandle* manipulator = mProjectedManipulators.getFirstData();
+ manipulator;
+ manipulator = mProjectedManipulators.getNextData())
+ {
+ manip_start_2d.setVec(manipulator->mStartPosition.mV[VX] * half_width, manipulator->mStartPosition.mV[VY] * half_height);
+ manip_end_2d.setVec(manipulator->mEndPosition.mV[VX] * half_width, manipulator->mEndPosition.mV[VY] * half_height);
+ manip_dir = manip_end_2d - manip_start_2d;
+
+ mouse_delta = mousePos - manip_start_2d;
+
+ F32 manip_length = manip_dir.normVec();
+
+ F32 mouse_pos_manip = mouse_delta * manip_dir;
+ F32 mouse_dist_manip_squared = mouse_delta.magVecSquared() - (mouse_pos_manip * mouse_pos_manip);
+
+ if (mouse_pos_manip > 0.f &&
+ mouse_pos_manip < manip_length &&
+ mouse_dist_manip_squared < manipulator->mHotSpotRadius * manipulator->mHotSpotRadius)
+ {
+ mHighlightedPart = manipulator->mManipID;
+ break;
+ }
+ }
+}
+
+F32 LLManipTranslate::getMinGridScale()
+{
+ F32 scale;
+ switch (mManipPart)
+ {
+ case LL_NO_PART:
+ default:
+ scale = 1.f;
+ break;
+ case LL_X_ARROW:
+ scale = mGridScale.mV[VX];
+ break;
+ case LL_Y_ARROW:
+ scale = mGridScale.mV[VY];
+ break;
+ case LL_Z_ARROW:
+ scale = mGridScale.mV[VZ];
+ break;
+ case LL_YZ_PLANE:
+ scale = llmin(mGridScale.mV[VY], mGridScale.mV[VZ]);
+ break;
+ case LL_XZ_PLANE:
+ scale = llmin(mGridScale.mV[VX], mGridScale.mV[VZ]);
+ break;
+ case LL_XY_PLANE:
+ scale = llmin(mGridScale.mV[VX], mGridScale.mV[VY]);
+ break;
+ }
+
+ return scale;
+}
+
+
+BOOL LLManipTranslate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // first, perform normal processing in case this was a quick-click
+ handleHover(x, y, mask);
+
+ // make sure arrow colors go back to normal
+ mManipPart = LL_NO_PART;
+ gSelectMgr->enableSilhouette(TRUE);
+
+ // Might have missed last update due to UPDATE_DELAY timing.
+ if (mSendUpdateOnMouseUp)
+ {
+ gSelectMgr->sendMultipleUpdate( UPD_POSITION );
+ mSendUpdateOnMouseUp = FALSE;
+ }
+
+// if (mCopyMadeThisDrag)
+// {
+// gSelectMgr->clearGridObjects();
+// }
+
+ mInSnapRegime = FALSE;
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+
+ return LLManip::handleMouseUp(x, y, mask);
+}
+
+
+void LLManipTranslate::render()
+{
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderGuidelines();
+ }
+ {
+ renderTranslationHandles();
+ renderSnapGuides();
+ }
+ glPopMatrix();
+
+ renderText();
+}
+
+void LLManipTranslate::renderSnapGuides()
+{
+ if (!gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ return;
+ }
+
+ F32 max_subdivisions = sGridMaxSubdivisionLevel;//(F32)gSavedSettings.getS32("GridSubdivision");
+ F32 line_alpha = gSavedSettings.getF32("GridOpacity");
+
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLDisable gls_cull(GL_CULL_FACE);
+ LLVector3 translate_axis;
+
+ if (mManipPart == LL_NO_PART)
+ {
+ return;
+ }
+
+ LLSelectNode *first_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ if (!first_node)
+ {
+ return;
+ }
+
+ updateGridSettings();
+
+ F32 smallest_grid_unit_scale = getMinGridScale() / max_subdivisions;
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+ LLVector3 saved_selection_center = getSavedPivotPoint(); //gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+ LLVector3 selection_center = getPivotPoint();
+
+ LLViewerObject *first_object = first_node->getObject();
+
+ //pick appropriate projection plane for snap rulers according to relative camera position
+ if (mManipPart >= LL_X_ARROW && mManipPart <= LL_Z_ARROW)
+ {
+ getManipAxis(first_object, mManipPart, translate_axis);
+
+ LLVector3 at_axis_abs;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ at_axis_abs = LLVector3::x_axis * ~grid_rotation;
+ }
+ else
+ {
+ at_axis_abs = saved_selection_center - gCamera->getOrigin();
+ at_axis_abs.normVec();
+
+ at_axis_abs = at_axis_abs * ~grid_rotation;
+ }
+ at_axis_abs.abs();
+
+ if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY] && at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
+ {
+ if (mManipPart == LL_Y_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else if (mManipPart == LL_Z_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ }
+ else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
+ {
+ if (mManipPart == LL_X_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else if (mManipPart == LL_Z_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ }
+ else
+ {
+ if (mManipPart == LL_X_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ else if (mManipPart == LL_Y_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY])
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ else
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ }
+
+ mSnapOffsetAxis = mSnapOffsetAxis * grid_rotation;
+
+ F32 guide_size_meters;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ guide_size_meters = 1.f / gAgent.getAvatarObject()->mHUDCurZoom;
+ mSnapOffsetMeters = mArrowLengthMeters * 1.5f;
+ }
+ else
+ {
+ LLVector3 cam_to_selection = getPivotPoint() - gCamera->getOrigin();
+ F32 current_range = cam_to_selection.normVec();
+ guide_size_meters = SNAP_GUIDE_SCREEN_SIZE * gViewerWindow->getWindowHeight() * current_range / gCamera->getPixelMeterRatio();
+
+ F32 fraction_of_fov = mAxisArrowLength / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ F32 offset_at_camera = tan(apparent_angle) * 1.5f;
+ F32 range = dist_vec(gAgent.getPosAgentFromGlobal(first_node->mSavedPositionGlobal), gCamera->getOrigin());
+ mSnapOffsetMeters = range * offset_at_camera;
+ }
+
+ LLVector3 tick_start;
+ LLVector3 tick_end;
+
+ // how far away from grid origin is the selection along the axis of translation?
+ F32 dist_grid_axis = (selection_center - mGridOrigin) * translate_axis;
+ // find distance to nearest smallest grid unit
+ F32 offset_nearest_grid_unit = fmodf(dist_grid_axis, smallest_grid_unit_scale);
+ // how many smallest grid units are we away from largest grid scale?
+ S32 sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() / sGridMinSubdivisionLevel) / smallest_grid_unit_scale);
+ S32 num_ticks_per_side = llmax(1, llfloor(0.5f * guide_size_meters / smallest_grid_unit_scale));
+
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ for (S32 pass = 0; pass < 3; pass++)
+ {
+ LLColor4 line_color = setupSnapGuideRenderPass(pass);
+
+ glBegin(GL_LINES);
+ {
+ LLVector3 line_start = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) + (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
+ LLVector3 line_end = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) - (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
+ LLVector3 line_mid = (line_start + line_end) * 0.5f;
+
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_start.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_end.mV);
+
+ line_start.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) + (translate_axis * guide_size_meters * 0.5f));
+ line_end.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) - (translate_axis * guide_size_meters * 0.5f));
+ line_mid = (line_start + line_end) * 0.5f;
+
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_start.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_end.mV);
+
+ for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
+ {
+ tick_start = selection_center + (translate_axis * (smallest_grid_unit_scale * (F32)i - offset_nearest_grid_unit));
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_start, translate_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / cur_subdivisions)) != 0.f)
+ {
+ continue;
+ }
+
+ // add in off-axis offset
+ tick_start += (mSnapOffsetAxis * mSnapOffsetMeters);
+
+ BOOL is_sub_tick = FALSE;
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ is_sub_tick = TRUE;
+ }
+
+// S32 num_ticks_to_fade = is_sub_tick ? num_ticks_per_side / 2 : num_ticks_per_side;
+// F32 alpha = line_alpha * (1.f - (0.8f * ((F32)llabs(i) / (F32)num_ticks_to_fade)));
+
+ tick_end = tick_start + (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
+
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+
+ tick_start = selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) +
+ (translate_axis * (getMinGridScale() / (F32)(max_subdivisions) * (F32)i - offset_nearest_grid_unit));
+ tick_end = tick_start - (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
+
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+ }
+ }
+ glEnd();
+
+ if (mInSnapRegime)
+ {
+ LLVector3 line_start = selection_center - mSnapOffsetAxis * mSnapOffsetMeters;
+ LLVector3 line_end = selection_center + mSnapOffsetAxis * mSnapOffsetMeters;
+
+ glBegin(GL_LINES);
+ {
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+
+ glVertex3fv(line_start.mV);
+ glVertex3fv(line_end.mV);
+ }
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = translate_axis;
+
+ arrow_dir = -mSnapOffsetAxis;
+ glVertex3fv((line_start + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_start + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_start - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+
+ arrow_dir = mSnapOffsetAxis;
+ glVertex3fv((line_end + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_end + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_end - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+ }
+ glEnd();
+ }
+ }
+
+ sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() * 32.f) / smallest_grid_unit_scale);
+
+ LLVector2 screen_translate_axis(llabs(translate_axis * gCamera->getLeftAxis()), llabs(translate_axis * gCamera->getUpAxis()));
+ screen_translate_axis.normVec();
+
+ S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
+
+ // render tickmark values
+ for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
+ {
+ LLVector3 tick_pos = selection_center + (translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit));
+ F32 alpha = line_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side)));
+
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, translate_axis, getMinGridScale(), tick_label_spacing)))) == 0.f)
+ {
+ F32 snap_offset_meters;
+
+ if (mSnapOffsetAxis * gCamera->getUpAxis() > 0.f)
+ {
+ snap_offset_meters = mSnapOffsetMeters;
+ }
+ else
+ {
+ snap_offset_meters = -mSnapOffsetMeters;
+ }
+ LLVector3 text_origin = selection_center +
+ (translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit)) +
+ (mSnapOffsetAxis * snap_offset_meters * (1.f + tick_scale));
+
+ LLVector3 tick_offset = (tick_pos - mGridOrigin) * ~mGridRotation;
+ F32 offset_val = 0.5f * tick_offset.mV[ARROW_TO_AXIS[mManipPart]] / getMinGridScale();
+ EGridMode grid_mode = gSelectMgr->getGridMode();
+ F32 text_highlight = 0.8f;
+ if(i - llround(offset_nearest_grid_unit / smallest_grid_unit_scale) == 0 && mInSnapRegime)
+ {
+ text_highlight = 1.f;
+ }
+
+ if (grid_mode == GRID_MODE_WORLD)
+ {
+ // rescale units to meters from multiple of grid scale
+ offset_val *= 2.f * grid_scale[ARROW_TO_AXIS[mManipPart]];
+ renderTickValue(text_origin, offset_val, "m", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ else
+ {
+ renderTickValue(text_origin, offset_val, "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ }
+ }
+ if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ // render helpful text
+ if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
+ {
+ F32 snap_offset_meters_up;
+ if (mSnapOffsetAxis * gCamera->getUpAxis() > 0.f)
+ {
+ snap_offset_meters_up = mSnapOffsetMeters;
+ }
+ else
+ {
+ snap_offset_meters_up = -mSnapOffsetMeters;
+ }
+
+ LLVector3 selection_center_start = getSavedPivotPoint();//gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+
+ LLVector3 help_text_pos = selection_center_start + (snap_offset_meters_up * 3.f * mSnapOffsetAxis);
+ const LLFontGL* big_fontp = LLFontGL::sSansSerif;
+
+ LLGLSTexture gls_texture;
+ std::string help_text = "Move mouse cursor over ruler to snap";
+ LLColor4 help_text_color = LLColor4::white;
+ help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, line_alpha, 0.f);
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ help_text = "to snap to grid";
+ help_text_pos -= gCamera->getUpAxis() * mSnapOffsetMeters * 0.2f;
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ }
+ }
+ }
+ else
+ {
+ // render gridlines for planar snapping
+
+ F32 u = 0, v = 0;
+ glPushMatrix();
+
+ F32 x,y,z,angle_radians;
+ grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+ glTranslatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ LLVector3 grid_center = selection_center - grid_origin;
+ grid_center *= ~grid_rotation;
+
+ F32 usc = 1;
+ F32 vsc = 1;
+
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ u = grid_center.mV[VY];
+ v = grid_center.mV[VZ];
+ usc = grid_scale.mV[VY];
+ vsc = grid_scale.mV[VZ];
+ break;
+ case LL_XZ_PLANE:
+ u = grid_center.mV[VX];
+ v = grid_center.mV[VZ];
+ usc = grid_scale.mV[VX];
+ vsc = grid_scale.mV[VZ];
+ break;
+ case LL_XY_PLANE:
+ u = grid_center.mV[VX];
+ v = grid_center.mV[VY];
+ usc = grid_scale.mV[VX];
+ vsc = grid_scale.mV[VY];
+ break;
+ default:
+ break;
+ }
+
+ F32 sz = mGridSizeMeters;
+ F32 tiles = sz;
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ usc = 1.0f/usc;
+ vsc = 1.0f/vsc;
+
+ while (usc > vsc*4.0f)
+ {
+ usc *= 0.5f;
+ }
+ while (vsc > usc * 4.0f)
+ {
+ vsc *= 0.5f;
+ }
+
+ glScalef(usc, vsc, 1.0f);
+ glTranslatef(u, v, 0);
+
+ float a = line_alpha;
+
+ LLColor4 col = gColors.getColor("SilhouetteChildColor");
+ {
+ //draw grid behind objects
+ LLGLSTexture tex2d;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GREATER);
+ glBindTexture(GL_TEXTURE_2D, sGridTex);
+ glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
+ renderGrid(u,v,tiles,0.9f, 0.9f, 0.9f,a*0.15f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ {
+ LLGLDisable alpha_test(GL_ALPHA_TEST);
+ //draw black overlay
+ glBindTexture(GL_TEXTURE_2D, 0);
+ renderGrid(u,v,tiles,0.0f, 0.0f, 0.0f,a*0.16f);
+
+ //draw grid top
+ glBindTexture(GL_TEXTURE_2D, sGridTex);
+ renderGrid(u,v,tiles,1,1,1,a);
+
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderGuidelines();
+ }
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GREATER);
+ LLGLEnable stipple(GL_LINE_STIPPLE);
+ glLineStipple(1, 0x3333);
+
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ renderGuidelines(FALSE, TRUE, TRUE);
+ break;
+ case LL_XZ_PLANE:
+ renderGuidelines(TRUE, FALSE, TRUE);
+ break;
+ case LL_XY_PLANE:
+ renderGuidelines(TRUE, TRUE, FALSE);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void LLManipTranslate::renderGrid(F32 x, F32 y, F32 size, F32 r, F32 g, F32 b, F32 a)
+{
+ F32 d = size*0.5f;
+
+ for (F32 xx = -size-d; xx < size+d; xx += d)
+ {
+ glBegin(GL_TRIANGLE_STRIP);
+ for (F32 yy = -size-d; yy < size+d; yy += d)
+ {
+ float dx, dy, da;
+
+ dx = xx; dy = yy;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+
+ dx = xx+d; dy = yy;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+
+ dx = xx; dy = yy+d;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+
+ dx = xx+d; dy = yy+d;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+ }
+ glEnd();
+ }
+
+
+}
+
+
+void LLManipTranslate::renderText()
+{
+ if (gSelectMgr->getRootObjectCount() && !gSelectMgr->selectionIsAttachment())
+ {
+ LLVector3 pos = getPivotPoint();
+ renderXYZ(pos);
+ }
+ else
+ {
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ if(!objectp)
+ {
+ objectp = gSelectMgr->getFirstObject();
+ }
+
+ if (objectp)
+ {
+ renderXYZ(objectp->getPositionEdit());
+ }
+ }
+}
+
+void LLManipTranslate::renderTranslationHandles()
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+ LLVector3 at_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ at_axis = LLVector3::x_axis * ~grid_rotation;
+ }
+ else
+ {
+ at_axis = gCamera->getAtAxis() * ~grid_rotation;
+ }
+
+ if (at_axis.mV[VX] > 0.f)
+ {
+ mPlaneManipPositions.mV[VX] = 1.f;
+ }
+ else
+ {
+ mPlaneManipPositions.mV[VX] = -1.f;
+ }
+
+ if (at_axis.mV[VY] > 0.f)
+ {
+ mPlaneManipPositions.mV[VY] = 1.f;
+ }
+ else
+ {
+ mPlaneManipPositions.mV[VY] = -1.f;
+ }
+
+ if (at_axis.mV[VZ] > 0.f)
+ {
+ mPlaneManipPositions.mV[VZ] = 1.f;
+ }
+ else
+ {
+ mPlaneManipPositions.mV[VZ] = -1.f;
+ }
+
+ LLViewerObject *first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+ if (!first_object) return;
+
+ LLVector3 selection_center = getPivotPoint();
+
+ // Drag handles
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mArrowLengthMeters = mAxisArrowLength / gViewerWindow->getWindowHeight();
+ mArrowLengthMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ LLVector3 camera_pos_agent = gAgent.getCameraPositionAgent();
+ F32 range = dist_vec(camera_pos_agent, selection_center);
+ F32 range_from_agent = dist_vec(gAgent.getPositionAgent(), selection_center);
+
+ // Don't draw handles if you're too far away
+ if (gSavedSettings.getBOOL("LimitSelectDistance"))
+ {
+ if (range_from_agent > gSavedSettings.getF32("MaxSelectDistance"))
+ {
+ return;
+ }
+ }
+
+ if (range > 0.001f)
+ {
+ // range != zero
+ F32 fraction_of_fov = mAxisArrowLength / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ mArrowLengthMeters = range * tan(apparent_angle);
+ }
+ else
+ {
+ // range == zero
+ mArrowLengthMeters = 1.0f;
+ }
+ }
+
+ mPlaneManipOffsetMeters = mArrowLengthMeters * 1.8f;
+ mGridSizeMeters = gSavedSettings.getF32("GridDrawSize");
+ mConeSize = mArrowLengthMeters / 4.f;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ {
+ glTranslatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
+
+ F32 angle_radians, x, y, z;
+ grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ LLQuaternion invRotation = grid_rotation;
+ invRotation.conjQuat();
+
+ LLVector3 relative_camera_dir;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ relative_camera_dir = LLVector3::x_axis * invRotation;
+ }
+ else
+ {
+ relative_camera_dir = (selection_center - gCamera->getOrigin()) * invRotation;
+ }
+ relative_camera_dir.normVec();
+
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ LLGLDisable cull_face(GL_CULL_FACE);
+
+ LLColor4 color1;
+ LLColor4 color2;
+
+ // update manipulator sizes
+ for (S32 index = 0; index < 3; index++)
+ {
+ if (index == mManipPart - LL_X_ARROW || index == mHighlightedPart - LL_X_ARROW)
+ {
+ mArrowScales.mV[index] = lerp(mArrowScales.mV[index], SELECTED_ARROW_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ }
+ else if (index == mManipPart - LL_YZ_PLANE || index == mHighlightedPart - LL_YZ_PLANE)
+ {
+ mArrowScales.mV[index] = lerp(mArrowScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], SELECTED_ARROW_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ }
+ else
+ {
+ mArrowScales.mV[index] = lerp(mArrowScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ }
+ }
+
+ if ((mManipPart == LL_NO_PART || mManipPart == LL_YZ_PLANE) && llabs(relative_camera_dir.mV[VX]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ // render YZ plane manipulator
+ glPushMatrix();
+ glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
+ glTranslatef(0.f, mPlaneManipOffsetMeters, mPlaneManipOffsetMeters);
+ glScalef(mPlaneScales.mV[VX], mPlaneScales.mV[VX], mPlaneScales.mV[VX]);
+ if (mHighlightedPart == LL_YZ_PLANE)
+ {
+ color1.setVec(0.f, 1.f, 0.f, 1.f);
+ color2.setVec(0.f, 0.f, 1.f, 1.f);
+ }
+ else
+ {
+ color1.setVec(0.f, 1.f, 0.f, 0.6f);
+ color2.setVec(0.f, 0.f, 1.f, 0.6f);
+ }
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4fv(color1.mV);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+
+ glColor4fv(color2.mV);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+ }
+ glEnd();
+
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINES);
+ {
+ glColor4f(0.f, 0.f, 0.f, 0.3f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f);
+
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ glPopMatrix();
+ }
+
+ if ((mManipPart == LL_NO_PART || mManipPart == LL_XZ_PLANE) && llabs(relative_camera_dir.mV[VY]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ // render XZ plane manipulator
+ glPushMatrix();
+ glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
+ glTranslatef(mPlaneManipOffsetMeters, 0.f, mPlaneManipOffsetMeters);
+ glScalef(mPlaneScales.mV[VY], mPlaneScales.mV[VY], mPlaneScales.mV[VY]);
+ if (mHighlightedPart == LL_XZ_PLANE)
+ {
+ color1.setVec(0.f, 0.f, 1.f, 1.f);
+ color2.setVec(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ color1.setVec(0.f, 0.f, 1.f, 0.6f);
+ color2.setVec(1.f, 0.f, 0.f, 0.6f);
+ }
+
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4fv(color1.mV);
+ glVertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+
+ glColor4fv(color2.mV);
+ glVertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f));
+ glVertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ }
+ glEnd();
+
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINES);
+ {
+ glColor4f(0.f, 0.f, 0.f, 0.3f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f);
+
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+
+ glPopMatrix();
+ }
+
+ if ((mManipPart == LL_NO_PART || mManipPart == LL_XY_PLANE) && llabs(relative_camera_dir.mV[VZ]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ // render XY plane manipulator
+ glPushMatrix();
+ glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
+
+/* Y
+ ^
+ v1
+ | \
+ |<- v0
+ | /| \
+ v2__v__v3 > X
+*/
+ LLVector3 v0,v1,v2,v3;
+#if 0
+ // This should theoretically work but looks off; could be tuned later -SJB
+ glTranslatef(-mPlaneManipOffsetMeters, -mPlaneManipOffsetMeters, 0.f);
+ v0 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
+ v1 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.75f), 0.f);
+ v2 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
+ v3 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
+#else
+ glTranslatef(mPlaneManipOffsetMeters, mPlaneManipOffsetMeters, 0.f);
+ v0 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
+ v1 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), 0.f);
+ v2 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
+ v3 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
+#endif
+ glScalef(mPlaneScales.mV[VZ], mPlaneScales.mV[VZ], mPlaneScales.mV[VZ]);
+ if (mHighlightedPart == LL_XY_PLANE)
+ {
+ color1.setVec(1.f, 0.f, 0.f, 1.f);
+ color2.setVec(0.f, 1.f, 0.f, 1.f);
+ }
+ else
+ {
+ color1.setVec(0.8f, 0.f, 0.f, 0.6f);
+ color2.setVec(0.f, 0.8f, 0.f, 0.6f);
+ }
+
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4fv(color1.mV);
+ glVertex3fv(v0.mV);
+ glVertex3fv(v1.mV);
+ glVertex3fv(v2.mV);
+
+ glColor4fv(color2.mV);
+ glVertex3fv(v2.mV);
+ glVertex3fv(v3.mV);
+ glVertex3fv(v0.mV);
+ }
+ glEnd();
+
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINES);
+ {
+ glColor4f(0.f, 0.f, 0.f, 0.3f);
+ LLVector3 v12 = (v1 + v2) * .5f;
+ glVertex3fv(v0.mV);
+ glVertex3fv(v12.mV);
+ glVertex3fv(v12.mV);
+ glVertex3fv((v12 + (v0-v12)*.3f + (v2-v12)*.3f).mV);
+ glVertex3fv(v12.mV);
+ glVertex3fv((v12 + (v0-v12)*.3f + (v1-v12)*.3f).mV);
+
+ LLVector3 v23 = (v2 + v3) * .5f;
+ glVertex3fv(v0.mV);
+ glVertex3fv(v23.mV);
+ glVertex3fv(v23.mV);
+ glVertex3fv((v23 + (v0-v23)*.3f + (v3-v23)*.3f).mV);
+ glVertex3fv(v23.mV);
+ glVertex3fv((v23 + (v0-v23)*.3f + (v2-v23)*.3f).mV);
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+
+ glPopMatrix();
+ }
+ }
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+
+ // Since we draw handles with depth testing off, we need to draw them in the
+ // proper depth order.
+
+ // Copied from LLDrawable::updateGeometry
+ LLVector3 pos_agent = first_object->getPositionAgent();
+ LLVector3 camera_agent = gAgent.getCameraPositionAgent();
+ LLVector3 headPos = pos_agent - camera_agent;
+
+ LLVector3 orientWRTHead = headPos * invRotation;
+
+ // Find nearest vertex
+ U32 nearest = (orientWRTHead.mV[0] < 0.0f ? 1 : 0) +
+ (orientWRTHead.mV[1] < 0.0f ? 2 : 0) +
+ (orientWRTHead.mV[2] < 0.0f ? 4 : 0);
+
+ // opposite faces on Linden cubes:
+ // 0 & 5
+ // 1 & 3
+ // 2 & 4
+
+ // Table of order to draw faces, based on nearest vertex
+ static U32 face_list[8][NUM_AXES*2] = {
+ { 2,0,1, 4,5,3 }, // v6 F201 F453
+ { 2,0,3, 4,5,1 }, // v7 F203 F451
+ { 4,0,1, 2,5,3 }, // v5 F401 F253
+ { 4,0,3, 2,5,1 }, // v4 F403 F251
+ { 2,5,1, 4,0,3 }, // v2 F251 F403
+ { 2,5,3, 4,0,1 }, // v3 F253 F401
+ { 4,5,1, 2,0,3 }, // v1 F451 F203
+ { 4,5,3, 2,0,1 }, // v0 F453 F201
+ };
+ static const EManipPart which_arrow[6] = {
+ LL_Z_ARROW,
+ LL_X_ARROW,
+ LL_Y_ARROW,
+ LL_X_ARROW,
+ LL_Y_ARROW,
+ LL_Z_ARROW};
+
+ // draw arrows for deeper faces first, closer faces last
+ LLVector3 camera_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ camera_axis = LLVector3::x_axis;
+ }
+ else
+ {
+ camera_axis.setVec(gAgent.getCameraPositionAgent() - first_object->getPositionAgent());
+ }
+
+ for (U32 i = 0; i < NUM_AXES*2; i++)
+ {
+ U32 face = face_list[nearest][i];
+
+ LLVector3 arrow_axis;
+ getManipAxis(first_object, which_arrow[face], arrow_axis);
+
+ if (fabs(angle_between(camera_axis, arrow_axis) - F_PI_BY_TWO) < F_PI_BY_TWO - HANDLE_HIDE_ANGLE)
+ {
+ renderArrow(which_arrow[face],
+ mManipPart,
+ (face >= 3) ? -mConeSize : mConeSize,
+ (face >= 3) ? -mArrowLengthMeters : mArrowLengthMeters,
+ mConeSize,
+ FALSE);
+ }
+ }
+ }
+ }
+ glPopMatrix();
+}
+
+
+void LLManipTranslate::renderArrow(S32 which_arrow, S32 selected_arrow, F32 box_size, F32 arrow_size, F32 handle_size, BOOL reverse_direction)
+{
+ LLGLSNoTexture gls_ui_no_texture;
+ LLGLEnable gls_blend(GL_BLEND);
+ LLGLEnable gls_color_material(GL_COLOR_MATERIAL);
+
+ for (S32 pass = 1; pass <= 2; pass++)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, pass == 1 ? GL_LEQUAL : GL_GREATER);
+ glPushMatrix();
+
+ S32 index = 0;
+
+ index = ARROW_TO_AXIS[which_arrow];
+
+ // assign a color for this arrow
+ LLColor4 color; // black
+ if (which_arrow == selected_arrow || which_arrow == mHighlightedPart)
+ {
+ color.mV[index] = (pass == 1) ? 1.f : 0.5f;
+ }
+ else if (selected_arrow != LL_NO_PART)
+ {
+ color.mV[VALPHA] = 0.f;
+ }
+ else
+ {
+ color.mV[index] = pass == 1 ? .8f : .35f ; // red, green, or blue
+ color.mV[VALPHA] = 0.6f;
+ }
+ glColor4fv( color.mV );
+
+ LLVector3 vec;
+
+ {
+// Stipple looks OK, but not great - SJB
+// LLGLEnable stipple(GL_LINE_STIPPLE);
+// glLineStipple(1, 0x3333);
+
+ LLUI::setLineWidth(2.0f);
+ glBegin(GL_LINES);
+ vec.mV[index] = box_size;
+ glVertex3f(vec.mV[0], vec.mV[1], vec.mV[2]);
+
+ vec.mV[index] = arrow_size;
+ glVertex3f(vec.mV[0], vec.mV[1], vec.mV[2]);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+
+ glTranslatef(vec.mV[0], vec.mV[1], vec.mV[2]);
+ glScalef(handle_size, handle_size, handle_size);
+
+ F32 rot = 0.0f;
+ LLVector3 axis;
+
+ switch(which_arrow)
+ {
+ case LL_X_ARROW:
+ rot = reverse_direction ? -90.0f : 90.0f;
+ axis.mV[1] = 1.0f;
+ break;
+ case LL_Y_ARROW:
+ rot = reverse_direction ? 90.0f : -90.0f;
+ axis.mV[0] = 1.0f;
+ break;
+ case LL_Z_ARROW:
+ rot = reverse_direction ? 180.0f : 0.0f;
+ axis.mV[0] = 1.0f;
+ break;
+ default:
+ llerrs << "renderArrow called with bad arrow " << which_arrow << llendl;
+ break;
+ }
+
+ glRotatef(rot, axis.mV[0], axis.mV[1], axis.mV[2]);
+ glScalef(mArrowScales.mV[index], mArrowScales.mV[index], mArrowScales.mV[index] * 1.5f);
+
+ gCone.render(CONE_LOD_HIGHEST);
+
+ glPopMatrix();
+ }
+}
+
+void LLManipTranslate::renderGridVert(F32 x_trans, F32 y_trans, F32 r, F32 g, F32 b, F32 alpha)
+{
+ glColor4f(r, g, b, alpha);
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ glVertex3f(0, x_trans, y_trans);
+ break;
+ case LL_XZ_PLANE:
+ glVertex3f(x_trans, 0, y_trans);
+ break;
+ case LL_XY_PLANE:
+ glVertex3f(x_trans, y_trans, 0);
+ break;
+ default:
+ glVertex3f(0,0,0);
+ break;
+ }
+
+}
diff --git a/indra/newview/llmaniptranslate.h b/indra/newview/llmaniptranslate.h
new file mode 100644
index 0000000000..5a93c0a24f
--- /dev/null
+++ b/indra/newview/llmaniptranslate.h
@@ -0,0 +1,97 @@
+/**
+ * @file llmaniptranslate.h
+ * @brief LLManipTranslate class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMANIPTRANSLATE_H
+#define LL_LLMANIPTRANSLATE_H
+
+#include "llmanip.h"
+#include "lltimer.h"
+#include "v4math.h"
+#include "linked_lists.h"
+#include "llquaternion.h"
+
+class LLManipTranslate : public LLManip
+{
+public:
+ class ManipulatorHandle
+ {
+ public:
+ LLVector3 mStartPosition;
+ LLVector3 mEndPosition;
+ EManipPart mManipID;
+ F32 mHotSpotRadius;
+
+ ManipulatorHandle(LLVector3 start_pos, LLVector3 end_pos, EManipPart id, F32 radius):mStartPosition(start_pos), mEndPosition(end_pos), mManipID(id), mHotSpotRadius(radius){}
+ };
+
+
+ LLManipTranslate( LLToolComposite* composite );
+ virtual ~LLManipTranslate();
+
+ static void restoreGL();
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void render();
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ EManipPart getHighlightedPart() { return mHighlightedPart; }
+ virtual void highlightManipulators(S32 x, S32 y);
+ /*virtual*/ BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask);
+
+protected:
+ enum EHandleType {
+ HANDLE_CONE,
+ HANDLE_BOX,
+ HANDLE_SPHERE
+ };
+
+ void renderArrow(S32 which_arrow, S32 selected_arrow, F32 box_size, F32 arrow_size, F32 handle_size, BOOL reverse_direction);
+ void renderTranslationHandles();
+ void renderText();
+ void renderSnapGuides();
+ void renderGrid(F32 x, F32 y, F32 size, F32 r, F32 g, F32 b, F32 a);
+ void renderGridVert(F32 x_trans, F32 y_trans, F32 r, F32 g, F32 b, F32 alpha);
+ F32 getMinGridScale();
+
+private:
+ S32 mLastHoverMouseX;
+ S32 mLastHoverMouseY;
+ BOOL mSendUpdateOnMouseUp;
+ BOOL mMouseOutsideSlop; // true after mouse goes outside slop region
+ BOOL mCopyMadeThisDrag;
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+ F32 mAxisArrowLength; // pixels
+ F32 mConeSize; // meters, world space
+ F32 mArrowLengthMeters; // meters
+ F32 mGridSizeMeters;
+ F32 mPlaneManipOffsetMeters;
+ EManipPart mManipPart;
+ LLVector3 mManipNormal;
+ LLVector3d mDragCursorStartGlobal;
+ LLVector3d mDragSelectionStartGlobal;
+ LLTimer mUpdateTimer;
+ LLLinkedList<ManipulatorHandle> mProjectedManipulators;
+ LLVector4 mManipulatorVertices[18];
+ EManipPart mHighlightedPart;
+ F32 mSnapOffsetMeters;
+ LLVector3 mSnapOffsetAxis;
+ LLQuaternion mGridRotation;
+ LLVector3 mGridOrigin;
+ LLVector3 mGridScale;
+ F32 mSubdivisions;
+ BOOL mInSnapRegime;
+ BOOL mSnapped;
+ LLVector3 mArrowScales;
+ LLVector3 mPlaneScales;
+ LLVector4 mPlaneManipPositions;
+};
+
+#endif
diff --git a/indra/newview/llmemoryview.cpp b/indra/newview/llmemoryview.cpp
new file mode 100644
index 0000000000..5edb373cbb
--- /dev/null
+++ b/indra/newview/llmemoryview.cpp
@@ -0,0 +1,223 @@
+/**
+ * @file llmemoryview.cpp
+ * @brief LLMemoryView class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "indra_constants.h"
+#include "llmemoryview.h"
+
+#include "llrect.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llmath.h"
+#include "llfontgl.h"
+
+#include "viewer.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "llstat.h"
+
+#include "llfasttimer.h"
+
+LLMemoryView::LLMemoryView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, TRUE)
+{
+ setVisible(FALSE);
+}
+
+LLMemoryView::~LLMemoryView()
+{
+}
+
+EWidgetType LLMemoryView::getWidgetType() const
+{
+ return WIDGET_TYPE_MEMORY_VIEW;
+}
+
+LLString LLMemoryView::getWidgetTag() const
+{
+ return LL_MEMORY_VIEW_TAG;
+}
+
+BOOL LLMemoryView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mask & MASK_SHIFT)
+ {
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ }
+ else
+ {
+ }
+ return TRUE;
+}
+
+BOOL LLMemoryView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return TRUE;
+}
+
+
+BOOL LLMemoryView::handleHover(S32 x, S32 y, MASK mask)
+{
+ return FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+struct mtv_display_info {
+ S32 memtype;
+ const char *desc;
+ LLColor4 *color;
+};
+
+static LLColor4 red0(0.5f, 0.0f, 0.0f, 1.0f);
+
+static struct mtv_display_info mtv_display_table[] =
+{
+ { LLMemType::MTYPE_INIT, "Init", &LLColor4::white },
+ { LLMemType::MTYPE_STARTUP, "Startup", &LLColor4::cyan1 },
+ { LLMemType::MTYPE_MAIN, "Main", &LLColor4::cyan2 },
+ { LLMemType::MTYPE_IMAGEBASE, "ImageBase", &LLColor4::yellow1 },
+ { LLMemType::MTYPE_IMAGERAW, "ImageRaw", &LLColor4::yellow2 },
+ { LLMemType::MTYPE_IMAGEFORMATTED, "ImageFmtd", &LLColor4::yellow3 },
+ { LLMemType::MTYPE_APPFMTIMAGE, "ViewerImageFmt", &LLColor4::orange1 },
+ { LLMemType::MTYPE_APPRAWIMAGE, "ViewerImageRaw", &LLColor4::orange2 },
+ { LLMemType::MTYPE_APPAUXRAWIMAGE, "ViewerImageAux", &LLColor4::orange3 },
+ { LLMemType::MTYPE_DRAWABLE, "Drawable", &LLColor4::green1 },
+ { LLMemType::MTYPE_OBJECT, "ViewerObject", &LLColor4::green2 },
+ { LLMemType::MTYPE_PIPELINE, "Pipeline", &LLColor4::green3 },
+ { LLMemType::MTYPE_PARTICLES, "Particles", &LLColor4::green4 },
+ { LLMemType::MTYPE_SPACE_PARTITION, "Space Partition", &LLColor4::blue2 },
+ { LLMemType::MTYPE_AVATAR, "Avatar", &LLColor4::purple1 },
+ { LLMemType::MTYPE_REGIONS, "Regions", &LLColor4::blue1 },
+ { LLMemType::MTYPE_TEMP1, "Temp1", &LLColor4::red1 },
+ { LLMemType::MTYPE_TEMP2, "Temp2", &LLColor4::magenta1 },
+ { LLMemType::MTYPE_TEMP3, "Temp3", &LLColor4::red2 },
+ { LLMemType::MTYPE_TEMP4, "Temp4", &LLColor4::magenta2 },
+ { LLMemType::MTYPE_TEMP5, "Temp5", &LLColor4::red3 },
+ { LLMemType::MTYPE_TEMP6, "Temp6", &LLColor4::magenta3 },
+ { LLMemType::MTYPE_TEMP7, "Temp7", &LLColor4::red4 },
+ { LLMemType::MTYPE_TEMP8, "Temp8", &LLColor4::magenta4 },
+
+ { LLMemType::MTYPE_OTHER, "Other", &red0 },
+};
+static const int MTV_DISPLAY_NUM = (sizeof(mtv_display_table)/sizeof(mtv_display_table[0]));
+
+void LLMemoryView::draw()
+{
+ std::string tdesc;
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_tex;
+ gl_rect_2d(0, height, width, 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
+
+#if MEM_TRACK_TYPE
+
+ S32 left, top, right, bottom;
+ S32 x, y;
+
+ S32 margin = 10;
+ S32 texth = (S32)LLFontGL::sMonospace->getLineHeight();
+
+ S32 xleft = margin;
+ S32 ytop = height - margin;
+ S32 labelwidth = 0;
+ S32 maxmaxbytes = 1;
+
+ // Make sure all timers are accounted for
+ // Set 'MT_OTHER' to unaccounted ticks last frame
+ {
+ S32 display_memtypes[LLMemType::MTYPE_NUM_TYPES];
+ for (S32 i=0; i < LLMemType::MTYPE_NUM_TYPES; i++)
+ {
+ display_memtypes[i] = 0;
+ }
+ for (S32 i=0; i < MTV_DISPLAY_NUM; i++)
+ {
+ S32 tidx = mtv_display_table[i].memtype;
+ display_memtypes[tidx]++;
+ }
+ LLMemType::sMemCount[LLMemType::MTYPE_OTHER] = 0;
+ LLMemType::sMaxMemCount[LLMemType::MTYPE_OTHER] = 0;
+ for (S32 tidx = 0; tidx < LLMemType::MTYPE_NUM_TYPES; tidx++)
+ {
+ if (display_memtypes[tidx] == 0)
+ {
+ LLMemType::sMemCount[LLMemType::MTYPE_OTHER] += LLMemType::sMemCount[tidx];
+ LLMemType::sMaxMemCount[LLMemType::MTYPE_OTHER] += LLMemType::sMaxMemCount[tidx];
+ }
+ }
+ }
+
+ // Labels
+ {
+ LLGLSTexture gls_texture;
+ y = ytop;
+ S32 peak = 0;
+ for (S32 i=0; i<MTV_DISPLAY_NUM; i++)
+ {
+ x = xleft;
+
+ int tidx = mtv_display_table[i].memtype;
+ S32 bytes = LLMemType::sMemCount[tidx];
+ S32 maxbytes = LLMemType::sMaxMemCount[tidx];
+ maxmaxbytes = llmax(maxbytes, maxmaxbytes);
+ peak += maxbytes;
+ S32 mbytes = bytes >> 20;
+
+ tdesc = llformat("%s [%4d MB]",mtv_display_table[i].desc,mbytes);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+ y -= (texth + 2);
+
+ S32 textw = LLFontGL::sMonospace->getWidth(tdesc);
+ if (textw > labelwidth)
+ labelwidth = textw;
+ }
+ x = xleft;
+ tdesc = llformat("Total Bytes: %d MB Overhead: %d KB",
+ LLMemType::sTotalMem >> 20, LLMemType::sOverheadMem >> 10);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ // Bars
+ y = ytop;
+ labelwidth += 8;
+ S32 barw = width - labelwidth - xleft - margin;
+ for (S32 i=0; i<MTV_DISPLAY_NUM; i++)
+ {
+ x = xleft + labelwidth;
+
+ int tidx = mtv_display_table[i].memtype;
+ S32 bytes = LLMemType::sMemCount[tidx];
+ F32 frac = (F32)bytes / (F32)maxmaxbytes;
+ S32 w = (S32)(frac * (F32)barw);
+ left = x; right = x + w;
+ top = y; bottom = y - texth;
+ gl_rect_2d(left, top, right, bottom, *mtv_display_table[i].color);
+
+ S32 maxbytes = LLMemType::sMaxMemCount[tidx];
+ F32 frac2 = (F32)maxbytes / (F32)maxmaxbytes;
+ S32 w2 = (S32)(frac2 * (F32)barw);
+ left = x + w + 1; right = x + w2;
+ top = y; bottom = y - texth;
+ LLColor4 tcolor = *mtv_display_table[i].color;
+ tcolor.setAlpha(.5f);
+ gl_rect_2d(left, top, right, bottom, tcolor);
+
+ y -= (texth + 2);
+ }
+
+#endif
+
+ LLView::draw();
+}
diff --git a/indra/newview/llmemoryview.h b/indra/newview/llmemoryview.h
new file mode 100644
index 0000000000..f32242d06c
--- /dev/null
+++ b/indra/newview/llmemoryview.h
@@ -0,0 +1,31 @@
+/**
+ * @file llmemoryview.h
+ * @brief LLMemoryView class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMEMORYVIEW_H
+#define LL_LLMEMORYVIEW_H
+
+#include "llview.h"
+
+class LLMemoryView : public LLView
+{
+public:
+ LLMemoryView(const std::string& name, const LLRect& rect);
+ virtual ~LLMemoryView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void draw();
+
+private:
+};
+
+#endif
diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp
new file mode 100644
index 0000000000..a3e0ab93b0
--- /dev/null
+++ b/indra/newview/llmenucommands.cpp
@@ -0,0 +1,135 @@
+/**
+ * @file llmenucommands.cpp
+ * @brief Implementations of menu commands.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmenucommands.h"
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "llcallingcard.h"
+#include "llchatbar.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloaterchat.h"
+#include "llfloaterclothing.h"
+#include "llfloaterdirectory.h"
+#include "llfloatermap.h"
+#include "llfloaterworldmap.h"
+#include "llgivemoney.h"
+#include "llinventoryview.h"
+#include "llnotify.h"
+#include "llstatusbar.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "llviewergesture.h" // for triggering gestures
+#include "llviewermessage.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "viewer.h"
+#include "llfocusmgr.h"
+
+void handle_track_avatar(const LLUUID& agent_id, const std::string& name)
+{
+ LLAvatarTracker::instance().track(agent_id, name);
+
+ LLFloaterDirectory::hide(NULL);
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+void handle_pay_by_id(const LLUUID& agent_id)
+{
+ const BOOL is_group = FALSE;
+ LLFloaterPay::payDirectly(&give_money, agent_id, is_group);
+}
+
+void handle_mouselook(void*)
+{
+ gAgent.changeCameraToMouselook();
+}
+
+
+void handle_map(void*)
+{
+ LLFloaterWorldMap::toggle(NULL);
+}
+
+void handle_mini_map(void*)
+{
+ LLFloaterMap::toggle(NULL);
+}
+
+
+void handle_find(void*)
+{
+ LLFloaterDirectory::toggleFind(NULL);
+}
+
+
+void handle_events(void*)
+{
+ LLFloaterDirectory::toggleEvents(NULL);
+}
+
+
+void handle_inventory(void*)
+{
+ // We're using the inventory, possibly for the
+ // first time.
+ LLFirstUse::useInventory();
+
+ LLInventoryView::toggleVisibility(NULL);
+}
+
+
+void handle_clothing(void*)
+{
+ LLFloaterClothing::toggleVisibility();
+}
+
+
+void handle_chat(void*)
+{
+ // give focus to chatbar if it's open but not focused
+ if (gSavedSettings.getBOOL("ChatVisible") && gFocusMgr.childHasKeyboardFocus(gChatBar))
+ {
+ LLChatBar::stopChat();
+ }
+ else
+ {
+ LLChatBar::startChat(NULL);
+ }
+}
+
+void handle_slash_key(void*)
+{
+ // LLChatBar::startChat("/");
+ //
+ // Don't do this, it results in a double-slash in the input field.
+ // Another "/" will be automatically typed for us, because the WM_KEYDOWN event
+ // that generated the menu accelerator call (and hence puts focus in
+ // the chat edtior) will be followed by a "/" WM_CHAR character message,
+ // which will type the slash. Yes, it's weird. It only matters for
+ // menu accelerators that put input focus into a field. And Mac works
+ // the same way. JC
+
+ LLChatBar::startChat(NULL);
+}
diff --git a/indra/newview/llmenucommands.h b/indra/newview/llmenucommands.h
new file mode 100644
index 0000000000..9cda66c52a
--- /dev/null
+++ b/indra/newview/llmenucommands.h
@@ -0,0 +1,27 @@
+/**
+ * @file llmenucommands.h
+ * @brief Implementations of menu commands.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMENUCOMMANDS_H
+#define LL_LLMENUCOMMANDS_H
+
+class LLUUID;
+
+void handle_track_avatar(const LLUUID& agent_id, const std::string& name);
+void handle_pay_by_id(const LLUUID& agent_id);
+void handle_mouselook(void*);
+void handle_map(void*);
+void handle_mini_map(void*);
+void handle_find(void*);
+void handle_events(void*);
+void handle_inventory(void*);
+void handle_clothing(void*);
+void handle_chat(void*);
+void handle_return_key(void*);
+void handle_slash_key(void*);
+
+#endif
diff --git a/indra/newview/llmorphview.cpp b/indra/newview/llmorphview.cpp
new file mode 100644
index 0000000000..de4c6dcbd6
--- /dev/null
+++ b/indra/newview/llmorphview.cpp
@@ -0,0 +1,193 @@
+/**
+ * @file llmorphview.cpp
+ * @brief Container for Morph functionality
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmorphview.h"
+
+#include "lljoint.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "llfirstuse.h"
+#include "llfloatercustomize.h"
+#include "llfloatertools.h"
+#include "llresmgr.h"
+#include "lltoolmgr.h"
+#include "lltoolmorph.h"
+#include "llviewercamera.h"
+#include "llvoavatar.h"
+#include "llviewerwindow.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+LLMorphView *gMorphView = NULL;
+
+
+const F32 EDIT_AVATAR_ORBIT_SPEED = 0.1f;
+const F32 EDIT_AVATAR_MAX_CAMERA_PITCH = 0.5f;
+
+const F32 CAMERA_MOVE_TIME = 0.5f;
+const F32 MORPH_NEAR_CLIP = 0.1f;
+
+const F32 CAMERA_DIST_MIN = 0.4f;
+const F32 CAMERA_DIST_MAX = 4.0f;
+const F32 CAMERA_DIST_STEP = 1.5f;
+
+//-----------------------------------------------------------------------------
+// LLMorphView()
+//-----------------------------------------------------------------------------
+LLMorphView::LLMorphView(const std::string& name, const LLRect& rect)
+ :
+ LLView(name, rect, FALSE, FOLLOWS_ALL),
+ mCameraTargetJoint( NULL ),
+ mCameraOffset(-0.5f, 0.05f, 0.07f ),
+ mCameraTargetOffset(0.f, 0.f, 0.05f ),
+ mOldCameraNearClip( 0.f ),
+ mCameraPitch( 0.f ),
+ mCameraYaw( 0.f ),
+ mCameraDist( -1.f ),
+ mCameraDrivenByKeys( FALSE )
+{
+}
+
+EWidgetType LLMorphView::getWidgetType() const
+{
+ return WIDGET_TYPE_MORPH_VIEW;
+}
+
+LLString LLMorphView::getWidgetTag() const
+{
+ return LL_MORPH_VIEW_TAG;
+}
+
+//-----------------------------------------------------------------------------
+// initialize()
+//-----------------------------------------------------------------------------
+void LLMorphView::initialize()
+{
+ mCameraPitch = 0.f;
+ mCameraYaw = 0.f;
+ mCameraDist = -1.f;
+
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if (!avatarp || avatarp->isDead())
+ {
+ gAgent.changeCameraToDefault();
+ return;
+ }
+
+ avatarp->stopMotion( ANIM_AGENT_BODY_NOISE );
+ avatarp->mSpecialRenderMode = 3;
+
+ // set up camera for close look at avatar
+ mOldCameraNearClip = gCamera->getNear();
+ gCamera->setNear(MORPH_NEAR_CLIP);
+}
+
+//-----------------------------------------------------------------------------
+// shutdown()
+//-----------------------------------------------------------------------------
+void LLMorphView::shutdown()
+{
+ LLVOAvatar::onCustomizeEnd();
+
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if(avatarp && !avatarp->isDead())
+ {
+ avatarp->startMotion( ANIM_AGENT_BODY_NOISE );
+ avatarp->mSpecialRenderMode = 0;
+ // reset camera
+ gCamera->setNear(mOldCameraNearClip);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setVisible()
+//-----------------------------------------------------------------------------
+void LLMorphView::setVisible(BOOL visible)
+{
+ if( visible != getVisible() )
+ {
+ LLView::setVisible(visible);
+
+ if (visible)
+ {
+ llassert( !gFloaterCustomize );
+ gFloaterCustomize = new LLFloaterCustomize();
+ gFloaterCustomize->fetchInventory();
+ gFloaterCustomize->open();
+
+ // Must do this _after_ gFloaterView is initialized.
+ gFloaterCustomize->switchToDefaultSubpart();
+
+ initialize();
+
+ // First run dialog
+ LLFirstUse::useAppearance();
+ }
+ else
+ {
+ if( gFloaterCustomize )
+ {
+ gFloaterView->removeChild( gFloaterCustomize );
+ delete gFloaterCustomize;
+ gFloaterCustomize = NULL;
+ }
+
+ shutdown();
+ }
+ }
+}
+
+void LLMorphView::updateCamera()
+{
+ if (!mCameraTargetJoint)
+ {
+ setCameraTargetJoint(gAgent.getAvatarObject()->getJoint("mHead"));
+ }
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return;
+ }
+ LLJoint* root_joint = avatar->getRootJoint();
+ if( !root_joint )
+ {
+ return;
+ }
+
+ const LLQuaternion& avatar_rot = root_joint->getWorldRotation();
+
+ LLVector3d joint_pos = gAgent.getPosGlobalFromAgent(mCameraTargetJoint->getWorldPosition());
+ LLVector3d target_pos = joint_pos + mCameraTargetOffset * avatar_rot;
+
+ LLQuaternion camera_rot_yaw(mCameraYaw, LLVector3::z_axis);
+ LLQuaternion camera_rot_pitch(mCameraPitch, LLVector3::y_axis);
+
+ LLVector3d camera_pos = joint_pos + mCameraOffset * camera_rot_pitch * camera_rot_yaw * avatar_rot;
+
+ gAgent.setCameraPosAndFocusGlobal( camera_pos, target_pos, gAgent.getID() );
+}
+
+void LLMorphView::setCameraDrivenByKeys(BOOL b)
+{
+ if( mCameraDrivenByKeys != b )
+ {
+ if( b )
+ {
+ // Reset to the default camera position specified by mCameraPitch, mCameraYaw, etc.
+ updateCamera();
+ }
+ mCameraDrivenByKeys = b;
+ }
+}
diff --git a/indra/newview/llmorphview.h b/indra/newview/llmorphview.h
new file mode 100644
index 0000000000..676564ac49
--- /dev/null
+++ b/indra/newview/llmorphview.h
@@ -0,0 +1,68 @@
+/**
+ * @file llmorphview.h
+ * @brief Container for character morph controls
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMORPHVIEW_H
+#define LL_LLMORPHVIEW_H
+
+#include "llview.h"
+#include "v3dmath.h"
+#include "llframetimer.h"
+
+class LLJoint;
+class LLFloaterCustomize;
+
+class LLMorphView : public LLView
+{
+public:
+ LLMorphView(const std::string& name, const LLRect& rect);
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ void initialize();
+ void shutdown();
+
+ // inherited methods
+ /*virtual*/ void setVisible(BOOL visible);
+
+ void setCameraTargetJoint(LLJoint *joint) {mCameraTargetJoint = joint;}
+ LLJoint* getCameraTargetJoint() {return mCameraTargetJoint;}
+
+ void setCameraOffset(const LLVector3d& camera_offset) {mCameraOffset = camera_offset;}
+ void setCameraTargetOffset(const LLVector3d& camera_target_offset) {mCameraTargetOffset = camera_target_offset;}
+ void setCameraDistToDefault() { mCameraDist = -1.f; }
+
+ void updateCamera();
+ void setCameraDrivenByKeys( BOOL b );
+
+protected:
+ LLJoint* mCameraTargetJoint;
+ LLVector3d mCameraOffset;
+ LLVector3d mCameraTargetOffset;
+ LLVector3d mOldCameraPos;
+ LLVector3d mOldTargetPos;
+ F32 mOldCameraNearClip;
+ LLFrameTimer mCameraMoveTimer;
+
+ // camera rotation
+ F32 mCameraPitch;
+ F32 mCameraYaw;
+
+ // camera zoom
+ F32 mCameraDist;
+
+ BOOL mCameraDrivenByKeys;
+};
+
+//
+// Globals
+//
+
+extern LLMorphView *gMorphView;
+
+#endif
diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp
new file mode 100644
index 0000000000..8485a8b177
--- /dev/null
+++ b/indra/newview/llmoveview.cpp
@@ -0,0 +1,198 @@
+/**
+ * @file llmoveview.cpp
+ * @brief Container for movement buttons like forward, left, fly
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmoveview.h"
+
+// Library includes
+#include "indra_constants.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "viewer.h"
+#include "llfontgl.h"
+#include "llbutton.h"
+#include "llviewerwindow.h"
+#include "lljoystickbutton.h"
+#include "llresmgr.h"
+#include "llvieweruictrlfactory.h"
+
+//
+// Constants
+//
+
+const F32 MOVE_BUTTON_DELAY = 0.0f;
+const F32 YAW_NUDGE_RATE = 0.05f; // fraction of normal speed
+const F32 NUDGE_TIME = 0.25f; // in seconds
+
+const char *MOVE_TITLE = "";
+
+//
+// Global statics
+//
+
+LLFloaterMove* LLFloaterMove::sInstance = NULL;
+
+
+//
+// Member functions
+//
+
+// protected
+LLFloaterMove::LLFloaterMove()
+: LLFloater("move floater", "FloaterMoveRect", MOVE_TITLE, FALSE, 100, 100, DRAG_ON_TOP,
+ MINIMIZE_NO)
+{
+ setIsChrome(TRUE);
+ gUICtrlFactory->buildFloater(this,"floater_moveview.xml");
+
+ mForwardButton = LLViewerUICtrlFactory::getJoystickAgentTurnByName(this, "forward btn");
+ mForwardButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mBackwardButton = LLViewerUICtrlFactory::getJoystickAgentTurnByName(this, "backward btn");
+ mBackwardButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mSlideLeftButton = LLViewerUICtrlFactory::getJoystickAgentSlideByName(this, "slide left btn");
+ mSlideLeftButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mSlideRightButton = LLViewerUICtrlFactory::getJoystickAgentSlideByName(this, "slide right btn");
+ mSlideRightButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mTurnLeftButton = LLUICtrlFactory::getButtonByName(this, "turn left btn");
+ mTurnLeftButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mTurnLeftButton->setHeldDownCallback( turnLeft );
+
+ mTurnRightButton = LLUICtrlFactory::getButtonByName(this, "turn right btn");
+ mTurnRightButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mTurnRightButton->setHeldDownCallback( turnRight );
+
+ mMoveUpButton = LLUICtrlFactory::getButtonByName(this, "move up btn");
+ childSetAction("move up btn",moveUp,NULL);
+ mMoveUpButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mMoveUpButton->setHeldDownCallback( moveUp );
+
+ mMoveDownButton = LLUICtrlFactory::getButtonByName(this, "move down btn");
+ childSetAction("move down btn",moveDown,NULL);
+ mMoveDownButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mMoveDownButton->setHeldDownCallback( moveDown );
+
+ mFlyButton = LLUICtrlFactory::getButtonByName(this, "fly btn");
+ childSetAction("fly btn",onFlyButtonClicked,NULL);
+
+ sInstance = this;
+}
+
+// protected
+LLFloaterMove::~LLFloaterMove()
+{
+ // children all deleted by LLView destructor
+ sInstance = NULL;
+}
+
+// virtual
+void LLFloaterMove::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowMovementControls", FALSE);
+ }
+}
+
+//
+// Static member functions
+//
+
+// static
+void LLFloaterMove::show(void*)
+{
+ if (sInstance)
+ {
+ sInstance->open();
+ }
+ else
+ {
+ LLFloaterMove* f = new LLFloaterMove();
+ f->open();
+ }
+
+ gSavedSettings.setBOOL("ShowMovementControls", TRUE);
+}
+
+// static
+void LLFloaterMove::toggle(void*)
+{
+ if (sInstance)
+ {
+ sInstance->close();
+ }
+ else
+ {
+ show(NULL);
+ }
+}
+
+// static
+BOOL LLFloaterMove::visible(void*)
+{
+ return (sInstance != NULL);
+}
+
+// protected static
+void LLFloaterMove::onFlyButtonClicked(void *)
+{
+ gAgent.toggleFlying();
+}
+
+
+// protected static
+F32 LLFloaterMove::getYawRate( F32 time )
+{
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = YAW_NUDGE_RATE + time * (1 - YAW_NUDGE_RATE)/ NUDGE_TIME;
+ return rate;
+ }
+ else
+ {
+ return 1.f;
+ }
+}
+
+// protected static
+void LLFloaterMove::turnLeft(void *)
+{
+ F32 time = sInstance->mTurnLeftButton->getHeldDownTime();
+ gAgent.moveYaw( getYawRate( time ) );
+}
+
+// protected static
+void LLFloaterMove::turnRight(void *)
+{
+ F32 time = sInstance->mTurnRightButton->getHeldDownTime();
+ gAgent.moveYaw( -getYawRate( time ) );
+}
+
+// protected static
+void LLFloaterMove::moveUp(void *)
+{
+ // Jumps or flys up, depending on fly state
+ gAgent.moveUp(1);
+}
+
+// protected static
+void LLFloaterMove::moveDown(void *)
+{
+ // Crouches or flys down, depending on fly state
+ gAgent.moveUp(-1);
+}
+
+// EOF
diff --git a/indra/newview/llmoveview.h b/indra/newview/llmoveview.h
new file mode 100644
index 0000000000..219f069de8
--- /dev/null
+++ b/indra/newview/llmoveview.h
@@ -0,0 +1,68 @@
+/**
+ * @file llmoveview.h
+ * @brief Container for buttons for walking, turning, flying
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMOVEVIEW_H
+#define LL_LLMOVEVIEW_H
+
+// Library includes
+#include "llfloater.h"
+
+class LLButton;
+class LLJoystickAgentTurn;
+class LLJoystickAgentSlide;
+
+//
+// Classes
+//
+class LLFloaterMove
+: public LLFloater
+{
+protected:
+ LLFloaterMove();
+ ~LLFloaterMove();
+
+public:
+ /*virtual*/ void onClose(bool app_quitting);
+ static void onFlyButtonClicked(void* userdata);
+ static F32 getYawRate(F32 time);
+
+ static void show(void*);
+ static void toggle(void*);
+ static BOOL visible(void*);
+
+ // HACK for agent-driven button highlighting
+ static LLFloaterMove* getInstance() { return sInstance; }
+
+protected:
+ static void turnLeftNudge(void* userdata);
+ static void turnLeft(void* userdata);
+
+ static void turnRightNudge(void* userdata);
+ static void turnRight(void* userdata);
+
+ static void moveUp(void* userdata);
+ static void moveDown(void* userdata);
+
+public:
+ LLButton* mFlyButton;
+
+ LLJoystickAgentTurn* mForwardButton;
+ LLJoystickAgentTurn* mBackwardButton;
+ LLJoystickAgentSlide* mSlideLeftButton;
+ LLJoystickAgentSlide* mSlideRightButton;
+ LLButton* mTurnLeftButton;
+ LLButton* mTurnRightButton;
+ LLButton* mMoveUpButton;
+ LLButton* mMoveDownButton;
+
+protected:
+ static LLFloaterMove* sInstance;
+};
+
+
+#endif
diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp
new file mode 100644
index 0000000000..fc8ac0a372
--- /dev/null
+++ b/indra/newview/llmutelist.cpp
@@ -0,0 +1,553 @@
+/**
+ * @file llmutelist.cpp
+ * @author Richard Nelson, James Cook
+ * @brief Management of list of muted players
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * How should muting work?
+ * Mute an avatar
+ * Mute a specific object (accidentally spamming)
+ *
+ * right-click avatar, mute
+ * see list of recent chatters, mute
+ * type a name to mute?
+ *
+ * show in list whether chatter is avatar or object
+ *
+ * need fast lookup by id
+ * need lookup by name, doesn't have to be fast
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmutelist.h"
+
+#include <boost/tokenizer.hpp>
+
+#include "llcrc.h"
+#include "lldispatcher.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llfloatermute.h"
+#include "llviewermessage.h" // for gGenericDispatcher
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llworld.h" //for particle system banning
+
+LLMuteList* gMuteListp = NULL;
+
+// "emptymutelist"
+class LLDispatchEmptyMuteList : public LLDispatchHandler
+{
+public:
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+ {
+ gMuteListp->setLoaded();
+ return true;
+ }
+};
+
+static LLDispatchEmptyMuteList sDispatchEmptyMuteList;
+
+//-----------------------------------------------------------------------------
+// LLMute()
+//-----------------------------------------------------------------------------
+const char BY_NAME_SUFFIX[] = " (by name)";
+const char AGENT_SUFFIX[] = " (resident)";
+const char OBJECT_SUFFIX[] = " (object)";
+const char GROUP_SUFFIX[] = " (group)";
+
+LLString LLMute::getDisplayName() const
+{
+ LLString name_with_suffix = mName;
+ switch (mType)
+ {
+ case BY_NAME:
+ default:
+ name_with_suffix += BY_NAME_SUFFIX;
+ break;
+ case AGENT:
+ name_with_suffix += AGENT_SUFFIX;
+ break;
+ case OBJECT:
+ name_with_suffix += OBJECT_SUFFIX;
+ break;
+ case GROUP:
+ name_with_suffix += GROUP_SUFFIX;
+ break;
+ }
+ return name_with_suffix;
+}
+
+void LLMute::setFromDisplayName(const LLString& display_name)
+{
+ size_t pos = 0;
+ mName = display_name;
+
+ pos = mName.rfind(GROUP_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = GROUP;
+ return;
+ }
+
+ pos = mName.rfind(OBJECT_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = OBJECT;
+ return;
+ }
+
+ pos = mName.rfind(AGENT_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = AGENT;
+ return;
+ }
+
+ pos = mName.rfind(BY_NAME_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = BY_NAME;
+ return;
+ }
+
+ llwarns << "Unable to set mute from display name " << display_name << llendl;
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// LLMuteList()
+//-----------------------------------------------------------------------------
+LLMuteList::LLMuteList() :
+ mIsLoaded(FALSE)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ // Register our various callbacks
+ msg->setHandlerFuncFast(_PREHASH_MuteListUpdate, processMuteListUpdate);
+ msg->setHandlerFuncFast(_PREHASH_UseCachedMuteList, processUseCachedMuteList);
+
+ gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList);
+}
+
+//-----------------------------------------------------------------------------
+// ~LLMuteList()
+//-----------------------------------------------------------------------------
+LLMuteList::~LLMuteList()
+{
+}
+
+BOOL LLMuteList::isLinden(const LLString& name) const
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(name, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ if (token_iter == tokens.end()) return FALSE;
+ token_iter++;
+ if (token_iter == tokens.end()) return FALSE;
+
+ LLString last_name = *token_iter;
+
+ if (last_name == "Linden")
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLMuteList::add(const LLMute& mute)
+{
+ // Can't mute Lindens
+ if ((mute.mType == LLMute::AGENT || mute.mType == LLMute::BY_NAME)
+ && isLinden(mute.mName))
+ {
+ gViewerWindow->alertXml("MuteLinden");
+ return FALSE;
+ }
+
+ // Can't mute self.
+ if (mute.mType == LLMute::AGENT
+ && mute.mID == gAgent.getID())
+ {
+ return FALSE;
+ }
+
+ if (mute.mType == LLMute::BY_NAME)
+ {
+ // Can't mute empty string by name
+ if (mute.mName.empty())
+ {
+ llwarns << "Trying to mute empty string by-name" << llendl;
+ return FALSE;
+ }
+
+ // Null mutes must have uuid null
+ if (mute.mID.notNull())
+ {
+ llwarns << "Trying to add by-name mute with non-null id" << llendl;
+ return FALSE;
+ }
+
+ std::pair<string_set_t::iterator, bool> result = mLegacyMutes.insert(mute.mName);
+ if (result.second)
+ {
+ llinfos << "Muting by name " << mute.mName << llendl;
+ updateAdd(mute);
+ notifyObservers();
+ return TRUE;
+ }
+ else
+ {
+ // was duplicate
+ return FALSE;
+ }
+ }
+ else
+ {
+ std::pair<mute_set_t::iterator, bool> result = mMutes.insert(mute);
+ if (result.second)
+ {
+ llinfos << "Muting " << mute.mName << " id " << mute.mID << llendl;
+ updateAdd(mute);
+ notifyObservers();
+ //Kill all particle systems owned by muted task
+ if(mute.mType == LLMute::AGENT || mute.mType == LLMute::OBJECT)
+ {
+ gWorldPointer->mPartSim.cleanMutedParticles(mute.mID);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+}
+
+void LLMuteList::updateAdd(const LLMute& mute)
+{
+ // Update the database
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateMuteListEntry);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MuteData);
+ msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
+ msg->addStringFast(_PREHASH_MuteName, mute.mName);
+ msg->addS32("MuteType", mute.mType);
+ msg->addU32("MuteFlags", 0x0); // future
+ gAgent.sendReliableMessage();
+
+ mIsLoaded = TRUE;
+}
+
+
+BOOL LLMuteList::remove(const LLMute& mute)
+{
+ BOOL found = FALSE;
+
+ // First, remove from main list.
+ mute_set_t::iterator it = mMutes.find(mute);
+ if (it != mMutes.end())
+ {
+ updateRemove(*it);
+ mMutes.erase(it);
+ // Must be after erase.
+ notifyObservers();
+ found = TRUE;
+ }
+
+ // Clean up any legacy mutes
+ string_set_t::iterator legacy_it = mLegacyMutes.find(mute.mName);
+ if (legacy_it != mLegacyMutes.end())
+ {
+ // Database representation of legacy mute is UUID null.
+ LLMute mute(LLUUID::null, *legacy_it, LLMute::BY_NAME);
+ updateRemove(mute);
+ mLegacyMutes.erase(legacy_it);
+ // Must be after erase.
+ notifyObservers();
+ found = TRUE;
+ }
+
+ return found;
+}
+
+
+void LLMuteList::updateRemove(const LLMute& mute)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveMuteListEntry);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MuteData);
+ msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
+ msg->addString("MuteName", mute.mName);
+ gAgent.sendReliableMessage();
+}
+
+
+std::vector<LLMute> LLMuteList::getMutes() const
+{
+ std::vector<LLMute> mutes;
+
+ for (mute_set_t::const_iterator it = mMutes.begin();
+ it != mMutes.end();
+ ++it)
+ {
+ mutes.push_back(*it);
+ }
+
+ for (string_set_t::const_iterator it = mLegacyMutes.begin();
+ it != mLegacyMutes.end();
+ ++it)
+ {
+ LLMute legacy(LLUUID::null, *it);
+ mutes.push_back(legacy);
+ }
+
+ std::sort(mutes.begin(), mutes.end(), compare_by_name());
+ return mutes;
+}
+
+//-----------------------------------------------------------------------------
+// loadFromFile()
+//-----------------------------------------------------------------------------
+BOOL LLMuteList::loadFromFile(const LLString& filename)
+{
+ FILE* fp = LLFile::fopen(filename.c_str(), "rb");
+ if (!fp)
+ {
+ llwarns << "Couldn't open mute list " << filename << llendl;
+ return FALSE;
+ }
+
+ char id_buffer[MAX_STRING];
+ char name_buffer[MAX_STRING];
+ char buffer[MAX_STRING];
+ while (!feof(fp)
+ && fgets(buffer, MAX_STRING, fp))
+ {
+ id_buffer[0] = '\0';
+ name_buffer[0] = '\0';
+ S32 type = 0;
+ sscanf(buffer, " %d %s %[^|]", &type, id_buffer, name_buffer);
+ LLUUID id = LLUUID(id_buffer);
+ LLMute mute(id, name_buffer, (LLMute::EType)type);
+ if (mute.mID.isNull()
+ || mute.mType == LLMute::BY_NAME)
+ {
+ mLegacyMutes.insert(mute.mName);
+ }
+ else
+ {
+ mMutes.insert(mute);
+ }
+ }
+ fclose(fp);
+ mIsLoaded = TRUE;
+ notifyObservers();
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// saveToFile()
+//-----------------------------------------------------------------------------
+BOOL LLMuteList::saveToFile(const LLString& filename)
+{
+ FILE* fp = LLFile::fopen(filename.c_str(), "wb");
+ if (!fp)
+ {
+ llwarns << "Couldn't open mute list " << filename << llendl;
+ return FALSE;
+ }
+ // legacy mutes have null uuid
+ char id_string[UUID_STR_LENGTH];
+ LLUUID::null.toString(id_string);
+ for (string_set_t::iterator it = mLegacyMutes.begin();
+ it != mLegacyMutes.end();
+ ++it)
+ {
+ fprintf(fp, "%d %s %s|\n", (S32)LLMute::BY_NAME, id_string, it->c_str());
+ }
+ for (mute_set_t::iterator it = mMutes.begin();
+ it != mMutes.end();
+ ++it)
+ {
+ it->mID.toString(id_string);
+ const LLString& name = it->mName;
+ fprintf(fp, "%d %s %s|\n", (S32)it->mType, id_string, name.c_str());
+ }
+ fclose(fp);
+ return TRUE;
+}
+
+
+BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name) const
+{
+ // don't need name or type for lookup
+ LLMute mute(id);
+ mute_set_t::const_iterator mute_it = mMutes.find(mute);
+ if (mute_it != mMutes.end()) return TRUE;
+
+ // empty names can't be legacy-muted
+ if (name.empty()) return FALSE;
+
+ // Look in legacy pile
+ string_set_t::const_iterator legacy_it = mLegacyMutes.find(name);
+ return legacy_it != mLegacyMutes.end();
+}
+
+//-----------------------------------------------------------------------------
+// requestFromServer()
+//-----------------------------------------------------------------------------
+void LLMuteList::requestFromServer(const LLUUID& agent_id)
+{
+ char agent_id_string[UUID_STR_LENGTH];
+ char filename[LL_MAX_PATH];
+ agent_id.toString(agent_id_string);
+ sprintf(filename, "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ LLCRC crc;
+ crc.update(filename);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MuteListRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MuteData);
+ msg->addU32Fast(_PREHASH_MuteCRC, crc.getCRC());
+ gAgent.sendReliableMessage();
+}
+
+//-----------------------------------------------------------------------------
+// cache()
+//-----------------------------------------------------------------------------
+
+void LLMuteList::cache(const LLUUID& agent_id)
+{
+ // Write to disk even if empty.
+ if(mIsLoaded)
+ {
+ char agent_id_string[UUID_STR_LENGTH];
+ char filename[LL_MAX_PATH];
+ agent_id.toString(agent_id_string);
+ sprintf(filename, "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ saveToFile(filename);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Static message handlers
+//-----------------------------------------------------------------------------
+
+void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLMuteList::processMuteListUpdate()" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_MuteData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got an mute list update for the wrong agent." << llendl;
+ return;
+ }
+ char filename[MAX_STRING];
+ filename[0] = '\0';
+ msg->getStringFast(_PREHASH_MuteData, _PREHASH_Filename, MAX_STRING, filename);
+
+ std::string *local_filename_and_path = new std::string(gDirUtilp->getExpandedFilename( LL_PATH_CACHE, filename ));
+ gXferManager->requestFile(local_filename_and_path->c_str(),
+ filename,
+ LL_PATH_CACHE,
+ msg->getSender(),
+ TRUE, // make the remote file temporary.
+ onFileMuteList,
+ (void**)local_filename_and_path,
+ LLXferManager::HIGH_PRIORITY);
+}
+
+void LLMuteList::processUseCachedMuteList(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLMuteList::processUseCachedMuteList()" << llendl;
+ if (!gMuteListp) return;
+
+ char agent_id_string[UUID_STR_LENGTH];
+ gAgent.getID().toString(agent_id_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ gMuteListp->loadFromFile(filename);
+}
+
+void LLMuteList::onFileMuteList(void** user_data, S32 error_code)
+{
+ llinfos << "LLMuteList::processMuteListFile()" << llendl;
+ if (!gMuteListp) return;
+
+ std::string *local_filename_and_path = (std::string*)user_data;
+ if(local_filename_and_path && !local_filename_and_path->empty() && (error_code == 0))
+ {
+ gMuteListp->loadFromFile(local_filename_and_path->c_str());
+ LLFile::remove(local_filename_and_path->c_str());
+ }
+ delete local_filename_and_path;
+}
+
+void LLMuteList::addObserver(LLMuteListObserver* observer)
+{
+ mObservers.insert(observer);
+}
+
+void LLMuteList::removeObserver(LLMuteListObserver* observer)
+{
+ mObservers.erase(observer);
+}
+
+void LLMuteList::setLoaded()
+{
+ mIsLoaded = TRUE;
+ notifyObservers();
+}
+
+void LLMuteList::notifyObservers()
+{
+ // HACK: LLFloaterMute is constructed before LLMuteList,
+ // so it can't easily observe it. Changing this requires
+ // much reshuffling of the startup process. JC
+ if (gFloaterMute)
+ {
+ gFloaterMute->refreshMuteList();
+ }
+
+ for (observer_set_t::iterator it = mObservers.begin();
+ it != mObservers.end();
+ )
+ {
+ LLMuteListObserver* observer = *it;
+ observer->onChange();
+ // In case onChange() deleted an entry.
+ it = mObservers.upper_bound(observer);
+ }
+}
diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h
new file mode 100644
index 0000000000..7f9b1b3bf6
--- /dev/null
+++ b/indra/newview/llmutelist.h
@@ -0,0 +1,128 @@
+/**
+ * @file llmutelist.h
+ * @brief Management of list of muted players
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MUTELIST_H
+#define LL_MUTELIST_H
+
+#include "llstring.h"
+#include "lluuid.h"
+
+class LLViewerObject;
+class LLMessageSystem;
+class LLMuteListObserver;
+
+// An entry in the mute list.
+class LLMute
+{
+public:
+ // Legacy mutes are BY_NAME and have null UUID.
+ enum EType { BY_NAME = 0, AGENT = 1, OBJECT = 2, GROUP = 3, COUNT = 4 };
+
+ LLMute(const LLUUID& id, const LLString& name = "", EType type = BY_NAME)
+ : mID(id), mName(name), mType(type) { }
+
+ // Returns name + suffix based on type
+ // For example: "James Tester (resident)"
+ LLString getDisplayName() const;
+
+ // Converts a UI name into just the agent or object name
+ // For example: "James Tester (resident)" sets the name to "James Tester"
+ // and the type to AGENT.
+ void setFromDisplayName(const LLString& display_name);
+
+public:
+ LLUUID mID; // agent or object id
+ LLString mName; // agent or object name
+ EType mType; // needed for UI display of existing mutes
+};
+
+class LLMuteList
+{
+public:
+ LLMuteList();
+ ~LLMuteList();
+
+ void addObserver(LLMuteListObserver* observer);
+ void removeObserver(LLMuteListObserver* observer);
+
+ // Add either a normal or a BY_NAME mute.
+ BOOL add(const LLMute& mute);
+
+ // Remove both normal and legacy mutes.
+ BOOL remove(const LLMute& mute);
+
+ // Name is required to test against legacy text-only mutes.
+ BOOL isMuted(const LLUUID& id, const LLString& name = LLString::null) const;
+
+ BOOL isLinden(const LLString& name) const;
+
+ BOOL isLoaded() const { return mIsLoaded; }
+
+ std::vector<LLMute> getMutes() const;
+
+ // request the mute list
+ void requestFromServer(const LLUUID& agent_id);
+
+ // call this method on logout to save everything.
+ void cache(const LLUUID& agent_id);
+
+private:
+ BOOL loadFromFile(const LLString& filename);
+ BOOL saveToFile(const LLString& filename);
+
+ void setLoaded();
+ void notifyObservers();
+
+ void updateAdd(const LLMute& mute);
+ void updateRemove(const LLMute& mute);
+
+ // TODO: NULL out mute_id in database
+ static void processMuteListUpdate(LLMessageSystem* msg, void**);
+ static void processUseCachedMuteList(LLMessageSystem* msg, void**);
+
+ static void onFileMuteList(void** user_data, S32 code);
+
+private:
+ struct compare_by_name
+ {
+ bool operator()(const LLMute& a, const LLMute& b) const
+ {
+ return a.mName < b.mName;
+ }
+ };
+ struct compare_by_id
+ {
+ bool operator()(const LLMute& a, const LLMute& b) const
+ {
+ return a.mID < b.mID;
+ }
+ };
+ typedef std::set<LLMute, compare_by_id> mute_set_t;
+ mute_set_t mMutes;
+
+ typedef std::set<LLString> string_set_t;
+ string_set_t mLegacyMutes;
+
+ typedef std::set<LLMuteListObserver*> observer_set_t;
+ observer_set_t mObservers;
+
+ BOOL mIsLoaded;
+
+ friend class LLDispatchEmptyMuteList;
+};
+
+class LLMuteListObserver
+{
+public:
+ virtual ~LLMuteListObserver() { }
+ virtual void onChange() = 0;
+};
+
+extern LLMuteList *gMuteListp;
+
+#endif //LL_MUTELIST_H
diff --git a/indra/newview/llnamebox.cpp b/indra/newview/llnamebox.cpp
new file mode 100644
index 0000000000..8782f529f1
--- /dev/null
+++ b/indra/newview/llnamebox.cpp
@@ -0,0 +1,102 @@
+/**
+ * @file llnamebox.cpp
+ * @brief A text display widget
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llnamebox.h"
+
+#include "llerror.h"
+#include "llfontgl.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "lluuid.h"
+
+#include "llcachename.h"
+#include "llagent.h"
+
+// statics
+std::set<LLNameBox*> LLNameBox::sInstances;
+
+
+LLNameBox::LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id, BOOL is_group, const LLFontGL* font, BOOL mouse_opaque)
+: LLTextBox(name, rect, "(retrieving)", font, mouse_opaque),
+ mNameID(name_id)
+{
+ LLNameBox::sInstances.insert(this);
+ if(!name_id.isNull())
+ {
+ setNameID(name_id, is_group);
+ }
+ else
+ {
+ setText("");
+ }
+}
+
+LLNameBox::~LLNameBox()
+{
+ LLNameBox::sInstances.erase(this);
+}
+
+void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group)
+{
+ mNameID = name_id;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ LLString name;
+
+ if (!is_group)
+ {
+ gCacheName->getName(name_id, first, last);
+
+ name.assign(first);
+ name.append(1, ' ');
+ name.append(last);
+ }
+ else
+ {
+ gCacheName->getGroupName(name_id, group_name);
+
+ name.assign(group_name);
+ }
+
+ setText(name);
+}
+
+void LLNameBox::refresh(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ if (id == mNameID)
+ {
+ LLString name;
+
+ name.assign(firstname);
+ if (!is_group)
+ {
+ name.append(1, ' ');
+ name.append(lastname);
+ }
+
+ setText(name);
+ }
+}
+
+void LLNameBox::refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ std::set<LLNameBox*>::iterator it;
+ for (it = LLNameBox::sInstances.begin();
+ it != LLNameBox::sInstances.end();
+ ++it)
+ {
+ LLNameBox* box = *it;
+ box->refresh(id, firstname, lastname, is_group);
+ }
+}
diff --git a/indra/newview/llnamebox.h b/indra/newview/llnamebox.h
new file mode 100644
index 0000000000..7af4b82f2d
--- /dev/null
+++ b/indra/newview/llnamebox.h
@@ -0,0 +1,46 @@
+/**
+ * @file llnamebox.h
+ * @brief display and refresh a name from the name cache
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMEBOX_H
+#define LL_LLNAMEBOX_H
+
+#include <set>
+
+#include "llview.h"
+#include "llstring.h"
+#include "llfontgl.h"
+#include "linked_lists.h"
+#include "lltextbox.h"
+
+class LLNameBox
+: public LLTextBox
+{
+public:
+ LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id = LLUUID::null, BOOL is_group = FALSE, const LLFontGL* font = NULL, BOOL mouse_opaque = TRUE );
+ // By default, follows top and left and is mouse-opaque.
+ // If no text, text = name.
+ // If no font, uses default system font.
+ virtual ~LLNameBox();
+
+ void setNameID(const LLUUID& name_id, BOOL is_group);
+
+ void refresh(const LLUUID& id, const char* first, const char* last,
+ BOOL is_group);
+
+ static void refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group);
+
+private:
+ static std::set<LLNameBox*> sInstances;
+
+private:
+ LLUUID mNameID;
+
+};
+
+#endif
diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp
new file mode 100644
index 0000000000..bc6a105a51
--- /dev/null
+++ b/indra/newview/llnameeditor.cpp
@@ -0,0 +1,184 @@
+/**
+ * @file llnameeditor.cpp
+ * @brief Name Editor to refresh a name.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llnameeditor.h"
+#include "llcachename.h"
+#include "llagent.h"
+
+#include "llfontgl.h"
+
+#include "lluuid.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "llui.h"
+
+
+// statics
+std::set<LLNameEditor*> LLNameEditor::sInstances;
+
+LLNameEditor::LLNameEditor(const std::string& name, const LLRect& rect,
+ const LLUUID& name_id,
+ BOOL is_group,
+ const LLFontGL* glfont,
+ S32 max_text_length,
+ void (*commit_callback)(LLUICtrl* caller, void* user_data),
+ void (*keystroke_callback)(LLLineEditor* caller, void* user_data),
+ void (*focus_lost_callback)(LLLineEditor* caller, void* user_data),
+ void* userdata,
+ LLLinePrevalidateFunc prevalidate_func,
+ LLViewBorder::EBevel border_bevel,
+ LLViewBorder::EStyle border_style,
+ S32 border_thickness)
+: LLLineEditor(name, rect,
+ "(retrieving)",
+ glfont,
+ max_text_length,
+ commit_callback,
+ keystroke_callback,
+ focus_lost_callback,
+ userdata,
+ prevalidate_func,
+ border_bevel,
+ border_style,
+ border_thickness),
+ mNameID(name_id)
+{
+ LLNameEditor::sInstances.insert(this);
+ if(!name_id.isNull())
+ {
+ setNameID(name_id, is_group);
+ }
+}
+
+
+LLNameEditor::~LLNameEditor()
+{
+ LLNameEditor::sInstances.erase(this);
+}
+
+void LLNameEditor::setNameID(const LLUUID& name_id, BOOL is_group)
+{
+ mNameID = name_id;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ LLString name;
+
+ if (!is_group)
+ {
+ gCacheName->getName(name_id, first, last);
+
+ name.assign(first);
+ name.append(1, ' ');
+ name.append(last);
+ }
+ else
+ {
+ gCacheName->getGroupName(name_id, group_name);
+ name.assign(group_name);
+ }
+
+ setText(name);
+}
+
+void LLNameEditor::refresh(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ if (id == mNameID)
+ {
+ LLString name;
+
+ name.assign(firstname);
+ if (!is_group)
+ {
+ name.append(1, ' ');
+ name.append(lastname);
+ }
+
+ setText(name);
+ }
+}
+
+void LLNameEditor::refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ std::set<LLNameEditor*>::iterator it;
+ for (it = LLNameEditor::sInstances.begin();
+ it != LLNameEditor::sInstances.end();
+ ++it)
+ {
+ LLNameEditor* box = *it;
+ box->refresh(id, firstname, lastname, is_group);
+ }
+}
+
+void LLNameEditor::setValue( LLSD value )
+{
+ setNameID(value.asUUID(), FALSE);
+}
+
+LLSD LLNameEditor::getValue() const
+{
+ return LLSD(mNameID);
+}
+
+LLView* LLNameEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("name_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ S32 max_text_length = 128;
+ node->getAttributeS32("max_length", max_text_length);
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE;
+ LLString border_string;
+ node->getAttributeString("border_style", border_string);
+ LLString::toLower(border_string);
+
+ if (border_string == "texture")
+ {
+ border_style = LLViewBorder::STYLE_TEXTURE;
+ }
+
+ S32 border_thickness = 1;
+ node->getAttributeS32("border_thickness", border_thickness);
+
+ LLUICtrlCallback commit_callback = NULL;
+
+ LLNameEditor* line_editor = new LLNameEditor(name,
+ rect,
+ LLUUID::null, FALSE,
+ font,
+ max_text_length,
+ commit_callback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ LLViewBorder::BEVEL_IN,
+ border_style,
+ border_thickness);
+
+ LLString label;
+ if(node->getAttributeString("label", label))
+ {
+ line_editor->setLabel(label);
+ }
+ line_editor->setColorParameters(node);
+ line_editor->initFromXML(node, parent);
+
+ return line_editor;
+}
diff --git a/indra/newview/llnameeditor.h b/indra/newview/llnameeditor.h
new file mode 100644
index 0000000000..b5e811fba4
--- /dev/null
+++ b/indra/newview/llnameeditor.h
@@ -0,0 +1,70 @@
+/**
+ * @file llnameeditor.h
+ * @brief display and refresh a name from the name cache
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMEEDITOR_H
+#define LL_LLNAMEEDITOR_H
+
+#include <set>
+
+#include "llview.h"
+#include "v4color.h"
+#include "llstring.h"
+#include "llfontgl.h"
+#include "linked_lists.h"
+#include "lllineeditor.h"
+
+
+class LLNameEditor
+: public LLLineEditor
+{
+public:
+ LLNameEditor(const std::string& name, const LLRect& rect,
+ const LLUUID& name_id = LLUUID::null,
+ BOOL is_group = FALSE,
+ const LLFontGL* glfont = NULL,
+ S32 max_text_length = 254,
+ void (*commit_callback)(LLUICtrl* caller, void* user_data) = NULL,
+ void (*keystroke_callback)(LLLineEditor* caller, void* user_data) = NULL,
+ void (*focus_lost_callback)(LLLineEditor* caller, void* user_data) = NULL,
+ void* userdata = NULL,
+ LLLinePrevalidateFunc prevalidate_func = NULL,
+ LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_IN,
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE,
+ S32 border_thickness = 1);
+ // By default, follows top and left and is mouse-opaque.
+ // If no text, text = name.
+ // If no font, uses default system font.
+
+ virtual ~LLNameEditor();
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ void setNameID(const LLUUID& name_id, BOOL is_group);
+
+ void refresh(const LLUUID& id, const char* first, const char* last,
+ BOOL is_group);
+
+ static void refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group);
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_NAME_EDITOR; }
+ virtual LLString getWidgetTag() const { return LL_NAME_EDITOR_TAG; }
+
+ // Take/return agent UUIDs
+ virtual void setValue( LLSD value );
+ virtual LLSD getValue() const;
+
+private:
+ static std::set<LLNameEditor*> sInstances;
+
+private:
+ LLUUID mNameID;
+
+};
+
+#endif
diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp
new file mode 100644
index 0000000000..520dd06af1
--- /dev/null
+++ b/indra/newview/llnamelistctrl.cpp
@@ -0,0 +1,409 @@
+/**
+ * @file llnamelistctrl.cpp
+ * @brief A list of names, automatically refreshed from name cache.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <boost/tokenizer.hpp>
+
+#include "llnamelistctrl.h"
+
+#include "llcachename.h"
+#include "llagent.h"
+#include "llinventory.h"
+
+// statics
+std::set<LLNameListCtrl*> LLNameListCtrl::sInstances;
+
+LLNameListCtrl::LLNameListCtrl(const LLString& name,
+ const LLRect& rect,
+ LLUICtrlCallback cb,
+ void* userdata,
+ BOOL allow_multiple_selection,
+ BOOL draw_border,
+ S32 name_column_index,
+ const LLString& tooltip)
+: LLScrollListCtrl(name, rect, cb, userdata, allow_multiple_selection,
+ draw_border),
+ mNameColumnIndex(name_column_index),
+ mAllowCallingCardDrop(FALSE)
+{
+ setToolTip(tooltip);
+ LLNameListCtrl::sInstances.insert(this);
+}
+
+
+// virtual
+LLNameListCtrl::~LLNameListCtrl()
+{
+ LLNameListCtrl::sInstances.erase(this);
+}
+
+
+// public
+BOOL LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos,
+ BOOL enabled, LLString& suffix)
+{
+ //llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+
+ BOOL result = gCacheName->getName(agent_id, first, last);
+
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+ fullname.append(suffix);
+
+ addStringUUIDItem(fullname, agent_id, pos, enabled);
+
+ return result;
+}
+
+// virtual, public
+BOOL LLNameListCtrl::handleDragAndDrop(
+ S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ if (!mAllowCallingCardDrop)
+ {
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+
+ if (cargo_type == DAD_CALLINGCARD)
+ {
+ if (drop)
+ {
+ LLInventoryItem* item = (LLInventoryItem *)cargo_data;
+ addNameItem(item->getCreatorUUID());
+ }
+
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ if (!getToolTip().empty())
+ {
+ tooltip_msg = getToolTip();
+ }
+ else
+ {
+ // backwards compatable English tooltip (should be overridden in xml)
+ tooltip_msg.assign("Drag a calling card here\nto add a resident.");
+ }
+ }
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLNameListCtrl " << getName() << llendl;
+
+ return handled;
+}
+
+// public
+void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos,
+ BOOL enabled)
+{
+ //llinfos << "LLNameListCtrl::addGroupNameItem " << group_id << llendl;
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ gCacheName->getGroupName(group_id, group_name);
+ addStringUUIDItem(group_name, group_id, pos, enabled);
+}
+
+// public
+void LLNameListCtrl::addGroupNameItem(LLScrollListItem* item, EAddPosition pos)
+
+{
+ //llinfos << "LLNameListCtrl::addGroupNameItem " << item->getUUID() << llendl;
+
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ gCacheName->getGroupName(item->getUUID(), group_name);
+
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+ ((LLScrollListText*)cell)->setText( group_name );
+
+ addItem(item, pos);
+}
+
+BOOL LLNameListCtrl::addNameItem(LLScrollListItem* item, EAddPosition pos)
+{
+ //llinfos << "LLNameListCtrl::addNameItem " << item->getUUID() << llendl;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+
+ BOOL result = gCacheName->getName(item->getUUID(), first, last);
+
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+ ((LLScrollListText*)cell)->setText( fullname );
+
+ addItem(item, pos);
+
+ return result;
+}
+
+LLScrollListItem* LLNameListCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata)
+{
+ LLScrollListItem* item = LLScrollListCtrl::addElement(value, pos, userdata);
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+
+ gCacheName->getName(item->getUUID(), first, last);
+
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+ ((LLScrollListText*)cell)->setText( fullname );
+
+ return item;
+}
+
+// public
+void LLNameListCtrl::removeNameItem(const LLUUID& agent_id)
+{
+ BOOL item_exists = selectByID( agent_id );
+ if(item_exists)
+ {
+ S32 index = getItemIndex(getFirstSelected());
+ if(index >= 0)
+ {
+ deleteSingleItem(index);
+ }
+ }
+}
+
+// public
+void LLNameListCtrl::refresh(const LLUUID& id, const char* first,
+ const char* last,
+ BOOL is_group)
+{
+ //llinfos << "LLNameListCtrl::refresh " << id << " '" << first << " "
+ // << last << "'" << llendl;
+
+ LLString fullname;
+ fullname.assign(first);
+ if (last[0] && !is_group)
+ {
+ fullname.append(1, ' ');
+ fullname.append(last);
+ }
+
+ // TODO: scan items for that ID, fix if necessary
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getUUID() == id)
+ {
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(0);
+ cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+
+ ((LLScrollListText*)cell)->setText( fullname );
+ }
+ }
+}
+
+
+// static
+void LLNameListCtrl::refreshAll(const LLUUID& id, const char* first,
+ const char* last,
+ BOOL is_group)
+{
+ std::set<LLNameListCtrl*>::iterator it;
+ for (it = LLNameListCtrl::sInstances.begin();
+ it != LLNameListCtrl::sInstances.end();
+ ++it)
+ {
+ LLNameListCtrl* ctrl = *it;
+ ctrl->refresh(id, first, last, is_group);
+ }
+}
+
+// virtual
+LLXMLNodePtr LLNameListCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLScrollListCtrl::getXML();
+
+ node->createChild("allow_calling_card_drop", TRUE)->setBoolValue(mAllowCallingCardDrop);
+
+ if (mNameColumnIndex != 0)
+ {
+ node->createChild("name_column_index", TRUE)->setIntValue(mNameColumnIndex);
+ }
+
+ // Don't save contents, probably filled by code
+
+ return node;
+}
+
+LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("name_list");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL multi_select = FALSE;
+ node->getAttributeBOOL("multi_select", multi_select);
+
+ BOOL draw_border = TRUE;
+ node->getAttributeBOOL("draw_border", draw_border);
+
+ BOOL draw_heading = FALSE;
+ node->getAttributeBOOL("draw_heading", draw_heading);
+
+ BOOL collapse_empty_columns = FALSE;
+ node->getAttributeBOOL("collapse_empty_columns", collapse_empty_columns);
+
+ S32 name_column_index = 0;
+ node->getAttributeS32("name_column_index", name_column_index);
+
+ LLUICtrlCallback callback = NULL;
+
+ LLNameListCtrl* name_list = new LLNameListCtrl(name,
+ rect,
+ callback,
+ NULL,
+ multi_select,
+ draw_border,
+ name_column_index);
+
+ name_list->setDisplayHeading(draw_heading);
+ if (node->hasAttribute("heading_height"))
+ {
+ S32 heading_height;
+ node->getAttributeS32("heading_height", heading_height);
+ name_list->setHeadingHeight(heading_height);
+ }
+ if (node->hasAttribute("heading_font"))
+ {
+ LLString heading_font("");
+ node->getAttributeString("heading_font", heading_font);
+ LLFontGL* gl_font = LLFontGL::fontFromName(heading_font.c_str());
+ name_list->setHeadingFont(gl_font);
+ }
+ name_list->setCollapseEmptyColumns(collapse_empty_columns);
+
+ BOOL allow_calling_card_drop = FALSE;
+ if (node->getAttributeBOOL("allow_calling_card_drop", allow_calling_card_drop))
+ {
+ name_list->setAllowCallingCardDrop(allow_calling_card_drop);
+ }
+
+ name_list->setScrollListParameters(node);
+
+ name_list->initFromXML(node, parent);
+
+ LLSD columns;
+ S32 index = 0;
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("column"))
+ {
+ LLString labelname("");
+ child->getAttributeString("label", labelname);
+
+ LLString columnname(labelname);
+ child->getAttributeString("name", columnname);
+
+ if (child->hasAttribute("relwidth"))
+ {
+ F32 columnrelwidth = 0.f;
+ child->getAttributeF32("relwidth", columnrelwidth);
+ columns[index]["relwidth"] = columnrelwidth;
+ }
+ else
+ {
+ S32 columnwidth = -1;
+ child->getAttributeS32("width", columnwidth);
+ columns[index]["width"] = columnwidth;
+ }
+
+ columns[index]["name"] = columnname;
+ columns[index]["label"] = labelname;
+ index++;
+ }
+ }
+ name_list->setColumnHeadings(columns);
+
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("row"))
+ {
+ LLUUID id;
+ child->getAttributeUUID("id", id);
+
+ LLSD row;
+
+ row["id"] = id;
+
+ S32 column_idx = 0;
+ LLXMLNodePtr row_child;
+ for (row_child = node->getFirstChild(); row_child.notNull(); row_child = row_child->getNextSibling())
+ {
+ if (row_child->hasName("column"))
+ {
+ LLString value = row_child->getTextContents();
+
+ LLString columnname("");
+ row_child->getAttributeString("name", columnname);
+
+ LLString font("");
+ row_child->getAttributeString("font", font);
+
+ LLString font_style("");
+ row_child->getAttributeString("font-style", font_style);
+
+ row["columns"][column_idx]["column"] = columnname;
+ row["columns"][column_idx]["value"] = value;
+ row["columns"][column_idx]["font"] = font;
+ row["columns"][column_idx]["font-style"] = font_style;
+ column_idx++;
+ }
+ }
+ name_list->addElement(row);
+ }
+ }
+
+ LLString contents = node->getTextContents();
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("\t\n");
+ tokenizer tokens(contents, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ while(token_iter != tokens.end())
+ {
+ const char* line = token_iter->c_str();
+ name_list->addSimpleItem(line);
+ ++token_iter;
+ }
+
+ return name_list;
+}
+
diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h
new file mode 100644
index 0000000000..4e9069e2a3
--- /dev/null
+++ b/indra/newview/llnamelistctrl.h
@@ -0,0 +1,71 @@
+/**
+ * @file llnamelistctrl.h
+ * @brief A list of names, automatically refreshing from the name cache.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMELISTCTRL_H
+#define LL_LLNAMELISTCTRL_H
+
+#include <set>
+
+#include "llscrolllistctrl.h"
+
+
+class LLNameListCtrl
+: public LLScrollListCtrl
+{
+public:
+ LLNameListCtrl(const LLString& name,
+ const LLRect& rect,
+ LLUICtrlCallback callback,
+ void* userdata,
+ BOOL allow_multiple_selection,
+ BOOL draw_border = TRUE,
+ S32 name_column_index = 0,
+ const LLString& tooltip = LLString::null);
+ virtual ~LLNameListCtrl();
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_NAME_LIST; }
+ virtual LLString getWidgetTag() const { return LL_NAME_LIST_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ // Add a user to the list by name. It will be added, the name
+ // requested from the cache, and updated as necessary.
+ BOOL addNameItem(const LLUUID& agent_id, EAddPosition pos = ADD_BOTTOM,
+ BOOL enabled = TRUE, LLString& suffix = LLString::null);
+ BOOL addNameItem(LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM);
+
+ virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
+
+ // Add a user to the list by name. It will be added, the name
+ // requested from the cache, and updated as necessary.
+ void addGroupNameItem(const LLUUID& group_id, EAddPosition pos = ADD_BOTTOM,
+ BOOL enabled = TRUE);
+ void addGroupNameItem(LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM);
+
+
+ void removeNameItem(const LLUUID& agent_id);
+
+ void refresh(const LLUUID& id, const char* first, const char* last,
+ BOOL is_group);
+
+ static void refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group);
+
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg);
+
+ void setAllowCallingCardDrop(BOOL b) { mAllowCallingCardDrop = b; }
+
+private:
+ static std::set<LLNameListCtrl*> sInstances;
+ S32 mNameColumnIndex;
+ BOOL mAllowCallingCardDrop;
+};
+
+#endif
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
new file mode 100644
index 0000000000..178d707bb0
--- /dev/null
+++ b/indra/newview/llnetmap.cpp
@@ -0,0 +1,806 @@
+/**
+ * @file llnetmap.cpp
+ * @author James Cook
+ * @brief Display of surrounding regions, objects, and agents. View contained by LLFloaterMap.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llnetmap.h"
+
+#include "indra_constants.h"
+#include "llui.h"
+#include "linked_lists.h"
+#include "llmath.h" // clampf()
+#include "llfocusmgr.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "llfloaterworldmap.h"
+#include "llfloatermap.h"
+#include "llframetimer.h"
+#include "lltracker.h"
+#include "llmenugl.h"
+#include "llstatgraph.h"
+#include "llsurface.h"
+#include "lltextbox.h"
+#include "llviewercamera.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "llworldmapview.h" // shared draw code
+#include "viewer.h" // Only for constants!
+
+#include "llglheaders.h"
+
+const F32 MAP_SCALE_MIN = 64;
+const F32 MAP_SCALE_MID = 172;
+const F32 MAP_SCALE_MAX = 512;
+const F32 MAP_SCALE_INCREMENT = 16;
+
+const S32 TRACKING_RADIUS = 3;
+
+//static
+BOOL LLNetMap::sRotateMap = FALSE;
+LLNetMap* LLNetMap::sInstance = NULL;
+
+LLNetMap::LLNetMap(
+ const std::string& name,
+ const LLRect& rect,
+ const LLColor4& bg_color )
+ :
+ LLUICtrl(name, rect, FALSE, NULL, NULL), mBackgroundColor( bg_color ),
+ mObjectMapTPM(1.f),
+ mObjectMapPixels(255.f),
+ mTargetPanX( 0.f ),
+ mTargetPanY( 0.f ),
+ mCurPanX( 0.f ),
+ mCurPanY( 0.f ),
+ mUpdateNow( FALSE )
+{
+ mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS;
+
+ LLNetMap::sRotateMap = gSavedSettings.getBOOL( "MiniMapRotate" );
+
+ // Surface texture is dynamically generated/updated.
+// createObjectImage();
+
+ mObjectImageCenterGlobal = gAgent.getCameraPositionGlobal();
+
+ const S32 DIR_WIDTH = 10;
+ const S32 DIR_HEIGHT = 10;
+ LLRect major_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH, 0 );
+
+ mTextBoxNorth = new LLTextBox( "N", major_dir_rect );
+ mTextBoxNorth->setDropshadowVisible( TRUE );
+ addChild( mTextBoxNorth );
+
+ LLColor4 minor_color( 1.f, 1.f, 1.f, .7f );
+
+ mTextBoxEast = new LLTextBox( "E", major_dir_rect );
+ mTextBoxEast->setColor( minor_color );
+ addChild( mTextBoxEast );
+
+ mTextBoxWest = new LLTextBox( "W", major_dir_rect );
+ mTextBoxWest->setColor( minor_color );
+ addChild( mTextBoxWest );
+
+ mTextBoxSouth = new LLTextBox( "S", major_dir_rect );
+ mTextBoxSouth->setColor( minor_color );
+ addChild( mTextBoxSouth );
+
+ LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 );
+
+ mTextBoxSouthEast = new LLTextBox( "SE", minor_dir_rect );
+ mTextBoxSouthEast->setColor( minor_color );
+ addChild( mTextBoxSouthEast );
+
+ mTextBoxNorthEast = new LLTextBox( "NE", minor_dir_rect );
+ mTextBoxNorthEast->setColor( minor_color );
+ addChild( mTextBoxNorthEast );
+
+ mTextBoxSouthWest = new LLTextBox( "SW", minor_dir_rect );
+ mTextBoxSouthWest->setColor( minor_color );
+ addChild( mTextBoxSouthWest );
+
+ mTextBoxNorthWest = new LLTextBox( "NW", minor_dir_rect );
+ mTextBoxNorthWest->setColor( minor_color );
+ addChild( mTextBoxNorthWest );
+
+ // Right-click menu
+ LLMenuGL* menu;
+ menu = new LLMenuGL("popup");
+ menu->setCanTearOff(FALSE);
+ menu->append(new LLMenuItemCallGL("Zoom Close", handleZoomLevel,
+ NULL, (void*)2) );
+ menu->append(new LLMenuItemCallGL("Zoom Medium", handleZoomLevel,
+ NULL, (void*)1) );
+ menu->append(new LLMenuItemCallGL("Zoom Far", handleZoomLevel,
+ NULL, (void*)0) );
+ menu->appendSeparator();
+ menu->append(new LLMenuItemCallGL("Stop Tracking", &LLTracker::stopTracking,
+ &LLTracker::isTracking, NULL) );
+ menu->setVisible(FALSE);
+ addChild(menu);
+ mPopupMenuHandle = menu->mViewHandle;
+
+ sInstance = this;
+
+ gSavedSettings.getControl("MiniMapRotate")->addListener(&mNetMapListener);
+}
+
+LLNetMap::~LLNetMap()
+{
+ sInstance = NULL;
+}
+
+EWidgetType LLNetMap::getWidgetType() const
+{
+ return WIDGET_TYPE_NET_MAP;
+}
+
+LLString LLNetMap::getWidgetTag() const
+{
+ return LL_NET_MAP_TAG;
+}
+
+
+void LLNetMap::setScale( F32 scale )
+{
+ gMiniMapScale = scale;
+ if (gMiniMapScale == 0.f)
+ {
+ gMiniMapScale = 0.1f;
+ }
+
+ if (mObjectImagep.notNull())
+ {
+ F32 half_width = (F32)(mRect.getWidth() / 2);
+ F32 half_height = (F32)(mRect.getHeight() / 2);
+ F32 radius = sqrt( half_width * half_width + half_height * half_height );
+
+ F32 region_widths = (2.f*radius)/gMiniMapScale;
+
+ F32 meters;
+ if (!gWorldPointer)
+ {
+ // Hack! Sometimes world hasn't been initialized at this point.
+ meters = 256.f*region_widths;
+ }
+ else
+ {
+ meters = region_widths * gWorldPointer->getRegionWidthInMeters();
+ }
+
+ F32 num_pixels = (F32)mObjectImagep->getWidth();
+ mObjectMapTPM = num_pixels/meters;
+ mObjectMapPixels = 2.f*radius;
+ }
+
+ mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS;
+
+ mUpdateNow = TRUE;
+}
+
+void LLNetMap::translatePan( F32 delta_x, F32 delta_y )
+{
+ mTargetPanX += delta_x;
+ mTargetPanY += delta_y;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////
+
+void LLNetMap::draw()
+{
+ static LLFrameTimer map_timer;
+
+ if (!getVisible() || !gWorldPointer)
+ {
+ return;
+ }
+ if (mObjectImagep.isNull())
+ {
+ createObjectImage();
+ }
+
+ mCurPanX = lerp(mCurPanX, mTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
+ mCurPanY = lerp(mCurPanY, mTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
+
+ // Prepare a scissor region
+ // GLint params[4];
+ // glGetIntegerv( GL_SCISSOR_BOX, params );
+ F32 rotation = 0;
+
+ {
+ LLGLEnable scissor(GL_SCISSOR_TEST);
+
+ {
+ LLGLSNoTexture no_texture;
+ LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+
+ glMatrixMode(GL_MODELVIEW);
+
+ // Draw background rectangle
+ glColor4fv( mBackgroundColor.mV );
+ gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0);
+ }
+
+ // region 0,0 is in the middle
+ S32 center_sw_left = mRect.getWidth() / 2 + llfloor(mCurPanX);
+ S32 center_sw_bottom = mRect.getHeight() / 2 + llfloor(mCurPanY);
+
+ glPushMatrix();
+
+ glTranslatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f);
+
+ if( LLNetMap::sRotateMap )
+ {
+ // rotate subsequent draws to agent rotation
+ rotation = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] );
+ glRotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f);
+ }
+
+ // figure out where agent is
+ S32 region_width = llround(gWorldPointer->getRegionWidthInMeters());
+
+ LLViewerRegion *regionp;
+
+ for (regionp = gWorldPointer->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldPointer->mActiveRegionList.getNextData())
+ {
+ // Find x and y position relative to camera's center.
+ LLVector3 origin_agent = regionp->getOriginAgent();
+ LLVector3 rel_region_pos = origin_agent - gAgent.getCameraPositionAgent();
+ F32 relative_x = (rel_region_pos.mV[0] / region_width) * gMiniMapScale;
+ F32 relative_y = (rel_region_pos.mV[1] / region_width) * gMiniMapScale;
+
+ // background region rectangle
+ F32 bottom = relative_y;
+ F32 left = relative_x;
+ F32 top = bottom + gMiniMapScale ;
+ F32 right = left + gMiniMapScale ;
+
+ if (regionp == gAgent.getRegion())
+ {
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.8f, 0.8f, 0.8f, 1.f);
+ }
+
+ if (!regionp->mAlive)
+ {
+ glColor4f(1.f, 0.5f, 0.5f, 1.f);
+ }
+
+
+ // Draw using texture.
+ LLViewerImage::bindTexture(regionp->getLand().getSTexture());
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex2f(left, top);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(left, bottom);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2f(right, bottom);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2f(right, top);
+ glEnd();
+
+ // Draw water
+ glAlphaFunc(GL_GREATER, ABOVE_WATERLINE_ALPHA / 255.f );
+ {
+ if (regionp->getLand().getWaterTexture())
+ {
+ LLViewerImage::bindTexture(regionp->getLand().getWaterTexture());
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex2f(left, top);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(left, bottom);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2f(right, bottom);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2f(right, top);
+ glEnd();
+ }
+ }
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+
+ LLVector3d old_center = mObjectImageCenterGlobal;
+ LLVector3d new_center = gAgent.getCameraPositionGlobal();
+
+ new_center.mdV[0] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[0]);
+ new_center.mdV[1] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[1]);
+ new_center.mdV[2] = 0.f;
+
+ if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f))
+ {
+ mUpdateNow = FALSE;
+ mObjectImageCenterGlobal = new_center;
+
+ // Center moved enough.
+ // Create the base texture.
+ U8 *default_texture = mObjectRawImagep->getData();
+ memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() );
+
+ // Draw buildings
+ gObjectList.renderObjectsForMap(*this);
+
+ mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight());
+
+ map_timer.reset();
+ }
+
+ LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal);
+ map_center_agent -= gAgent.getCameraPositionAgent();
+ map_center_agent.mV[VX] *= gMiniMapScale/region_width;
+ map_center_agent.mV[VY] *= gMiniMapScale/region_width;
+
+ LLViewerImage::bindTexture(mObjectImagep);
+ F32 image_half_width = 0.5f*mObjectMapPixels;
+ F32 image_half_height = 0.5f*mObjectMapPixels;
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]);
+ glEnd();
+
+ glPopMatrix();
+
+ LLVector3d pos_global;
+ LLVector3 pos_map;
+
+ // Draw avatars
+ for (regionp = gWorldPointer->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldPointer->mActiveRegionList.getNextData())
+ {
+ const LLVector3d& origin_global = regionp->getOriginGlobal();
+
+ S32 count = regionp->mMapAvatars.count();
+ S32 i;
+ LLVector3 pos_local;
+ U32 compact_local;
+ U8 bits;
+ for (i = 0; i < count; i++)
+ {
+ compact_local = regionp->mMapAvatars.get(i);
+
+ bits = compact_local & 0xFF;
+ pos_local.mV[VZ] = F32(bits) * 4.f;
+ compact_local >>= 8;
+
+ bits = compact_local & 0xFF;
+ pos_local.mV[VY] = (F32)bits;
+ compact_local >>= 8;
+
+ bits = compact_local & 0xFF;
+ pos_local.mV[VX] = (F32)bits;
+
+ pos_global.setVec( pos_local );
+ pos_global += origin_global;
+
+ pos_map = globalPosToView(pos_global);
+ LLWorldMapView::drawAvatar(pos_map.mV[VX], pos_map.mV[VY],
+ gAvatarMapColor,
+ pos_map.mV[VZ]);
+ }
+ }
+
+ // Draw dot for autopilot target
+ if (gAgent.getAutoPilot())
+ {
+ drawTracking( gAgent.getAutoPilotTargetGlobal(), gTrackColor );
+ }
+ else
+ {
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if ( LLTracker::TRACKING_AVATAR == tracking_status )
+ {
+ drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor );
+ }
+ else if ( LLTracker::TRACKING_LANDMARK == tracking_status
+ || LLTracker::TRACKING_LOCATION == tracking_status )
+ {
+ drawTracking( LLTracker::getTrackedPositionGlobal(), gTrackColor );
+ }
+ }
+
+ // Draw dot for self avatar position
+ //drawTracking( gAgent.getPosGlobalFromAgent(gAgent.getFrameAgent().getCenter()), gSelfMapColor );
+ pos_global = gAgent.getPositionGlobal();
+ pos_map = globalPosToView(pos_global);
+ gl_draw_image(llround(pos_map.mV[VX]) - 4,
+ llround(pos_map.mV[VY]) - 4,
+ LLWorldMapView::sAvatarYouSmallImage,
+ LLColor4::white);
+
+ // Draw frustum
+ F32 meters_to_pixels = gMiniMapScale/ gWorldPointer->getRegionWidthInMeters();
+
+ F32 horiz_fov = gCamera->getView() * gCamera->getAspect();
+ F32 far_clip_meters = gCamera->getFar();
+ F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
+
+ F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
+ F32 half_width_pixels = half_width_meters * meters_to_pixels;
+
+ F32 ctr_x = (F32)center_sw_left;
+ F32 ctr_y = (F32)center_sw_bottom;
+
+
+ LLGLSNoTexture no_texture;
+
+ if( LLNetMap::sRotateMap )
+ {
+ glColor4fv(gFrustumMapColor.mV);
+
+ glBegin( GL_TRIANGLES );
+ glVertex2f( ctr_x, ctr_y );
+ glVertex2f( ctr_x - half_width_pixels, ctr_y + far_clip_pixels );
+ glVertex2f( ctr_x + half_width_pixels, ctr_y + far_clip_pixels );
+ glEnd();
+ }
+ else
+ {
+ glColor4fv(gRotatingFrustumMapColor.mV);
+
+ // If we don't rotate the map, we have to rotate the frustum.
+ glPushMatrix();
+ glTranslatef( ctr_x, ctr_y, 0 );
+ glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
+ glBegin( GL_TRIANGLES );
+ glVertex2f( 0, 0 );
+ glVertex2f( -half_width_pixels, far_clip_pixels );
+ glVertex2f( half_width_pixels, far_clip_pixels );
+ glEnd();
+ glPopMatrix();
+ }
+ }
+
+ // Rotation of 0 means that North is up
+ setDirectionPos( mTextBoxEast, rotation );
+ setDirectionPos( mTextBoxNorth, rotation + F_PI_BY_TWO );
+ setDirectionPos( mTextBoxWest, rotation + F_PI );
+ setDirectionPos( mTextBoxSouth, rotation + F_PI + F_PI_BY_TWO );
+
+ setDirectionPos( mTextBoxNorthEast, rotation + F_PI_BY_TWO / 2);
+ setDirectionPos( mTextBoxNorthWest, rotation + F_PI_BY_TWO + F_PI_BY_TWO / 2);
+ setDirectionPos( mTextBoxSouthWest, rotation + F_PI + F_PI_BY_TWO / 2);
+ setDirectionPos( mTextBoxSouthEast, rotation + F_PI + F_PI_BY_TWO + F_PI_BY_TWO / 2);
+
+ LLUICtrl::draw();
+}
+
+LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos )
+{
+ LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal();
+ LLVector3 pos_local;
+ pos_local.setVec(relative_pos_global); // convert to floats from doubles
+
+ pos_local.mV[VX] *= mPixelsPerMeter;
+ pos_local.mV[VY] *= mPixelsPerMeter;
+ // leave Z component in meters
+
+ if( LLNetMap::sRotateMap )
+ {
+ F32 radians = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] );
+ LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f));
+ pos_local.rotVec( rot );
+ }
+
+ pos_local.mV[VX] += mRect.getWidth() / 2 + mCurPanX;
+ pos_local.mV[VY] += mRect.getHeight() / 2 + mCurPanY;
+
+ return pos_local;
+}
+
+void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color,
+ BOOL draw_arrow )
+{
+ LLVector3 pos_local = globalPosToView( pos_global );
+ if( (pos_local.mV[VX] < 0) ||
+ (pos_local.mV[VY] < 0) ||
+ (pos_local.mV[VX] >= mRect.getWidth()) ||
+ (pos_local.mV[VY] >= mRect.getHeight()) )
+ {
+ if (draw_arrow)
+ {
+ S32 x = llround( pos_local.mV[VX] );
+ S32 y = llround( pos_local.mV[VY] );
+ LLWorldMapView::drawTrackingCircle( mRect, x, y, color, 1, 10 );
+ LLWorldMapView::drawTrackingArrow( mRect, x, y, color );
+ }
+ }
+ else
+ {
+ LLWorldMapView::drawTrackingDot(pos_local.mV[VX],
+ pos_local.mV[VY],
+ color,
+ pos_local.mV[VZ]);
+ }
+}
+
+LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
+{
+ x -= llround(mRect.getWidth() / 2 + mCurPanX);
+ y -= llround(mRect.getHeight() / 2 + mCurPanY);
+
+ LLVector3 pos_local( (F32)x, (F32)y, 0 );
+
+ F32 radians = - atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] );
+
+ if( LLNetMap::sRotateMap )
+ {
+ LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f));
+ pos_local.rotVec( rot );
+ }
+
+ pos_local *= ( gWorldPointer->getRegionWidthInMeters() / gMiniMapScale );
+
+ LLVector3d pos_global;
+ pos_global.setVec( pos_local );
+ pos_global += gAgent.getCameraPositionGlobal();
+
+ return pos_global;
+}
+
+BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ // note that clicks are reversed from what you'd think
+ setScale(llclamp(gMiniMapScale - clicks*MAP_SCALE_INCREMENT, MAP_SCALE_MIN, MAP_SCALE_MAX));
+ return TRUE;
+}
+
+BOOL LLNetMap::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen )
+{
+ BOOL handled = FALSE;
+ if (gDisconnected)
+ {
+ return FALSE;
+ }
+ if( getVisible() && pointInView( x, y ) )
+ {
+ LLViewerRegion* region = gWorldPointer->getRegionFromPosGlobal( viewPosToGlobal( x, y ) );
+ if( region )
+ {
+ msg.assign( region->getName() );
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ char buffer[MAX_STRING];
+ msg.append("\n");
+ region->getHost().getHostName(buffer, MAX_STRING);
+ msg.append(buffer);
+ msg.append("\n");
+ region->getHost().getString(buffer, MAX_STRING);
+ msg.append(buffer);
+#endif
+ // FIXME: put in XML so it can be translated
+ msg.append("\n(Double-click to open Map)");
+
+ S32 SLOP = 4;
+ localPointToScreen(
+ x - SLOP, y - SLOP,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
+ sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
+ }
+ handled = TRUE;
+ }
+ return handled;
+}
+
+
+void LLNetMap::setDirectionPos( LLTextBox* text_box, F32 rotation )
+{
+ // Rotation is in radians.
+ // Rotation of 0 means x = 1, y = 0 on the unit circle.
+
+
+ F32 map_half_height = (F32)(mRect.getHeight() / 2);
+ F32 map_half_width = (F32)(mRect.getWidth() / 2);
+ F32 text_half_height = (F32)(text_box->getRect().getHeight() / 2);
+ F32 text_half_width = (F32)(text_box->getRect().getWidth() / 2);
+ F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width );
+
+ // Inset by a little to account for position display.
+ radius -= 8.f;
+
+ text_box->setOrigin(
+ llround(map_half_width - text_half_width + radius * cos( rotation )),
+ llround(map_half_height - text_half_height + radius * sin( rotation )) );
+}
+
+void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters )
+{
+ LLVector3 local_pos;
+ local_pos.setVec( pos - mObjectImageCenterGlobal );
+
+ S32 diameter_pixels = llround(2 * radius_meters * mObjectMapTPM);
+ renderPoint( local_pos, color, diameter_pixels );
+}
+
+
+void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color,
+ S32 diameter, S32 relative_height)
+{
+ if (diameter <= 0)
+ {
+ return;
+ }
+
+ const S32 image_width = (S32)mObjectImagep->getWidth();
+ const S32 image_height = (S32)mObjectImagep->getHeight();
+
+ S32 x_offset = llround(pos_local.mV[VX] * mObjectMapTPM + image_width / 2);
+ S32 y_offset = llround(pos_local.mV[VY] * mObjectMapTPM + image_height / 2);
+
+ if ((x_offset < 0) || (x_offset >= image_width))
+ {
+ return;
+ }
+ if ((y_offset < 0) || (y_offset >= image_height))
+ {
+ return;
+ }
+
+ U8 *datap = mObjectRawImagep->getData();
+
+ S32 neg_radius = diameter / 2;
+ S32 pos_radius = diameter - neg_radius;
+ S32 x, y;
+
+ if (relative_height > 0)
+ {
+ // ...point above agent
+ S32 px, py;
+
+ // vertical line
+ px = x_offset;
+ for (y = -neg_radius; y < pos_radius; y++)
+ {
+ py = y_offset + y;
+ if ((py < 0) || (py >= image_height))
+ {
+ continue;
+ }
+ S32 offset = px + py * image_width;
+ ((U32*)datap)[offset] = color.mAll;
+ }
+
+ // top line
+ py = y_offset + pos_radius - 1;
+ for (x = -neg_radius; x < pos_radius; x++)
+ {
+ px = x_offset + x;
+ if ((px < 0) || (px >= image_width))
+ {
+ continue;
+ }
+ S32 offset = px + py * image_width;
+ ((U32*)datap)[offset] = color.mAll;
+ }
+ }
+ else
+ {
+ // ...point level with agent
+ for (x = -neg_radius; x < pos_radius; x++)
+ {
+ S32 p_x = x_offset + x;
+ if ((p_x < 0) || (p_x >= image_width))
+ {
+ continue;
+ }
+
+ for (y = -neg_radius; y < pos_radius; y++)
+ {
+ S32 p_y = y_offset + y;
+ if ((p_y < 0) || (p_y >= image_height))
+ {
+ continue;
+ }
+ S32 offset = p_x + p_y * image_width;
+ ((U32*)datap)[offset] = color.mAll;
+ }
+ }
+ }
+}
+
+void LLNetMap::createObjectImage()
+{
+ // Find the size of the side of a square that surrounds the circle that surrounds mRect.
+ F32 half_width = (F32)(mRect.getWidth() / 2);
+ F32 half_height = (F32)(mRect.getHeight() / 2);
+ F32 radius = sqrt( half_width * half_width + half_height * half_height );
+ S32 square_size = S32( 2 * radius );
+
+ // Find the least power of two >= the minimum size.
+ const S32 MIN_SIZE = 32;
+ const S32 MAX_SIZE = 256;
+ S32 img_size = MIN_SIZE;
+ while( (img_size*2 < square_size ) && (img_size < MAX_SIZE) )
+ {
+ img_size <<= 1;
+ }
+
+ if( mObjectImagep.isNull() ||
+ (mObjectImagep->getWidth() != img_size) ||
+ (mObjectImagep->getHeight() != img_size) )
+ {
+ mObjectRawImagep = new LLImageRaw(img_size, img_size, 4);
+ U8* data = mObjectRawImagep->getData();
+ memset( data, 0, img_size * img_size * 4 );
+ mObjectImagep = new LLImageGL( mObjectRawImagep, FALSE);
+ setScale(gMiniMapScale);
+ }
+ mUpdateNow = TRUE;
+}
+
+BOOL LLNetMap::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ LLFloaterWorldMap::show(NULL, FALSE);
+ return TRUE;
+}
+
+BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ if (menu)
+ {
+ menu->buildDrawLabels();
+ menu->updateParent(gMenuHolder);
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+ return TRUE;
+}
+
+
+// static
+void LLNetMap::handleZoomLevel(void* which)
+{
+ intptr_t level = (intptr_t)which;
+
+ switch(level)
+ {
+ case 0:
+ LLNetMap::sInstance->setScale(MAP_SCALE_MIN);
+ break;
+ case 1:
+ LLNetMap::sInstance->setScale(MAP_SCALE_MID);
+ break;
+ case 2:
+ LLNetMap::sInstance->setScale(MAP_SCALE_MAX);
+ break;
+ default:
+ break;
+ }
+}
+
+bool LLRotateNetMapListener::handleEvent(LLPointer<LLEvent> event, const LLSD& user_data)
+{
+ LLNetMap::setRotateMap(event->getValue().asBoolean());
+ return true;
+}
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
new file mode 100644
index 0000000000..bd5c47008b
--- /dev/null
+++ b/indra/newview/llnetmap.h
@@ -0,0 +1,104 @@
+/**
+ * @file llnetmap.h
+ * @brief A little map of the world with network information
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNETMAP_H
+#define LL_LLNETMAP_H
+
+#include "llmath.h"
+#include "lluictrl.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4color.h"
+#include "llimage.h"
+#include "llimagegl.h"
+
+class LLColor4U;
+class LLCoordGL;
+class LLTextBox;
+class LLMenuGL;
+
+class LLRotateNetMapListener : public LLSimpleListener
+{
+public:
+ bool handleEvent(LLPointer<LLEvent>, const LLSD& user_data);
+};
+
+class LLNetMap : public LLUICtrl
+{
+public:
+ LLNetMap(const std::string& name, const LLRect& rect, const LLColor4& bg_color );
+ virtual ~LLNetMap();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void draw();
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen );
+
+ void setScale( F32 scale );
+ void translatePan( F32 delta_x, F32 delta_y );
+ void setPan( F32 x, F32 y ) { mTargetPanX = x; mTargetPanY = y; }
+
+ const LLVector3d& getObjectImageCenterGlobal() { return mObjectImageCenterGlobal; }
+ void renderPoint(const LLVector3 &pos, const LLColor4U &color,
+ S32 diameter, S32 relative_height = 0);
+ void renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius );
+
+ LLVector3 globalPosToView(const LLVector3d& global_pos);
+ LLVector3d viewPosToGlobal(S32 x,S32 y);
+
+ static void setRotateMap( BOOL b ) { LLNetMap::sRotateMap = b; }
+ static void handleZoomLevel(void* which);
+
+ void drawTracking( const LLVector3d& pos_global,
+ const LLColor4& color,
+ BOOL draw_arrow = TRUE);
+
+protected:
+ void setDirectionPos( LLTextBox* text_box, F32 rotation );
+ void createObjectImage();
+ static void teleport( const LLVector3d& destination );
+ static void fly( const LLVector3d& destination );
+
+public:
+ LLViewHandle mPopupMenuHandle;
+ LLColor4 mBackgroundColor;
+
+ F32 mScale; // Size of a region in pixels
+ F32 mPixelsPerMeter; // world meters to map pixels
+ F32 mObjectMapTPM; // texels per meter on map
+ F32 mObjectMapPixels; // Width of object map in pixels;
+ F32 mTargetPanX;
+ F32 mTargetPanY;
+ F32 mCurPanX;
+ F32 mCurPanY;
+ BOOL mUpdateNow;
+ LLVector3d mObjectImageCenterGlobal;
+ LLPointer<LLImageRaw> mObjectRawImagep;
+ LLPointer<LLImageGL> mObjectImagep;
+ LLTextBox* mTextBoxEast;
+ LLTextBox* mTextBoxNorth;
+ LLTextBox* mTextBoxWest;
+ LLTextBox* mTextBoxSouth;
+
+ LLTextBox* mTextBoxSouthEast;
+ LLTextBox* mTextBoxNorthEast;
+ LLTextBox* mTextBoxNorthWest;
+ LLTextBox* mTextBoxSouthWest;
+
+ LLRotateNetMapListener mNetMapListener;
+
+ static BOOL sRotateMap;
+ static LLNetMap* sInstance;
+};
+
+
+#endif
diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp
new file mode 100644
index 0000000000..ed99cbe855
--- /dev/null
+++ b/indra/newview/lloverlaybar.cpp
@@ -0,0 +1,573 @@
+/**
+ * @file lloverlaybar.cpp
+ * @brief LLOverlayBar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Temporary buttons that appear at the bottom of the screen when you
+// are in a mode.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lloverlaybar.h"
+
+#include "audioengine.h"
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llvoavatar.h"
+#include "llmediaengine.h"
+#include "viewer.h"
+#include "llui.h"
+#include "llviewermenu.h" // handle_reset_view()
+#include "llviewerparcelmgr.h"
+#include "llwebbrowserctrl.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llfocusmgr.h"
+
+//
+// Globals
+//
+
+LLOverlayBar *gOverlayBar = NULL;
+
+extern S32 MENU_BAR_HEIGHT;
+
+//
+// Functions
+//
+
+
+//static
+void* LLOverlayBar::createMediaRemote(void* userdata)
+{
+
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+
+
+ self->mMediaRemote = new LLMediaRemoteCtrl ( "media_remote",
+ "media",
+ LLRect(),
+ "panel_media_remote.xml");
+ return self->mMediaRemote;
+}
+
+
+
+void* LLOverlayBar::createMusicRemote(void* userdata)
+{
+
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+
+ self->mMusicRemote = new LLMediaRemoteCtrl ( "music_remote",
+ "music",
+ LLRect(),
+ "panel_music_remote.xml" );
+ return self->mMusicRemote;
+}
+
+
+
+
+LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
+: LLPanel(name, rect, FALSE) // not bordered
+{
+ setMouseOpaque(FALSE);
+ setIsChrome(TRUE);
+
+ isBuilt = FALSE;
+
+ LLCallbackMap::map_t factory_map;
+ factory_map["media_remote"] = LLCallbackMap(LLOverlayBar::createMediaRemote, this);
+ factory_map["music_remote"] = LLCallbackMap(LLOverlayBar::createMusicRemote, this);
+
+ gUICtrlFactory->buildPanel(this, "panel_overlaybar.xml", &factory_map);
+
+ childSetAction("IM Received",onClickIMReceived,this);
+ childSetAction("Set Not Busy",onClickSetNotBusy,this);
+ childSetAction("Release Keys",onClickReleaseKeys,this);
+ childSetAction("Mouselook",onClickMouselook,this);
+ childSetAction("Stand Up",onClickStandUp,this);
+
+ mMusicRemote->addObserver ( this );
+
+ if ( gAudiop )
+ {
+ mMusicRemote->setVolume ( gSavedSettings.getF32 ( "AudioLevelMusic" ) );
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ };
+
+ mIsFocusRoot = TRUE;
+
+ mMediaRemote->addObserver ( this );
+ mMediaRemote->setVolume ( gSavedSettings.getF32 ( "MediaAudioVolume" ) );
+
+ isBuilt = true;
+
+ layoutButtons();
+}
+
+LLOverlayBar::~LLOverlayBar()
+{
+ // LLView destructor cleans up children
+
+ mMusicRemote->remObserver ( this );
+ mMediaRemote->remObserver ( this );
+}
+
+EWidgetType LLOverlayBar::getWidgetType() const
+{
+ return WIDGET_TYPE_OVERLAY_BAR;
+}
+
+LLString LLOverlayBar::getWidgetTag() const
+{
+ return LL_OVERLAY_BAR_TAG;
+}
+
+// virtual
+void LLOverlayBar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLView::reshape(width, height, called_from_parent);
+
+ if (isBuilt)
+ {
+ layoutButtons();
+ }
+}
+
+
+void LLOverlayBar::layoutButtons()
+{
+ S32 width = mRect.getWidth();
+ if (width > 800) width = 800;
+
+ S32 count = getChildCount();
+ const S32 PAD = gSavedSettings.getS32("StatusBarPad");
+
+ F32 segment_width = (F32)(width) / (F32)count;
+
+ S32 btn_width = lltrunc(segment_width - PAD);
+
+ S32 remote_width = mMusicRemote->getRect().getWidth();
+
+ // Evenly space all views
+ LLRect r;
+ S32 i = 0;
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ r = view->getRect();
+ r.mLeft = (width) - llround((i+1)*segment_width);
+ r.mRight = r.mLeft + btn_width;
+ view->setRect(r);
+ i++;
+ }
+
+ // Fix up remotes to have constant width because they can't shrink
+ r = mMusicRemote->getRect();
+ r.mRight = r.mLeft + remote_width;
+ mMusicRemote->setRect(r);
+
+ r = mMediaRemote->getRect();
+ r.mLeft = mMusicRemote->getRect().mRight + PAD;
+ r.mRight = r.mLeft + remote_width;
+ mMediaRemote->setRect(r);
+
+ updateRect();
+}
+
+void LLOverlayBar::draw()
+{
+ // retrieve rounded rect image
+ LLUUID image_id;
+ image_id.set(gViewerArt.getString("rounded_square.tga"));
+ LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ if (imagep)
+ {
+ LLGLSTexture texture_enabled;
+ LLViewerImage::bindTexture(imagep);
+
+ const S32 PAD = gSavedSettings.getS32("StatusBarPad");
+
+ // draw rounded rect tabs behind all children
+ LLRect r;
+ // focus highlights
+ LLColor4 color = gColors.getColor("FloaterFocusBorderColor");
+ glColor4fv(color.mV);
+ if(gFocusMgr.childHasKeyboardFocus(gBottomPanel))
+ {
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if(view->getEnabled() && view->getVisible())
+ {
+ r = view->getRect();
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3 - 1,
+ r.mTop + 3,
+ r.mRight + PAD/3 + 1,
+ r.mBottom,
+ imagep->getWidth(),
+ imagep->getHeight(),
+ 16,
+ ROUNDED_RECT_TOP);
+ }
+ }
+ }
+
+ // main tabs
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if(view->getEnabled() && view->getVisible())
+ {
+ r = view->getRect();
+ // draw a nice little pseudo-3D outline
+ color = gColors.getColor("DefaultShadowDark");
+ glColor4fv(color.mV);
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3 + 1, r.mTop + 2, r.mRight + PAD/3, r.mBottom,
+ imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP);
+ color = gColors.getColor("DefaultHighlightLight");
+ glColor4fv(color.mV);
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3, r.mTop + 2, r.mRight + PAD/3 - 3, r.mBottom,
+ imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP);
+ // here's the main background. Note that it overhangs on the bottom so as to hide the
+ // focus highlight on the bottom panel, thus producing the illusion that the focus highlight
+ // continues around the tabs
+ color = gColors.getColor("FocusBackgroundColor");
+ glColor4fv(color.mV);
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3 + 1, r.mTop + 1, r.mRight + PAD/3 - 1, r.mBottom - 1,
+ imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP);
+ }
+ }
+ }
+
+ // draw children on top
+ LLPanel::draw();
+}
+
+
+// Per-frame updates of visibility
+void LLOverlayBar::refresh()
+{
+ BOOL im_received = gIMView->getIMReceived();
+ childSetVisible("IM Received", im_received);
+ childSetEnabled("IM Received", im_received);
+
+ BOOL busy = gAgent.getBusy();
+ childSetVisible("Set Not Busy", busy);
+ childSetEnabled("Set Not Busy", busy);
+
+ BOOL controls_grabbed = gAgent.anyControlGrabbed();
+
+ childSetVisible("Release Keys", controls_grabbed);
+ childSetEnabled("Release Keys", controls_grabbed);
+
+
+ BOOL mouselook_grabbed;
+ mouselook_grabbed = gAgent.isControlGrabbed(CONTROL_ML_LBUTTON_DOWN_INDEX)
+ || gAgent.isControlGrabbed(CONTROL_ML_LBUTTON_UP_INDEX);
+
+
+ childSetVisible("Mouselook", mouselook_grabbed);
+ childSetEnabled("Mouselook", mouselook_grabbed);
+
+ BOOL sitting = FALSE;
+ if (gAgent.getAvatarObject())
+ {
+ sitting = gAgent.getAvatarObject()->mIsSitting;
+ childSetVisible("Stand Up", sitting);
+ childSetEnabled("Stand Up", sitting);
+
+ }
+
+ if ( gAudiop )
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (!parcel
+ || !parcel->getMusicURL()
+ || !parcel->getMusicURL()[0]
+ || !gSavedSettings.getBOOL("AudioStreamingMusic"))
+ {
+ mMusicRemote->setVisible(FALSE);
+ mMusicRemote->setEnabled(FALSE);
+ }
+ else
+ {
+ mMusicRemote->setVisible(TRUE);
+ mMusicRemote->setEnabled(TRUE);
+
+ S32 musicPlaying = gAudiop->isInternetStreamPlaying();
+
+ if ( musicPlaying == 0 ) // stopped
+ {
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ else
+ if ( musicPlaying == 1 ) // playing
+ {
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ if (gAudiop)
+ {
+ gAudiop->setInternetStreamGain ( gSavedSettings.getF32 ( "AudioLevelMusic" ) );
+ }
+ }
+ else
+ if ( musicPlaying == 2 ) // paused
+ {
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ }
+ }
+
+ // if there is a url and a texture and media is enabled and available and media streaming is on... (phew!)
+ if ( LLMediaEngine::getInstance () &&
+ LLMediaEngine::getInstance ()->getUrl ().length () &&
+ LLMediaEngine::getInstance ()->getImageUUID ().notNull () &&
+ LLMediaEngine::getInstance ()->isEnabled () &&
+ LLMediaEngine::getInstance ()->isAvailable () &&
+ gSavedSettings.getBOOL ( "AudioStreamingVideo" ) )
+ {
+ // display remote control
+ mMediaRemote->setVisible ( TRUE );
+ mMediaRemote->setEnabled ( TRUE );
+
+ if ( LLMediaEngine::getInstance ()->getMediaRenderer () )
+ {
+ if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPlaying () ||
+ LLMediaEngine::getInstance ()->getMediaRenderer ()->isLooping () )
+ {
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
+ }
+ else
+ if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPaused () )
+ {
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
+ }
+ else
+ {
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
+ };
+ };
+ }
+ else
+ {
+ mMediaRemote->setVisible ( FALSE );
+ mMediaRemote->setEnabled ( FALSE );
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
+ };
+
+ BOOL any_button = (childIsVisible("IM Received")
+ || childIsVisible("Set Not Busy")
+ || childIsVisible("Release Keys")
+ || childIsVisible("Mouselook")
+ || childIsVisible("Stand Up")
+ || mMusicRemote->getVisible()
+ || mMediaRemote->getVisible() );
+
+
+ // turn off the whole bar in mouselook
+ if (gAgent.cameraMouselook())
+ {
+ setVisible(FALSE);
+ }
+ else
+ {
+ setVisible(any_button);
+ };
+}
+
+//-----------------------------------------------------------------------
+// Static functions
+//-----------------------------------------------------------------------
+
+// static
+void LLOverlayBar::onClickIMReceived(void*)
+{
+ gIMView->setFloaterOpen(TRUE);
+}
+
+
+// static
+void LLOverlayBar::onClickSetNotBusy(void*)
+{
+ gAgent.clearBusy();
+}
+
+
+// static
+void LLOverlayBar::onClickReleaseKeys(void*)
+{
+ gAgent.forceReleaseControls();
+}
+
+// static
+void LLOverlayBar::onClickResetView(void* data)
+{
+ handle_reset_view();
+}
+
+//static
+void LLOverlayBar::onClickMouselook(void*)
+{
+ gAgent.changeCameraToMouselook();
+}
+
+//static
+void LLOverlayBar::onClickStandUp(void*)
+{
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void
+LLOverlayBar::
+onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+ F32 value = eventIn.getValue ();
+
+ if ( control == mMusicRemote )
+ {
+ if (gAudiop)
+ {
+ gAudiop->setInternetStreamGain ( value );
+ };
+ gSavedSettings.setF32 ( "AudioLevelMusic", value );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLMediaEngine::getInstance ()->setVolume ( value );
+ gSavedSettings.setF32 ( "MediaAudioVolume", value );
+
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void
+LLOverlayBar::
+onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+
+ if ( control == mMusicRemote )
+ {
+ if ( gAudiop )
+ {
+ gAudiop->stopInternetStream ();
+ };
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLMediaEngine::getInstance ()->stop ();
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void LLOverlayBar::onPlayButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if ( control == mMusicRemote )
+ {
+ if (gAudiop)
+ {
+ if ( parcel )
+ {
+ // this doesn't work properly when crossing parcel boundaries - even when the
+ // stream is stopped, it doesn't return the right thing - commenting out for now.
+ //if ( gAudiop->isInternetStreamPlaying() == 0 )
+ //{
+ const char* music_url = parcel->getMusicURL();
+
+ gAudiop->startInternetStream(music_url);
+
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ //}
+ }
+ };
+
+ // CP: this is the old way of doing things (click play each time on a parcel to start stream)
+ //if (gAudiop)
+ //{
+ // if (gAudiop->isInternetStreamPlaying() > 0)
+ // {
+ // gAudiop->pauseInternetStream ( 0 );
+ // }
+ // else
+ // {
+ // if (parcel)
+ // {
+ // const char* music_url = parcel->getMusicURL();
+ // gAudiop->startInternetStream(music_url);
+ // }
+ // }
+ //};
+ //mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
+ {
+ bool web_url = (parcel->getParcelFlag(PF_URL_WEB_PAGE) || parcel->getParcelFlag(PF_URL_RAW_HTML));
+ LLString path( "" );
+ #if LL_MOZILLA_ENABLED
+ LLString mozilla_subdir;
+ if (web_url)
+ {
+ path = get_mozilla_path();
+ }
+ #endif
+ LLMediaEngine::getInstance ()->convertImageAndLoadUrl( true, web_url, path );
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
+ }
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void LLOverlayBar::onPauseButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+
+ if ( control == mMusicRemote )
+ {
+ if (gAudiop)
+ {
+ gAudiop->pauseInternetStream ( 1 );
+ };
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLMediaEngine::getInstance ()->pause ();
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
+ };
+}
diff --git a/indra/newview/lloverlaybar.h b/indra/newview/lloverlaybar.h
new file mode 100644
index 0000000000..78f544df57
--- /dev/null
+++ b/indra/newview/lloverlaybar.h
@@ -0,0 +1,77 @@
+/**
+ * @file lloverlaybar.h
+ * @brief LLOverlayBar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLOVERLAYBAR_H
+#define LL_LLOVERLAYBAR_H
+
+#include "llpanel.h"
+#include "llmediaremotectrl.h"
+
+// "Constants" loaded from settings.xml at start time
+extern S32 STATUS_BAR_HEIGHT;
+
+class LLButton;
+class LLLineEditor;
+class LLMessageSystem;
+class LLTextBox;
+class LLTextEditor;
+class LLUICtrl;
+class LLUUID;
+class LLFrameTimer;
+class LLStatGraph;
+class LLSlider;
+class LLVolumeSliderCtrl;
+
+class LLOverlayBar
+: public LLPanel,
+ public LLMediaRemoteCtrlObserver
+{
+public:
+ LLOverlayBar(const std::string& name, const LLRect& rect );
+ ~LLOverlayBar();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ void refresh();
+
+ void layoutButtons();
+
+ /*virtual*/ void draw();
+
+ static void onClickIMReceived(void* data);
+ static void onClickSetNotBusy(void* data);
+ static void onClickReleaseKeys(void* data);
+ static void onClickMouselook(void* data);
+ static void onClickStandUp(void* data);
+ static void onClickResetView(void* data);
+
+ // observer overrides
+ void onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+ void onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+ void onPlayButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+ void onPauseButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+
+ LLMediaRemoteCtrl* getMusicRemoteControl () { return mMusicRemote; };
+
+protected:
+
+ static void* createMusicRemote(void* userdata);
+ static void* createMediaRemote(void* userdata);
+
+protected:
+ LLMediaRemoteCtrl* mMusicRemote;
+ LLMediaRemoteCtrl* mMediaRemote;
+ BOOL isBuilt;
+};
+
+extern LLOverlayBar* gOverlayBar;
+
+#endif
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
new file mode 100644
index 0000000000..185073b6e7
--- /dev/null
+++ b/indra/newview/llpanelavatar.cpp
@@ -0,0 +1,2347 @@
+/**
+ * @file llpanelavatar.cpp
+ * @brief LLPanelAvatar and related class implementations
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelavatar.h"
+
+#include "llfontgl.h"
+#include "llcachename.h"
+
+#include "llavatarconstants.h"
+#include "lluiconstants.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llcheckboxctrl.h"
+#include "llfloater.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterworldmap.h"
+#include "llfloatermute.h"
+#include "llfloaterrate.h"
+#include "llfloateravatarinfo.h"
+#include "lliconctrl.h"
+#include "llinventoryview.h"
+#include "lllineeditor.h"
+#include "llnameeditor.h"
+#include "llmutelist.h"
+#include "llpanelclassified.h"
+#include "llpanelpick.h"
+#include "llscrolllistctrl.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltabcontainervertical.h"
+#include "llimview.h"
+#include "lltooldraganddrop.h"
+#include "lluiconstants.h"
+#include "llvoavatar.h"
+#include "llviewermenu.h" // FIXME: for is_agent_friend()
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewborder.h"
+#include "llweb.h"
+#include "llinventorymodel.h"
+#include "viewer.h" // for gUserServer
+#include "roles_constants.h"
+#include "llwebbrowserctrl.h"
+
+#define kArraySize( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) )
+
+#include "llvieweruictrlfactory.h"
+
+// Statics
+LLLinkedList<LLPanelAvatar> LLPanelAvatar::sAllPanels;
+BOOL LLPanelAvatar::sAllowFirstLife = FALSE;
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+// RN: move these to lldbstrings.h
+static const S32 DB_USER_FAVORITES_STR_LEN = 254;
+
+static const char IM_DISABLED_TOOLTIP[] = "Instant Message (IM).\nDisabled because you do not have their card.";
+static const char IM_ENABLED_TOOLTIP[] = "Instant Message (IM)";
+static const S32 LEFT = HPAD;
+
+static const S32 RULER0 = 65;
+static const S32 RULER1 = RULER0 + 5;
+static const S32 RULER2 = RULER1 + 90;
+static const S32 RULER3 = RULER2 + 90;
+static const S32 RULER4 = RULER3 + 10;
+
+static const S32 PICT_WIDTH = 180;
+static const S32 PICT_HEIGHT = 135;
+
+static const S32 RULER5 = RULER4 + 140;
+static const S32 WIDTH = RULER5 + 16;
+
+static const S32 MAX_CHARS = 254;
+
+static const LLColor4 WHITE(1,1,1,1);
+static const LLColor4 BLACK(0,0,0,1);
+static const LLColor4 CLEAR(0,0,0,0);
+
+extern void handle_lure(const LLUUID& invitee);
+extern void handle_pay_by_id(const LLUUID& payee);
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDropTarget
+//
+// This handy class is a simple way to drop something on another
+// view. It handles drop events, always setting itself to the size of
+// its parent.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLDropTarget : public LLView
+{
+public:
+ LLDropTarget(const std::string& name, const LLRect& rect, const LLUUID& agent_id);
+ ~LLDropTarget();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ void doDrop(EDragAndDropType cargo_type, void* cargo_data);
+
+ //
+ // LLView functionality
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+ void setAgentID(const LLUUID &agent_id) { mAgentID = agent_id; }
+protected:
+ LLUUID mAgentID;
+};
+
+
+LLDropTarget::LLDropTarget(const std::string& name, const LLRect& rect,
+ const LLUUID& agent_id) :
+ LLView(name, rect, NOT_MOUSE_OPAQUE, FOLLOWS_ALL),
+ mAgentID(agent_id)
+{
+}
+
+LLDropTarget::~LLDropTarget()
+{
+}
+
+EWidgetType LLDropTarget::getWidgetType() const
+{
+ return WIDGET_TYPE_DROP_TARGET;
+}
+
+LLString LLDropTarget::getWidgetTag() const
+{
+ return LL_DROP_TARGET_TAG;
+}
+
+void LLDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data)
+{
+ llinfos << "LLDropTarget::doDrop()" << llendl;
+}
+
+BOOL LLDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+ if(getParent())
+ {
+ // check if inside
+ //LLRect parent_rect = mParentView->getRect();
+ //mRect.set(0, parent_rect.getHeight(), parent_rect.getWidth(), 0);
+ handled = TRUE;
+
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLViewerInventoryItem* inv_item = (LLViewerInventoryItem*)cargo_data;
+ if(gInventory.getItem(inv_item->getUUID())
+ && LLToolDragAndDrop::isInventoryGiveAcceptable(inv_item))
+ {
+ //FIXME: get multiple object transfers working
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventory(mAgentID, inv_item);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably
+ // in an object's contents), so disallow dragging
+ // it here. You can't give something you don't
+ // yet have.
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ {
+ LLViewerInventoryCategory* inv_cat = (LLViewerInventoryCategory*)cargo_data;
+ if( gInventory.getCategory( inv_cat->getUUID() ) )
+ {
+ //FIXME: get multiple object transfers working
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventoryCategory(mAgentID,
+ inv_cat);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably
+ // in an object's contents), so disallow dragging
+ // it here. You can't give something you don't
+ // yet have.
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ case DAD_CALLINGCARD:
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+ }
+ return handled;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarSecondLife()
+//-----------------------------------------------------------------------------
+LLPanelAvatarSecondLife::LLPanelAvatarSecondLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar )
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar),
+ mPartnerID()
+{
+}
+
+LLPanelAvatarSecondLife::~LLPanelAvatarSecondLife()
+{
+}
+
+void LLPanelAvatarSecondLife::draw()
+{
+ if (getVisible())
+ {
+ updatePartnerName();
+ }
+
+ LLPanel::draw();
+}
+
+void LLPanelAvatarSecondLife::updatePartnerName()
+{
+ if (mPartnerID.notNull())
+ {
+ char first[128];
+ char last[128];
+ BOOL found = gCacheName->getName(mPartnerID, first, last);
+ if (found)
+ {
+ childSetTextArg("partner_edit", "[FIRST]", first);
+ childSetTextArg("partner_edit", "[LAST]", last);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearControls()
+// Empty the data out of the controls, since we have to wait for new
+// data off the network.
+//-----------------------------------------------------------------------------
+void LLPanelAvatarSecondLife::clearControls()
+{
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(this,"img");
+ if(image_ctrl)
+ {
+ image_ctrl->setImageAssetID(LLUUID::null);
+ }
+ childSetValue("about", "");
+ childSetValue("born", "");
+ childSetValue("acct", "");
+
+ childSetTextArg("partner_edit", "[FIRST]", "");
+ childSetTextArg("partner_edit", "[LAST]", "");
+
+ mPartnerID = LLUUID::null;
+
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(this,"groups");
+ if(group_list)
+ {
+ group_list->deleteAllItems();
+ }
+ LLScrollListCtrl* ratings_list = LLUICtrlFactory::getScrollListByName(this,"ratings");
+ if(ratings_list)
+ {
+ ratings_list->deleteAllItems();
+ }
+
+}
+
+
+//-----------------------------------------------------------------------------
+// enableControls()
+//-----------------------------------------------------------------------------
+void LLPanelAvatarSecondLife::enableControls(BOOL self)
+{
+ childSetEnabled("img", self);
+ childSetEnabled("about", self);
+ childSetVisible("allow_publish", self);
+ childSetEnabled("allow_publish", self);
+ childSetVisible("?", self);
+ childSetEnabled("?", self);
+
+ if (!self)
+ {
+ // This is because the LLTextEditor
+ // appears to reset the read only background color when
+ // setEnable is called, for some reason
+ LLTextEditor* about = LLUICtrlFactory::getTextEditorByName(this,"about");
+ if (about) about->setReadOnlyBgColor(CLEAR);
+ }
+}
+
+
+// static
+void LLPanelAvatarSecondLife::onClickImage(void *)
+{ }
+
+// static
+void LLPanelAvatarSecondLife::onDoubleClickGroup(void* data)
+{
+ LLPanelAvatarSecondLife* self = (LLPanelAvatarSecondLife*)data;
+
+
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(self,"groups");
+ if(group_list)
+ {
+ LLScrollListItem* item = group_list->getFirstSelected();
+ if(item && item->getUUID().notNull())
+ {
+ llinfos << "Show group info " << item->getUUID() << llendl;
+
+ LLFloaterGroupInfo::showFromUUID(item->getUUID());
+ }
+ }
+}
+
+// static
+void LLPanelAvatarSecondLife::onClickPublishHelp(void *)
+{
+ gViewerWindow->alertXml("ClickPublishHelpAvatar");
+}
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarFirstLife()
+//-----------------------------------------------------------------------------
+LLPanelAvatarFirstLife::LLPanelAvatarFirstLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar )
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+
+LLPanelAvatarFirstLife::~LLPanelAvatarFirstLife()
+{
+}
+
+
+void LLPanelAvatarFirstLife::enableControls(BOOL self)
+{
+ childSetEnabled("img", self);
+ childSetEnabled("about", self);
+}
+
+//-----------------------------------------------------------------------------
+// postBuild
+//-----------------------------------------------------------------------------
+
+BOOL LLPanelAvatarSecondLife::postBuild(void)
+{
+ childSetEnabled("born", FALSE);
+ childSetEnabled("partner_edit", FALSE);
+
+ childSetAction("?",onClickPublishHelp,this);
+ BOOL own_avatar = (mPanelAvatar->getAvatarID() == gAgent.getID() );
+ enableControls(own_avatar);
+
+ childSetVisible("About:",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("(500 chars)",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("about",LLPanelAvatar::sAllowFirstLife);
+
+ childSetVisible("allow_publish",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("?",LLPanelAvatar::sAllowFirstLife);
+
+ childSetVisible("online_unknown",TRUE);
+ childSetVisible("online_yes",FALSE);
+ childSetVisible("online_no",FALSE);
+
+ childSetAction("Show on Map", LLPanelAvatar::onClickTrack, mPanelAvatar);
+ childSetAction("Instant Message...", LLPanelAvatar::onClickIM, mPanelAvatar);
+ childSetAction("Rate...", LLPanelAvatar::onClickRate, mPanelAvatar);
+ childSetAction("Pay...", LLPanelAvatar::onClickPay, mPanelAvatar);
+ childSetAction("Mute", LLPanelAvatar::onClickMute, mPanelAvatar );
+
+ childSetAction("Offer Teleport...", LLPanelAvatar::onClickOfferTeleport, mPanelAvatar);
+
+ childSetDoubleClickCallback("groups", onDoubleClickGroup, this );
+
+ return TRUE;
+}
+BOOL LLPanelAvatarFirstLife::postBuild(void)
+{
+ BOOL own_avatar = (mPanelAvatar->getAvatarID() == gAgent.getID() );
+ enableControls(own_avatar);
+ return TRUE;
+}
+BOOL LLPanelAvatarNotes::postBuild(void)
+{ childSetCommitCallback("notes edit",onCommitNotes,this);
+
+ LLTextEditor* te = LLUICtrlFactory::getTextEditorByName(this,"notes edit");
+ if(te) te->setCommitOnFocusLost(TRUE);
+ return TRUE;
+}
+
+BOOL LLPanelAvatarWeb::postBuild(void)
+{
+ childSetAction("load",onClickLoad,this);
+ childSetAction("open",onClickOpen,this);
+ childSetAction("home",onClickLoad,this);
+ childSetAction("web_profile_help",onClickWebProfileHelp,this);
+
+ childSetCommitCallback("url_edit",onCommitURL,this);
+
+ childSetControlName("auto_load","AutoLoadWebProfiles");
+
+#if LL_LIBXUL_ENABLED
+ mWebBrowser = (LLWebBrowserCtrl*)getChildByName("profile_html");
+
+ // links open in internally
+ mWebBrowser->setOpenInExternalBrowser( false );
+
+ // observe browser events
+ LLMozLib::getInstance()->addObserver( mWebBrowser->getEmbeddedBrowserWindowId(), this );
+#endif // LL_LIBXUL_ENABLED
+
+ return TRUE;
+}
+
+BOOL LLPanelAvatarClassified::postBuild(void)
+{
+ childSetAction("New...",onClickNew,NULL);
+ childSetAction("Delete...",onClickDelete,NULL);
+ return TRUE;
+}
+BOOL LLPanelAvatarPicks::postBuild(void)
+{
+ childSetAction("New...",onClickNew,NULL);
+ childSetAction("Delete...",onClickDelete,NULL);
+ return TRUE;
+}
+
+BOOL LLPanelAvatarAdvanced::postBuild()
+{
+ for( S32 i = 0; i < kArraySize(mWantToCheck); i++ )
+ mWantToCheck[i] = NULL;
+ for( S32 i = 0; i < kArraySize(mSkillsCheck); i++ )
+ mSkillsCheck[i] = NULL;
+ mWantToCount = (8>kArraySize(mWantToCheck))?kArraySize(mWantToCheck):8;
+ for(int t=0;t < mWantToCount ;t++)
+ {
+ LLString ctlname = llformat("chk%d",t);
+ mWantToCheck[t] = LLUICtrlFactory::getCheckBoxByName(this,ctlname);
+ }
+ mSkillsCount = (6>kArraySize(mSkillsCheck))?kArraySize(mSkillsCheck):6;
+
+ for(int t=0;t<mSkillsCount;t++)
+ {
+ //Find the Skills checkboxes and save off thier controls
+ LLString ctlname = llformat("schk%d",t);
+ mSkillsCheck[t] = LLUICtrlFactory::getCheckBoxByName(this,ctlname);
+ }
+
+ mWantToEdit = LLUICtrlFactory::getLineEditorByName(this,"want_to_edit");
+ mSkillsEdit = LLUICtrlFactory::getLineEditorByName(this,"skills_edit");
+ childSetVisible("skills_edit",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("want_to_edit",LLPanelAvatar::sAllowFirstLife);
+
+ return TRUE;
+}
+
+LLPanelAvatarWeb::LLPanelAvatarWeb(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar),
+ mWebBrowser(NULL)
+{
+}
+
+LLPanelAvatarWeb::~LLPanelAvatarWeb()
+{
+#if LL_LIBXUL_ENABLED
+ // stop observing browser events
+ LLMozLib::getInstance()->remObserver( mWebBrowser->getEmbeddedBrowserWindowId(), this );
+#endif
+}
+
+void LLPanelAvatarWeb::enableControls(BOOL self)
+{
+ childSetEnabled("url_edit",self);
+ childSetVisible("status_text",!self);
+}
+
+void LLPanelAvatarWeb::setWebURL(std::string url)
+{
+ bool changed_url = (mURL != url);
+
+ mURL = url;
+ bool have_url = !mURL.empty();
+
+ childSetText("url_edit",mURL);
+
+ childSetEnabled("load",have_url);
+ childSetEnabled("open",have_url);
+
+ childSetVisible("home",false);
+ childSetVisible("load",true);
+
+ if (have_url
+ && gSavedSettings.getBOOL("AutoLoadWebProfiles"))
+ {
+ if (changed_url)
+ {
+ load();
+ }
+ }
+ else
+ {
+ childSetVisible("profile_html",false);
+ }
+
+#if !LL_LIBXUL_ENABLED
+ childSetVisible("load",false);
+ childSetVisible("profile_html",false);
+ childSetVisible("status_text",false);
+#endif
+
+}
+
+// static
+void LLPanelAvatarWeb::onCommitURL(LLUICtrl* ctrl, void* data)
+{
+ LLPanelAvatarWeb* self = (LLPanelAvatarWeb*)data;
+
+ if (!self) return;
+
+ self->load( self->childGetText("url_edit") );
+}
+
+// static
+void LLPanelAvatarWeb::onClickWebProfileHelp(void *)
+{
+#if LL_LIBXUL_ENABLED
+ gViewerWindow->alertXml("ClickWebProfileHelpAvatar");
+#else
+ gViewerWindow->alertXml("ClickWebProfileNoWebHelpAvatar");
+#endif
+}
+
+void LLPanelAvatarWeb::load(std::string url)
+{
+ bool have_url = (!url.empty());
+
+#if LL_LIBXUL_ENABLED
+ if (have_url)
+ {
+ llinfos << "Loading " << url << llendl;
+ mWebBrowser->navigateTo( url );
+ }
+
+ // If we have_url then we loaded so use the home button
+ // Or if the url in address bar is not the home url show the home button.
+ bool use_home = (have_url
+ || url != mURL);
+
+ childSetVisible("profile_html",have_url);
+ childSetVisible("load",!use_home);
+ childSetVisible("home",use_home);
+
+ childSetEnabled("load",!use_home);
+ childSetEnabled("home",use_home);
+ childSetEnabled("open",have_url);
+
+#else
+ childSetEnabled("open",have_url);
+#endif
+}
+
+void LLPanelAvatarWeb::load()
+{
+ load(mURL);
+}
+
+// static
+void LLPanelAvatarWeb::onClickLoad(void* data)
+{
+ LLPanelAvatarWeb* self = (LLPanelAvatarWeb*)data;
+
+ if (!self) return;
+
+ self->load();
+}
+
+// static
+void LLPanelAvatarWeb::onClickOpen(void* data)
+{
+ LLPanelAvatarWeb* self = (LLPanelAvatarWeb*)data;
+
+ if (!self) return;
+
+ std::string url = self->childGetText("url_edit");
+ if (!url.empty())
+ {
+ LLWeb::loadURLExternal(url);
+ }
+}
+
+#if LL_LIBXUL_ENABLED
+void LLPanelAvatarWeb::onStatusTextChange( const EventType& eventIn )
+{
+ childSetText("status_text", eventIn.getStringValue() );
+}
+
+void LLPanelAvatarWeb::onLocationChange( const EventType& eventIn )
+{
+ childSetText("url_edit", eventIn.getStringValue() );
+}
+#endif
+
+LLPanelAvatarAdvanced::LLPanelAvatarAdvanced(
+ const std::string& name,
+ const LLRect& rect,
+ LLPanelAvatar* panel_avatar)
+ :
+ LLPanel(name, rect),
+ mPanelAvatar(panel_avatar),
+ mWantToCount(0),
+ mSkillsCount(0),
+ mWantToEdit( NULL ),
+ mSkillsEdit( NULL )
+{
+}
+
+LLPanelAvatarAdvanced::~LLPanelAvatarAdvanced()
+{
+}
+
+
+void LLPanelAvatarAdvanced::enableControls(BOOL self)
+{ int t;
+ for(t=0;t<mWantToCount;t++)
+ {
+ if(mWantToCheck[t])mWantToCheck[t]->setEnabled(self);
+ }
+ for(t=0;t<mSkillsCount;t++)
+ {
+ if(mSkillsCheck[t])mSkillsCheck[t]->setEnabled(self);
+ }
+
+ if (mWantToEdit) mWantToEdit->setEnabled(self);
+ if (mSkillsEdit) mSkillsEdit->setEnabled(self);
+ childSetEnabled("languages_edit",self);
+
+ if (!self)
+ {
+ // This is because the LLTextEditor
+ // appears to reset the read only background color when
+ // setEnable is called, for some reason
+ if (mWantToEdit) mWantToEdit->setReadOnlyBgColor(CLEAR);
+ if (mSkillsEdit) mSkillsEdit->setReadOnlyBgColor(CLEAR);
+ LLLineEditor* languages_edit = (LLLineEditor*)getChildByName("languages_edit");
+ languages_edit->setReadOnlyBgColor(CLEAR);
+ }
+}
+
+void LLPanelAvatarAdvanced::setWantSkills(U32 want_to_mask, const std::string& want_to_text,
+ U32 skills_mask, const std::string& skills_text,
+ const std::string& languages_text)
+{
+ for(int id =0;id<mWantToCount;id++)
+ {
+ mWantToCheck[id]->set( want_to_mask & 1<<id );
+ }
+ for(int id =0;id<mSkillsCount;id++)
+ {
+ mSkillsCheck[id]->set( skills_mask & 1<<id );
+ }
+ if (mWantToEdit && mSkillsEdit)
+ {
+ mWantToEdit->setText( want_to_text );
+ mSkillsEdit->setText( skills_text );
+ }
+
+ childSetText("languages_edit",languages_text);
+}
+
+void LLPanelAvatarAdvanced::getWantSkills(U32* want_to_mask, std::string& want_to_text,
+ U32* skills_mask, std::string& skills_text,
+ std::string& languages_text)
+{
+ if (want_to_mask)
+ {
+ *want_to_mask = 0;
+ for(int t=0;t<mWantToCount;t++)
+ {
+ if(mWantToCheck[t]->get())
+ *want_to_mask |= 1<<t;
+ }
+ }
+ if (skills_mask)
+ {
+ *skills_mask = 0;
+ for(int t=0;t<mSkillsCount;t++)
+ {
+ if(mSkillsCheck[t]->get())
+ *skills_mask |= 1<<t;
+ }
+ }
+ if (mWantToEdit)
+ {
+ want_to_text = mWantToEdit->getText();
+ }
+
+ if (mSkillsEdit)
+ {
+ skills_text = mSkillsEdit->getText();
+ }
+
+ languages_text = childGetText("languages_edit");
+}
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarNotes()
+//-----------------------------------------------------------------------------
+LLPanelAvatarNotes::LLPanelAvatarNotes(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+LLPanelAvatarNotes::~LLPanelAvatarNotes()
+{
+}
+
+
+void LLPanelAvatarNotes::enableControls(BOOL self)
+{ }
+
+// static
+void LLPanelAvatarNotes::onCommitNotes(LLUICtrl*, void* userdata)
+{
+ LLPanelAvatarNotes* self = (LLPanelAvatarNotes*)userdata;
+
+ self->mPanelAvatar->sendAvatarNotesUpdate();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarClassified()
+//-----------------------------------------------------------------------------
+LLPanelAvatarClassified::LLPanelAvatarClassified(const LLString& name, const LLRect& rect,
+ LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+
+LLPanelAvatarClassified::~LLPanelAvatarClassified()
+{
+ // children deleted by view destructor
+}
+
+
+void LLPanelAvatarClassified::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelAvatarClassified::refresh()
+{
+ BOOL self = (gAgent.getID() == mPanelAvatar->getAvatarID());
+
+
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(this,"classified tab");
+
+ S32 tab_count = tabs ? tabs->getTabCount() : 0;
+
+ BOOL allow_new = TRUE; //tab_count < MAX_CLASSIFIEDS;
+ BOOL allow_delete = (tab_count > 0);
+ BOOL show_help = (tab_count == 0);
+
+ childSetEnabled("New...",self && allow_new);
+ childSetEnabled("Delete...",self && allow_delete);
+ childSetVisible("help_text",self && show_help);
+ childSetVisible("classified tab",!show_help);
+}
+
+
+void LLPanelAvatarClassified::enableControls(BOOL self)
+{
+}
+
+
+void LLPanelAvatarClassified::apply()
+{
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this, "classified tab");
+ for (S32 i = 0; i < tabs->getTabCount(); i++)
+ {
+ LLPanelClassified* panel = (LLPanelClassified*)tabs->getPanelByIndex(i);
+ panel->apply();
+ }
+}
+
+
+void LLPanelAvatarClassified::deleteClassifiedPanels()
+{
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this,"classified tab");
+ if (tabs)
+ {
+ tabs->deleteAllTabs();
+ }
+}
+
+
+void LLPanelAvatarClassified::processAvatarClassifiedReply(LLMessageSystem* msg, void**)
+{
+ S32 block = 0;
+ S32 block_count = 0;
+ LLUUID classified_id;
+ char classified_name[DB_PICK_NAME_SIZE];
+ LLPanelClassified* panel_classified = NULL;
+
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this,"classified tab");
+
+ // Clear out all the old panels.
+ // We'll replace them with the correct number of new panels.
+ deleteClassifiedPanels();
+
+ block_count = msg->getNumberOfBlocksFast(_PREHASH_Data);
+ for (block = 0; block < block_count; block++)
+ {
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ClassifiedID, classified_id, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, DB_PICK_NAME_SIZE, classified_name, block);
+
+ panel_classified = new LLPanelClassified(FALSE);
+
+ panel_classified->setClassifiedID(classified_id);
+
+ // This will request data from the server when the pick is first drawn.
+ panel_classified->markForServerRequest();
+
+ // The button should automatically truncate long names for us
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_classified, classified_name);
+ }
+ }
+
+ // Make sure somebody is highlighted. This works even if there
+ // are no tabs in the container.
+ if(tabs)
+ {
+ tabs->selectFirstTab();
+ }
+}
+
+
+// Create a new classified panel. It will automatically handle generating
+// its own id when it's time to save.
+// static
+void LLPanelAvatarClassified::onClickNew(void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+
+ gViewerWindow->alertXml("AddClassified",callbackNew,self);
+
+}
+
+// static
+void LLPanelAvatarClassified::callbackNew(S32 option, void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+
+ if (0 == option)
+ {
+ LLPanelClassified* panel_classified = new LLPanelClassified(FALSE);
+ panel_classified->initNewClassified();
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(self,"classified tab");
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_classified, panel_classified->getClassifiedName());
+ tabs->selectLastTab();
+ }
+ }
+}
+
+
+// static
+void LLPanelAvatarClassified::onClickDelete(void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(self,"classified tab");
+ LLPanelClassified* panel_classified = NULL;
+ if(tabs)
+ {
+ panel_classified = (LLPanelClassified*)tabs->getCurrentPanel();
+ }
+ if (!panel_classified) return;
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = panel_classified->getClassifiedName();
+ gViewerWindow->alertXml("DeleteClassified", args, callbackDelete, self);
+
+}
+
+
+// static
+void LLPanelAvatarClassified::callbackDelete(S32 option, void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(self,"classified tab");
+ LLPanelClassified* panel_classified=NULL;
+ if(tabs)
+ {
+ panel_classified = (LLPanelClassified*)tabs->getCurrentPanel();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ if (!panel_classified) return;
+
+ if (0 == option)
+ {
+ msg->newMessageFast(_PREHASH_ClassifiedDelete);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_ClassifiedID, panel_classified->getClassifiedID());
+ gAgent.sendReliableMessage();
+
+ if(tabs)
+ {
+ tabs->removeTabPanel(panel_classified);
+ }
+ delete panel_classified;
+ panel_classified = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarPicks()
+//-----------------------------------------------------------------------------
+LLPanelAvatarPicks::LLPanelAvatarPicks(const std::string& name, const LLRect& rect,
+ LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+
+LLPanelAvatarPicks::~LLPanelAvatarPicks()
+{
+ // children deleted by view destructor
+}
+
+
+void LLPanelAvatarPicks::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelAvatarPicks::refresh()
+{
+ BOOL self = (gAgent.getID() == mPanelAvatar->getAvatarID());
+
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this,"picks tab");
+ S32 tab_count = tabs ? tabs->getTabCount() : 0;
+ BOOL allow_new = (tab_count < MAX_AVATAR_PICKS);
+ BOOL allow_delete = (tab_count > 0);
+
+ childSetEnabled("New...",self && allow_new);
+ childSetEnabled("Delete...",self && allow_delete);
+}
+
+
+// You are only allowed 10 picks.
+void LLPanelAvatarPicks::enableControls(BOOL self)
+{
+}
+
+
+void LLPanelAvatarPicks::deletePickPanels()
+{
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(this,"picks tab");
+ if(tabs)
+ {
+ tabs->deleteAllTabs();
+ }
+}
+
+
+void LLPanelAvatarPicks::processAvatarPicksReply(LLMessageSystem* msg, void**)
+{
+ S32 block = 0;
+ S32 block_count = 0;
+ LLUUID pick_id;
+ char pick_name[DB_PICK_NAME_SIZE];
+ LLPanelPick* panel_pick = NULL;
+
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(this,"picks tab");
+
+ // Clear out all the old panels. We'll replace them with the correct
+ // number of new panels.
+ deletePickPanels();
+
+ block_count = msg->getNumberOfBlocks("Data");
+ for (block = 0; block < block_count; block++)
+ {
+ msg->getUUID("Data", "PickID", pick_id, block);
+ msg->getString("Data", "PickName", DB_PICK_NAME_SIZE, pick_name, block);
+
+ panel_pick = new LLPanelPick(FALSE);
+
+ panel_pick->setPickID(pick_id);
+
+ // This will request data from the server when the pick is first
+ // drawn.
+ panel_pick->markForServerRequest();
+
+ // The button should automatically truncate long names for us
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_pick, pick_name);
+ }
+ }
+
+ // Make sure somebody is highlighted. This works even if there
+ // are no tabs in the container.
+ if(tabs)
+ {
+ tabs->selectFirstTab();
+ }
+}
+
+
+// Create a new pick panel. It will automatically handle generating
+// its own id when it's time to save.
+// static
+void LLPanelAvatarPicks::onClickNew(void* data)
+{
+ LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data;
+ LLPanelPick* panel_pick = new LLPanelPick(FALSE);
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(self,"picks tab");
+
+ panel_pick->initNewPick();
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_pick, panel_pick->getPickName());
+ tabs->selectLastTab();
+ }
+}
+
+
+// static
+void LLPanelAvatarPicks::onClickDelete(void* data)
+{
+ LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data;
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(self,"picks tab");
+ LLPanelPick* panel_pick = tabs?(LLPanelPick*)tabs->getCurrentPanel():NULL;
+
+ if (!panel_pick) return;
+
+ LLString::format_map_t args;
+ args["[PICK]"] = panel_pick->getPickName();
+
+ gViewerWindow->alertXml("DeleteAvatarPick", args,
+ callbackDelete,
+ self);
+}
+
+
+// static
+void LLPanelAvatarPicks::callbackDelete(S32 option, void* data)
+{
+ LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data;
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(self,"picks tab");
+ LLPanelPick* panel_pick = tabs?(LLPanelPick*)tabs->getCurrentPanel():NULL;
+ LLMessageSystem* msg = gMessageSystem;
+
+ if (!panel_pick) return;
+
+ if (0 == option)
+ {
+ // If the viewer has a hacked god-mode, then this call will
+ // fail.
+ if(gAgent.isGodlike())
+ {
+ msg->newMessage("PickGodDelete");
+ }
+ else
+ {
+ msg->newMessage("PickDelete");
+ }
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("PickID", panel_pick->getPickID());
+
+ //God delete receiving end expects a query ID but we dont need it, so send a null.
+ //This is to resolve SL-24170 God Picks Delete results in crash.
+ if(gAgent.isGodlike())
+ msg->addUUID( "QueryID", LLUUID::null );
+
+
+ gAgent.sendReliableMessage();
+
+ if(tabs)
+ {
+ tabs->removeTabPanel(panel_pick);
+ }
+ delete panel_pick;
+ panel_pick = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatar
+//-----------------------------------------------------------------------------
+LLPanelAvatar::LLPanelAvatar(
+ const std::string& name,
+ const LLRect &rect,
+ BOOL allow_edit)
+ :
+ LLPanel(name, rect, FALSE),
+ mPanelSecondLife(NULL),
+ mPanelAdvanced(NULL),
+ mPanelClassified(NULL),
+ mPanelPicks(NULL),
+ mPanelNotes(NULL),
+ mPanelFirstLife(NULL),
+ mPanelWeb(NULL),
+ mDropTarget(NULL),
+ mAvatarID( LLUUID::null ), // mAvatarID is set with 'setAvatar' or 'setAvatarID'
+ mHaveProperties(FALSE),
+ mHaveStatistics(FALSE),
+ mAllowEdit(allow_edit),
+ mDisableRate(FALSE)
+{
+
+ sAllPanels.addData(this);
+
+ LLCallbackMap::map_t factory_map;
+
+ factory_map["2nd Life"] = LLCallbackMap(createPanelAvatarSecondLife, this);
+ factory_map["WebProfile"] = LLCallbackMap(createPanelAvatarWeb, this);
+ factory_map["Interests"] = LLCallbackMap(createPanelAvatarInterests, this);
+ factory_map["Picks"] = LLCallbackMap(createPanelAvatarPicks, this);
+ factory_map["Classified"] = LLCallbackMap(createPanelAvatarClassified, this);
+ factory_map["1st Life"] = LLCallbackMap(createPanelAvatarFirstLife, this);
+ factory_map["My Notes"] = LLCallbackMap(createPanelAvatarNotes, this);
+
+ gUICtrlFactory->buildPanel(this, "panel_avatar.xml", &factory_map);
+
+ selectTab(0);
+
+
+}
+
+BOOL LLPanelAvatar::postBuild(void)
+{
+ mTab = LLUICtrlFactory::getTabContainerByName(this,"tab");
+ childSetAction("Kick",onClickKick,this);
+ childSetAction("Freeze",onClickFreeze, this);
+ childSetAction("Unfreeze", onClickUnfreeze, this);
+ childSetAction("csr_btn", onClickCSR, this);
+ childSetAction("OK", onClickOK, this);
+ childSetAction("Cancel", onClickCancel, this);
+
+ if(mTab && !sAllowFirstLife)
+ {
+ LLPanel* panel = mTab->getPanelByName("1st Life");
+ if (panel) mTab->removeTabPanel(panel);
+
+ panel = mTab->getPanelByName("WebProfile");
+ if (panel) mTab->removeTabPanel(panel);
+ }
+ childSetVisible("Kick",FALSE);
+ childSetEnabled("Kick",FALSE);
+ childSetVisible("Freeze",FALSE);
+ childSetEnabled("Freeze",FALSE);
+ childSetVisible("Unfreeze",FALSE);
+ childSetEnabled("Unfreeze",FALSE);
+ childSetVisible("csr_btn", FALSE);
+ childSetEnabled("csr_btn", FALSE);
+
+ return TRUE;
+}
+
+
+LLPanelAvatar::~LLPanelAvatar()
+{
+ sAllPanels.removeData(this);
+}
+
+
+void LLPanelAvatar::setAvatar(LLViewerObject *avatarp)
+{
+ // find the avatar and grab the name
+ LLNameValue *firstname = avatarp->getNVPair("FirstName");
+ LLNameValue *lastname = avatarp->getNVPair("LastName");
+
+ LLString name;
+ if (firstname && lastname)
+ {
+ name.assign( firstname->getString() );
+ name.append(" ");
+ name.append( lastname->getString() );
+ }
+ else
+ {
+ name.assign("");
+ }
+
+ // If we have an avatar pointer, they must be online.
+ setAvatarID(avatarp->getID(), name, ONLINE_STATUS_YES);
+}
+
+
+void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
+ EOnlineStatus online_status)
+{
+ if (avatar_id.isNull()) return;
+
+ BOOL avatar_changed = FALSE;
+ if (avatar_id != mAvatarID)
+ {
+ avatar_changed = TRUE;
+ }
+ mAvatarID = avatar_id;
+
+ // Determine if we have their calling card.
+ mIsFriend = is_agent_friend(mAvatarID);
+
+ if (ONLINE_STATUS_UNKNOWN == online_status)
+ {
+ // Determine if we know that they are online. If we can see them,
+ // we know they're online. Likewise, if we have a calling card,
+ // we know. Otherwise we don't.
+ LLViewerObject* object = gObjectList.findObject( mAvatarID );
+ if (object && !object->isDead())
+ {
+ online_status = ONLINE_STATUS_YES;
+ }
+ else if (mIsFriend)
+ {
+ if (LLAvatarTracker::instance().isBuddyOnline( mAvatarID ))
+ {
+ online_status = ONLINE_STATUS_YES;
+ }
+ else
+ {
+ online_status = ONLINE_STATUS_NO;
+ }
+ }
+ else
+ {
+ // Don't actually know if they are online.
+ }
+ }
+
+ mPanelSecondLife->childSetVisible("online_unknown",FALSE);
+ mPanelSecondLife->childSetVisible("online_yes",FALSE);
+ mPanelSecondLife->childSetVisible("online_no",FALSE);
+
+ switch(online_status)
+ {
+ case ONLINE_STATUS_YES:
+ mPanelSecondLife->childSetVisible("online_yes",TRUE);
+ break;
+ case ONLINE_STATUS_NO:
+ mPanelSecondLife->childSetVisible("online_no",TRUE);
+ break;
+ case ONLINE_STATUS_UNKNOWN:
+ default:
+ mPanelSecondLife->childSetVisible("online_unknown",TRUE);
+ break;
+ }
+
+ BOOL own_avatar = (mAvatarID == gAgent.getID() );
+
+ mPanelSecondLife->enableControls(own_avatar && mAllowEdit);
+ mPanelWeb->enableControls(own_avatar && mAllowEdit);
+ mPanelAdvanced->enableControls(own_avatar && mAllowEdit);
+ mPanelPicks->enableControls(own_avatar && mAllowEdit);
+ mPanelClassified->enableControls(own_avatar && mAllowEdit);
+ // Teens don't have this.
+ if (mPanelFirstLife) mPanelFirstLife->enableControls(own_avatar && mAllowEdit);
+ mPanelNotes->enableControls(own_avatar && mAllowEdit);
+
+
+ LLView *target_view = getChildByName("drop_target_rect", TRUE);
+ if(target_view)
+ {
+ if (mDropTarget)
+ {
+ delete mDropTarget;
+ }
+ mDropTarget = new LLDropTarget("drop target", target_view->getRect(), mAvatarID);
+ addChild(mDropTarget);
+ mDropTarget->setAgentID(mAvatarID);
+ }
+
+ LLNameEditor* NameEdit = LLViewerUICtrlFactory::getNameEditorByName(this, "name");
+ if(NameEdit)
+ {
+ if (name.empty())
+ {
+ NameEdit->setNameID(avatar_id, FALSE);
+ }
+ else
+ {
+ NameEdit->setText(name);
+ }
+ }
+// if (avatar_changed)
+ {
+ // While we're waiting for data off the network, clear out the
+ // old data.
+ mPanelSecondLife->clearControls();
+ mPanelPicks->deletePickPanels();
+ mPanelClassified->deleteClassifiedPanels();
+
+ sendAvatarPropertiesRequest();
+
+ if (own_avatar)
+ {
+ if (mAllowEdit)
+ {
+ // OK button disabled until properties data arrives
+ childSetVisible("OK",TRUE);
+ childSetEnabled("OK",TRUE);
+ childSetVisible("Cancel",TRUE);
+ childSetEnabled("Cancel",TRUE);
+ }
+ else
+ {
+ childSetVisible("OK",FALSE);
+ childSetEnabled("OK",FALSE);
+ }
+ childSetVisible("Instant Message...",FALSE);
+ childSetEnabled("Instant Message...",FALSE);
+ childSetVisible("Mute",FALSE);
+ childSetEnabled("Mute",FALSE);
+ childSetVisible("Offer Teleport...",FALSE);
+ childSetEnabled("Offer Teleport...",FALSE);
+ childSetVisible("drop target",FALSE);
+ childSetEnabled("drop target",FALSE);
+ childSetVisible("Show on Map",FALSE);
+ childSetEnabled("Show on Map",FALSE);
+ childSetVisible("Rate...",FALSE);
+ childSetEnabled("Rate...",FALSE);
+ childSetVisible("Pay...",FALSE);
+ childSetEnabled("Pay...",FALSE);
+ }
+ else
+ {
+ childSetVisible("OK",FALSE);
+ childSetEnabled("OK",FALSE);
+
+ childSetVisible("Cancel",FALSE);
+ childSetEnabled("Cancel",FALSE);
+
+ childSetVisible("Instant Message...",TRUE);
+ childSetEnabled("Instant Message...",FALSE);
+ childSetToolTip("Instant Message...",IM_ENABLED_TOOLTIP);
+ childSetVisible("Mute",TRUE);
+ childSetEnabled("Mute",FALSE);
+
+ childSetVisible("Offer Teleport...",TRUE);
+ BOOL in_prelude = gAgent.inPrelude();
+ if(gAgent.isGodlike())
+ {
+ childSetEnabled("Offer Teleport...", TRUE);
+ childSetToolTip("Offer Teleport...", childGetValue("TeleportGod").asString());
+ }
+ else if (in_prelude)
+ {
+ childSetEnabled("Offer Teleport...",FALSE);
+ childSetToolTip("Offer Teleport...",childGetValue("TeleportPrelude").asString());
+ }
+ else
+ {
+ // Even if user might be offline, allow a teleport offer.
+ BOOL maybe_online = (online_status != ONLINE_STATUS_NO);
+ childSetEnabled("Offer Teleport...", maybe_online);
+ childSetToolTip("Offer Teleport...", childGetValue("TeleportNormal").asString());
+ }
+ childSetVisible("drop target",TRUE);
+ childSetEnabled("drop target",FALSE);
+
+ childSetVisible("Show on Map",TRUE);
+ // Note: we don't always know online status, so always allow gods to try to track
+ BOOL enable_track = gAgent.isGodlike() || is_agent_mappable(mAvatarID);
+ childSetEnabled("Show on Map",enable_track);
+ if (!mIsFriend)
+ {
+ childSetToolTip("Show on Map",childGetValue("ShowOnMapNonFriend").asString());
+ }
+ else if (ONLINE_STATUS_YES != online_status)
+ {
+ childSetToolTip("Show on Map",childGetValue("ShowOnMapFriendOffline").asString());
+ }
+ else
+ {
+ childSetToolTip("Show on Map",childGetValue("ShowOnMapFriendOnline").asString());
+ }
+ childSetVisible("Rate...",TRUE);
+ childSetEnabled("Rate...",FALSE);
+ childSetVisible("Pay...",TRUE);
+ childSetEnabled("Pay...",FALSE);
+ }
+ }
+
+ BOOL is_god = FALSE;
+ if (gAgent.isGodlike()) is_god = TRUE;
+
+ childSetVisible("Kick", is_god);
+ childSetEnabled("Kick", is_god);
+ childSetVisible("Freeze", is_god);
+ childSetEnabled("Freeze", is_god);
+ childSetVisible("Unfreeze", is_god);
+ childSetEnabled("Unfreeze", is_god);
+ childSetVisible("csr_btn", is_god);
+ childSetEnabled("csr_btn", is_god);
+}
+
+
+void LLPanelAvatar::resetGroupList()
+{
+ // only get these updates asynchronously via the group floater, which works on the agent only
+ if (mAvatarID != gAgent.getID())
+ {
+ return;
+ }
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(mPanelSecondLife,"groups");
+
+ if (mPanelSecondLife && group_list)
+ {
+ group_list->deleteAllItems();
+ LLScrollListItem* item;
+
+ S32 count = gAgent.mGroups.count();
+ LLUUID id;
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLGroupData group_data = gAgent.mGroups.get(i);
+ id = group_data.mID;
+ std::string group_string;
+ /* Show group title? DUMMY_POWER for Don Grep
+ if(group_data.mOfficer)
+ {
+ group_string = "Officer of ";
+ }
+ else
+ {
+ group_string = "Member of ";
+ }
+ */
+
+ group_string += group_data.mName;
+ item = new LLScrollListItem(TRUE, NULL, id);
+ item->addColumn(group_string, LLFontGL::sSansSerifSmall, 0, LLFontGL::NORMAL);
+ group_list->addItem(item);
+ }
+ group_list->sortByColumn(0, TRUE);
+ }
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onClickIM()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickIM(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+ gIMView->setFloaterOpen(TRUE);
+
+ std::string name;
+ LLNameEditor* nameedit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
+ if (nameedit) name = nameedit->getText();
+ gIMView->addSession(name, IM_NOTHING_SPECIAL, self->mAvatarID);
+}
+
+
+// static
+//-----------------------------------------------------------------------------
+// onClickTrack()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickTrack(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if( gFloaterWorldMap )
+ {
+ std::string name;
+ LLNameEditor* nameedit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
+ if (nameedit) name = nameedit->getText();
+ gFloaterWorldMap->trackAvatar(self->mAvatarID, name);
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onClickRate()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickRate(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ LLFloaterRate::show(self->mAvatarID);
+}
+
+//-----------------------------------------------------------------------------
+// onClickMute()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickMute(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ LLUUID agent_id = self->getAvatarID();
+ LLNameEditor* name_edit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
+
+ if (name_edit)
+ {
+ std::string agent_name = name_edit->getText();
+ gFloaterMute->show();
+
+ if (gMuteListp->isMuted(agent_id))
+ {
+ gFloaterMute->selectMute(agent_id);
+ }
+ else
+ {
+ LLMute mute(agent_id, agent_name, LLMute::AGENT);
+ gMuteListp->add(mute);
+ }
+ }
+}
+
+
+void LLPanelAvatar::disableRate()
+{
+ // Force off the rate button, but enable IM.
+ // Note that these buttons may not exist if it is your own profile.
+ childSetEnabled("Rate...",FALSE);
+ mDisableRate = TRUE;
+}
+
+
+// static
+void LLPanelAvatar::onClickOfferTeleport(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ handle_lure(self->mAvatarID);
+}
+
+
+// static
+void LLPanelAvatar::onClickPay(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+ handle_pay_by_id(self->mAvatarID);
+}
+
+
+// static
+void LLPanelAvatar::onClickOK(void *userdata)
+{
+ LLPanelAvatar *self = (LLPanelAvatar *)userdata;
+
+ // JC: Only save the data if we actually got the original
+ // properties. Otherwise we might save blanks into
+ // the database.
+ if (self
+ && self->mHaveProperties)
+ {
+ self->sendAvatarPropertiesUpdate();
+
+ self->mPanelClassified->apply();
+
+ LLFloaterAvatarInfo *infop = LLFloaterAvatarInfo::getInstance(self->mAvatarID);
+ if (infop)
+ {
+ infop->close();
+ }
+ }
+}
+
+// static
+void LLPanelAvatar::onClickCancel(void *userdata)
+{
+ LLPanelAvatar *self = (LLPanelAvatar *)userdata;
+
+ if (self)
+ {
+ self->sendAvatarPropertiesRequest();
+ LLFloaterAvatarInfo *infop;
+ if ((infop = LLFloaterAvatarInfo::getInstance(self->mAvatarID)))
+ {
+ infop->close();
+ }
+ }
+}
+
+
+void LLPanelAvatar::sendAvatarPropertiesRequest()
+{
+ lldebugs << "LLPanelAvatar::sendAvatarPropertiesRequest()" << llendl;
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_AvatarPropertiesRequest);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast( _PREHASH_AvatarID, mAvatarID);
+ gAgent.sendReliableMessage();
+}
+
+void LLPanelAvatar::sendAvatarNotesUpdate()
+{
+ std::string notes = mPanelNotes->childGetValue("notes edit").asString();
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessage("AvatarNotesUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("TargetID", mAvatarID);
+ msg->addString("Notes", notes);
+
+ gAgent.sendReliableMessage();
+}
+
+
+// static
+void LLPanelAvatar::processAvatarPropertiesReply(LLMessageSystem *msg, void**)
+{
+ LLPanelAvatar* self = NULL;
+
+ LLUUID agent_id; // your id
+ LLUUID avatar_id; // target of this panel
+ LLUUID image_id;
+ LLUUID fl_image_id;
+ LLUUID partner_id;
+ char about_text[DB_USER_ABOUT_BUF_SIZE];
+ char fl_about_text[DB_USER_FL_ABOUT_BUF_SIZE];
+ char born_on[DB_BORN_BUF_SIZE];
+ S32 charter_member_size = 0;
+ BOOL allow_publish = FALSE;
+ //BOOL mature = FALSE;
+ BOOL identified = FALSE;
+ BOOL transacted = FALSE;
+ char profile_url[DB_USER_PROFILE_URL_BUF_SIZE];
+
+ //llinfos << "properties packet size " << msg->getReceiveSize() << llendl;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id );
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+ self->childSetEnabled("Instant Message...",TRUE);
+ self->childSetEnabled("Pay...",TRUE);
+ self->childSetEnabled("Mute",TRUE);
+
+ if (!self->mDisableRate)
+ {
+ self->childSetEnabled("Rate...",TRUE);
+ }
+ lldebugs << "!!!!!!!!!!!!!!!!!!!!!!Enabling drop target" << llendl;
+ self->childSetEnabled("drop target",TRUE);
+
+ self->mHaveProperties = TRUE;
+ self->enableOKIfReady();
+
+ msg->getUUIDFast( _PREHASH_PropertiesData, _PREHASH_ImageID, image_id );
+ msg->getUUIDFast( _PREHASH_PropertiesData, _PREHASH_FLImageID, fl_image_id );
+ msg->getUUIDFast(_PREHASH_PropertiesData, _PREHASH_PartnerID, partner_id);
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_AboutText, DB_USER_ABOUT_BUF_SIZE, about_text );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_FLAboutText, DB_USER_FL_ABOUT_BUF_SIZE, fl_about_text );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_BornOn, DB_BORN_BUF_SIZE, born_on);
+ msg->getBOOLFast(_PREHASH_PropertiesData, _PREHASH_Identified, identified);
+ msg->getBOOLFast(_PREHASH_PropertiesData, _PREHASH_Transacted, transacted);
+ msg->getString("PropertiesData","ProfileURL", DB_USER_PROFILE_URL_BUF_SIZE, profile_url);
+
+ self->mPanelWeb->setWebURL(std::string(profile_url));
+ U8 caption_index = 0;
+ LLString caption_text;
+ charter_member_size = msg->getSize("PropertiesData", "CharterMember");
+ if(1 == charter_member_size)
+ {
+ msg->getBinaryData("PropertiesData", "CharterMember", &caption_index, 1);
+ }
+ else if(1 < charter_member_size)
+ {
+ char caption[MAX_STRING];
+ msg->getString("PropertiesData", "CharterMember", MAX_STRING, caption);
+ caption_text = caption;
+ }
+
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(self->mPanelSecondLife,"img");
+ if(image_ctrl)
+ {
+ image_ctrl->setImageAssetID(image_id);
+ }
+ self->childSetValue("about", about_text);
+
+ if(caption_text.empty())
+ {
+ LLString::format_map_t args;
+ caption_text = self->mPanelSecondLife->childGetValue("CaptionTextAcctInfo").asString();
+
+ const char* ACCT_TYPE[] = {
+ "AcctTypeResident",
+ "AcctTypeTrial",
+ "AcctTypeCharterMember",
+ "AcctTypeEmployee"
+ };
+ caption_index = llclamp(caption_index, (U8)0, (U8)(sizeof(ACCT_TYPE)/sizeof(ACCT_TYPE[0])-1));
+ args["[ACCTTYPE]"] = self->mPanelSecondLife->childGetValue(ACCT_TYPE[caption_index]).asString();
+
+ LLString payment_text = " ";
+ const S32 DEFAULT_CAPTION_LINDEN_INDEX = 3;
+ if(caption_index != DEFAULT_CAPTION_LINDEN_INDEX)
+ {
+ if(transacted)
+ {
+ payment_text = "PaymentInfoUsed";
+ }
+ else if (identified)
+ {
+ payment_text = "PaymentInfoOnFile";
+ }
+ else
+ {
+ payment_text = "NoPaymentInfoOnFile";
+ }
+ args["[PAYMENTINFO]"] = self->mPanelSecondLife->childGetValue(payment_text).asString();
+ }
+ else
+ {
+ args["[PAYMENTINFO]"] = " ";
+ }
+ LLString::format(caption_text, args);
+ }
+
+ self->mPanelSecondLife->childSetValue("acct", caption_text);
+ self->mPanelSecondLife->childSetValue("born", born_on);
+
+ self->mPanelSecondLife->setPartnerID(partner_id);
+ self->mPanelSecondLife->updatePartnerName();
+
+ if (self->mPanelFirstLife)
+ {
+ // Teens don't get these
+ self->mPanelFirstLife->childSetValue("about", fl_about_text);
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(self->mPanelFirstLife,"img");
+ if(image_ctrl)
+ {
+ image_ctrl->setImageAssetID(fl_image_id);
+ }
+ msg->getBOOL("PropertiesData", "AllowPublish", allow_publish);
+
+ self->mPanelSecondLife->childSetValue("allow_publish", allow_publish);
+
+ }
+ }
+}
+
+// static
+void LLPanelAvatar::processAvatarInterestsReply(LLMessageSystem *msg, void**)
+{
+ LLPanelAvatar* self = NULL;
+
+ LLUUID agent_id; // your id
+ LLUUID avatar_id; // target of this panel
+
+ U32 want_to_mask;
+ char want_to_text[DB_USER_WANT_TO_BUF_SIZE];
+ U32 skills_mask;
+ char skills_text[DB_USER_SKILLS_BUF_SIZE];
+ char languages_text[DB_USER_SKILLS_BUF_SIZE];
+
+ //llinfos << "properties packet size " << msg->getReceiveSize() << llendl;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id );
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+
+ msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_WantToMask, want_to_mask );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_WantToText, DB_USER_WANT_TO_BUF_SIZE, want_to_text );
+ msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_SkillsMask, skills_mask );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_SkillsText, DB_USER_SKILLS_BUF_SIZE, skills_text );
+ msg->getString(_PREHASH_PropertiesData, "LanguagesText", DB_USER_SKILLS_BUF_SIZE, languages_text );
+
+ self->mPanelAdvanced->setWantSkills(want_to_mask, want_to_text, skills_mask, skills_text, languages_text);
+ }
+}
+
+// Separate function because the groups list can be very long, almost
+// filling a packet. JC
+// static
+void LLPanelAvatar::processAvatarGroupsReply(LLMessageSystem *msg, void**)
+{
+ LLUUID agent_id; // your id
+ LLUUID avatar_id; // target of this panel
+ U64 group_powers;
+ char group_title[DB_GROUP_TITLE_BUF_SIZE];
+ LLUUID group_id;
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ LLUUID group_insignia_id;
+ const LLFontGL* FONT = LLFontGL::sSansSerifSmall;
+
+ llinfos << "groups packet size " << msg->getReceiveSize() << llendl;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id );
+
+ for (LLPanelAvatar* self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(self->mPanelSecondLife,"groups");
+// if(group_list)
+// {
+// group_list->deleteAllItems();
+// }
+
+ S32 group_count = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
+ if (0 == group_count)
+ {
+ if(group_list) group_list->addSimpleItem("None");
+ }
+ else
+ {
+ for(S32 i = 0; i < group_count; ++i)
+ {
+ msg->getU64( _PREHASH_GroupData, "GroupPowers", group_powers, i );
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupTitle, DB_GROUP_TITLE_BUF_SIZE, group_title, i );
+ msg->getUUIDFast( _PREHASH_GroupData, _PREHASH_GroupID, group_id, i);
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, group_name, i );
+ msg->getUUIDFast( _PREHASH_GroupData, _PREHASH_GroupInsigniaID, group_insignia_id, i );
+
+ LLString group_string;
+ if (group_id.notNull())
+ {
+ group_string.assign("Member of ");
+ group_string.append(group_name);
+ }
+ else
+ {
+ group_string.assign("");
+ }
+
+ // Is this really necessary? Remove existing entry if it exists.
+ // TODO: clear the whole list when a request for data is made
+ if (group_list)
+ {
+ S32 index = group_list->getItemIndex(group_id);
+ if ( index >= 0 )
+ {
+ group_list->deleteSingleItem(index);
+ }
+ }
+ LLScrollListItem *group_item = new LLScrollListItem(TRUE, NULL, group_id);
+ group_item->addColumn(group_string, FONT);
+ if(group_list) group_list->addItem(group_item);
+ }
+ }
+ if(group_list) group_list->sortByColumn(0, TRUE);
+ }
+}
+
+// Don't enable the OK button until you actually have the data.
+// Otherwise you will write blanks back into the database.
+void LLPanelAvatar::enableOKIfReady()
+{
+ if(mHaveProperties && mHaveStatistics && childIsVisible("OK"))
+ {
+ childSetEnabled("OK", TRUE);
+ }
+ else
+ {
+ childSetEnabled("OK", FALSE);
+ }
+}
+
+void LLPanelAvatar::sendAvatarPropertiesUpdate()
+{
+ llinfos << "Sending avatarinfo update" << llendl;
+ BOOL allow_publish = FALSE;
+ BOOL mature = FALSE;
+ if (LLPanelAvatar::sAllowFirstLife)
+ {
+ allow_publish = childGetValue("allow_publish");
+ //A profile should never be mature.
+ mature = FALSE;
+ }
+ U32 want_to_mask = 0x0;
+ U32 skills_mask = 0x0;
+ std::string want_to_text;
+ std::string skills_text;
+ std::string languages_text;
+ mPanelAdvanced->getWantSkills(&want_to_mask, want_to_text, &skills_mask, skills_text, languages_text);
+
+ LLUUID first_life_image_id;
+ LLString first_life_about_text;
+ if (mPanelFirstLife)
+ {
+ first_life_about_text = mPanelFirstLife->childGetValue("about").asString();
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(mPanelFirstLife,"img");
+ if(image_ctrl)
+ {
+ first_life_image_id = image_ctrl->getImageAssetID();
+ }
+ }
+
+ LLString about_text = mPanelSecondLife->childGetValue("about").asString();
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_AvatarPropertiesUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_PropertiesData);
+
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(mPanelSecondLife,"img");
+ if(image_ctrl)
+ {
+ msg->addUUIDFast( _PREHASH_ImageID, image_ctrl->getImageAssetID());
+ }
+ else
+ {
+ msg->addUUIDFast( _PREHASH_ImageID, LLUUID::null);
+ }
+// msg->addUUIDFast( _PREHASH_ImageID, mPanelSecondLife->mimage_ctrl->getImageAssetID() );
+ msg->addUUIDFast( _PREHASH_FLImageID, first_life_image_id);
+ msg->addStringFast( _PREHASH_AboutText, about_text);
+ msg->addStringFast( _PREHASH_FLAboutText, first_life_about_text);
+
+ msg->addBOOL("AllowPublish", allow_publish);
+ msg->addBOOL("MaturePublish", mature);
+ msg->addString("ProfileURL", mPanelWeb->childGetText("url_edit"));
+ gAgent.sendReliableMessage();
+
+ msg->newMessage("AvatarInterestsUpdate");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_PropertiesData);
+ msg->addU32Fast( _PREHASH_WantToMask, want_to_mask);
+ msg->addStringFast( _PREHASH_WantToText, want_to_text);
+ msg->addU32Fast( _PREHASH_SkillsMask, skills_mask);
+ msg->addStringFast( _PREHASH_SkillsText, skills_text);
+ msg->addString( "LanguagesText", languages_text);
+ gAgent.sendReliableMessage();
+}
+
+void LLPanelAvatar::selectTab(S32 tabnum)
+{
+ if(mTab)
+ {
+ mTab->selectTab(tabnum);
+ }
+}
+
+void LLPanelAvatar::selectTabByName(std::string tab_name)
+{
+ if (mTab)
+ {
+ if (tab_name.empty())
+ {
+ mTab->selectFirstTab();
+ }
+ else
+ {
+ mTab->selectTabByName(tab_name);
+ }
+ }
+}
+
+
+// static
+void LLPanelAvatar::processAvatarStatisticsReply(LLMessageSystem *msg, void**)
+{
+ // extract the agent id
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ LLUUID avatar_id;
+ msg->getUUIDFast(_PREHASH_AvatarData, _PREHASH_AvatarID, avatar_id);
+
+ // look up all panels which have this avatar
+ LLPanelAvatar *self = NULL;
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+
+ self->mHaveStatistics = TRUE;
+ self->enableOKIfReady();
+
+ // clear out list
+ LLScrollListCtrl* ratings_list = LLUICtrlFactory::getScrollListByName(self->mPanelSecondLife,"ratings");
+ if (ratings_list)
+ {
+ ratings_list->deleteAllItems();
+ }
+ // build the item list
+ LLFontGL *font = LLFontGL::sSansSerifSmall;
+
+ S32 items = msg->getNumberOfBlocksFast(_PREHASH_StatisticsData);
+ for (S32 i = 0; i < items; i++)
+ {
+ char name[MAX_STRING];
+ S32 positive;
+ S32 negative;
+ char value_string[MAX_STRING];
+
+ msg->getStringFast( _PREHASH_StatisticsData,
+ _PREHASH_Name, MAX_STRING, name, i);
+ msg->getS32( "StatisticsData", "Positive", positive, i);
+ msg->getS32( "StatisticsData", "Negative", negative, i);
+
+ LLScrollListItem *item = NULL;
+
+ const S32 TEXT_WIDTH = 75;
+
+ item = new LLScrollListItem();
+ item->addColumn( name, font, TEXT_WIDTH );
+
+ sprintf( value_string, "+%d", positive);
+ item->addColumn( value_string, font, 50 );
+
+ item->addColumn("", font); // extra column to force striped appearance
+ if(ratings_list) ratings_list->addItem( item );
+ }
+ }
+}
+
+
+void LLPanelAvatar::processAvatarNotesReply(LLMessageSystem *msg, void**)
+{
+ // extract the agent id
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+
+ LLUUID target_id;
+ msg->getUUID("Data", "TargetID", target_id);
+
+ // look up all panels which have this avatar
+ LLPanelAvatar *self = NULL;
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != target_id)
+ {
+ continue;
+ }
+
+ char text[DB_USER_NOTE_SIZE];
+ msg->getString("Data", "Notes", DB_USER_NOTE_SIZE, text);
+ self->childSetValue("notes edit", text);
+ }
+}
+
+
+void LLPanelAvatar::processAvatarClassifiedReply(LLMessageSystem *msg, void** userdata)
+{
+ LLPanelAvatar *self = NULL;
+ LLUUID agent_id;
+ LLUUID target_id;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TargetID, target_id);
+
+ // look up all panels which have this avatar target
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != target_id)
+ {
+ continue;
+ }
+
+ self->mPanelClassified->processAvatarClassifiedReply(msg, userdata);
+ }
+}
+
+void LLPanelAvatar::processAvatarPicksReply(LLMessageSystem *msg, void** userdata)
+{
+ LLPanelAvatar *self = NULL;
+ LLUUID agent_id;
+ LLUUID target_id;
+
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ msg->getUUID("AgentData", "TargetID", target_id);
+
+ // look up all panels which have this avatar target
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != target_id)
+ {
+ continue;
+ }
+
+ self->mPanelPicks->processAvatarPicksReply(msg, userdata);
+ }
+}
+
+// static
+void LLPanelAvatar::onClickKick(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect(left, top, left+400, top-300);
+
+ gViewerWindow->alertXmlEditText("KickUser", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelAvatar::finishKick, self);
+}
+
+// static
+void LLPanelAvatar::finishKick(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, self->mAvatarID );
+ msg->addU32("KickFlags", KICK_FLAGS_DEFAULT );
+ msg->addStringFast(_PREHASH_Reason, text );
+ gAgent.sendReliableMessage();
+ }
+}
+
+// static
+void LLPanelAvatar::onClickFreeze(void* userdata)
+{
+ gViewerWindow->alertXmlEditText("FreezeUser", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelAvatar::finishFreeze, userdata);
+}
+
+// static
+void LLPanelAvatar::finishFreeze(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, self->mAvatarID );
+ msg->addU32("KickFlags", KICK_FLAGS_FREEZE );
+ msg->addStringFast(_PREHASH_Reason, text );
+ gAgent.sendReliableMessage();
+ }
+}
+
+// static
+void LLPanelAvatar::onClickUnfreeze(void* userdata)
+{
+ gViewerWindow->alertXmlEditText("UnFreezeUser", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelAvatar::finishUnfreeze, userdata);
+}
+
+// static
+void LLPanelAvatar::finishUnfreeze(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, self->mAvatarID );
+ msg->addU32("KickFlags", KICK_FLAGS_UNFREEZE );
+ msg->addStringFast(_PREHASH_Reason, text );
+ gAgent.sendReliableMessage();
+ }
+}
+
+// static
+void LLPanelAvatar::onClickCSR(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)userdata;
+ if (!self) return;
+
+ LLNameEditor* name_edit = LLViewerUICtrlFactory::getNameEditorByName(self, "name");
+ if (!name_edit) return;
+
+ LLString name = name_edit->getText();
+ if (name.empty()) return;
+
+ LLString url = "http://csr.lindenlab.com/agent/";
+
+ // slow and stupid, but it's late
+ S32 len = name.length();
+ for (S32 i = 0; i < len; i++)
+ {
+ if (name[i] == ' ')
+ {
+ url += "%20";
+ }
+ else
+ {
+ url += name[i];
+ }
+ }
+
+ LLWeb::loadURL(url);
+}
+
+
+void* LLPanelAvatar::createPanelAvatarSecondLife(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelSecondLife = new LLPanelAvatarSecondLife("2nd Life",LLRect(),self);
+ return self->mPanelSecondLife;
+}
+
+void* LLPanelAvatar::createPanelAvatarWeb(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelWeb = new LLPanelAvatarWeb("Web",LLRect(),self);
+ return self->mPanelWeb;
+}
+
+void* LLPanelAvatar::createPanelAvatarInterests(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelAdvanced = new LLPanelAvatarAdvanced("Interests",LLRect(),self);
+ return self->mPanelAdvanced;
+}
+
+
+void* LLPanelAvatar::createPanelAvatarPicks(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelPicks = new LLPanelAvatarPicks("Picks",LLRect(),self);
+ return self->mPanelPicks;
+}
+
+void* LLPanelAvatar::createPanelAvatarClassified(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelClassified = new LLPanelAvatarClassified("Classified",LLRect(),self);
+ return self->mPanelClassified;
+}
+
+void* LLPanelAvatar::createPanelAvatarFirstLife(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelFirstLife = new LLPanelAvatarFirstLife("1st Life", LLRect(), self);
+ return self->mPanelFirstLife;
+}
+
+void* LLPanelAvatar::createPanelAvatarNotes(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelNotes = new LLPanelAvatarNotes("My Notes",LLRect(),self);
+ return self->mPanelNotes;
+}
diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h
new file mode 100644
index 0000000000..d6b0d235b0
--- /dev/null
+++ b/indra/newview/llpanelavatar.h
@@ -0,0 +1,335 @@
+/**
+ * @file llpanelavatar.h
+ * @brief LLPanelAvatar and related class definitions
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELAVATAR_H
+#define LL_LLPANELAVATAR_H
+
+#include "llpanel.h"
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "linked_lists.h"
+#include "llmozlib.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLDropTarget;
+class LLInventoryItem;
+class LLLineEditor;
+class LLNameEditor;
+class LLPanelAvatar;
+class LLScrollListCtrl;
+class LLTabContainerCommon;
+class LLTextBox;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLViewerImage;
+class LLViewerObject;
+class LLMessageSystem;
+class LLIconCtrl;
+class LLWebBrowserCtrl;
+
+enum EOnlineStatus
+{
+ ONLINE_STATUS_UNKNOWN = -1,
+ ONLINE_STATUS_NO = 0,
+ ONLINE_STATUS_YES = 1
+};
+
+class LLPanelAvatarFirstLife
+: public LLPanel
+{
+public:
+ LLPanelAvatarFirstLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarFirstLife();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+
+class LLPanelAvatarSecondLife
+: public LLPanel
+{
+public:
+ LLPanelAvatarSecondLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar );
+ /*virtual*/ ~LLPanelAvatarSecondLife();
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void draw();
+
+ static void onClickImage( void *userdata);
+ static void onClickFriends( void *userdata);
+ static void onDoubleClickGroup(void* userdata);
+ static void onClickPublishHelp(void *userdata);
+
+ // Clear out the controls anticipating new network data.
+ void clearControls();
+ void enableControls(BOOL own_avatar);
+ void updateOnlineText(BOOL online, BOOL have_calling_card);
+ void updatePartnerName();
+
+ void setPartnerID(LLUUID id) { mPartnerID = id; }
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+
+ LLUUID mPartnerID;
+};
+
+// WARNING! The order of the inheritance here matters!! Do not change. - KLW
+class LLPanelAvatarWeb :
+ public LLPanel
+#if LL_LIBXUL_ENABLED
+ , public LLEmbeddedBrowserWindowObserver
+#endif
+{
+public:
+ LLPanelAvatarWeb(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarWeb();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+
+ void setWebURL(std::string url);
+
+ void load();
+ void load(std::string url);
+ static void onClickLoad(void* data);
+ static void onClickOpen(void* data);
+ static void onCommitURL(LLUICtrl* ctrl, void* data);
+ static void onClickWebProfileHelp(void *);
+
+#if LL_LIBXUL_ENABLED
+ // browser observer impls
+ virtual void onStatusTextChange( const EventType& eventIn );
+ virtual void onLocationChange( const EventType& eventIn );
+#endif
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+ std::string mURL;
+ LLWebBrowserCtrl* mWebBrowser;
+};
+
+class LLPanelAvatarAdvanced : public LLPanel
+{
+public:
+ LLPanelAvatarAdvanced(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarAdvanced();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+ void setWantSkills(U32 want_to_mask, const std::string& want_to_text,
+ U32 skills_mask, const std::string& skills_text,
+ const std::string& languages_text);
+ void getWantSkills(U32* want_to_mask, std::string& want_to_text,
+ U32* skills_mask, std::string& skills_text,
+ std::string& languages_text);
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+ S32 mWantToCount;
+ S32 mSkillsCount;
+ LLCheckBoxCtrl *mWantToCheck[8];
+ LLLineEditor *mWantToEdit;
+ LLCheckBoxCtrl *mSkillsCheck[8];
+ LLLineEditor *mSkillsEdit;
+};
+
+
+class LLPanelAvatarNotes : public LLPanel
+{
+public:
+ LLPanelAvatarNotes(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarNotes();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+
+ static void onCommitNotes(LLUICtrl* field, void* userdata);
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+
+class LLPanelAvatarClassified : public LLPanel
+{
+public:
+ LLPanelAvatarClassified(const LLString& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarClassified();
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ void apply();
+ void enableControls(BOOL own_avatar);
+
+ // Delete all the classified sub-panels from the tab container
+ void deleteClassifiedPanels();
+
+ // Unpack the outline of classified for this avatar (count, names, but not
+ // actual data).
+ void processAvatarClassifiedReply(LLMessageSystem* msg, void**);
+
+private:
+ static void onClickNew(void* data);
+ static void onClickDelete(void* data);
+
+ static void callbackDelete(S32 option, void* data);
+ static void callbackNew(S32 option, void* data);
+
+private:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+class LLPanelAvatarPicks : public LLPanel
+{
+public:
+ LLPanelAvatarPicks(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarPicks();
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ void enableControls(BOOL own_avatar);
+
+ // Delete all the pick sub-panels from the tab container
+ void deletePickPanels();
+
+ // Unpack the outline of picks for this avatar (count, names, but not
+ // actual data).
+ void processAvatarPicksReply(LLMessageSystem* msg, void**);
+ void processAvatarClassifiedReply(LLMessageSystem* msg, void**);
+
+private:
+ static void onClickNew(void* data);
+ static void onClickDelete(void* data);
+
+ static void callbackDelete(S32 option, void* data);
+
+private:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+
+class LLPanelAvatar : public LLPanel
+{
+public:
+ LLPanelAvatar(const std::string& name, const LLRect &rect, BOOL allow_edit);
+ /*virtual*/ ~LLPanelAvatar();
+
+ /*virtual*/ BOOL postBuild(void);
+
+ void setAvatar(LLViewerObject *avatarp);
+
+ // Fill in the avatar ID and handle some field fill-in, as well as
+ // button enablement.
+ // Pass one of the ONLINE_STATUS_foo constants above.
+ void setAvatarID(const LLUUID &avatar_id, const LLString &name, EOnlineStatus online_status);
+
+ const LLUUID& getAvatarID() const { return mAvatarID; }
+
+ void disableRate();
+
+ void resetGroupList();
+
+ void sendAvatarStatisticsRequest();
+
+ void sendAvatarPropertiesRequest();
+ void sendAvatarPropertiesUpdate();
+
+ void sendAvatarNotesRequest();
+ void sendAvatarNotesUpdate();
+
+ void sendAvatarPicksRequest();
+
+ void selectTab(S32 tabnum);
+ void selectTabByName(std::string tab_name);
+
+ BOOL haveData() { return mHaveProperties && mHaveStatistics; }
+
+ static void processAvatarPropertiesReply(LLMessageSystem *msg, void **);
+ static void processAvatarInterestsReply(LLMessageSystem *msg, void **);
+ static void processAvatarGroupsReply(LLMessageSystem* msg, void**);
+ static void processAvatarStatisticsReply(LLMessageSystem *msg, void **);
+ static void processAvatarNotesReply(LLMessageSystem *msg, void **);
+ static void processAvatarPicksReply(LLMessageSystem *msg, void **);
+ static void processAvatarClassifiedReply(LLMessageSystem *msg, void **);
+
+ static void onClickTrack( void *userdata);
+ static void onClickIM( void *userdata);
+ static void onClickOfferTeleport( void *userdata);
+ static void onClickPay( void *userdata);
+ static void onClickRate( void *userdata);
+ static void onClickOK( void *userdata);
+ static void onClickCancel( void *userdata);
+ static void onClickKick( void *userdata);
+ static void onClickFreeze( void *userdata);
+ static void onClickUnfreeze(void *userdata);
+ static void onClickCSR( void *userdata);
+ static void onClickMute( void *userdata);
+ static void onClickAddFriend(void* data);
+
+ static void finishKick(S32 option, const LLString& text, void* userdata);
+ static void finishFreeze(S32 option, const LLString& text, void* userdata);
+ static void finishUnfreeze(S32 option, const LLString& text, void* userdata);
+
+ static void showProfileCallback(S32 option, void *userdata);
+
+ // Teen users are not allowed to see or enter data into the first life page,
+ // or their own about/interests text entry fields.
+ static BOOL sAllowFirstLife;
+
+ static void* createPanelAvatar(void* data);
+ static void* createFloaterAvatarInfo(void* data);
+ static void* createPanelAvatarSecondLife(void* data);
+ static void* createPanelAvatarWeb(void* data);
+ static void* createPanelAvatarInterests(void* data);
+ static void* createPanelAvatarPicks(void* data);
+ static void* createPanelAvatarClassified(void* data);
+ static void* createPanelAvatarFirstLife(void* data);
+ static void* createPanelAvatarNotes(void* data);
+
+public:
+ LLPanelAvatarSecondLife* mPanelSecondLife;
+ LLPanelAvatarAdvanced* mPanelAdvanced;
+ LLPanelAvatarClassified* mPanelClassified;
+ LLPanelAvatarPicks* mPanelPicks;
+ LLPanelAvatarNotes* mPanelNotes;
+ LLPanelAvatarFirstLife* mPanelFirstLife;
+ LLPanelAvatarWeb* mPanelWeb;
+
+ LLDropTarget* mDropTarget;
+
+protected:
+ void enableOKIfReady();
+ LLUUID mAvatarID; // for which avatar is this window?
+ BOOL mIsFriend; // Are we friends?
+ BOOL mHaveProperties;
+ BOOL mHaveStatistics;
+ LLTabContainerCommon* mTab;
+ BOOL mAllowEdit;
+ BOOL mDisableRate;
+
+ static LLLinkedList<LLPanelAvatar> sAllPanels;
+};
+
+// helper funcs
+void add_left_label(LLPanel *panel, const LLString& name, S32 y);
+
+
+#endif // LL_LLPANELAVATAR_H
diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp
new file mode 100644
index 0000000000..7c84d0f6a5
--- /dev/null
+++ b/indra/newview/llpanelclassified.cpp
@@ -0,0 +1,852 @@
+/**
+ * @file llpanelclassified.cpp
+ * @brief LLPanelClassified class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a classified used both for the global view in the
+// Find directory, and also for each individual user's classified in their
+// profile.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelclassified.h"
+
+#include "lldir.h"
+#include "lldispatcher.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llclassifiedflags.h"
+#include "llviewercontrol.h"
+#include "lllineeditor.h"
+#include "llfloateravatarinfo.h"
+#include "lltabcontainervertical.h"
+#include "lltextbox.h"
+#include "llcombobox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "lluiconstants.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llworldmap.h"
+#include "llfloaterworldmap.h"
+#include "llviewermessage.h" // send_generic_message
+#include "llviewerwindow.h" // for window width, height
+
+const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$
+
+// "classifiedclickthrough"
+// strings[0] = classified_id
+// strings[1] = teleport_clicks
+// strings[2] = map_clicks
+// strings[3] = profile_clicks
+class LLDispatchClassifiedClickThrough : public LLDispatchHandler
+{
+public:
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+ {
+ if (strings.size() != 4) return false;
+ LLUUID classified_id(strings[0]);
+ S32 teleport_clicks = atoi(strings[1].c_str());
+ S32 map_clicks = atoi(strings[2].c_str());
+ S32 profile_clicks = atoi(strings[3].c_str());
+ LLPanelClassified::setClickThrough(classified_id, teleport_clicks,
+ map_clicks,
+ profile_clicks);
+ return true;
+ }
+};
+
+static LLDispatchClassifiedClickThrough sClassifiedClickThrough;
+
+//static
+LLLinkedList<LLPanelClassified> LLPanelClassified::sAllPanels;
+
+LLPanelClassified::LLPanelClassified(BOOL in_finder)
+: LLPanel("Classified Panel"),
+ mInFinder(in_finder),
+ mClassifiedID(),
+ mCreatorID(),
+ mPriceForListing(0),
+ mDataRequested(FALSE),
+ mEnableCommit(FALSE),
+ mPaidFor(FALSE),
+ mParcelID(),
+ mPosGlobal(),
+ mSnapshotCtrl(NULL),
+ mNameEditor(NULL),
+ mLocationEditor(NULL),
+ mDescEditor(NULL),
+ mTeleportBtn(NULL),
+ mMapBtn(NULL),
+ //mLandmarkBtn(NULL),
+ //mEnabledCheck(NULL),
+ mMatureCheck(NULL),
+ mAutoRenewCheck(NULL),
+ mSetBtn(NULL),
+ mClickThroughText(NULL)
+{
+ sAllPanels.addData(this);
+
+ std::string classified_def_file;
+ if (mInFinder)
+ {
+ gUICtrlFactory->buildPanel(this, "panel_classified.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildPanel(this, "panel_avatar_classified.xml");
+ }
+
+ // Register dispatcher
+ gGenericDispatcher.addHandler("classifiedclickthrough",
+ &sClassifiedClickThrough);
+}
+
+
+LLPanelClassified::~LLPanelClassified()
+{
+ sAllPanels.removeData(this);
+}
+
+
+void LLPanelClassified::reset()
+{
+ mClassifiedID.setNull();
+ mCreatorID.setNull();
+ mParcelID.setNull();
+
+ // Don't request data, this isn't valid
+ mDataRequested = TRUE;
+
+ mEnableCommit = FALSE;
+ mPaidFor = FALSE;
+
+ mPosGlobal.clearVec();
+
+ clearCtrls();
+}
+
+
+BOOL LLPanelClassified::postBuild()
+{
+ mSnapshotCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setCommitCallback(onCommitAny);
+ mSnapshotCtrl->setCallbackUserData(this);
+ mSnapshotSize = mSnapshotCtrl->getRect();
+
+ mNameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "given_name_editor");
+ mNameEditor->setMaxTextLength(DB_PARCEL_NAME_LEN);
+ mNameEditor->setCommitOnFocusLost(TRUE);
+ mNameEditor->setFocusReceivedCallback(onFocusReceived);
+ mNameEditor->setCommitCallback(onCommitAny);
+ mNameEditor->setCallbackUserData(this);
+ mNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII );
+
+ mDescEditor = LLUICtrlFactory::getTextEditorByName(this, "desc_editor");
+ mDescEditor->setCommitOnFocusLost(TRUE);
+ mDescEditor->setFocusReceivedCallback(onFocusReceived);
+ mDescEditor->setCommitCallback(onCommitAny);
+ mDescEditor->setCallbackUserData(this);
+ mDescEditor->setTabToNextField(TRUE);
+
+ mLocationEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "location_editor");
+
+ mSetBtn = LLViewerUICtrlFactory::getButtonByName(this, "set_location_btn");
+ mSetBtn->setClickedCallback(onClickSet);
+ mSetBtn->setCallbackUserData(this);
+
+ mTeleportBtn = LLViewerUICtrlFactory::getButtonByName(this, "classified_teleport_btn");
+ mTeleportBtn->setClickedCallback(onClickTeleport);
+ mTeleportBtn->setCallbackUserData(this);
+
+ mMapBtn = LLViewerUICtrlFactory::getButtonByName(this, "classified_map_btn");
+ mMapBtn->setClickedCallback(onClickMap);
+ mMapBtn->setCallbackUserData(this);
+
+ if(mInFinder)
+ {
+ mProfileBtn = LLViewerUICtrlFactory::getButtonByName(this, "classified_profile_btn");
+ mProfileBtn->setClickedCallback(onClickProfile);
+ mProfileBtn->setCallbackUserData(this);
+ }
+
+ mCategoryCombo = LLViewerUICtrlFactory::getComboBoxByName(this, "classified_category_combo");
+ LLClassifiedInfo::cat_map::iterator iter;
+ for (iter = LLClassifiedInfo::sCategories.begin();
+ iter != LLClassifiedInfo::sCategories.end();
+ iter++)
+ {
+ mCategoryCombo->add(iter->second, (void *)((intptr_t)iter->first), ADD_BOTTOM);
+ }
+ mCategoryCombo->setCurrentByIndex(0);
+ mCategoryCombo->setCommitCallback(onCommitAny);
+ mCategoryCombo->setCallbackUserData(this);
+
+ mMatureCheck = LLViewerUICtrlFactory::getCheckBoxByName(this, "classified_mature_check");
+ mMatureCheck->setCommitCallback(onCommitAny);
+ mMatureCheck->setCallbackUserData(this);
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Teens don't get to set mature flag. JC
+ mMatureCheck->setVisible(FALSE);
+ }
+
+ if (!mInFinder)
+ {
+ mAutoRenewCheck = LLUICtrlFactory::getCheckBoxByName(this, "auto_renew_check");
+ mAutoRenewCheck->setCommitCallback(onCommitAny);
+ mAutoRenewCheck->setCallbackUserData(this);
+ }
+
+ mUpdateBtn = LLUICtrlFactory::getButtonByName(this, "classified_update_btn");
+ mUpdateBtn->setClickedCallback(onClickUpdate);
+ mUpdateBtn->setCallbackUserData(this);
+ mEnableCommit = TRUE;
+
+ if (!mInFinder)
+ {
+ mClickThroughText = LLUICtrlFactory::getTextBoxByName(this, "click_through_text");
+ }
+
+ return TRUE;
+}
+
+
+void LLPanelClassified::apply()
+{
+ // Apply is used for automatically saving results, so only
+ // do that if there is a difference, and this is a save not create.
+ if (mEnableCommit && mPaidFor)
+ {
+ sendClassifiedInfoUpdate();
+ }
+}
+
+// Fill in some reasonable defaults for a new classified.
+void LLPanelClassified::initNewClassified()
+{
+ // TODO: Don't generate this on the client.
+ mClassifiedID.generate();
+
+ mCreatorID = gAgent.getID();
+
+ mPosGlobal = gAgent.getPositionGlobal();
+
+ mPaidFor = FALSE;
+
+ // Try to fill in the current parcel
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
+ {
+ mNameEditor->setText(parcel->getName());
+ //mDescEditor->setText(parcel->getDesc());
+ mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
+ //mPriceEditor->setText("0");
+ mCategoryCombo->setCurrentByIndex(0);
+ }
+
+ // default new classifieds to publish
+ //mEnabledCheck->set(TRUE);
+
+ // delay commit until user hits save
+ // sendClassifiedInfoUpdate();
+
+ mUpdateBtn->setLabelSelected("Publish...");
+ mUpdateBtn->setLabelUnselected("Publish...");
+}
+
+
+void LLPanelClassified::setClassifiedID(const LLUUID& id)
+{
+ mClassifiedID = id;
+}
+
+
+//static
+void LLPanelClassified::setClickThrough(const LLUUID& classified_id,
+ S32 teleport,
+ S32 map,
+ S32 profile)
+{
+ LLPanelClassified *self = NULL;
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ // For top picks, must match pick id
+ if (self->mClassifiedID != classified_id)
+ {
+ continue;
+ }
+
+ if (self->mClickThroughText)
+ {
+ std::string msg = llformat("Clicks: %d teleport, %d map, %d profile",
+ teleport,
+ map,
+ profile);
+ self->mClickThroughText->setText(msg);
+ }
+ }
+}
+
+// Schedules the panel to request data
+// from the server next time it is drawn.
+void LLPanelClassified::markForServerRequest()
+{
+ mDataRequested = FALSE;
+}
+
+
+std::string LLPanelClassified::getClassifiedName()
+{
+ return mNameEditor->getText();
+}
+
+
+void LLPanelClassified::sendClassifiedInfoRequest()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ if (mClassifiedID != mRequestedID)
+ {
+ msg->newMessageFast(_PREHASH_ClassifiedInfoRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_ClassifiedID, mClassifiedID);
+ gAgent.sendReliableMessage();
+
+ mDataRequested = TRUE;
+ mRequestedID = mClassifiedID;
+ }
+}
+
+
+void LLPanelClassified::sendClassifiedInfoUpdate()
+{
+ // If we don't have a classified id yet, we'll need to generate one,
+ // otherwise we'll keep overwriting classified_id 00000 in the database.
+ if (mClassifiedID.isNull())
+ {
+ // TODO: Don't do this on the client.
+ mClassifiedID.generate();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ClassifiedInfoUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_ClassifiedID, mClassifiedID);
+ // TODO: fix this
+ U32 category = mCategoryCombo->getCurrentIndex() + 1;
+ msg->addU32Fast(_PREHASH_Category, category);
+ msg->addStringFast(_PREHASH_Name, mNameEditor->getText());
+ msg->addStringFast(_PREHASH_Desc, mDescEditor->getText());
+
+ // fills in on simulator if null
+ msg->addUUIDFast(_PREHASH_ParcelID, mParcelID);
+ // fills in on simulator if null
+ msg->addU32Fast(_PREHASH_ParentEstate, 0);
+ msg->addUUIDFast(_PREHASH_SnapshotID, mSnapshotCtrl->getImageAssetID());
+ msg->addVector3dFast(_PREHASH_PosGlobal, mPosGlobal);
+ BOOL mature = mMatureCheck->get();
+ // Classifieds are always enabled/published 11/2005 JC
+ //BOOL enabled = mEnabledCheck->get();
+ BOOL auto_renew = FALSE;
+ if (mAutoRenewCheck)
+ {
+ auto_renew = mAutoRenewCheck->get();
+ }
+ U8 flags = pack_classified_flags(mature, auto_renew);
+ msg->addU8Fast(_PREHASH_ClassifiedFlags, flags);
+ msg->addS32("PriceForListing", mPriceForListing);
+ gAgent.sendReliableMessage();
+}
+
+
+//static
+void LLPanelClassified::processClassifiedInfoReply(LLMessageSystem *msg, void **)
+{
+ lldebugs << "processClassifiedInfoReply()" << llendl;
+ // Extract the agent id and verify the message is for this
+ // client.
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Agent ID mismatch in processClassifiedInfoReply"
+ << llendl;
+ return;
+ }
+
+ LLUUID classified_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ClassifiedID, classified_id);
+
+ LLUUID creator_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_CreatorID, creator_id);
+
+ LLUUID parcel_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ParcelID, parcel_id);
+
+ char name[DB_PARCEL_NAME_SIZE];
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, DB_PARCEL_NAME_SIZE, name);
+
+ char desc[DB_PICK_DESC_SIZE];
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Desc, DB_PICK_DESC_SIZE, desc);
+
+ LLUUID snapshot_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_SnapshotID, snapshot_id);
+
+ // "Location text" is actually the original
+ // name that owner gave the parcel, and the location.
+ char buffer[256];
+ LLString location_text;
+
+ msg->getStringFast(_PREHASH_Data, _PREHASH_ParcelName, 256, buffer);
+ if (buffer[0] != '\0')
+ {
+ location_text.assign(buffer);
+ location_text.append(", ");
+ }
+ else
+ {
+ location_text.assign("");
+ }
+
+ char sim_name[256];
+ msg->getStringFast(_PREHASH_Data, _PREHASH_SimName, 256, sim_name);
+
+ LLVector3d pos_global;
+ msg->getVector3dFast(_PREHASH_Data, _PREHASH_PosGlobal, pos_global);
+
+ S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)pos_global.mdV[VZ]);
+
+ sprintf(buffer, "%s (%d, %d, %d)", sim_name, region_x, region_y, region_z);
+ location_text.append(buffer);
+
+ U8 flags;
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_ClassifiedFlags, flags);
+ //BOOL enabled = is_cf_enabled(flags);
+ bool mature = is_cf_mature(flags);
+ bool auto_renew = is_cf_auto_renew(flags);
+
+ U32 date = 0;
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_CreationDate, date);
+ time_t tim = date;
+ tm *now=localtime(&tim);
+
+ // future use
+ U32 expiration_date = 0;
+ msg->getU32("Data", "ExpirationDate", expiration_date);
+
+ U32 category = 0;
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_Category, category);
+
+ S32 price_for_listing = 0;
+ msg->getS32("Data", "PriceForListing", price_for_listing);
+
+ // Look up the panel to fill in
+ LLPanelClassified *self = NULL;
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ // For top picks, must match pick id
+ if (self->mClassifiedID != classified_id)
+ {
+ continue;
+ }
+
+ // Found the panel, now fill in the information
+ self->mClassifiedID = classified_id;
+ self->mCreatorID = creator_id;
+ self->mParcelID = parcel_id;
+ self->mPriceForListing = price_for_listing;
+ self->mSimName.assign(sim_name);
+ self->mPosGlobal = pos_global;
+
+ // Update UI controls
+ self->mNameEditor->setText(name);
+ self->mDescEditor->setText(desc);
+ self->mSnapshotCtrl->setImageAssetID(snapshot_id);
+ self->mLocationEditor->setText(location_text);
+
+ self->mCategoryCombo->setCurrentByIndex(category - 1);
+ self->mMatureCheck->set(mature);
+ if (self->mAutoRenewCheck)
+ {
+ self->mAutoRenewCheck->set(auto_renew);
+ }
+
+ LLString datestr = llformat("%02d/%02d/%d", now->tm_mon+1, now->tm_mday, now->tm_year+1900);
+ self->childSetTextArg("ad_placed_paid", "[DATE]", datestr);
+ self->childSetTextArg("ad_placed_paid", "[AMT]", llformat("%d", price_for_listing));
+ self->childSetText("classified_info_text", self->childGetValue("ad_placed_paid").asString());
+
+ // If we got data from the database, we know the listing is paid for.
+ self->mPaidFor = TRUE;
+
+ self->mUpdateBtn->setLabelSelected("Update");
+ self->mUpdateBtn->setLabelUnselected("Update");
+ }
+}
+
+void LLPanelClassified::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelClassified::refresh()
+{
+ if (!mDataRequested)
+ {
+ sendClassifiedInfoRequest();
+ }
+
+ // Check for god mode
+ BOOL godlike = gAgent.isGodlike();
+ BOOL is_self = (gAgent.getID() == mCreatorID);
+
+ // Set button visibility/enablement appropriately
+ if (mInFinder)
+ {
+
+ // End user doesn't ned to see price twice, or date posted.
+
+ mSnapshotCtrl->setEnabled(godlike);
+ if(godlike)
+ {
+ //make it smaller, so text is more legible
+ mSnapshotCtrl->setRect(LLRect(20, 375, 320, 175));
+
+ }
+ else
+ {
+ mSnapshotCtrl->setRect(mSnapshotSize);
+ //normal
+ }
+ mNameEditor->setEnabled(godlike);
+ mDescEditor->setEnabled(godlike);
+ mCategoryCombo->setEnabled(godlike);
+ mCategoryCombo->setVisible(godlike);
+
+ //mEnabledCheck->setVisible(godlike);
+ //mEnabledCheck->setEnabled(godlike);
+
+ mMatureCheck->setEnabled(godlike);
+ mMatureCheck->setVisible(godlike);
+
+ // Jesse (who is the only one who uses this, as far as we can tell
+ // Says that he does not want a set location button - he has used it
+ // accidently in the past.
+ mSetBtn->setVisible(FALSE);
+ mSetBtn->setEnabled(FALSE);
+
+ mUpdateBtn->setEnabled(godlike);
+ mUpdateBtn->setVisible(godlike);
+ }
+ else
+ {
+ mSnapshotCtrl->setEnabled(is_self);
+ mNameEditor->setEnabled(is_self);
+ mDescEditor->setEnabled(is_self);
+ //mPriceEditor->setEnabled(is_self);
+ mCategoryCombo->setEnabled(is_self);
+
+ //mEnabledCheck->setVisible(FALSE);
+ //mEnabledCheck->setEnabled(is_self);
+ mMatureCheck->setEnabled(is_self);
+
+ mAutoRenewCheck->setEnabled(is_self);
+ mAutoRenewCheck->setVisible(is_self);
+
+ mClickThroughText->setEnabled(is_self);
+ mClickThroughText->setVisible(is_self);
+
+ mSetBtn->setVisible(is_self);
+ mSetBtn->setEnabled(is_self);
+
+ mUpdateBtn->setEnabled(is_self && mEnableCommit);
+ mUpdateBtn->setVisible(is_self);
+ }
+}
+
+// static
+void LLPanelClassified::onClickUpdate(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ if(self == NULL) return;
+
+ // Disallow leading spaces, punctuation, etc. that screw up
+ // sort order.
+ const LLString& name = self->mNameEditor->getText();
+ if (name.empty())
+ {
+ gViewerWindow->alertXml("BlankClassifiedName");
+ return;
+ }
+ if (!isalnum(name[0]))
+ {
+ gViewerWindow->alertXml("ClassifiedMustBeAlphanumeric");
+ return;
+ }
+
+ // if already paid for, just do the update
+ if (self->mPaidFor)
+ {
+ callbackConfirmPublish(0, self);
+ }
+ else
+ {
+ // Ask the user how much they want to pay
+ LLFloaterPriceForListing::show( callbackGotPriceForListing, self );
+ }
+}
+
+// static
+void LLPanelClassified::callbackGotPriceForListing(S32 option, LLString text, void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ // Only do something if user hits publish
+ if (option != 0) return;
+
+ S32 price_for_listing = strtol(text.c_str(), NULL, 10);
+ if (price_for_listing < MINIMUM_PRICE_FOR_LISTING)
+ {
+ LLString::format_map_t args;
+ LLString price_text = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
+ args["[MIN_PRICE]"] = price_text;
+
+ gViewerWindow->alertXml("MinClassifiedPrice", args);
+ return;
+ }
+
+ // price is acceptable, put it in the dialog for later read by
+ // update send
+ self->mPriceForListing = price_for_listing;
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d", price_for_listing);
+ gViewerWindow->alertXml("PublishClassified", args, &callbackConfirmPublish, self);
+
+}
+
+// static
+void LLPanelClassified::callbackConfirmPublish(S32 option, void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ // Option 0 = publish
+ if (option != 0) return;
+
+ self->sendClassifiedInfoUpdate();
+
+ // Big hack - assume that top picks are always in a browser,
+ // and non-finder-classifieds are always in a tab container.
+ if (self->mInFinder)
+ {
+ // TODO: enable this
+ //LLPanelDirClassifieds* panel = (LLPanelDirClassifieds*)self->getParent();
+ //panel->renameClassified(self->mClassifiedID, self->mNameEditor->getText().c_str());
+ }
+ else
+ {
+ LLTabContainerVertical* tab = (LLTabContainerVertical*)self->getParent();
+ tab->setCurrentTabName(self->mNameEditor->getText());
+ }
+ self->mEnableCommit = FALSE;
+}
+
+// static
+void LLPanelClassified::onClickTeleport(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gAgent.teleportViaLocation(self->mPosGlobal);
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+
+ self->sendClassifiedClickMessage("teleport");
+ }
+}
+
+
+// static
+void LLPanelClassified::onClickMap(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ LLFloaterWorldMap::show(NULL, TRUE);
+
+ self->sendClassifiedClickMessage("map");
+}
+
+// static
+void LLPanelClassified::onClickProfile(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ LLFloaterAvatarInfo::showFromDirectory(self->mCreatorID);
+ self->sendClassifiedClickMessage("profile");
+}
+
+// static
+/*
+void LLPanelClassified::onClickLandmark(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ create_landmark(self->mNameEditor->getText().c_str(), "", self->mPosGlobal);
+}
+*/
+
+// static
+void LLPanelClassified::onClickSet(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ // Save location for later.
+ self->mPosGlobal = gAgent.getPositionGlobal();
+
+ LLString location_text;
+ location_text.assign("(will update after save)");
+ location_text.append(", ");
+
+ S32 region_x = llround((F32)self->mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)self->mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)self->mPosGlobal.mdV[VZ]);
+
+ location_text.append(self->mSimName);
+ location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z));
+
+ self->mLocationEditor->setText(location_text);
+
+ // Set this to null so it updates on the next save.
+ self->mParcelID.setNull();
+
+ onCommitAny(NULL, data);
+}
+
+
+// static
+void LLPanelClassified::onCommitAny(LLUICtrl* ctrl, void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ if (self)
+ {
+ self->mEnableCommit = TRUE;
+ }
+}
+
+// static
+void LLPanelClassified::onFocusReceived(LLUICtrl* ctrl, void* data)
+{
+ // first, allow the data to be saved
+ onCommitAny(ctrl, data);
+}
+
+
+void LLPanelClassified::sendClassifiedClickMessage(const char* type)
+{
+ // You're allowed to click on your own ads to reassure yourself
+ // that the system is working.
+ std::vector<std::string> strings;
+ strings.push_back(mClassifiedID.getString());
+ strings.push_back(type);
+ LLUUID no_invoice;
+ send_generic_message("classifiedclick", strings, no_invoice);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+LLFloaterPriceForListing::LLFloaterPriceForListing()
+: LLFloater("PriceForListing"),
+ mCallback(NULL),
+ mUserData(NULL)
+{ }
+
+//virtual
+LLFloaterPriceForListing::~LLFloaterPriceForListing()
+{ }
+
+//virtual
+BOOL LLFloaterPriceForListing::postBuild()
+{
+ LLLineEditor* edit = LLUICtrlFactory::getLineEditorByName(this, "price_edit");
+ if (edit)
+ {
+ edit->setPrevalidate(LLLineEditor::prevalidateNonNegativeS32);
+ LLString min_price = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
+ edit->setText(min_price);
+ edit->selectAll();
+ edit->setFocus(TRUE);
+ }
+
+ childSetAction("set_price_btn", onClickSetPrice, this);
+
+ childSetAction("cancel_btn", onClickCancel, this);
+
+ setDefaultBtn("set_price_btn");
+ return TRUE;
+}
+
+//static
+void LLFloaterPriceForListing::show( void (*callback)(S32, LLString, void*), void* userdata)
+{
+ LLFloaterPriceForListing *self = new LLFloaterPriceForListing();
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(self, "floater_price_for_listing.xml");
+ self->center();
+
+ self->mCallback = callback;
+ self->mUserData = userdata;
+}
+
+//static
+void LLFloaterPriceForListing::onClickSetPrice(void* data)
+{
+ buttonCore(0, data);
+}
+
+//static
+void LLFloaterPriceForListing::onClickCancel(void* data)
+{
+ buttonCore(1, data);
+}
+
+//static
+void LLFloaterPriceForListing::buttonCore(S32 button, void* data)
+{
+ LLFloaterPriceForListing* self = (LLFloaterPriceForListing*)data;
+
+ if (self->mCallback)
+ {
+ LLString text = self->childGetText("price_edit");
+ self->mCallback(button, text, self->mUserData);
+ self->close();
+ }
+}
diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h
new file mode 100644
index 0000000000..e3e7a299bf
--- /dev/null
+++ b/indra/newview/llpanelclassified.h
@@ -0,0 +1,148 @@
+/**
+ * @file llpanelclassified.h
+ * @brief LLPanelClassified class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a classified used both for the global view in the
+// Find directory, and also for each individual user's classified in their
+// profile.
+
+#ifndef LL_LLPANELCLASSIFIED_H
+#define LL_LLPANELCLASSIFIED_H
+
+#include "llpanel.h"
+#include "llclassifiedinfo.h"
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "llfloater.h"
+//#include "llrect.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLIconCtrl;
+class LLLineEditor;
+class LLTextBox;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLMessageSystem;
+
+class LLPanelClassified : public LLPanel
+{
+public:
+ LLPanelClassified(BOOL in_finder);
+ /*virtual*/ ~LLPanelClassified();
+
+ void reset();
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ void apply();
+
+ // Setup a new classified, including creating an id, giving a sane
+ // initial position, etc.
+ void initNewClassified();
+
+ void setClassifiedID(const LLUUID& id);
+ static void setClickThrough(const LLUUID& classified_id,
+ S32 teleport, S32 map, S32 profile);
+
+ // Schedules the panel to request data
+ // from the server next time it is drawn.
+ void markForServerRequest();
+
+ std::string getClassifiedName();
+ const LLUUID& getClassifiedID() const { return mClassifiedID; }
+
+ void sendClassifiedInfoRequest();
+ void sendClassifiedInfoUpdate();
+
+ static void processClassifiedInfoReply(LLMessageSystem* msg, void**);
+
+ static void callbackGotPriceForListing(S32 option, LLString text, void* data);
+ static void callbackConfirmPublish(S32 option, void* data);
+
+protected:
+ static void onClickUpdate(void* data);
+ static void onClickTeleport(void* data);
+ static void onClickMap(void* data);
+ static void onClickProfile(void* data);
+ static void onClickSet(void* data);
+
+ static void onFocusReceived(LLUICtrl* ctrl, void* data);
+ static void onCommitAny(LLUICtrl* ctrl, void* data);
+
+ void sendClassifiedClickMessage(const char* type);
+
+protected:
+ BOOL mInFinder;
+ LLUUID mClassifiedID;
+ LLUUID mRequestedID;
+ LLUUID mCreatorID;
+ LLUUID mParcelID;
+ S32 mPriceForListing;
+
+ // Data will be requested on first draw
+ BOOL mDataRequested;
+ BOOL mEnableCommit;
+
+ // For avatar panel classifieds only, has the user been charged
+ // yet for this classified? That is, have they saved once?
+ BOOL mPaidFor;
+
+ LLString mSimName;
+ LLVector3d mPosGlobal;
+
+ LLTextureCtrl* mSnapshotCtrl;
+ LLLineEditor* mNameEditor;
+ LLLineEditor* mDateEditor;
+ LLTextEditor* mDescEditor;
+ LLLineEditor* mLocationEditor;
+ LLComboBox* mCategoryCombo;
+
+ LLButton* mUpdateBtn;
+ LLButton* mTeleportBtn;
+ LLButton* mMapBtn;
+ LLButton* mProfileBtn;
+
+ LLTextBox* mInfoText;
+ LLCheckBoxCtrl* mMatureCheck;
+ LLCheckBoxCtrl* mAutoRenewCheck;
+ LLButton* mSetBtn;
+ LLTextBox* mClickThroughText;
+
+ LLRect mSnapshotSize;
+ static LLLinkedList<LLPanelClassified> sAllPanels;
+};
+
+
+class LLFloaterPriceForListing
+: public LLFloater
+{
+public:
+ LLFloaterPriceForListing();
+ virtual ~LLFloaterPriceForListing();
+ virtual BOOL postBuild();
+
+ static void show( void (*callback)(S32 option, LLString value, void* userdata), void* userdata );
+
+private:
+ static void onClickSetPrice(void*);
+ static void onClickCancel(void*);
+ static void buttonCore(S32 button, void* data);
+
+private:
+ void (*mCallback)(S32 option, LLString, void*);
+ void* mUserData;
+};
+
+
+#endif // LL_LLPANELCLASSIFIED_H
diff --git a/indra/newview/llpanelcontents.cpp b/indra/newview/llpanelcontents.cpp
new file mode 100644
index 0000000000..ad3c3d42b0
--- /dev/null
+++ b/indra/newview/llpanelcontents.cpp
@@ -0,0 +1,189 @@
+/**
+ * @file llpanelcontents.cpp
+ * @brief Object contents panel in the tools floater.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelcontents.h"
+
+// linden library includes
+#include "llerror.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "llmaterialtable.h"
+#include "llfontgl.h"
+#include "m3math.h"
+#include "llpermissionsflags.h"
+#include "lleconomy.h"
+#include "material_codes.h"
+
+// project includes
+#include "llui.h"
+#include "llspinctrl.h"
+#include "llcheckboxctrl.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llresmgr.h"
+#include "lltexturetable.h"
+#include "llselectmgr.h"
+#include "llpreviewscript.h"
+#include "lltool.h"
+#include "lltoolmgr.h"
+#include "lltoolcomp.h"
+#include "llpanelinventory.h"
+#include "viewer.h"
+
+//
+// Imported globals
+//
+
+
+//
+// Globals
+//
+
+BOOL LLPanelContents::postBuild()
+{
+ LLRect rect = this->getRect();
+
+ setMouseOpaque(FALSE);
+
+ childSetAction("button new script",&LLPanelContents::onClickNewScript, this);
+
+ return TRUE;
+}
+
+LLPanelContents::LLPanelContents(const std::string& name)
+ : LLPanel(name),
+ mPanelInventory(NULL)
+{
+}
+
+
+LLPanelContents::~LLPanelContents()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+
+void LLPanelContents::getState(LLViewerObject *objectp )
+{
+ if( !objectp )
+ {
+ childSetEnabled("button new script",FALSE);
+ //mBtnNewScript->setEnabled( FALSE );
+ return;
+ }
+
+ // BUG? Check for all objects being editable?
+ BOOL editable = gAgent.isGodlike()
+ || (objectp->permModify() && objectp->permYouOwner());
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+
+ // Edit script button - ok if object is editable and there's an
+ // unambiguous destination for the object.
+ if( editable &&
+ all_volume &&
+ ((gSelectMgr->getRootObjectCount() == 1)
+ || (gSelectMgr->getObjectCount() == 1)))
+ {
+ //mBtnNewScript->setEnabled(TRUE);
+ childSetEnabled("button new script",TRUE);
+ }
+ else
+ {
+ //mBtnNewScript->setEnabled(FALSE);
+ childSetEnabled("button new script",FALSE);
+ }
+}
+
+
+void LLPanelContents::refresh()
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ }
+
+ getState(object);
+ if (mPanelInventory)
+ {
+ mPanelInventory->refresh();
+ }
+}
+
+
+
+//
+// Static functions
+//
+
+// static
+void LLPanelContents::onClickNewScript(void *userdata)
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ }
+ if(object)
+ {
+ // *HACK: In order to resolve SL-22177, we need to create the
+ // script first, and then you have to click it in inventory to
+ // edit it. Bring this back when the functionality is secure.
+ LLPermissions perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
+ perm.initMasks(
+ PERM_ALL,
+ PERM_ALL,
+ PERM_NONE,
+ PERM_NONE,
+ PERM_MOVE | PERM_TRANSFER);
+ LLString desc;
+ LLAssetType::generateDescriptionFor(LLAssetType::AT_LSL_TEXT, desc);
+ LLPointer<LLViewerInventoryItem> new_item =
+ new LLViewerInventoryItem(
+ LLUUID::null,
+ LLUUID::null,
+ perm,
+ LLUUID::null,
+ LLAssetType::AT_LSL_TEXT,
+ LLInventoryType::IT_LSL,
+ LLString("New Script"),
+ desc,
+ LLSaleInfo::DEFAULT,
+ LLViewerInventoryItem::II_FLAGS_NONE,
+ time_corrected());
+ object->saveScript(new_item, TRUE, true);
+#if 0
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewScriptRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLLiveLSLEditor* editor;
+ editor = new LLLiveLSLEditor("script ed",
+ rect,
+ "Script: New Script",
+ object->mID,
+ LLUUID::null);
+ editor->open();
+
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(editor, FALSE);
+#endif
+ }
+}
diff --git a/indra/newview/llpanelcontents.h b/indra/newview/llpanelcontents.h
new file mode 100644
index 0000000000..273ab20bc7
--- /dev/null
+++ b/indra/newview/llpanelcontents.h
@@ -0,0 +1,39 @@
+/**
+ * @file llpanelcontents.h
+ * @brief Object contents panel in the tools floater.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELCONTENTS_H
+#define LL_LLPANELCONTENTS_H
+
+#include "v3math.h"
+#include "llpanel.h"
+
+class LLButton;
+class LLPanelInventory;
+class LLViewerObject;
+class LLCheckBoxCtrl;
+class LLSpinCtrl;
+
+class LLPanelContents : public LLPanel
+{
+public:
+ virtual BOOL LLPanelContents::postBuild();
+ LLPanelContents(const std::string& name);
+ virtual ~LLPanelContents();
+
+ void refresh();
+
+ static void onClickNewScript( void* userdata);
+
+protected:
+ void getState(LLViewerObject *object);
+
+public:
+ LLPanelInventory* mPanelInventory;
+};
+
+#endif
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
new file mode 100644
index 0000000000..d023be66f2
--- /dev/null
+++ b/indra/newview/llpanelface.cpp
@@ -0,0 +1,956 @@
+/**
+ * @file llpanelface.cpp
+ * @brief Panel in the tools floater for editing face textures, colors, etc.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelface.h"
+
+// library includes
+#include "llerror.h"
+#include "llfocusmgr.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "llfontgl.h"
+
+// project includes
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcolorswatch.h"
+#include "llcombobox.h"
+#include "lldrawpoolbump.h"
+#include "lllineeditor.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "lltextureentry.h"
+#include "lltexturetable.h"
+#include "lltooldraganddrop.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "llviewerobject.h"
+#include "llviewerstats.h"
+#include "llmediaengine.h"
+
+#include "llvieweruictrlfactory.h"
+
+//
+// Methods
+//
+
+BOOL LLPanelFace::postBuild()
+{
+ LLRect rect = this->getRect();
+ LLTextureCtrl* mTextureCtrl;
+ LLColorSwatchCtrl* mColorSwatch;
+
+ #if LL_MOZILLA_ENABLED
+ LLTextBox* mLabelMediaType;
+ LLComboBox* mComboMediaType;
+ LLTextBox* mLabelMediaURL;
+ LLLineEditor* mLineMediaURL;
+ #endif
+
+ LLTextBox* mLabelTexGen;
+ LLComboBox* mComboTexGen;
+
+ LLCheckBoxCtrl *mCheckFullbright;
+
+ LLTextBox* mLabelColorTransp;
+ LLSpinCtrl* mCtrlColorTransp; // transparency = 1 - alpha
+
+ setMouseOpaque(FALSE);
+ mTextureCtrl = LLUICtrlFactory::getTexturePickerByName(this,"texture control");
+ if(mTextureCtrl)
+ {
+ mTextureCtrl->setDefaultImageAssetID(LLUUID( gSavedSettings.getString( "DefaultObjectTexture" )));
+ mTextureCtrl->setCommitCallback( LLPanelFace::onCommitTexture );
+ mTextureCtrl->setOnCancelCallback( LLPanelFace::onCancelTexture );
+ mTextureCtrl->setOnSelectCallback( LLPanelFace::onSelectTexture );
+ mTextureCtrl->setDragCallback(LLPanelFace::onDragTexture);
+ mTextureCtrl->setCallbackUserData( this );
+ mTextureCtrl->setFollowsTop();
+ mTextureCtrl->setFollowsLeft();
+ // Don't allow (no copy) or (no transfer) textures to be selected during immediate mode
+ mTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+ // Allow any texture to be used during non-immediate mode.
+ mTextureCtrl->setNonImmediateFilterPermMask(PERM_NONE);
+ LLAggregatePermissions texture_perms;
+ if (gSelectMgr->selectGetAggregateTexturePermissions(texture_perms))
+ {
+ BOOL can_copy =
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_ALL;
+ BOOL can_transfer =
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_ALL;
+ mTextureCtrl->setCanApplyImmediately(can_copy && can_transfer);
+ }
+ else
+ {
+ mTextureCtrl->setCanApplyImmediately(FALSE);
+ }
+ }
+
+ mColorSwatch = LLUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ if(mColorSwatch)
+ {
+ mColorSwatch->setCommitCallback(LLPanelFace::onCommitColor);
+ mColorSwatch->setOnCancelCallback(LLPanelFace::onCancelColor);
+ mColorSwatch->setOnSelectCallback(LLPanelFace::onSelectColor);
+ mColorSwatch->setCallbackUserData( this );
+ mColorSwatch->setFollowsTop();
+ mColorSwatch->setFollowsLeft();
+ mColorSwatch->setCanApplyImmediately(TRUE);
+ }
+
+#if LL_MOZILLA_ENABLED
+ mLabelMediaType = LLUICtrlFactory::getTextBoxByName(this,"web_label");
+ if(mLabelMediaType)
+ mLabelMediaType->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+
+ mComboMediaType = LLUICtrlFactory::getComboBoxByName(this,"web_type_combo");
+ if(mComboMediaType)
+ {
+ mComboMediaType->setCommitCallback(onCommitMediaInfo);
+ mComboMediaType->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+ mComboMediaType->setCallbackUserData( this );
+ mComboMediaType->add("None");
+ mComboMediaType->add("Web page");
+ }
+
+ mLabelMediaURL = LLUICtrlFactory::getTextBoxByName(this,"url_label");
+ if(mLabelMediaURL)
+ {
+ mLabelMediaURL->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+ }
+
+ mLineMediaURL = LLUICtrlFactory::getLineEditorByName(this,"url_line");
+ if(mLineMediaURL)
+ {
+ mLineMediaURL->setCommitCallback(onCommitMediaInfo);
+ mLineMediaURL->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+ mLineMediaURL->setCommitOnFocusLost(TRUE);
+ mLineMediaURL->setCallbackUserData( this );
+ }
+#endif
+ mLabelColorTransp = LLUICtrlFactory::getTextBoxByName(this,"color trans");
+ if(mLabelColorTransp)
+ {
+ mLabelColorTransp->setFollowsTop();
+ mLabelColorTransp->setFollowsLeft();
+ }
+
+ mCtrlColorTransp = LLUICtrlFactory::getSpinnerByName(this,"ColorTrans");
+ if(mCtrlColorTransp)
+ {
+ mCtrlColorTransp->setCommitCallback(LLPanelFace::onCommitAlpha);
+ mCtrlColorTransp->setCallbackUserData(this);
+ mCtrlColorTransp->setPrecision(0);
+ mCtrlColorTransp->setFollowsTop();
+ mCtrlColorTransp->setFollowsLeft();
+ }
+
+ mCheckFullbright = LLUICtrlFactory::getCheckBoxByName(this,"checkbox fullbright");
+ if (mCheckFullbright)
+ {
+ mCheckFullbright->setCommitCallback(LLPanelFace::onCommitFullbright);
+ mCheckFullbright->setCallbackUserData( this );
+ }
+ mLabelTexGen = LLUICtrlFactory::getTextBoxByName(this,"tex gen");
+ mComboTexGen = LLUICtrlFactory::getComboBoxByName(this,"combobox texgen");
+ if(mComboTexGen)
+ {
+ mComboTexGen->setCommitCallback(LLPanelFace::onCommitTexGen);
+ mComboTexGen->setFollows(FOLLOWS_LEFT | FOLLOWS_TOP);
+ mComboTexGen->setCallbackUserData( this );
+ }
+ childSetCommitCallback("combobox shininess",&LLPanelFace::onCommitShiny,this);
+ childSetCommitCallback("combobox bumpiness",&LLPanelFace::onCommitBump,this);
+ childSetCommitCallback("TexScaleU",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("checkbox flip s",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("TexScaleV",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("checkbox flip t",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("TexRot",&LLPanelFace::onCommitTextureInfo, this);
+ childSetAction("button apply",&onClickApply,this);
+ childSetCommitCallback("TexOffsetU",LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("TexOffsetV",LLPanelFace::onCommitTextureInfo, this);
+ childSetAction("button align",onClickAutoFix,this);
+
+ clearCtrls();
+
+ return TRUE;
+}
+
+LLPanelFace::LLPanelFace(const std::string& name)
+: LLPanel(name)
+{
+}
+
+
+LLPanelFace::~LLPanelFace()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+
+void LLPanelFace::sendTexture()
+{
+ LLTextureCtrl* mTextureCtrl = gUICtrlFactory->getTexturePickerByName(this,"texture control");
+ if(!mTextureCtrl) return;
+ if( !mTextureCtrl->getTentative() )
+ {
+ // we grab the item id first, because we want to do a
+ // permissions check in the selection manager. ARGH!
+ LLUUID id = mTextureCtrl->getImageItemID();
+ if(id.isNull())
+ {
+ id = mTextureCtrl->getImageAssetID();
+ }
+ gSelectMgr->selectionSetImage(id);
+ }
+}
+
+void LLPanelFace::sendBump()
+{
+ LLComboBox* mComboBumpiness = gUICtrlFactory->getComboBoxByName(this,"combobox bumpiness");
+ if(!mComboBumpiness)return;
+ U8 bump = (U8) mComboBumpiness->getCurrentIndex() & TEM_BUMP_MASK;
+ gSelectMgr->selectionSetBumpmap( bump );
+}
+
+void LLPanelFace::sendTexGen()
+{
+ LLComboBox* mComboTexGen = gUICtrlFactory->getComboBoxByName(this,"combobox texgen");
+ if(!mComboTexGen)return;
+ U8 tex_gen = (U8) mComboTexGen->getCurrentIndex() << TEM_TEX_GEN_SHIFT;
+ gSelectMgr->selectionSetTexGen( tex_gen );
+}
+
+void LLPanelFace::sendShiny()
+{
+ LLComboBox* mComboShininess = gUICtrlFactory->getComboBoxByName(this,"combobox shininess");
+ if(!mComboShininess)return;
+ U8 shiny = (U8) mComboShininess->getCurrentIndex() & TEM_SHINY_MASK;
+ gSelectMgr->selectionSetShiny( shiny );
+}
+
+void LLPanelFace::sendFullbright()
+{
+ LLCheckBoxCtrl* mCheckFullbright = gUICtrlFactory->getCheckBoxByName(this,"checkbox fullbright");
+ if(!mCheckFullbright)return;
+ U8 fullbright = mCheckFullbright->get() ? TEM_FULLBRIGHT_MASK : 0;
+ gSelectMgr->selectionSetFullbright( fullbright );
+}
+
+#if LL_MOZILLA_ENABLED
+void LLPanelFace::sendMediaInfo()
+{
+ if (mComboMediaType)
+ {
+ U8 media_type = (U8) mComboMediaType->getCurrentIndex();
+ std::string media_url = mLineMediaURL->getText();
+
+ gSelectMgr->selectionSetMediaTypeAndURL( media_type, media_url );
+ }
+}
+#endif
+
+void LLPanelFace::sendColor()
+{
+
+ LLColorSwatchCtrl* mColorSwatch = LLViewerUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ if(!mColorSwatch)return;
+ LLColor4 color = mColorSwatch->get();
+
+ gSelectMgr->selectionSetColorOnly( color );
+}
+
+void LLPanelFace::sendAlpha()
+{
+ LLSpinCtrl* mCtrlColorTransp = LLViewerUICtrlFactory::getSpinnerByName(this,"ColorTrans");
+ if(!mCtrlColorTransp)return;
+ F32 alpha = (100.f - mCtrlColorTransp->get()) / 100.f;
+
+ gSelectMgr->selectionSetAlphaOnly( alpha );
+}
+
+
+
+void LLPanelFace::sendTextureInfo()
+{
+ S32 te;
+ LLViewerObject* object;
+ for ( gSelectMgr->getFirstTE(&object, &te); object; gSelectMgr->getNextTE(&object, &te) )
+ {
+ BOOL valid;
+ F32 value;
+ LLSpinCtrl* mCtrlTexScaleS = LLViewerUICtrlFactory::getSpinnerByName(this,"TexScaleU");
+ LLSpinCtrl* mCtrlTexScaleT = LLViewerUICtrlFactory::getSpinnerByName(this,"TexScaleV");
+ LLSpinCtrl* mCtrlTexOffsetS = LLViewerUICtrlFactory::getSpinnerByName(this,"TexOffsetU");
+ LLSpinCtrl* mCtrlTexOffsetT = LLViewerUICtrlFactory::getSpinnerByName(this,"TexOffsetV");
+ LLSpinCtrl* mCtrlTexRotation = LLViewerUICtrlFactory::getSpinnerByName(this,"TexRot");
+ LLCheckBoxCtrl* mCheckFlipScaleS = LLViewerUICtrlFactory::getCheckBoxByName(this,"checkbox flip s");
+ LLCheckBoxCtrl* mCheckFlipScaleT = LLViewerUICtrlFactory::getCheckBoxByName(this,"checkbox flip t");
+ LLComboBox* mComboTexGen = LLViewerUICtrlFactory::getComboBoxByName(this,"combobox texgen");
+ if (mCtrlTexScaleS)
+ {
+ valid = !mCtrlTexScaleS->getTentative() || !mCheckFlipScaleS->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexScaleS->get();
+ if( mCheckFlipScaleS->get() )
+ {
+ value = -value;
+ }
+ if (mComboTexGen->getCurrentIndex() == 1)
+ {
+ value *= 0.5f;
+ }
+ object->setTEScaleS( te, value );
+ }
+ }
+
+ if (mCtrlTexScaleT)
+ {
+ valid = !mCtrlTexScaleT->getTentative() || !mCheckFlipScaleT->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexScaleT->get();
+ if( mCheckFlipScaleT->get() )
+ {
+ value = -value;
+ }
+ if (mComboTexGen->getCurrentIndex() == 1)
+ {
+ value *= 0.5f;
+ }
+ object->setTEScaleT( te, value );
+ }
+ }
+
+ if (mCtrlTexOffsetS)
+ {
+ valid = !mCtrlTexOffsetS->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexOffsetS->get();
+ object->setTEOffsetS( te, value );
+ }
+ }
+
+ if (mCtrlTexOffsetT)
+ {
+ valid = !mCtrlTexOffsetT->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexOffsetT->get();
+ object->setTEOffsetT( te, value );
+ }
+ }
+
+ if (mCtrlTexRotation)
+ {
+ valid = !mCtrlTexRotation->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexRotation->get() * DEG_TO_RAD;
+ object->setTERotation( te, value );
+ }
+ }
+ }
+
+ for ( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ object->sendTEUpdate();
+ }
+}
+
+void LLPanelFace::getState()
+{
+ LLViewerObject* objectp = gSelectMgr->getFirstObject();
+
+ if( objectp
+ && objectp->getPCode() == LL_PCODE_VOLUME)
+ {
+ BOOL editable = objectp->permModify();
+
+ // only turn on auto-adjust button if there is a media renderer and the media is loaded
+ childSetEnabled("textbox autofix",FALSE);
+ //mLabelTexAutoFix->setEnabled ( FALSE );
+ childSetEnabled("button align",FALSE);
+ //mBtnAutoFix->setEnabled ( FALSE );
+
+ if ( LLMediaEngine::getInstance()->getMediaRenderer () )
+ if ( LLMediaEngine::getInstance()->getMediaRenderer ()->isLoaded () )
+ {
+ childSetEnabled("textbox autofix",editable);
+ //mLabelTexAutoFix->setEnabled ( editable );
+ childSetEnabled("button align",editable);
+ //mBtnAutoFix->setEnabled ( editable );
+ }
+ childSetEnabled("button apply",editable);
+ //mBtnApply->setEnabled( editable );
+
+ #if LL_MOZILLA_ENABLED
+ if (gSavedSettings.getBOOL("UseWebPagesOnPrims"))
+ {
+ // Web page selection
+ mLabelMediaType->setEnabled(editable);
+ mLabelMediaType->setToolTip("Experimental");
+
+ // JAMESDEBUG - use viewerobject mMedia->mMediaType when transmission is wired in
+ U8 media_type = LLViewerObject::MEDIA_TYPE_NONE;
+ bool same = gSelectMgr->selectionGetMediaType( &media_type );
+ mComboMediaType->setTentative( !same );
+ mComboMediaType->setEnabled( editable );
+ mComboMediaType->setCurrentByIndex( (S32)media_type );
+ mComboMediaType->setToolTip("Experimental");
+
+ mLabelMediaURL->setEnabled(editable);
+ mLabelMediaURL->setToolTip("Experimental");
+
+ const std::string& media_url = objectp->getMediaURL();
+ mLineMediaURL->setEnabled( editable );
+ mLineMediaURL->setText( media_url );
+ mLineMediaURL->setToolTip("Experimental");
+ }
+ else
+ {
+ mLabelMediaType->setEnabled(FALSE);
+ mLabelMediaType->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+
+ mComboMediaType->setEnabled( FALSE );
+ mComboMediaType->setCurrentByIndex( LLViewerObject::MEDIA_TYPE_NONE );
+ mComboMediaType->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+
+ mLabelMediaURL->setEnabled(FALSE);
+ mLabelMediaURL->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+
+ mLineMediaURL->setEnabled( FALSE );
+ mLineMediaURL->setText( "" );
+ mLineMediaURL->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+ }
+ #endif
+
+ // Texture
+ LLUUID id;
+ BOOL identical = gSelectMgr->selectionGetTexUUID(id);
+ LLTextureCtrl* mTextureCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this,"texture control");
+ if (identical)
+ {
+ // All selected have the same texture
+ if(mTextureCtrl){
+ mTextureCtrl->setTentative( FALSE );
+ mTextureCtrl->setEnabled( editable );
+ mTextureCtrl->setImageAssetID( id );
+ }
+ }
+ else
+ {
+ if(mTextureCtrl){
+ if( id.isNull() )
+ {
+ // None selected
+ mTextureCtrl->setTentative( FALSE );
+ mTextureCtrl->setEnabled( FALSE );
+ mTextureCtrl->setImageAssetID( LLUUID::null );
+ }
+ else
+ {
+ // Tentative: multiple selected with different textures
+ mTextureCtrl->setTentative( TRUE );
+ mTextureCtrl->setEnabled( editable );
+ mTextureCtrl->setImageAssetID( id );
+ }
+ }
+ }
+
+ LLAggregatePermissions texture_perms;
+ if(mTextureCtrl)
+ {
+// mTextureCtrl->setValid( editable );
+
+ if (gSelectMgr->selectGetAggregateTexturePermissions(texture_perms))
+ {
+ BOOL can_copy =
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_ALL;
+ BOOL can_transfer =
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_ALL;
+ mTextureCtrl->setCanApplyImmediately(can_copy && can_transfer);
+ }
+ else
+ {
+ mTextureCtrl->setCanApplyImmediately(FALSE);
+ }
+ }
+
+ // Texture scale
+ {
+ childSetEnabled("tex scale",editable);
+ //mLabelTexScale->setEnabled( editable );
+ F32 scale_s = 1.f;
+ identical = allFacesSameValue( &LLPanelFace::valueScaleS, &scale_s );
+ childSetValue("TexScaleU",editable ? llabs(scale_s) : 0);
+ childSetTentative("TexScaleU",LLSD((BOOL)(!identical)));
+ childSetEnabled("TexScaleU",editable);
+ childSetValue("checkbox flip s",LLSD((BOOL)(scale_s < 0 ? TRUE : FALSE )));
+ childSetTentative("checkbox flip s",LLSD((BOOL)((!identical) ? TRUE : FALSE )));
+ childSetEnabled("checkbox flip s",editable);
+ }
+
+ {
+ F32 scale_t = 1.f;
+ identical = allFacesSameValue( &LLPanelFace::valueScaleT, &scale_t );
+
+ childSetValue("TexScaleV",llabs(editable ? llabs(scale_t) : 0));
+ childSetTentative("TexScaleV",LLSD((BOOL)(!identical)));
+ childSetEnabled("TexScaleV",editable);
+ childSetValue("checkbox flip t",LLSD((BOOL)(scale_t< 0 ? TRUE : FALSE )));
+ childSetTentative("checkbox flip t",LLSD((BOOL)((!identical) ? TRUE : FALSE )));
+ childSetEnabled("checkbox flip t",editable);
+ }
+
+ // Texture offset
+ {
+ childSetEnabled("tex offset",editable);
+ F32 offset_s = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueOffsetS, &offset_s );
+ childSetValue("TexOffsetU", editable ? offset_s : 0);
+ childSetTentative("TexOffsetU",!identical);
+ childSetEnabled("TexOffsetU",editable);
+ }
+
+ {
+ F32 offset_t = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueOffsetT, &offset_t );
+ childSetValue("TexOffsetV", editable ? offset_t : 0);
+ childSetTentative("TexOffsetV",!identical);
+ childSetEnabled("TexOffsetV",editable);
+ }
+
+ // Texture rotation
+ {
+ childSetEnabled("tex rotate",editable);
+ F32 rotation = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueTexRotation, &rotation );
+ childSetValue("TexRot", editable ? rotation * RAD_TO_DEG : 0);
+ childSetTentative("TexRot",!identical);
+ childSetEnabled("TexRot",editable);
+ }
+
+ // Color swatch
+ LLColorSwatchCtrl* mColorSwatch = LLViewerUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ LLColor4 color = LLColor4::white;
+ if(mColorSwatch)
+ {
+ identical = gSelectMgr->selectionGetColor(color);
+ mColorSwatch->setOriginal(color);
+ mColorSwatch->set(color, TRUE);
+
+ mColorSwatch->setValid(editable);
+ mColorSwatch->setEnabled( editable );
+ mColorSwatch->setCanApplyImmediately( editable );
+ }
+ // Color transparency
+ {
+ childSetEnabled("color trans",editable);
+ }
+
+ F32 transparency = (1.f - color.mV[VALPHA]) * 100.f;
+ {
+ childSetValue("ColorTrans", editable ? transparency : 0);
+ childSetEnabled("ColorTrans",editable);
+ }
+
+ // Bump
+ {
+ F32 shinyf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueShiny, &shinyf );
+ childGetSelectionInterface("combobox shininess")->selectNthItem((S32)shinyf);
+ childSetEnabled("combobox shininess",editable);
+ childSetTentative("combobox shininess",!identical);
+ childSetEnabled("label shininess",editable);
+ }
+
+ {
+ F32 bumpf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueBump, &bumpf );
+ childGetSelectionInterface("combobox bumpiness")->selectNthItem((S32)bumpf);
+ childSetEnabled("combobox bumpiness",editable);
+ childSetTentative("combobox bumpiness",!identical);
+ childSetEnabled("label bumpiness",editable);
+ }
+
+ {
+ F32 genf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueTexGen, &genf);
+ S32 selected_texgen = ((S32) genf) >> TEM_TEX_GEN_SHIFT;
+ childGetSelectionInterface("combobox texgen")->selectNthItem(selected_texgen);
+ childSetEnabled("combobox texgen",editable);
+ childSetTentative("combobox texgen",!identical);
+ childSetEnabled("tex gen",editable);
+
+ if (selected_texgen == 1)
+ {
+ childSetText("tex scale",childGetText("string repeats per meter"));
+ childSetValue("TexScaleU", 2.0f * childGetValue("TexScaleU").asReal() );
+ childSetValue("TexScaleV", 2.0f * childGetValue("TexScaleV").asReal() );
+ }
+ else
+ {
+ childSetText("tex scale",childGetText("string repeats per face"));
+ }
+
+ }
+
+ {
+ F32 fullbrightf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueFullbright, &fullbrightf );
+
+ childSetValue("checkbox fullbright",(S32)fullbrightf);
+ childSetEnabled("checkbox fullbright",editable);
+ childSetTentative("checkbox fullbright",!identical);
+ }
+
+ // Repeats per meter label
+ {
+ childSetEnabled("rpt",editable);
+ }
+
+ // Repeats per meter
+ F32 repeats = 1.f;
+ identical = allFacesSameValue( &LLPanelFace::valueRepeatsPerMeter, &repeats );
+ {
+ childSetValue("rptctrl", editable ? repeats : 0);
+ childSetTentative("rptctrl",!identical);
+ LLComboBox* mComboTexGen = LLViewerUICtrlFactory::getComboBoxByName(this,"combobox texgen");
+ if (mComboTexGen)
+ {
+ BOOL enabled = editable && (!mComboTexGen || mComboTexGen->getCurrentIndex() != 1);
+ childSetEnabled("rptctrl",enabled);
+ childSetEnabled("button apply",enabled);
+ }
+ }
+ }
+ else
+ {
+ // Disable all UICtrls
+ clearCtrls();
+
+ // Disable non-UICtrls
+ LLTextureCtrl* mTextureCtrl = LLUICtrlFactory::getTexturePickerByName(this,"texture control");
+ if(mTextureCtrl)
+ {
+ mTextureCtrl->setImageAssetID( LLUUID::null );
+ mTextureCtrl->setEnabled( FALSE ); // this is a LLUICtrl, but we don't want it to have keyboard focus so we add it as a child, not a ctrl.
+// mTextureCtrl->setValid(FALSE);
+ }
+ LLColorSwatchCtrl* mColorSwatch = LLUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ if(mColorSwatch)
+ {
+ mColorSwatch->setEnabled( FALSE );
+ mColorSwatch->setValid(FALSE);
+ }
+ childSetEnabled("color trans",FALSE);
+ childSetEnabled("rpt",FALSE);
+ childSetEnabled("tex scale",FALSE);
+ childSetEnabled("tex offset",FALSE);
+ childSetEnabled("tex rotate",FALSE);
+ childSetEnabled("tex gen",FALSE);
+ childSetEnabled("label shininess",FALSE);
+ childSetEnabled("label bumpiness",FALSE);
+
+ childSetEnabled("textbox autofix",FALSE);
+
+ childSetEnabled("button align",FALSE);
+ childSetEnabled("button apply",FALSE);
+ }
+}
+
+
+void LLPanelFace::refresh()
+{
+ getState();
+}
+
+
+BOOL LLPanelFace::allFacesSameValue( F32 (get_face_value(LLViewerObject*, S32)), F32 *value)
+{
+ LLViewerObject* object;
+ S32 te;
+
+ // Get the value from the primary selected TE
+ F32 first_value = *value;
+ BOOL got_first = FALSE;
+ gSelectMgr->getPrimaryTE(&object, &te);
+ if (object)
+ {
+ first_value = get_face_value(object, te);
+ got_first = true;
+ }
+
+ // Now iterate through all TEs to test for sameness
+ BOOL identical = TRUE;
+ for ( gSelectMgr->getFirstTE(&object, &te); object; gSelectMgr->getNextTE(&object, &te) )
+ {
+ if (!got_first)
+ {
+ first_value = get_face_value(object, te);
+ got_first = true;
+ }
+ if ( get_face_value(object, te) != first_value )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *value = first_value;
+ return identical;
+}
+
+
+//
+// Static functions
+//
+
+// static
+F32 LLPanelFace::valueRepeatsPerMeter(LLViewerObject* object, S32 face)
+{
+ U32 s_axis = VX;
+ U32 t_axis = VY;
+
+ // BUG: Only repeats along S axis
+ // BUG: Only works for boxes.
+ gSelectMgr->getTESTAxes(object, face, &s_axis, &t_axis);
+ return object->getTE(face)->mScaleS / object->getScale().mV[s_axis];
+}
+
+// static
+F32 LLPanelFace::valueScaleS(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mScaleS;
+}
+
+
+// static
+F32 LLPanelFace::valueScaleT(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mScaleT;
+}
+
+// static
+F32 LLPanelFace::valueOffsetS(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mOffsetS;
+}
+
+// static
+F32 LLPanelFace::valueOffsetT(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mOffsetT;
+}
+
+// static
+F32 LLPanelFace::valueTexRotation(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mRotation;
+}
+
+// static
+F32 LLPanelFace::valueBump(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getBumpmap());
+}
+
+// static
+F32 LLPanelFace::valueTexGen(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getTexGen());
+}
+
+// static
+F32 LLPanelFace::valueShiny(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getShiny());
+}
+
+// static
+F32 LLPanelFace::valueFullbright(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getFullbright());
+}
+
+
+// static
+void LLPanelFace::onCommitColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendColor();
+}
+
+// static
+void LLPanelFace::onCommitAlpha(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendAlpha();
+}
+
+// static
+void LLPanelFace::onCancelColor(LLUICtrl* ctrl, void* userdata)
+{
+ gSelectMgr->selectionRevertColors();
+}
+
+// static
+void LLPanelFace::onSelectColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ gSelectMgr->saveSelectedObjectColors();
+ self->sendColor();
+}
+
+// static
+void LLPanelFace::onCommitBump(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendBump();
+}
+
+// static
+void LLPanelFace::onCommitTexGen(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendTexGen();
+}
+
+// static
+void LLPanelFace::onCommitShiny(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendShiny();
+}
+
+// static
+void LLPanelFace::onCommitFullbright(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendFullbright();
+}
+
+// static
+BOOL LLPanelFace::onDragTexture(LLUICtrl*, LLInventoryItem* item, void*)
+{
+ BOOL accept = TRUE;
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ while(accept && obj)
+ {
+ if(!LLToolDragAndDrop::isInventoryDropAcceptable(obj, item))
+ accept = FALSE;
+ else
+ obj = gSelectMgr->getNextRootObject();
+ }
+ return accept;
+}
+
+// static
+void LLPanelFace::onCommitTexture( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+
+ gViewerStats->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
+
+ self->sendTexture();
+}
+
+// static
+void LLPanelFace::onCancelTexture(LLUICtrl* ctrl, void* userdata)
+{
+ gSelectMgr->selectionRevertTextures();
+}
+
+// static
+void LLPanelFace::onSelectTexture(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ gSelectMgr->saveSelectedObjectTextures();
+ self->sendTexture();
+}
+
+
+// static
+void LLPanelFace::onCommitTextureInfo( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendTextureInfo();
+}
+
+#if LL_MOZILLA_ENABLED
+// static
+void LLPanelFace::onCommitMediaInfo(LLUICtrl* ctrl, void* data)
+{
+ LLPanelFace* self = (LLPanelFace*) data;
+ self->sendMediaInfo();
+}
+#endif
+
+// Commit the number of repeats per meter
+// static
+void LLPanelFace::onClickApply(void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+
+ gFocusMgr.setKeyboardFocus( NULL, NULL );
+
+ //F32 repeats_per_meter = self->mCtrlRepeatsPerMeter->get();
+ F32 repeats_per_meter = (F32)self->childGetValue( "rptctrl" ).asReal();//self->mCtrlRepeatsPerMeter->get();
+ gSelectMgr->selectionTexScaleAutofit( repeats_per_meter );
+}
+
+// commit the fit media texture to prim button
+void LLPanelFace::onClickAutoFix(void* userdata)
+{
+ S32 te;
+ LLViewerObject* object;
+
+ // for all selected objects
+ for ( gSelectMgr->getFirstTE(&object, &te); object; gSelectMgr->getNextTE(&object, &te) )
+ {
+ // only do this if it's a media texture
+ if ( object->getTE ( te )->getID() == LLMediaEngine::getInstance()->getImageUUID () )
+ {
+ // make sure we're valid
+ if ( LLMediaEngine::getInstance()->getMediaRenderer() )
+ {
+ // calculate correct scaling based on media dimensions and next-power-of-2 texture dimensions
+ F32 scaleS = (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getMediaWidth() /
+ (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getTextureWidth();
+
+ F32 scaleT = (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getMediaHeight() /
+ (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getTextureHeight();
+
+ // set scale and adjust offset
+ object->setTEScaleS( te, scaleS );
+ object->setTEScaleT( te, scaleT ); // don't need to flip Y anymore since QT does this for us now.
+ object->setTEOffsetS( te, -( 1.0f - scaleS ) / 2.0f );
+ object->setTEOffsetT( te, -( 1.0f - scaleT ) / 2.0f );
+ };
+ };
+ };
+
+ // not clear why this is in a separate loop but i followed the patter from further up this file just in case.
+ for ( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ object->sendTEUpdate();
+ };
+}
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
new file mode 100644
index 0000000000..03f361d23c
--- /dev/null
+++ b/indra/newview/llpanelface.h
@@ -0,0 +1,136 @@
+/**
+ * @file llpanelface.h
+ * @brief Panel in the tools floater for editing face textures, colors, etc.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELFACE_H
+#define LL_LLPANELFACE_H
+
+#include "v4color.h"
+#include "llpanel.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLColorSwatchCtrl;
+class LLComboBox;
+class LLInventoryItem;
+class LLLineEditor;
+class LLSpinCtrl;
+class LLTextBox;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLViewerObject;
+
+class LLPanelFace : public LLPanel
+{
+public:
+ virtual BOOL postBuild();
+ LLPanelFace(const std::string& name);
+ virtual ~LLPanelFace();
+
+ void refresh();
+
+protected:
+ // Given a callback function that returns an F32, figures out
+ // if that F32 is the same for all selected faces. "value"
+ // contains the identical value, or the first object's value.
+ BOOL allFacesSameValue( F32 (get_face_value(LLViewerObject*, S32)), F32 *value);
+
+ void getState();
+
+ void sendTexture(); // applies and sends texture
+ void sendTextureInfo(); // applies and sends texture scale, offset, etc.
+ void sendColor(); // applies and sends color
+ void sendAlpha(); // applies and sends transparency
+ void sendBump(); // applies and sends bump map
+ void sendTexGen(); // applies and sends bump map
+ void sendShiny(); // applies and sends shininess
+ void sendFullbright(); // applies and sends full bright
+ #if LL_MOZILLA_ENABLED
+ void sendMediaInfo(); // web page settings and URL
+ #endif
+
+ // this function is to return TRUE if the dra should succeed.
+ static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item, void* ud);
+
+ static void onCommitTexture( LLUICtrl* ctrl, void* userdata);
+ static void onCancelTexture( LLUICtrl* ctrl, void* userdata);
+ static void onSelectTexture( LLUICtrl* ctrl, void* userdata);
+ static void onCommitTextureInfo( LLUICtrl* ctrl, void* userdata);
+ static void onCommitColor( LLUICtrl* ctrl, void* userdata);
+ static void onCommitAlpha( LLUICtrl* ctrl, void* userdata);
+ static void onCancelColor( LLUICtrl* ctrl, void* userdata);
+ static void onSelectColor( LLUICtrl* ctrl, void* userdata);
+ static void onCommitBump( LLUICtrl* ctrl, void* userdata);
+ static void onCommitTexGen( LLUICtrl* ctrl, void* userdata);
+ static void onCommitShiny( LLUICtrl* ctrl, void* userdata);
+ static void onCommitFullbright( LLUICtrl* ctrl, void* userdata);
+ #if LL_MOZILLA_ENABLED
+ static void onCommitMediaInfo( LLUICtrl* ctrl, void* data);
+ #endif
+
+ static void onClickApply(void*);
+ static void onClickAutoFix(void*);
+
+ static F32 valueScaleS(LLViewerObject* object, S32 face);
+ static F32 valueScaleT(LLViewerObject* object, S32 face);
+ static F32 valueOffsetS(LLViewerObject* object, S32 face);
+ static F32 valueOffsetT(LLViewerObject* object, S32 face);
+ static F32 valueTexRotation(LLViewerObject* object, S32 face);
+ static F32 valueRepeatsPerMeter(LLViewerObject* object, S32 face);
+ static F32 valueBump(LLViewerObject* object, S32 face);
+ static F32 valueTexGen(LLViewerObject* object, S32 face);
+ static F32 valueShiny(LLViewerObject* object, S32 face);
+ static F32 valueFullbright(LLViewerObject* object, S32 face);
+
+protected:
+ //LLTextureCtrl* mTextureCtrl;
+ //LLColorSwatchCtrl* mColorSwatch;
+
+ //#if LL_MOZILLA_ENABLED
+ //LLTextBox* mLabelMediaType;
+ //LLComboBox* mComboMediaType;
+ //LLTextBox* mLabelMediaURL;
+ //LLLineEditor* mLineMediaURL;
+ //#endif
+
+ //LLTextBox *mLabelTexScale;
+ //LLSpinCtrl *mCtrlTexScaleS;
+ //LLSpinCtrl *mCtrlTexScaleT;
+
+ //LLCheckBoxCtrl *mCheckFlipScaleS;
+ //LLCheckBoxCtrl *mCheckFlipScaleT;
+
+ //LLTextBox *mLabelTexOffset;
+ //LLSpinCtrl *mCtrlTexOffsetS;
+ //LLSpinCtrl *mCtrlTexOffsetT;
+
+ //LLTextBox *mLabelTexRotation;
+ //LLSpinCtrl *mCtrlTexRotation;
+
+ //LLTextBox* mLabelTexGen;
+ //LLComboBox* mComboTexGen;
+
+ //LLTextBox* mLabelShininess;
+ //LLComboBox* mComboShininess;
+
+ //LLTextBox* mLabelBumpiness;
+ //LLComboBox* mComboBumpiness;
+
+ //LLCheckBoxCtrl *mCheckFullbright;
+ //
+ //LLTextBox* mLabelColorTransp;
+ //LLSpinCtrl* mCtrlColorTransp; // transparency = 1 - alpha
+
+ //LLTextBox* mLabelRepeatsPerMeter;
+ //LLSpinCtrl* mCtrlRepeatsPerMeter;
+ //LLButton* mBtnApply;
+
+ //LLTextBox* mLabelTexAutoFix;
+ //LLButton* mBtnAutoFix;
+};
+
+#endif
diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp
new file mode 100644
index 0000000000..9ad5855b8e
--- /dev/null
+++ b/indra/newview/llpanelgroup.cpp
@@ -0,0 +1,651 @@
+/**
+ * @file llpanelgroup.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroup.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llpanelgroupgeneral.h"
+#include "llpanelgrouproles.h"
+#include "llpanelgroupvoting.h"
+#include "llpanelgrouplandmoney.h"
+#include "llpanelgroupnotices.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "llviewermessage.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+// static
+void* LLPanelGroupTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupTab("panel group tab", *group_id);
+}
+
+LLPanelGroupTab::~LLPanelGroupTab()
+{
+ mObservers.clear();
+}
+
+BOOL LLPanelGroupTab::isVisibleByAgent(LLAgent* agentp)
+{
+ //default to being visible
+ return TRUE;
+}
+
+BOOL LLPanelGroupTab::postBuild()
+{
+ // Hook up the help button callback.
+ LLButton* button = (LLButton*) getChildByName("help_button");
+ if (button)
+ {
+ button->setClickedCallback(onClickHelp);
+ button->setCallbackUserData(this);
+ }
+
+ // Read help text from the xml file.
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*) getChildByName("help_text");
+ if (txt)
+ {
+ mHelpText = txt->getText();
+ removeChild(txt);
+ }
+ return TRUE;
+}
+
+void LLPanelGroupTab::addObserver(LLPanelGroupTabObserver *obs)
+{
+ mObservers.insert(obs);
+}
+
+void LLPanelGroupTab::removeObserver(LLPanelGroupTabObserver *obs)
+{
+ mObservers.erase(obs);
+}
+
+void LLPanelGroupTab::notifyObservers()
+{
+
+ for (observer_list_t::iterator iter = mObservers.begin();
+ iter != mObservers.end(); )
+ {
+ LLPanelGroupTabObserver* observer = *iter;
+ observer->tabChanged();
+
+ // safe way to incrament since changed may delete entries! (@!##%@!@&*!)
+ iter = mObservers.upper_bound(observer);
+ }
+}
+
+// static
+void LLPanelGroupTab::onClickHelp(void* user_data)
+{
+ LLPanelGroupTab* self = static_cast<LLPanelGroupTab*>(user_data);
+ self->handleClickHelp();
+}
+
+void LLPanelGroupTab::handleClickHelp()
+{
+ // Display the help text.
+ LLString help_text( getHelpText() );
+ if ( !help_text.empty() )
+ {
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = help_text;
+ LLAlertDialog* dialogp = gViewerWindow->alertXml("GenericAlert", args);
+ if (dialogp)
+ {
+ LLView* viewp = this;
+ LLFloater* root_floater = NULL;
+ while(viewp)
+ {
+ if(viewp->getWidgetType() == WIDGET_TYPE_FLOATER)
+ {
+ root_floater = (LLFloater*)viewp;
+ }
+ viewp = viewp->getParent();
+ }
+ if (root_floater)
+ {
+ root_floater->addDependentFloater(dialogp);
+ }
+ }
+ }
+}
+
+LLPanelGroup::LLPanelGroup(const std::string& filename,
+ const std::string& name,
+ const LLUUID& group_id,
+ const std::string& initial_tab_selected)
+: LLPanel(name, LLRect(), FALSE),
+ LLGroupMgrObserver( group_id ),
+ mCurrentTab( NULL ),
+ mRequestedTab( NULL ),
+ mTabContainer( NULL ),
+ mForceClose( FALSE ),
+ mIgnoreTransition( FALSE ),
+ mInitialTab(initial_tab_selected),
+ mAllowEdit( TRUE ),
+ mShowingNotifyDialog( FALSE )
+{
+ // Set up the factory callbacks.
+ mFactoryMap["general_tab"] = LLCallbackMap(LLPanelGroupGeneral::createTab,
+ &mID);
+ mFactoryMap["roles_tab"] = LLCallbackMap(LLPanelGroupRoles::createTab,
+ &mID);
+ mFactoryMap["notices_tab"] = LLCallbackMap(LLPanelGroupNotices::createTab,
+ &mID);
+ mFactoryMap["voting_tab"] = LLCallbackMap(LLPanelGroupVoting::createTab,
+ &mID);
+ mFactoryMap["land_money_tab"]= LLCallbackMap(LLPanelGroupLandMoney::createTab,
+ &mID);
+ // Roles sub tabs
+ mFactoryMap["members_sub_tab"] = LLCallbackMap(LLPanelGroupMembersSubTab::createTab, &mID);
+ mFactoryMap["roles_sub_tab"] = LLCallbackMap(LLPanelGroupRolesSubTab::createTab, &mID);
+ mFactoryMap["actions_sub_tab"] = LLCallbackMap(LLPanelGroupActionsSubTab::createTab, &mID);
+
+ // Pass on construction of this panel to the control factory.
+ gUICtrlFactory->buildPanel(this, filename, &getFactoryMap());
+ mFilename = filename;
+
+ gGroupMgr->addObserver(this);
+}
+
+LLPanelGroup::~LLPanelGroup()
+{
+ gGroupMgr->removeObserver(this);
+
+ int i;
+ int tab_count = mTabContainer->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ LLPanelGroupTab* panelp =
+ (LLPanelGroupTab*) mTabContainer->getPanelByIndex(i);
+
+ if ( panelp ) panelp->removeObserver(this);
+ }
+}
+
+void LLPanelGroup::updateTabVisibility()
+{
+ S32 i;
+ S32 tab_count = mTabContainer->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ LLPanelGroupTab* panelp =
+ (LLPanelGroupTab*) mTabContainer->getPanelByIndex(i);
+
+ BOOL visible = panelp->isVisibleByAgent(&gAgent);
+ mTabContainer->enableTabButton(i, visible);
+
+ if ( !visible && mCurrentTab == panelp )
+ {
+ //we are disabling the currently selected tab
+ //select the previous one
+ mTabContainer->selectPrevTab();
+ mCurrentTab =
+ (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+ }
+ }
+}
+
+
+
+BOOL LLPanelGroup::postBuild()
+{
+ mTabContainer = (LLTabContainerCommon*) getChildByName("group_tab_container");
+
+ if (mTabContainer)
+ {
+ // Select the initial tab specified via constructor
+ const BOOL recurse = TRUE;
+ LLPanelGroupTab* tabp =
+ (LLPanelGroupTab*) getChildByName(mInitialTab, recurse);
+
+ if (!tabp)
+ {
+ //our initial tab selection was invalid, just select the
+ //first tab then or default to selecting the initial
+ //selected tab specified in the layout file
+ tabp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+
+ //no tab was initially selected through constructor
+ //or the XML, select the first tab
+ if (!tabp)
+ {
+ mTabContainer->selectFirstTab();
+ tabp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+ }
+ }
+ else
+ {
+ mTabContainer->selectTabPanel(tabp);
+ }
+
+ mCurrentTab = tabp;
+
+ // Add click callbacks.
+ S32 i;
+ S32 tab_count = mTabContainer->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ LLPanel* tab_panel = mTabContainer->getPanelByIndex(i);
+ LLPanelGroupTab* panelp =(LLPanelGroupTab*)tab_panel; // bit of a hack
+
+ // Pass on whether or not to allow edit to tabs.
+ panelp->setAllowEdit(mAllowEdit);
+ panelp->addObserver(this);
+
+ mTabContainer->setTabChangeCallback(panelp, onClickTab);
+ mTabContainer->setTabUserData(panelp, this);
+ }
+ updateTabVisibility();
+
+ // Act as though this tab was just activated.
+ mCurrentTab->activate();
+ }
+
+ // Read apply text from the xml file.
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*)getChildByName("default_needs_apply_text");
+ if (txt)
+ {
+ mDefaultNeedsApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+ txt = (LLTextBox*)getChildByName("want_apply_text");
+ if (txt)
+ {
+ mWantApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+
+ LLButton* button = (LLButton*) getChildByName("btn_ok");
+ if (button)
+ {
+ button->setClickedCallback(onBtnOK);
+ button->setCallbackUserData(this);
+ button->setVisible(mAllowEdit);
+ }
+
+ button = (LLButton*) getChildByName("btn_cancel");
+ if (button)
+ {
+ button->setClickedCallback(onBtnCancel);
+ button->setCallbackUserData(this);
+ button->setVisible(mAllowEdit);
+ }
+
+ button = (LLButton*) getChildByName("btn_apply");
+ if (button)
+ {
+ button->setClickedCallback(onBtnApply);
+ button->setVisible(mAllowEdit);
+ button->setEnabled(FALSE);
+
+ mApplyBtn = button;
+ }
+
+ button = (LLButton*) getChildByName("btn_refresh");
+ if (button)
+ {
+ button->setClickedCallback(onBtnRefresh);
+ button->setCallbackUserData(this);
+ button->setVisible(mAllowEdit);
+ }
+
+ return TRUE;
+}
+
+void LLPanelGroup::changed(LLGroupChange gc)
+{
+ updateTabVisibility();
+ // Notify the currently active panel that group manager information has changed.
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+
+ if (panelp)
+ {
+ panelp->update(gc);
+ }
+}
+
+// PanelGroupTab observer trigger
+void LLPanelGroup::tabChanged()
+{
+ //some tab information has changed,....enable/disable the apply button
+ //based on if they need an apply
+ if ( mApplyBtn )
+ {
+ LLString mesg;
+ mApplyBtn->setEnabled(mAllowEdit && mCurrentTab->needsApply(mesg));
+ }
+}
+
+// static
+void LLPanelGroup::onClickTab(void* user_data, bool from_click)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->handleClickTab();
+}
+
+void LLPanelGroup::handleClickTab()
+{
+ // If we are already handling a transition,
+ // ignore this.
+ if (mIgnoreTransition)
+ {
+ return;
+ }
+
+ mRequestedTab = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+
+ // Make sure they aren't just clicking the same tab...
+ if (mRequestedTab == mCurrentTab)
+ {
+ return;
+ }
+
+ // Try to switch from the current panel to the panel the user selected.
+ attemptTransition();
+}
+
+void LLPanelGroup::setGroupID(const LLUUID& group_id)
+{
+ LLRect rect(getRect());
+
+ gGroupMgr->removeObserver(this);
+ mID = group_id;
+ gGroupMgr->addObserver(this);
+ //TODO: this is really bad, we should add a method
+ // where the panels can just update themselves
+ // on a group id change. Similar to update() but with a group
+ // id change.
+
+ // For now, rebuild panel
+ //delete children and rebuild panel
+ deleteAllChildren();
+ gUICtrlFactory->buildPanel(this, mFilename, &getFactoryMap());
+}
+
+void LLPanelGroup::selectTab(std::string tab_name)
+{
+ const BOOL recurse = TRUE;
+
+ LLPanelGroupTab* tabp =
+ (LLPanelGroupTab*) getChildByName(tab_name, recurse);
+
+ if ( tabp && mTabContainer )
+ {
+ mTabContainer->selectTabPanel(tabp);
+ onClickTab(this, false);
+ }
+}
+
+BOOL LLPanelGroup::canClose()
+{
+ if (mShowingNotifyDialog) return FALSE;
+ if (mCurrentTab && mCurrentTab->hasModal()) return FALSE;
+ if (mForceClose || !mAllowEdit) return TRUE;
+
+ // Try to switch from the current panel to nothing, indicating a close action.
+ mRequestedTab = NULL;
+ return attemptTransition();
+}
+
+BOOL LLPanelGroup::attemptTransition()
+{
+ // Check if the current tab needs to be applied.
+ LLString mesg;
+ if (mCurrentTab && mCurrentTab->needsApply(mesg))
+ {
+ // If no message was provided, give a generic one.
+ if (mesg.empty())
+ {
+ mesg = mDefaultNeedsApplyMesg;
+ }
+ // Create a notify box, telling the user about the unapplied tab.
+ LLString::format_map_t args;
+ args["[NEEDS_APPLY_MESSAGE]"] = mesg;
+ args["[WANT_APPLY_MESSAGE]"] = mWantApplyMesg;
+ gViewerWindow->alertXml("PanelGroupApply", args,
+ onNotifyCallback, (void*) this);
+ mShowingNotifyDialog = TRUE;
+
+ // We need to reselect the current tab, since it isn't finished.
+ if (mTabContainer)
+ {
+ // selectTabPanel is going to trigger another
+ // click event. We want to ignore it so that
+ // mRequestedTab is not updated.
+ mIgnoreTransition = TRUE;
+ mTabContainer->selectTabPanel( mCurrentTab );
+ mIgnoreTransition = FALSE;
+ }
+ // Returning FALSE will block a close action from finishing until
+ // we get a response back from the user.
+ return FALSE;
+ }
+ else
+ {
+ // The current panel didn't have anything it needed to apply.
+ if ( mRequestedTab )
+ {
+ transitionToTab();
+ }
+ // Returning TRUE will allow any close action to proceed.
+ return TRUE;
+ }
+}
+
+void LLPanelGroup::transitionToTab()
+{
+ // Tell the current panel that it is being deactivated.
+ if (mCurrentTab)
+ {
+ mCurrentTab->deactivate();
+ }
+
+ // If the requested panel exists, activate it.
+ if (mRequestedTab)
+ {
+ // This is now the current tab;
+ mCurrentTab = mRequestedTab;
+ mCurrentTab->activate();
+ }
+ else // NULL requested indicates a close action.
+ {
+ close();
+ }
+}
+
+// static
+void LLPanelGroup::onNotifyCallback(S32 option, void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ if (self)
+ {
+ self->handleNotifyCallback(option);
+ }
+}
+
+void LLPanelGroup::handleNotifyCallback(S32 option)
+{
+ mShowingNotifyDialog = FALSE;
+ switch (option)
+ {
+ case 0: // "Apply Changes"
+ // Try to apply changes, and switch to the requested tab.
+ if ( !apply() )
+ {
+ // There was a problem doing the apply.
+ // Skip switching tabs.
+ break;
+ }
+
+ // This panel's info successfully applied.
+ // Switch to the next panel.
+ mIgnoreTransition = TRUE;
+ mTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ case 1: // "Ignore Changes"
+ // Switch to the requested panel without applying changes
+ // (Changes may already have been applied in the previous block)
+ mCurrentTab->cancel();
+ mIgnoreTransition = TRUE;
+ mTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ case 2: // "Cancel"
+ default:
+ // Do nothing. The user is canceling the action.
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+// static
+void LLPanelGroup::onBtnOK(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ // If we are able to apply changes, then close.
+ if(self->apply())
+ {
+ self->close();
+ }
+}
+
+// static
+void LLPanelGroup::onBtnCancel(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->close();
+}
+
+// static
+void LLPanelGroup::onBtnApply(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->apply();
+}
+
+bool LLPanelGroup::apply()
+{
+ // Pass this along to the currently visible tab.
+ if (!mTabContainer) return false;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+ if (!panelp) return false;
+
+ LLString mesg;
+ if ( !panelp->needsApply(mesg) )
+ {
+ // We don't need to apply anything.
+ // We're done.
+ return true;
+ }
+
+ // Ignore the needs apply message.
+ // Try to do the actual apply.
+ LLString apply_mesg;
+ if ( panelp->apply( apply_mesg ) )
+ {
+ // Everything worked. We're done.
+ return true;
+ }
+
+ // There was a problem doing the actual apply.
+ // Inform the user.
+ if ( !apply_mesg.empty() )
+ {
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = apply_mesg;
+ gViewerWindow->alertXml("GenericAlert", args);
+ }
+
+ return false;
+}
+
+// static
+void LLPanelGroup::onBtnRefresh(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->refreshData();
+}
+
+// virtual
+void LLPanelGroup::draw()
+{
+ LLPanel::draw();
+
+ if (mRefreshTimer.hasExpired())
+ {
+ mRefreshTimer.stop();
+ childEnable("btn_refresh");
+ }
+}
+
+void LLPanelGroup::refreshData()
+{
+ gGroupMgr->clearGroupData(getID());
+ mCurrentTab->activate();
+
+ // 5 second timeout
+ childDisable("btn_refresh");
+ mRefreshTimer.start();
+ mRefreshTimer.setTimerExpirySec(5);
+}
+
+void LLPanelGroup::close()
+{
+ // Pass this to the parent, if it is a floater.
+ LLView* viewp = getParent();
+ if (viewp
+ && WIDGET_TYPE_FLOATER == viewp->getWidgetType())
+ {
+ // First, set the force close flag, since the floater
+ // will be asking us whether it can close.
+ mForceClose = TRUE;
+ // Tell the parent floater to close.
+ LLFloater* floaterp = (LLFloater*) viewp;
+ floaterp->close();
+ }
+}
+
+void LLPanelGroup::showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer)
+{
+ if (mCurrentTab->getName() != "notices_tab")
+ {
+ // We need to clean up that inventory offer.
+ if (inventory_offer)
+ {
+ inventory_offer_callback( 1 , inventory_offer);
+ }
+ return;
+ }
+
+ LLPanelGroupNotices* notices = static_cast<LLPanelGroupNotices*>(mCurrentTab);
+
+ notices->showNotice(subject,message,has_inventory,inventory_name,inventory_offer);
+}
diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h
new file mode 100644
index 0000000000..c8ac001056
--- /dev/null
+++ b/indra/newview/llpanelgroup.h
@@ -0,0 +1,173 @@
+/**
+ * @file llpanelgroup.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUP_H
+#define LL_LLPANELGROUP_H
+
+#include "llgroupmgr.h"
+#include "llpanel.h"
+#include "lltimer.h"
+
+struct LLOfferInfo;
+
+const S32 UPDATE_MEMBERS_PER_FRAME = 500;
+
+// Forward declares
+class LLPanelGroupTab;
+class LLTabContainerCommon;
+class LLAgent;
+
+class LLPanelGroupTabObserver
+{
+public:
+ LLPanelGroupTabObserver() {};
+ virtual ~LLPanelGroupTabObserver(){};
+ virtual void tabChanged() = 0;
+};
+
+class LLPanelGroup : public LLPanel,
+ public LLGroupMgrObserver,
+ public LLPanelGroupTabObserver
+{
+public:
+ LLPanelGroup(const std::string& filename,
+ const std::string& name,
+ const LLUUID& group_id,
+ const std::string& initial_tab_selected = "");
+ virtual ~LLPanelGroup();
+
+ virtual BOOL postBuild();
+
+ static void onBtnOK(void*);
+ static void onBtnCancel(void*);
+ static void onBtnApply(void*);
+ static void onBtnRefresh(void*);
+ static void onClickTab(void*,bool);
+ void handleClickTab();
+
+ void setGroupID(const LLUUID& group_id);
+ void selectTab(std::string tab_name);
+
+ // Called when embedded in a floater during a close attempt.
+ BOOL canClose();
+
+ // Checks if the current tab needs to be applied, and tries to switch to the requested tab.
+ BOOL attemptTransition();
+
+ // Switches to the requested tab (will close() if requested is NULL)
+ void transitionToTab();
+
+ void updateTabVisibility();
+
+ // Used by attemptTransition to query the user's response to a tab that needs to apply.
+ static void onNotifyCallback(S32 option, void* user_data);
+ void handleNotifyCallback(S32 option);
+
+ bool apply();
+ void refreshData();
+ void close();
+ void draw();
+
+ // Group manager observer trigger.
+ virtual void changed(LLGroupChange gc);
+
+ // PanelGroupTab observer trigger
+ virtual void tabChanged();
+
+ void setAllowEdit(BOOL v) { mAllowEdit = v; }
+
+ void showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer);
+protected:
+ LLPanelGroupTab* mCurrentTab;
+ LLPanelGroupTab* mRequestedTab;
+ LLTabContainerCommon* mTabContainer;
+ BOOL mIgnoreTransition;
+
+ LLButton* mApplyBtn;
+
+ LLTimer mRefreshTimer;
+
+ BOOL mForceClose;
+
+ std::string mInitialTab;
+ std::string mFilename;
+
+ LLString mDefaultNeedsApplyMesg;
+ LLString mWantApplyMesg;
+
+ BOOL mAllowEdit;
+ BOOL mShowingNotifyDialog;
+};
+
+class LLPanelGroupTab : public LLPanel
+{
+public:
+ LLPanelGroupTab(const std::string& name, const LLUUID& group_id)
+ : LLPanel(name), mGroupID(group_id), mAllowEdit(TRUE), mHasModal(FALSE) { }
+ virtual ~LLPanelGroupTab();
+
+ // Factory that returns a new LLPanelGroupFoo tab.
+ static void* createTab(void* data);
+
+ // Triggered when the tab becomes active.
+ virtual void activate() { }
+
+ // Triggered when the tab becomes inactive.
+ virtual void deactivate() { }
+
+ // Asks if something needs to be applied.
+ // If returning true, this function should modify the message to the user.
+ virtual bool needsApply(LLString& mesg) { return false; }
+
+ // Asks if there is currently a modal dialog being shown.
+ virtual BOOL hasModal() { return mHasModal; }
+
+ // Request to apply current data.
+ // If returning fail, this function should modify the message to the user.
+ virtual bool apply(LLString& mesg) { return true; }
+
+ // Request a cancel of changes
+ virtual void cancel() { }
+
+ // Triggered when group information changes in the group manager.
+ virtual void update(LLGroupChange gc) { }
+
+ // This is the text to be displayed when a help button is pressed.
+ virtual LLString getHelpText() const { return mHelpText; }
+
+ // Display anything returned by getHelpText
+ static void onClickHelp(void* data);
+ void handleClickHelp();
+
+ // This just connects the help button callback.
+ virtual BOOL postBuild();
+
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ void setAllowEdit(BOOL v) { mAllowEdit = v; }
+
+ void addObserver(LLPanelGroupTabObserver *obs);
+ void removeObserver(LLPanelGroupTabObserver *obs);
+ void notifyObservers();
+
+protected:
+ LLUUID mGroupID;
+ LLTabContainerCommon* mTabContainer;
+ LLString mHelpText;
+
+ BOOL mAllowEdit;
+ BOOL mHasModal;
+
+ typedef std::set<LLPanelGroupTabObserver*> observer_list_t;
+ observer_list_t mObservers;
+};
+
+#endif // LL_LLPANELGROUP_H
diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp
new file mode 100644
index 0000000000..c996354044
--- /dev/null
+++ b/indra/newview/llpanelgroupgeneral.cpp
@@ -0,0 +1,771 @@
+/**
+ * @file llpanelgroupgeneral.cpp
+ * @brief General information about a group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupgeneral.h"
+
+#include "llvieweruictrlfactory.h"
+#include "llagent.h"
+#include "roles_constants.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroupinfo.h"
+
+// UI elements
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldbstrings.h"
+#include "lllineeditor.h"
+#include "llnamebox.h"
+#include "llnamelistctrl.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltexteditor.h"
+#include "lltexturectrl.h"
+#include "llviewermessage.h"
+#include "llviewerwindow.h"
+
+// static
+void* LLPanelGroupGeneral::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupGeneral("panel group general", *group_id);
+}
+
+
+LLPanelGroupGeneral::LLPanelGroupGeneral(const std::string& name,
+ const LLUUID& group_id)
+: LLPanelGroupTab(name, group_id),
+ mPendingMemberUpdate(FALSE),
+ mChanged(FALSE),
+ mFirstUse(TRUE),
+ mGroupNameEditor(NULL),
+ mGroupName(NULL),
+ mFounderName(NULL),
+ mInsignia(NULL),
+ mEditCharter(NULL),
+ mEditName(NULL),
+ mBtnJoinGroup(NULL),
+ mListVisibleMembers(NULL),
+ mCtrlShowInGroupList(NULL),
+ mCtrlPublishOnWeb(NULL),
+ mCtrlMature(NULL),
+ mCtrlOpenEnrollment(NULL),
+ mCtrlEnrollmentFee(NULL),
+ mSpinEnrollmentFee(NULL),
+ mCtrlReceiveNotices(NULL),
+ mActiveTitleLabel(NULL),
+ mComboActiveTitle(NULL)
+{
+
+}
+
+LLPanelGroupGeneral::~LLPanelGroupGeneral()
+{
+}
+
+BOOL LLPanelGroupGeneral::postBuild()
+{
+ llinfos << "LLPanelGroupGeneral::postBuild()" << llendl;
+
+ bool recurse = true;
+
+ // General info
+ mGroupNameEditor = (LLLineEditor*) getChildByName("group_name_editor", recurse);
+ mGroupName = (LLTextBox*) getChildByName("group_name", recurse);
+
+ mInsignia = (LLTextureCtrl*) getChildByName("insignia", recurse);
+ if (mInsignia)
+ {
+ mInsignia->setCommitCallback(onCommitAny);
+ mInsignia->setCallbackUserData(this);
+ mDefaultIconID = mInsignia->getImageAssetID();
+ }
+
+ mEditCharter = (LLTextEditor*) getChildByName("charter", recurse);
+ if(mEditCharter)
+ {
+ mEditCharter->setCommitCallback(onCommitAny);
+ mEditCharter->setFocusReceivedCallback(onCommitAny);
+ mEditCharter->setFocusChangedCallback(onCommitAny);
+ mEditCharter->setCallbackUserData(this);
+ }
+
+ mBtnJoinGroup = (LLButton*) getChildByName("join_button", recurse);
+ if ( mBtnJoinGroup )
+ {
+ mBtnJoinGroup->setClickedCallback(onClickJoin);
+ mBtnJoinGroup->setCallbackUserData(this);
+ }
+
+ mBtnInfo = (LLButton*) getChildByName("info_button", recurse);
+ if ( mBtnInfo )
+ {
+ mBtnInfo->setClickedCallback(onClickInfo);
+ mBtnInfo->setCallbackUserData(this);
+ }
+
+ LLTextBox* founder = (LLTextBox*) getChildByName("founder_name");
+ if (founder)
+ {
+ mFounderName = new LLNameBox(founder->getName(),founder->getRect(),LLUUID::null,FALSE,founder->getFont(),founder->getMouseOpaque());
+ removeChild(founder);
+ addChild(mFounderName);
+ }
+
+ mListVisibleMembers = (LLNameListCtrl*) getChildByName("visible_members", recurse);
+ if (mListVisibleMembers)
+ {
+ mListVisibleMembers->setDoubleClickCallback(openProfile);
+ mListVisibleMembers->setCallbackUserData(this);
+ }
+
+ // Options
+ mCtrlShowInGroupList = (LLCheckBoxCtrl*) getChildByName("show_in_group_list", recurse);
+ if (mCtrlShowInGroupList)
+ {
+ mCtrlShowInGroupList->setCommitCallback(onCommitAny);
+ mCtrlShowInGroupList->setCallbackUserData(this);
+ }
+
+ mCtrlPublishOnWeb = (LLCheckBoxCtrl*) getChildByName("publish_on_web", recurse);
+ if (mCtrlPublishOnWeb)
+ {
+ mCtrlPublishOnWeb->setCommitCallback(onCommitAny);
+ mCtrlPublishOnWeb->setCallbackUserData(this);
+ }
+
+ mCtrlMature = (LLCheckBoxCtrl*) getChildByName("mature", recurse);
+ if (mCtrlMature)
+ {
+ mCtrlMature->setCommitCallback(onCommitAny);
+ mCtrlMature->setCallbackUserData(this);
+ }
+
+ mCtrlOpenEnrollment = (LLCheckBoxCtrl*) getChildByName("open_enrollement", recurse);
+ if (mCtrlOpenEnrollment)
+ {
+ mCtrlOpenEnrollment->setCommitCallback(onCommitAny);
+ mCtrlOpenEnrollment->setCallbackUserData(this);
+ }
+
+ mCtrlEnrollmentFee = (LLCheckBoxCtrl*) getChildByName("check_enrollment_fee", recurse);
+ if (mCtrlEnrollmentFee)
+ {
+ mCtrlEnrollmentFee->setCommitCallback(onCommitEnrollment);
+ mCtrlEnrollmentFee->setCallbackUserData(this);
+ }
+
+ mSpinEnrollmentFee = (LLSpinCtrl*) getChildByName("spin_enrollment_fee", recurse);
+ if (mSpinEnrollmentFee)
+ {
+ mSpinEnrollmentFee->setCommitCallback(onCommitAny);
+ mSpinEnrollmentFee->setCallbackUserData(this);
+ }
+
+ BOOL accept_notices = FALSE;
+ LLGroupData data;
+ if(gAgent.getGroupData(mGroupID,data))
+ {
+ accept_notices = data.mAcceptNotices;
+ }
+ mCtrlReceiveNotices = (LLCheckBoxCtrl*) getChildByName("receive_notices", recurse);
+ if (mCtrlReceiveNotices)
+ {
+ mCtrlReceiveNotices->setCommitCallback(onReceiveNotices);
+ mCtrlReceiveNotices->setCallbackUserData(this);
+ mCtrlReceiveNotices->set(accept_notices);
+ mCtrlReceiveNotices->setEnabled(data.mID.notNull());
+ }
+
+ mActiveTitleLabel = (LLTextBox*) getChildByName("active_title_label", recurse);
+
+ mComboActiveTitle = (LLComboBox*) getChildByName("active_title", recurse);
+ if (mComboActiveTitle)
+ {
+ mComboActiveTitle->setCommitCallback(onCommitTitle);
+ mComboActiveTitle->setCallbackUserData(this);
+ }
+
+ // Extra data
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*)getChildByName("incomplete_member_data_str");
+ if (txt)
+ {
+ mIncompleteMemberDataStr = txt->getText();
+ removeChild(txt);
+ }
+
+ txt = (LLTextBox*)getChildByName("confirm_group_create_str");
+ if (txt)
+ {
+ mConfirmGroupCreateStr = txt->getText();
+ removeChild(txt);
+ }
+
+ // If the group_id is null, then we are creating a new group
+ if (mGroupID.isNull())
+ {
+ mGroupNameEditor->setEnabled(TRUE);
+ mEditCharter->setEnabled(TRUE);
+
+ mCtrlShowInGroupList->setEnabled(TRUE);
+ mCtrlPublishOnWeb->setEnabled(TRUE);
+ mCtrlMature->setEnabled(TRUE);
+ mCtrlOpenEnrollment->setEnabled(TRUE);
+ mCtrlEnrollmentFee->setEnabled(TRUE);
+ mSpinEnrollmentFee->setEnabled(TRUE);
+
+ mBtnJoinGroup->setVisible(FALSE);
+ mBtnInfo->setVisible(FALSE);
+ mGroupName->setVisible(FALSE);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+// static
+void LLPanelGroupGeneral::onCommitAny(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ self->mChanged = TRUE;
+ self->notifyObservers();
+}
+
+// static
+void LLPanelGroupGeneral::onCommitEnrollment(LLUICtrl* ctrl, void* data)
+{
+ onCommitAny(ctrl, data);
+
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ // Make sure both enrollment related widgets are there.
+ if (!self->mCtrlEnrollmentFee || !self->mSpinEnrollmentFee)
+ {
+ return;
+ }
+
+ // Make sure the agent can change enrollment info.
+ if (!gAgent.hasPowerInGroup(self->mGroupID,GP_MEMBER_OPTIONS)
+ || !self->mAllowEdit)
+ {
+ return;
+ }
+
+ if (self->mCtrlEnrollmentFee->get())
+ {
+ self->mSpinEnrollmentFee->setEnabled(TRUE);
+ }
+ else
+ {
+ self->mSpinEnrollmentFee->setEnabled(FALSE);
+ self->mSpinEnrollmentFee->set(0);
+ }
+}
+
+// static
+void LLPanelGroupGeneral::onCommitTitle(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ if (self->mGroupID.isNull() || !self->mAllowEdit) return;
+ gGroupMgr->sendGroupTitleUpdate(self->mGroupID,self->mComboActiveTitle->getCurrentID());
+ self->update(GC_TITLES);
+}
+
+// static
+void LLPanelGroupGeneral::onClickInfo(void *userdata)
+{
+ LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata;
+
+ if ( !self ) return;
+
+ lldebugs << "open group info: " << self->mGroupID << llendl;
+
+ LLFloaterGroupInfo::showFromUUID(self->mGroupID);
+}
+
+// static
+void LLPanelGroupGeneral::onClickJoin(void *userdata)
+{
+ LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata;
+
+ if ( !self ) return;
+
+ lldebugs << "joining group: " << self->mGroupID << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(self->mGroupID);
+
+ S32 cost = gdatap->mMembershipFee;
+ LLString::format_map_t args;
+ args["[COST]"] = llformat("%d", cost);
+
+ if (can_afford_transaction(cost))
+ {
+ gViewerWindow->alertXml("JoinGroupCanAfford", args,
+ LLPanelGroupGeneral::joinDlgCB,
+ self);
+ }
+ else
+ {
+ gViewerWindow->alertXml("JoinGroupCannotAfford", args);
+ }
+}
+
+// static
+void LLPanelGroupGeneral::joinDlgCB(S32 which, void *userdata)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*) userdata;
+
+ if (which == 1 || !self)
+ {
+ // user clicked cancel
+ return;
+ }
+
+ gGroupMgr->sendGroupMemberJoin(self->mGroupID);
+}
+
+// static
+void LLPanelGroupGeneral::onReceiveNotices(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl;
+
+ if(!self) return;
+ gAgent.setGroupAcceptNotices(self->mGroupID, check->get());
+}
+
+// static
+void LLPanelGroupGeneral::openProfile(void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+
+ if (self && self->mListVisibleMembers)
+ {
+ LLScrollListItem* selected = self->mListVisibleMembers->getFirstSelected();
+ if (selected)
+ {
+ LLFloaterAvatarInfo::showFromDirectory( selected->getUUID() );
+ }
+ }
+}
+
+bool LLPanelGroupGeneral::needsApply(LLString& mesg)
+{
+ llinfos << "LLPanelGroupGeneral::needsApply(LLString& mesg) " << mChanged << llendl;
+
+ mesg = "General group information has changed.";
+ return mChanged || mGroupID.isNull();
+}
+
+void LLPanelGroupGeneral::activate()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (mGroupID.notNull()
+ && (!gdatap || mFirstUse))
+ {
+ gGroupMgr->sendGroupTitlesRequest(mGroupID);
+ gGroupMgr->sendGroupPropertiesRequest(mGroupID);
+
+
+ if (!gdatap || !gdatap->isMemberDataComplete() )
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ mFirstUse = FALSE;
+ }
+ mChanged = FALSE;
+
+ update(GC_ALL);
+}
+
+void LLPanelGroupGeneral::draw()
+{
+ LLPanelGroupTab::draw();
+
+ if (mPendingMemberUpdate)
+ {
+ updateMembers();
+ }
+}
+
+bool LLPanelGroupGeneral::apply(LLString& mesg)
+{
+ if (!mAllowEdit)
+ {
+ llwarns << "LLPanelGroupGeneral::apply() called with false mAllowEdit"
+ << llendl;
+ return true;
+ }
+
+ llinfos << "LLPanelGroupGeneral::apply" << llendl;
+ if (mGroupID.isNull())
+ {
+ // Validate the group name length.
+ S32 group_name_len = mGroupNameEditor->getText().size();
+ if ( group_name_len < DB_GROUP_NAME_MIN_LEN
+ || group_name_len > DB_GROUP_NAME_STR_LEN)
+ {
+ std::ostringstream temp_error;
+ temp_error << "A group name must be between " << DB_GROUP_NAME_MIN_LEN
+ << " and " << DB_GROUP_NAME_STR_LEN << " characters.";
+ mesg = temp_error.str();
+ return false;
+ }
+
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = mConfirmGroupCreateStr;
+ gViewerWindow->alertXml("GenericAlertYesCancel", args,
+ createGroupCallback,this);
+
+ return false;
+ }
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap)
+ {
+ mesg = "No group data found for group ";
+ mesg.append(mGroupID.getString());
+ return false;
+ }
+
+ bool can_change_ident = false;
+ bool can_change_member_opts = false;
+ can_change_ident = gAgent.hasPowerInGroup(mGroupID,GP_GROUP_CHANGE_IDENTITY);
+ can_change_member_opts = gAgent.hasPowerInGroup(mGroupID,GP_MEMBER_OPTIONS);
+
+ if (can_change_ident)
+ {
+ if (mCtrlPublishOnWeb) gdatap->mAllowPublish = mCtrlPublishOnWeb->get();
+ if (mEditCharter) gdatap->mCharter = mEditCharter->getText();
+ if (mInsignia) gdatap->mInsigniaID = mInsignia->getImageAssetID();
+ if (mCtrlMature) gdatap->mMaturePublish = mCtrlMature->get();
+ if (mCtrlShowInGroupList) gdatap->mShowInList = mCtrlShowInGroupList->get();
+ }
+
+ if (can_change_member_opts)
+ {
+ if (mCtrlOpenEnrollment) gdatap->mOpenEnrollment = mCtrlOpenEnrollment->get();
+ if (mCtrlEnrollmentFee && mSpinEnrollmentFee)
+ {
+ gdatap->mMembershipFee = (mCtrlEnrollmentFee->get()) ?
+ (S32) mSpinEnrollmentFee->get() : 0;
+ }
+ }
+
+ if (can_change_ident || can_change_member_opts)
+ {
+ gGroupMgr->sendUpdateGroupInfo(mGroupID);
+ }
+
+ if (mCtrlReceiveNotices) gAgent.setGroupAcceptNotices(mGroupID, mCtrlReceiveNotices->get());
+
+ mChanged = FALSE;
+ notifyObservers();
+
+ return true;
+}
+
+void LLPanelGroupGeneral::cancel()
+{
+ mChanged = FALSE;
+
+ //cancel out all of the click changes to, although since we are
+ //shifting tabs or closing the floater, this need not be done...yet
+ notifyObservers();
+}
+
+// static
+void LLPanelGroupGeneral::createGroupCallback(S32 option, void* userdata)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)userdata;
+ if (!self) return;
+
+ switch(option)
+ {
+ case 0:
+ {
+ // Yay! We are making a new group!
+ U32 enrollment_fee = (self->mCtrlEnrollmentFee->get() ?
+ (U32) self->mSpinEnrollmentFee->get() : 0);
+
+ gGroupMgr->sendCreateGroupRequest(self->mGroupNameEditor->getText(),
+ self->mEditCharter->getText(),
+ self->mCtrlShowInGroupList->get(),
+ self->mInsignia->getImageAssetID(),
+ enrollment_fee,
+ self->mCtrlOpenEnrollment->get(),
+ self->mCtrlPublishOnWeb->get(),
+ self->mCtrlMature->get());
+
+ }
+ break;
+ case 1:
+ default:
+ break;
+ }
+}
+
+static F32 sSDTime = 0.0f;
+static F32 sElementTime = 0.0f;
+static F32 sAllTime = 0.0f;
+
+// virtual
+void LLPanelGroupGeneral::update(LLGroupChange gc)
+{
+ if (mGroupID.isNull()) return;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ LLGroupData agent_gdatap;
+ bool is_member = false;
+ if (gAgent.getGroupData(mGroupID,agent_gdatap)) is_member = true;
+
+ if (mComboActiveTitle)
+ {
+ mComboActiveTitle->setVisible(is_member);
+ mComboActiveTitle->setEnabled(mAllowEdit);
+
+ if ( mActiveTitleLabel) mActiveTitleLabel->setVisible(is_member);
+
+ if (is_member)
+ {
+ LLUUID current_title_role;
+
+ mComboActiveTitle->clear();
+ mComboActiveTitle->removeall();
+ bool has_selected_title = false;
+
+ if (1 == gdatap->mTitles.size())
+ {
+ // Only the everyone title. Don't bother letting them try changing this.
+ mComboActiveTitle->setEnabled(FALSE);
+ }
+ else
+ {
+ mComboActiveTitle->setEnabled(TRUE);
+ }
+
+ std::vector<LLGroupTitle>::const_iterator citer = gdatap->mTitles.begin();
+ std::vector<LLGroupTitle>::const_iterator end = gdatap->mTitles.end();
+
+ for ( ; citer != end; ++citer)
+ {
+ mComboActiveTitle->add(citer->mTitle,citer->mRoleID, (citer->mSelected ? ADD_TOP : ADD_BOTTOM));
+ if (citer->mSelected)
+ {
+ mComboActiveTitle->setCurrentByID(citer->mRoleID);
+ has_selected_title = true;
+ }
+ }
+
+ if (!has_selected_title)
+ {
+ mComboActiveTitle->setCurrentByID(LLUUID::null);
+ }
+ }
+ }
+
+ // If this was just a titles update, we are done.
+ if (gc == GC_TITLES) return;
+
+ bool can_change_ident = false;
+ bool can_change_member_opts = false;
+ can_change_ident = gAgent.hasPowerInGroup(mGroupID,GP_GROUP_CHANGE_IDENTITY);
+ can_change_member_opts = gAgent.hasPowerInGroup(mGroupID,GP_MEMBER_OPTIONS);
+
+ if (mCtrlShowInGroupList)
+ {
+ mCtrlShowInGroupList->set(gdatap->mShowInList);
+ mCtrlShowInGroupList->setEnabled(mAllowEdit && can_change_ident);
+ }
+ if (mCtrlPublishOnWeb)
+ {
+ mCtrlPublishOnWeb->set(gdatap->mAllowPublish);
+ mCtrlPublishOnWeb->setEnabled(mAllowEdit && can_change_ident);
+ }
+ if (mCtrlMature)
+ {
+ mCtrlMature->set(gdatap->mMaturePublish);
+ mCtrlMature->setEnabled(mAllowEdit && can_change_ident);
+ }
+ if (mCtrlOpenEnrollment)
+ {
+ mCtrlOpenEnrollment->set(gdatap->mOpenEnrollment);
+ mCtrlOpenEnrollment->setEnabled(mAllowEdit && can_change_member_opts);
+ }
+ if (mCtrlEnrollmentFee)
+ {
+ mCtrlEnrollmentFee->set(gdatap->mMembershipFee > 0);
+ mCtrlEnrollmentFee->setEnabled(mAllowEdit && can_change_member_opts);
+ }
+
+ if (mSpinEnrollmentFee)
+ {
+ S32 fee = gdatap->mMembershipFee;
+ mSpinEnrollmentFee->set((F32)fee);
+ mSpinEnrollmentFee->setEnabled( mAllowEdit
+ && (fee > 0) && can_change_member_opts);
+ }
+ if ( mBtnJoinGroup )
+ {
+ char fee_buff[20];
+ bool visible;
+
+ visible = !is_member && gdatap->mOpenEnrollment;
+ mBtnJoinGroup->setVisible(visible);
+
+ if ( visible )
+ {
+ sprintf(fee_buff, "Join (L$%d)", gdatap->mMembershipFee);
+ mBtnJoinGroup->setLabelSelected(std::string(fee_buff));
+ mBtnJoinGroup->setLabelUnselected(std::string(fee_buff));
+ }
+ }
+ if ( mBtnInfo )
+ {
+ mBtnInfo->setVisible(is_member && !mAllowEdit);
+ }
+
+ if (mCtrlReceiveNotices)
+ {
+ mCtrlReceiveNotices->setVisible(is_member);
+ if (is_member)
+ {
+ mCtrlReceiveNotices->set(agent_gdatap.mAcceptNotices);
+ mCtrlReceiveNotices->setEnabled(mAllowEdit);
+ }
+ }
+
+
+ if (mInsignia) mInsignia->setEnabled(mAllowEdit && can_change_ident);
+ if (mEditCharter) mEditCharter->setEnabled(mAllowEdit && can_change_ident);
+
+ if (mGroupName) mGroupName->setText(gdatap->mName);
+ if (mGroupNameEditor) mGroupNameEditor->setVisible(FALSE);
+ if (mFounderName) mFounderName->setNameID(gdatap->mFounderID,FALSE);
+ if (mInsignia)
+ {
+ if (gdatap->mInsigniaID.notNull())
+ {
+ mInsignia->setImageAssetID(gdatap->mInsigniaID);
+ }
+ else
+ {
+
+ mInsignia->setImageAssetID(mDefaultIconID);
+ }
+ }
+ if (mEditCharter) mEditCharter->setText(gdatap->mCharter);
+
+ if (mListVisibleMembers)
+ {
+ mListVisibleMembers->deleteAllItems();
+
+ if (gdatap->isMemberDataComplete())
+ {
+ mMemberProgress = gdatap->mMembers.begin();
+ mPendingMemberUpdate = TRUE;
+
+ sSDTime = 0.0f;
+ sElementTime = 0.0f;
+ sAllTime = 0.0f;
+ }
+ else
+ {
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, LLUUID::null );
+ std::stringstream pending;
+ pending << "Retrieving member list (" << gdatap->mMembers.size() << "\\" << gdatap->mMemberCount << ")";
+ row->addColumn(pending.str(), LLFontGL::sSansSerif);
+ mListVisibleMembers->setEnabled(FALSE);
+ mListVisibleMembers->addItem(row);
+ }
+ }
+}
+
+void LLPanelGroupGeneral::updateMembers()
+{
+ mPendingMemberUpdate = FALSE;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!mListVisibleMembers || !gdatap
+ || !gdatap->isMemberDataComplete())
+ {
+ return;
+ }
+
+ static LLTimer all_timer;
+ static LLTimer sd_timer;
+ static LLTimer element_timer;
+
+ all_timer.reset();
+ S32 i = 0;
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+
+ for( ; mMemberProgress != end && i<UPDATE_MEMBERS_PER_FRAME;
+ ++mMemberProgress, ++i)
+ {
+ //llinfos << "Adding " << iter->first << ", " << iter->second->getTitle() << llendl;
+ LLGroupMemberData* member = mMemberProgress->second;
+ if (!member)
+ {
+ continue;
+ }
+ // Owners show up in bold.
+ LLString style = "NORMAL";
+ if ( member->isOwner() )
+ {
+ style = "BOLD";
+ }
+
+ sd_timer.reset();
+ LLSD row;
+ row["id"] = member->getID();
+
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["font-style"] = style;
+ // value is filled in by name list control
+
+ row["columns"][1]["column"] = "title";
+ row["columns"][1]["value"] = member->getTitle();
+ row["columns"][1]["font-style"] = style;
+
+ row["columns"][2]["column"] = "online";
+ row["columns"][2]["value"] = member->getOnlineStatus();
+ row["columns"][2]["font-style"] = style;
+
+ sSDTime += sd_timer.getElapsedTimeF32();
+
+ element_timer.reset();
+ mListVisibleMembers->addElement(row);//, ADD_SORTED);
+ sElementTime += element_timer.getElapsedTimeF32();
+ }
+ sAllTime += all_timer.getElapsedTimeF32();
+
+ llinfos << "Updated " << i << " of " << UPDATE_MEMBERS_PER_FRAME << "members in the list." << llendl;
+ if (mMemberProgress == end)
+ {
+ llinfos << " member list completed." << llendl;
+ mListVisibleMembers->setEnabled(TRUE);
+
+ llinfos << "All Time: " << sAllTime << llendl;
+ llinfos << "SD Time: " << sSDTime << llendl;
+ llinfos << "Element Time: " << sElementTime << llendl;
+ }
+ else
+ {
+ mPendingMemberUpdate = TRUE;
+ mListVisibleMembers->setEnabled(FALSE);
+ }
+}
diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h
new file mode 100644
index 0000000000..d62cab0133
--- /dev/null
+++ b/indra/newview/llpanelgroupgeneral.h
@@ -0,0 +1,91 @@
+/**
+ * @file llpanelgroupgeneral.h
+ * @brief General information about a group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPGENERAL_H
+#define LL_LLPANELGROUPGENERAL_H
+
+#include "llpanelgroup.h"
+
+class LLLineEditor;
+class LLTextBox;
+class LLTextureCtrl;
+class LLTextEditor;
+class LLButton;
+class LLNameListCtrl;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLNameBox;
+class LLSpinCtrl;
+
+class LLPanelGroupGeneral : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupGeneral(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupGeneral();
+
+ // LLPanelGroupTab
+ static void* createTab(void* data);
+ virtual void activate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ static void createGroupCallback(S32 option, void* user_data);
+
+ virtual void update(LLGroupChange gc);
+
+ virtual BOOL postBuild();
+
+ virtual void draw();
+
+private:
+ static void onCommitAny(LLUICtrl* ctrl, void* data);
+ static void onCommitTitle(LLUICtrl* ctrl, void* data);
+ static void onCommitEnrollment(LLUICtrl* ctrl, void* data);
+ static void onClickJoin(void* userdata);
+ static void onClickInfo(void* userdata);
+ static void onReceiveNotices(LLUICtrl* ctrl, void* data);
+ static void openProfile(void* data);
+
+ static void joinDlgCB(S32 which, void *userdata);
+
+ void updateMembers();
+
+ BOOL mPendingMemberUpdate;
+ BOOL mChanged;
+ BOOL mFirstUse;
+ std::string mIncompleteMemberDataStr;
+ std::string mConfirmGroupCreateStr;
+ LLUUID mDefaultIconID;
+
+ // Group information
+ LLLineEditor *mGroupNameEditor;
+ LLTextBox *mGroupName;
+ LLNameBox *mFounderName;
+ LLTextureCtrl *mInsignia;
+ LLTextEditor *mEditCharter;
+ LLLineEditor *mEditName;
+ LLButton *mBtnJoinGroup;
+ LLButton *mBtnInfo;
+
+ LLNameListCtrl *mListVisibleMembers;
+
+ // Options
+ LLCheckBoxCtrl *mCtrlShowInGroupList;
+ LLCheckBoxCtrl *mCtrlPublishOnWeb;
+ LLCheckBoxCtrl *mCtrlMature;
+ LLCheckBoxCtrl *mCtrlOpenEnrollment;
+ LLCheckBoxCtrl *mCtrlEnrollmentFee;
+ LLSpinCtrl *mSpinEnrollmentFee;
+ LLCheckBoxCtrl *mCtrlReceiveNotices;
+ LLTextBox *mActiveTitleLabel;
+ LLComboBox *mComboActiveTitle;
+
+ LLGroupMgrGroupData::member_iter mMemberProgress;
+};
+
+#endif
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp
new file mode 100644
index 0000000000..231551a756
--- /dev/null
+++ b/indra/newview/llpanelgroupinvite.cpp
@@ -0,0 +1,392 @@
+/**
+ * @file llpanelgroupinvite.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupinvite.h"
+
+#include "llagent.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llgroupmgr.h"
+#include "llnamelistctrl.h"
+#include "llspinctrl.h"
+#include "llvieweruictrlfactory.h"
+
+class LLPanelGroupInvite::impl
+{
+public:
+ impl(const LLUUID& group_id);
+ ~impl();
+
+ void addUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& agent_ids);
+ void submitInvitations();
+ void addRoleNames(LLGroupMgrGroupData* gdatap);
+ void handleRemove();
+ void handleSelection();
+
+ static void callbackClickCancel(void* userdata);
+ static void callbackClickOK(void* userdata);
+ static void callbackClickAdd(void* userdata);
+ static void callbackClickRemove(void* userdata);
+ static void callbackSelect(LLUICtrl* ctrl, void* userdata);
+ static void callbackAddUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& agent_ids,
+ void* user_data);
+
+public:
+ LLUUID mGroupID;
+
+ LLNameListCtrl *mInvitees;
+ LLComboBox *mRoleNames;
+ LLButton *mRemoveButton;
+
+ void (*mCloseCallback)(void* data);
+
+ void* mCloseCallbackUserData;
+};
+
+
+LLPanelGroupInvite::impl::impl(const LLUUID& group_id)
+{
+ mGroupID = group_id;
+
+ mInvitees = NULL;
+ mRoleNames = NULL;
+ mRemoveButton = NULL;
+
+ mCloseCallback = NULL;
+ mCloseCallbackUserData = NULL;
+}
+
+LLPanelGroupInvite::impl::~impl()
+{
+}
+
+void LLPanelGroupInvite::impl::addUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& agent_ids)
+{
+ std::string name;
+ LLUUID id;
+
+ for (S32 i = 0; i < (S32)names.size(); i++)
+ {
+ name = names[i];
+ id = agent_ids[i];
+
+ // Make sure this agent isn't already in the list.
+ bool already_in_list = false;
+ std::vector<LLScrollListItem*> items = mInvitees->getAllData();
+ for (std::vector<LLScrollListItem*>::iterator iter = items.begin();
+ iter != items.end(); ++iter)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getUUID() == id)
+ {
+ already_in_list = true;
+ break;
+ }
+ }
+ if (already_in_list)
+ {
+ continue;
+ }
+
+ //add the name to the names list
+ const BOOL enabled = TRUE;
+ LLScrollListItem* row = new LLScrollListItem(
+ enabled, NULL, id);
+ row->addColumn(name.c_str(), LLFontGL::sSansSerif);
+ mInvitees->addItem(row);
+ }
+}
+
+void LLPanelGroupInvite::impl::submitInvitations()
+{
+ std::map<LLUUID, LLUUID> role_member_pairs;
+
+ // Default to everyone role.
+ LLUUID role_id = LLUUID::null;
+
+ if (mRoleNames)
+ {
+ role_id = mRoleNames->getCurrentID();
+ }
+
+ //loop over the users
+ std::vector<LLScrollListItem*> items = mInvitees->getAllData();
+ for (std::vector<LLScrollListItem*>::iterator iter = items.begin();
+ iter != items.end(); ++iter)
+ {
+ LLScrollListItem* item = *iter;
+ role_member_pairs[item->getUUID()] = role_id;
+ }
+
+ gGroupMgr->sendGroupMemberInvites(mGroupID, role_member_pairs);
+
+ //then close
+ (*mCloseCallback)(mCloseCallbackUserData);
+}
+
+void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
+{
+ LLGroupMgrGroupData::member_iter agent_iter =
+ gdatap->mMembers.find(gAgent.getID());
+
+ //get the member data for the agent if it exists
+ if ( agent_iter != gdatap->mMembers.end() )
+ {
+ LLGroupMemberData* member_data = (*agent_iter).second;
+
+ //loop over the agent's roles in the group
+ //then add those roles to the list of roles that the agent
+ //can invite people to be
+ if ( member_data && mRoleNames)
+ {
+ //if the user is the owner then we add
+ //all of the roles in the group
+ //else if they have the add to roles power
+ //we add every role but owner,
+ //else if they have the limited add to roles power
+ //we add every role the user is in
+ //else we just add to everyone
+ bool is_owner = member_data->isInRole(gdatap->mOwnerRole);
+ bool can_assign_any = gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_ASSIGN_MEMBER);
+ bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_ASSIGN_MEMBER_LIMITED);
+
+ LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+
+ //populate the role list
+ for ( ; rit != end; ++rit)
+ {
+ LLUUID role_id = (*rit).first;
+ LLRoleData rd;
+ if ( gdatap->getRoleData(role_id,rd) )
+ {
+ // Owners can add any role.
+ if ( is_owner
+ // Even 'can_assign_any' can't add owner role.
+ || (can_assign_any && role_id != gdatap->mOwnerRole)
+ // Add all roles user is in
+ || (can_assign_limited && member_data->isInRole(role_id))
+ // Everyone role.
+ || role_id == LLUUID::null )
+ {
+ mRoleNames->add(rd.mRoleName,
+ role_id,
+ ADD_BOTTOM);
+ }
+ }
+ }
+ }//end if member data is not null
+ }//end if agent is in the group
+}
+
+//static
+void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata)
+{
+ LLPanelGroupInvite* panelp = (LLPanelGroupInvite*) userdata;
+
+ if ( panelp )
+ {
+ //Right now this is hard coded with some knowledge that it is part
+ //of a floater since the avatar picker needs to be added as a dependent
+ //floater to the parent floater.
+ //Soon the avatar picker will be embedded into this panel
+ //instead of being it's own separate floater. But that is next week.
+ //This will do for now. -jwolk May 10, 2006
+ LLFloater* parentp;
+
+ parentp = gFloaterView->getParentFloater(panelp);
+ parentp->addDependentFloater(LLFloaterAvatarPicker::show(callbackAddUsers,
+ panelp->mImplementation,
+ TRUE));
+ }
+}
+
+//static
+void LLPanelGroupInvite::impl::callbackClickRemove(void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+
+ if ( selfp ) selfp->handleRemove();
+}
+
+void LLPanelGroupInvite::impl::handleRemove()
+{
+ // Check if there is anything selected.
+ std::vector<LLScrollListItem*> selection =
+ mInvitees->getAllSelected();
+ if (selection.empty()) return;
+
+ // Remove all selected invitees.
+ mInvitees->deleteSelectedItems();
+ mRemoveButton->setEnabled(FALSE);
+}
+
+// static
+void LLPanelGroupInvite::impl::callbackSelect(
+ LLUICtrl* ctrl, void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+ if ( selfp ) selfp->handleSelection();
+}
+
+void LLPanelGroupInvite::impl::handleSelection()
+{
+ // Check if there is anything selected.
+ std::vector<LLScrollListItem*> selection =
+ mInvitees->getAllSelected();
+ if (selection.empty())
+ {
+ mRemoveButton->setEnabled(FALSE);
+ }
+ else
+ {
+ mRemoveButton->setEnabled(TRUE);
+ }
+}
+
+void LLPanelGroupInvite::impl::callbackClickCancel(void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+
+ if ( selfp )
+ {
+ (*(selfp->mCloseCallback))(selfp->mCloseCallbackUserData);
+ }
+}
+
+void LLPanelGroupInvite::impl::callbackClickOK(void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+
+ if ( selfp ) selfp->submitInvitations();
+}
+
+//static
+void LLPanelGroupInvite::impl::callbackAddUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& ids,
+ void* user_data)
+{
+ impl* selfp = (impl*) user_data;
+
+ if ( selfp) selfp->addUsers(names, ids);
+}
+
+LLPanelGroupInvite::LLPanelGroupInvite(const std::string& name,
+ const LLUUID& group_id)
+ : LLPanel(name)
+{
+ mImplementation = new impl(group_id);
+
+ std::string panel_def_file;
+
+ // Pass on construction of this panel to the control factory.
+ gUICtrlFactory->buildPanel(this, "panel_group_invite.xml", &getFactoryMap());
+}
+
+LLPanelGroupInvite::~LLPanelGroupInvite()
+{
+ delete mImplementation;
+}
+
+void LLPanelGroupInvite::setCloseCallback(void (*close_callback)(void*),
+ void* data)
+{
+ mImplementation->mCloseCallback = close_callback;
+ mImplementation->mCloseCallbackUserData = data;
+}
+
+void LLPanelGroupInvite::clear()
+{
+ mImplementation->mInvitees->deleteAllItems();
+ mImplementation->mRoleNames->clear();
+ mImplementation->mRoleNames->removeall();
+}
+
+void LLPanelGroupInvite::update()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mImplementation->mGroupID);
+
+ if (!gdatap || !gdatap->isRoleDataComplete())
+ {
+ gGroupMgr->sendGroupRoleDataRequest(mImplementation->mGroupID);
+ }
+ else
+ {
+ if ( mImplementation->mRoleNames )
+ {
+ mImplementation->mRoleNames->clear();
+ mImplementation->mRoleNames->removeall();
+
+ //add the role names and select the everybody role by default
+ mImplementation->addRoleNames(gdatap);
+ mImplementation->mRoleNames->setCurrentByID(LLUUID::null);
+ }
+ }
+}
+
+BOOL LLPanelGroupInvite::postBuild()
+{
+ BOOL recurse = TRUE;
+
+ mImplementation->mRoleNames = (LLComboBox*) getChildByName("role_name",
+ recurse);
+ mImplementation->mInvitees =
+ (LLNameListCtrl*) getChildByName("invitee_list", recurse);
+ if ( mImplementation->mInvitees )
+ {
+ mImplementation->mInvitees->setCallbackUserData(mImplementation);
+ mImplementation->mInvitees->setCommitOnSelectionChange(TRUE);
+ mImplementation->mInvitees->setCommitCallback(impl::callbackSelect);
+ }
+
+ LLButton* button = (LLButton*) getChildByName("add_button", recurse);
+ if ( button )
+ {
+ // default to opening avatarpicker automatically
+ // (*impl::callbackClickAdd)((void*)this);
+ button->setClickedCallback(impl::callbackClickAdd);
+ button->setCallbackUserData(this);
+ }
+
+ mImplementation->mRemoveButton =
+ (LLButton*) getChildByName("remove_button", recurse);
+ if ( mImplementation->mRemoveButton )
+ {
+ mImplementation->mRemoveButton->
+ setClickedCallback(impl::callbackClickRemove);
+ mImplementation->mRemoveButton->setCallbackUserData(mImplementation);
+ mImplementation->mRemoveButton->setEnabled(FALSE);
+ }
+
+ button = (LLButton*) getChildByName("ok_button", recurse);
+ if ( button )
+ {
+ button->setClickedCallback(impl::callbackClickOK);
+ button->setCallbackUserData(mImplementation);
+ }
+
+ button = (LLButton*) getChildByName("cancel_button", recurse);
+ if ( button )
+ {
+ button->setClickedCallback(impl::callbackClickCancel);
+ button->setCallbackUserData(mImplementation);
+ }
+
+ update();
+
+ return (mImplementation->mRoleNames &&
+ mImplementation->mInvitees &&
+ mImplementation->mRemoveButton);
+}
diff --git a/indra/newview/llpanelgroupinvite.h b/indra/newview/llpanelgroupinvite.h
new file mode 100644
index 0000000000..bab39a1a66
--- /dev/null
+++ b/indra/newview/llpanelgroupinvite.h
@@ -0,0 +1,32 @@
+/**
+ * @file llpanelgroupinvite.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPINVITE_H
+#define LL_LLPANELGROUPINVITE_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+
+class LLPanelGroupInvite
+: public LLPanel
+{
+public:
+ LLPanelGroupInvite(const std::string& name, const LLUUID& group_id);
+ ~LLPanelGroupInvite();
+
+ void clear();
+ void update();
+
+ void setCloseCallback(void (*close_callback)(void*), void* data);
+
+ virtual BOOL postBuild();
+protected:
+ class impl;
+ impl* mImplementation;
+};
+
+#endif
diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp
new file mode 100644
index 0000000000..1cff3a6ea4
--- /dev/null
+++ b/indra/newview/llpanelgrouplandmoney.cpp
@@ -0,0 +1,1387 @@
+/**
+ * @file llpanelgrouplandmoney.cpp
+ * @brief Panel for group land and money.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgrouplandmoney.h"
+
+#include "lluiconstants.h"
+#include "roles_constants.h"
+
+#include "llparcel.h"
+#include "llqueryflags.h"
+
+#include "llagent.h"
+#include "lliconctrl.h"
+#include "lllineeditor.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "lltabcontainer.h"
+#include "lltransactiontypes.h"
+#include "llvieweruictrlfactory.h"
+
+#include "llstatusbar.h"
+#include "llfloaterworldmap.h"
+#include "llviewermessage.h"
+
+const char LOADING_STRING[] = "Computing...";
+
+class LLPanelGroupLandMoney::impl
+{
+public:
+ impl(const LLUUID& group_id); //constructor
+ virtual ~impl();
+
+ void requestGroupLandInfo();
+
+ int getStoredContribution();
+ void setYourContributionTextField(int contrib);
+ void setYourMaxContributionTextBox(int max);
+
+ virtual void onMapButton();
+ virtual bool applyContribution();
+ virtual void processGroupLand(LLMessageSystem* msg);
+
+ static void mapCallback(void* data);
+ static void contributionCommitCallback(LLUICtrl* ctrl, void* userdata);
+ static void contributionKeystrokeCallback(LLLineEditor* caller, void* userdata);
+
+//member variables
+public:
+ LLTextBox *mTotalContributedLandp;
+ LLTextBox *mTotalLandInUsep;
+ LLTextBox *mLandAvailablep;
+ LLTextBox* mGroupOverLimitTextp;
+ LLIconCtrl* mGroupOverLimitIconp;
+ LLTextBox* mYourContributionMaxTextp;
+
+ LLLineEditor* mYourContributionEditorp;
+
+ LLButton* mMapButtonp;
+
+ LLGroupMoneyTabEventHandler* mMoneyDetailsTabEHp;
+ LLGroupMoneyTabEventHandler* mMoneyPlanningTabEHp;
+ LLGroupMoneyTabEventHandler* mMoneySalesTabEHp;
+
+ LLScrollListCtrl* mGroupParcelsp;
+
+ LLUUID mGroupID;
+ LLUUID mTransID;
+
+ bool mBeenActivated;
+ bool mNeedsSendGroupLandRequest;
+ bool mNeedsApply;
+
+ std::string mCantViewParcelsText;
+ std::string mCantViewAccountsText;
+};
+
+//*******************************************
+//** LLPanelGroupLandMoney::impl Functions **
+//*******************************************
+LLPanelGroupLandMoney::impl::impl(const LLUUID& group_id)
+{
+ mGroupID = group_id;
+ mTransID = LLUUID::null;
+
+ mBeenActivated = false;
+ mNeedsSendGroupLandRequest = true;
+ mNeedsApply = false;
+
+ mTotalLandInUsep = NULL;
+ mTotalContributedLandp = NULL;
+ mLandAvailablep = NULL;
+ mYourContributionEditorp = NULL;
+ mMapButtonp = NULL;
+ mGroupParcelsp = NULL;
+ mYourContributionMaxTextp = NULL;
+ mGroupOverLimitTextp = NULL;
+ mGroupOverLimitIconp = NULL;
+
+ mMoneySalesTabEHp = NULL;
+ mMoneyPlanningTabEHp = NULL;
+ mMoneyDetailsTabEHp = NULL;
+}
+
+LLPanelGroupLandMoney::impl::~impl()
+{
+ if ( mMoneySalesTabEHp ) delete mMoneySalesTabEHp;
+ if ( mMoneyDetailsTabEHp ) delete mMoneyDetailsTabEHp;
+ if ( mMoneyPlanningTabEHp ) delete mMoneyPlanningTabEHp;
+}
+
+void LLPanelGroupLandMoney::impl::requestGroupLandInfo()
+{
+ U32 query_flags = DFQ_GROUP_OWNED;
+
+ mTransID.generate();
+ mGroupParcelsp->deleteAllItems();
+
+ send_places_query(mGroupID, mTransID, "", query_flags, LLParcel::C_ANY, "");
+}
+
+void LLPanelGroupLandMoney::impl::onMapButton()
+{
+ LLScrollListItem* itemp;
+ itemp = mGroupParcelsp->getFirstSelected();
+ if (!itemp) return;
+
+ const LLScrollListCell* cellp;
+ // name
+ // location
+ // area
+ cellp = itemp->getColumn(3); // hidden
+
+ F32 global_x = 0.f;
+ F32 global_y = 0.f;
+ sscanf(cellp->getText().c_str(), "%f %f", &global_x, &global_y);
+
+ // Hack: Use the agent's z-height
+ F64 global_z = gAgent.getPositionGlobal().mdV[VZ];
+
+ LLVector3d pos_global(global_x, global_y, global_z);
+ gFloaterWorldMap->trackLocation(pos_global);
+
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+bool LLPanelGroupLandMoney::impl::applyContribution()
+{
+ // calculate max donation, which is sum of available and current.
+ S32 your_contribution = 0;
+ S32 sqm_avail;
+
+ your_contribution = getStoredContribution();
+ sqm_avail = your_contribution;
+
+ if(gStatusBar)
+ {
+ sqm_avail += gStatusBar->getSquareMetersLeft();
+ }
+
+ // get new contribution and compare to available
+ S32 new_contribution = atoi(mYourContributionEditorp->getText().c_str());
+
+ if( new_contribution != your_contribution &&
+ new_contribution >= 0 &&
+ new_contribution <= sqm_avail )
+ {
+ // update group info and server
+ if(!gAgent.setGroupContribution(mGroupID, new_contribution))
+ {
+ // should never happen...
+ llwarns << "Unable to set contribution." << llendl;
+ return false;
+ }
+ }
+ else
+ {
+ //TODO: throw up some error message here and return? right now we just
+ //fail silently and force the previous value -jwolk
+ new_contribution = your_contribution;
+ }
+
+ //set your contribution
+ setYourContributionTextField(new_contribution);
+
+ return true;
+}
+
+// Retrieves the land contribution for this agent that is currently
+// stored in the database, NOT what is currently entered in the text field
+int LLPanelGroupLandMoney::impl::getStoredContribution()
+{
+ LLGroupData group_data;
+
+ group_data.mContribution = 0;
+ gAgent.getGroupData(mGroupID, group_data);
+
+ return group_data.mContribution;
+}
+
+// Fills in the text field with the contribution, contrib
+void LLPanelGroupLandMoney::impl::setYourContributionTextField(int contrib)
+{
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+ sprintf(buffer, "%d", contrib);
+
+ if ( mYourContributionEditorp )
+ {
+ mYourContributionEditorp->setText(buffer);
+ mYourContributionEditorp->draw();
+ }
+}
+
+void LLPanelGroupLandMoney::impl::setYourMaxContributionTextBox(int max)
+{
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+
+ sprintf(buffer, "(%d max)", max);
+ if ( mYourContributionMaxTextp )
+ {
+ mYourContributionMaxTextp->setText(buffer);
+ }
+}
+
+//static
+void LLPanelGroupLandMoney::impl::mapCallback(void* data)
+{
+ LLPanelGroupLandMoney::impl* selfp = (LLPanelGroupLandMoney::impl*) data;
+
+ if ( selfp ) selfp->onMapButton();
+}
+
+void LLPanelGroupLandMoney::impl::contributionCommitCallback(LLUICtrl* ctrl,
+ void* userdata)
+{
+ LLPanelGroupLandMoney* tabp = (LLPanelGroupLandMoney*) userdata;
+ LLLineEditor* editorp = (LLLineEditor*) ctrl;
+
+ if ( tabp && editorp )
+ {
+ impl* self = tabp->mImplementationp;
+ int your_contribution = 0;
+ int new_contribution = 0;
+
+ new_contribution= atoi(editorp->getText().c_str());
+ your_contribution = self->getStoredContribution();
+
+ //reset their junk data to be "good" data to us
+ self->setYourContributionTextField(new_contribution);
+
+ //check to see if they're contribution text has changed
+ self->mNeedsApply = new_contribution != your_contribution;
+ tabp->notifyObservers();
+ }
+}
+
+void LLPanelGroupLandMoney::impl::contributionKeystrokeCallback(LLLineEditor* caller,
+ void* userdata)
+{
+ impl::contributionCommitCallback(caller, userdata);
+}
+
+//static
+void LLPanelGroupLandMoney::impl::processGroupLand(LLMessageSystem* msg)
+{
+ S32 count = msg->getNumberOfBlocks("QueryData");
+ if(count > 0)
+ {
+ S32 first_block = 0;
+
+ LLUUID owner_id;
+ LLUUID trans_id;
+
+ msg->getUUID("QueryData", "OwnerID", owner_id, 0);
+ msg->getUUID("TransactionData", "TransactionID", trans_id);
+
+ if(owner_id.isNull())
+ {
+ // special block which has total contribution
+ ++first_block;
+ S32 total_contribution;
+ msg->getS32("QueryData", "ActualArea", total_contribution, 0);
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d sq. meters", total_contribution);
+ mTotalContributedLandp->setText(buffer);
+ S32 committed;
+ msg->getS32("QueryData", "BillableArea", committed, 0);
+ sprintf(buffer, "%d sq. meters", committed);
+ mTotalLandInUsep->setText(buffer);
+ S32 available = total_contribution - committed;
+ sprintf(buffer, "%d sq. meters", available);
+ mLandAvailablep->setText(buffer);
+ buffer[0] = '\0';
+ if ( mGroupOverLimitTextp && mGroupOverLimitIconp )
+ {
+ mGroupOverLimitIconp->setVisible(available < 0);
+ mGroupOverLimitTextp->setVisible(available < 0);
+ }
+ }
+
+ if ( trans_id != mTransID ) return;
+ // This power was removed to make group roles simpler
+ //if ( !gAgent.hasPowerInGroup(mGroupID, GP_LAND_VIEW_OWNED) ) return;
+ if (!gAgent.isInGroup(mGroupID)) return;
+
+ //we updated more than just the available area special block
+ if ( count > 1)
+ {
+ mMapButtonp->setEnabled(TRUE);
+ }
+
+ char name[MAX_STRING];
+ char desc[MAX_STRING];
+ S32 actual_area;
+ S32 billable_area;
+ U8 flags;
+ F32 global_x;
+ F32 global_y;
+ char sim_name[MAX_STRING];
+ for(S32 i = first_block; i < count; ++i)
+ {
+ msg->getUUID("QueryData", "OwnerID", owner_id, i);
+ msg->getString("QueryData", "Name", MAX_STRING, name, i);
+ msg->getString("QueryData", "Desc", MAX_STRING, desc, i);
+ msg->getS32("QueryData", "ActualArea", actual_area, i);
+ msg->getS32("QueryData", "BillableArea", billable_area, i);
+ msg->getU8("QueryData", "Flags", flags, i);
+ msg->getF32("QueryData", "GlobalX", global_x, i);
+ msg->getF32("QueryData", "GlobalY", global_y, i);
+ msg->getString("QueryData", "SimName", MAX_STRING, sim_name, i);
+
+ S32 region_x = llround(global_x) % REGION_WIDTH_UNITS;
+ S32 region_y = llround(global_y) % REGION_WIDTH_UNITS;
+ char location[MAX_STRING];
+ sprintf(location, "%s (%d, %d)", sim_name, region_x, region_y);
+ char area[MAX_STRING];
+ if(billable_area == actual_area)
+ {
+ sprintf(area, "%d", billable_area);
+ }
+ else
+ {
+ sprintf(area, "%d / %d", billable_area, actual_area);
+ }
+ char hidden[MAX_STRING];
+ sprintf(hidden, "%f %f", global_x, global_y);
+
+ LLSD row;
+
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["value"] = name;
+ row["columns"][0]["font"] = "SANSSERIFSMALL";
+
+ row["columns"][1]["column"] = "location";
+ row["columns"][1]["value"] = location;
+ row["columns"][1]["font"] = "SANSSERIFSMALL";
+
+ row["columns"][2]["column"] = "area";
+ row["columns"][2]["value"] = area;
+ row["columns"][2]["font"] = "SANSSERIFSMALL";
+
+ row["columns"][3]["column"] = "hidden";
+ row["columns"][3]["value"] = hidden;
+
+ mGroupParcelsp->addElement(row, ADD_SORTED);
+ }
+ }
+}
+
+//*************************************
+//** LLPanelGroupLandMoney Functions **
+//*************************************
+
+//static
+void* LLPanelGroupLandMoney::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupLandMoney("panel group land money", *group_id);
+}
+
+//static
+LLMap<LLUUID, LLPanelGroupLandMoney*> LLPanelGroupLandMoney::sGroupIDs;
+
+LLPanelGroupLandMoney::LLPanelGroupLandMoney(const std::string& name,
+ const LLUUID& group_id) :
+ LLPanelGroupTab(name, group_id)
+{
+ mImplementationp = new impl(group_id);
+
+ //problem what if someone has both the group floater open and the finder
+ //open to the same group? Some maps that map group ids to panels
+ //will then only be working for the last panel for a given group id :(
+ LLPanelGroupLandMoney::sGroupIDs.addData(group_id, this);
+}
+
+LLPanelGroupLandMoney::~LLPanelGroupLandMoney()
+{
+ delete mImplementationp;
+ LLPanelGroupLandMoney::sGroupIDs.removeData(mGroupID);
+}
+
+void LLPanelGroupLandMoney::activate()
+{
+ if ( !mImplementationp->mBeenActivated )
+ {
+ //select the first tab
+ LLTabContainerCommon* tabp = (LLTabContainerCommon*) getChildByName("group_money_tab_container");
+
+ if ( tabp )
+ {
+ tabp->selectFirstTab();
+ mImplementationp->mBeenActivated = true;
+ }
+
+ //fill in the max contribution
+
+ //This calculation is unfortunately based on
+ //the status bar's concept of how much land the user has
+ //which can change dynamically if the user buys new land, gives
+ //more land to a group, etc.
+ //A race condition can occur if we want to update the UI's
+ //concept of the user's max contribution before the status
+ //bar has been updated from a change in the user's group contribution.
+
+ //Since the max contribution should not change solely on changing
+ //a user's group contribution, (it would only change through
+ //purchasing of new land) this code is placed here
+ //and only updated once to prevent the race condition
+ //at the price of having stale data.
+ //We need to have the status bar have observers
+ //or find better way of distributing up to date land data. - jwolk
+ S32 max_avail = mImplementationp->getStoredContribution();
+ if(gStatusBar)
+ {
+ max_avail += gStatusBar->getSquareMetersLeft();
+ }
+ mImplementationp->setYourMaxContributionTextBox(max_avail);
+ }
+
+ update(GC_ALL);
+}
+
+void LLPanelGroupLandMoney::update(LLGroupChange gc)
+{
+ if (gc != GC_ALL) return; //Don't update if it's the wrong panel!
+
+ LLTabContainerCommon* tabp = (LLTabContainerCommon*) getChildByName("group_money_tab_container");
+
+ if ( tabp )
+ {
+ LLPanel* panelp;
+ LLGroupMoneyTabEventHandler* eh;
+
+ panelp = tabp->getCurrentPanel();
+
+ //now pull the event handler associated with that money tab
+ if ( panelp )
+ {
+ eh = get_if_there(LLGroupMoneyTabEventHandler::sTabsToHandlers,
+ panelp,
+ (LLGroupMoneyTabEventHandler*)NULL);
+ if ( eh ) eh->onClickTab();
+ }
+ }
+
+ mImplementationp->requestGroupLandInfo();
+ mImplementationp->setYourContributionTextField(mImplementationp->getStoredContribution());
+}
+
+bool LLPanelGroupLandMoney::needsApply(LLString& mesg)
+{
+ return mImplementationp->mNeedsApply;
+}
+
+bool LLPanelGroupLandMoney::apply(LLString& mesg)
+{
+ if (!mImplementationp->applyContribution() )
+ {
+ mesg.assign("Unable to set your land contribution.");
+ return false;
+ }
+
+ mImplementationp->mNeedsApply = false;
+ notifyObservers();
+
+ return true;
+}
+
+void LLPanelGroupLandMoney::cancel()
+{
+ //set the contribution back to the "stored value"
+ mImplementationp->setYourContributionTextField(mImplementationp->getStoredContribution());
+
+ mImplementationp->mNeedsApply = false;
+ notifyObservers();
+}
+
+
+BOOL LLPanelGroupLandMoney::postBuild()
+{
+ /* This power was removed to make group roles simpler
+ bool has_parcel_view = gAgent.hasPowerInGroup(mGroupID,
+ GP_LAND_VIEW_OWNED);
+ bool has_accounting_view = gAgent.hasPowerInGroup(mGroupID,
+ GP_ACCOUNTING_VIEW);
+ */
+
+ bool can_view = gAgent.isInGroup(mGroupID);
+
+ mImplementationp->mTotalLandInUsep =
+ (LLTextBox*) getChildByName("total_land_in_use_value");
+ mImplementationp->mTotalContributedLandp =
+ (LLTextBox*) getChildByName("total_contributed_land_value");
+ mImplementationp->mLandAvailablep =
+ (LLTextBox*) getChildByName("land_available_value");
+ mImplementationp->mGroupOverLimitIconp =
+ (LLIconCtrl*) getChildByName("group_over_limit_icon");
+ mImplementationp->mGroupOverLimitTextp =
+ (LLTextBox*) getChildByName("group_over_limit_text");
+ mImplementationp->mYourContributionMaxTextp =
+ (LLTextBox*) getChildByName("your_contribution_max_value");
+
+ mImplementationp->mYourContributionEditorp
+ = (LLLineEditor*) getChildByName("your_contribution_line_editor");
+ if ( mImplementationp->mYourContributionEditorp )
+ {
+ LLLineEditor* editor = mImplementationp->mYourContributionEditorp;
+
+ editor->setCommitCallback(mImplementationp->contributionCommitCallback);
+ editor->setKeystrokeCallback(mImplementationp->contributionKeystrokeCallback);
+ editor->setCallbackUserData(this);
+ }
+
+ mImplementationp->mMapButtonp = (LLButton*) getChildByName("map_button");
+
+ mImplementationp->mGroupParcelsp =
+ (LLScrollListCtrl*) getChildByName("group_parcel_list");
+
+ LLTextBox *no_permsp =
+ (LLTextBox*) getChildByName("cant_view_group_land_text");
+ if ( no_permsp )
+ {
+ mImplementationp->mCantViewParcelsText = no_permsp->getText();
+ removeChild(no_permsp);
+ }
+
+ no_permsp = (LLTextBox*) getChildByName("cant_view_group_accounting_text");
+ if ( no_permsp )
+ {
+ mImplementationp->mCantViewAccountsText = no_permsp->getText();
+ removeChild(no_permsp);
+ }
+
+
+ if ( mImplementationp->mMapButtonp )
+ {
+ mImplementationp->mMapButtonp->setClickedCallback(LLPanelGroupLandMoney::impl::mapCallback, mImplementationp);
+ }
+
+ if ( mImplementationp->mGroupOverLimitTextp )
+ {
+ mImplementationp->mGroupOverLimitTextp->setVisible(FALSE);
+ }
+
+ if ( mImplementationp->mGroupOverLimitIconp )
+ {
+ mImplementationp->mGroupOverLimitIconp->setVisible(FALSE);
+ }
+
+ if ( mImplementationp->mMapButtonp )
+ {
+ mImplementationp->mMapButtonp->setEnabled(FALSE);
+ }
+
+ if ( !can_view )
+ {
+ if ( mImplementationp->mGroupParcelsp )
+ {
+ mImplementationp->mGroupParcelsp->addSimpleItem(
+ mImplementationp->mCantViewParcelsText);
+ mImplementationp->mGroupParcelsp->setEnabled(FALSE);
+ }
+ }
+
+
+
+ LLButton* earlierp, *laterp;
+ LLTextEditor* textp;
+ LLPanel* panelp;
+
+ LLTabContainerCommon* tabcp = (LLTabContainerCommon*)
+ getChildByName("group_money_tab_container", true);
+
+ if ( !can_view )
+ {
+ if ( tabcp )
+ {
+ S32 i;
+ S32 tab_count = tabcp->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ tabcp->enableTabButton(i, false);
+ }
+ }
+ }
+
+
+ //pull out the widgets for the money details tab
+ earlierp = (LLButton*) getChildByName("earlier_details_button", true);
+ laterp = (LLButton*) getChildByName("later_details_button", true);
+ textp = (LLTextEditor*) getChildByName("group_money_details_text", true);
+ panelp = (LLPanel*) getChildByName("group_money_details_tab", true);
+
+ if ( !can_view )
+ {
+ textp->setText(mImplementationp->mCantViewAccountsText);
+ }
+ else
+ {
+ mImplementationp->mMoneyDetailsTabEHp =
+ new LLGroupMoneyDetailsTabEventHandler(earlierp,
+ laterp,
+ textp,
+ tabcp,
+ panelp,
+ mGroupID);
+ }
+
+ textp = (LLTextEditor*) getChildByName("group_money_planning_text", true);
+ panelp = (LLPanel*) getChildByName("group_money_planning_tab", true);
+
+ if ( !can_view )
+ {
+ textp->setText(mImplementationp->mCantViewAccountsText);
+ }
+ else
+ {
+ mImplementationp->mMoneyPlanningTabEHp =
+ new LLGroupMoneyPlanningTabEventHandler(textp,
+ tabcp,
+ panelp,
+ mGroupID);
+ }
+
+ //pull out the widgets for the money sales tab
+ earlierp = (LLButton*) getChildByName("earlier_sales_button", true);
+ laterp = (LLButton*) getChildByName("later_sales_button", true);
+ textp = (LLTextEditor*) getChildByName("group_money_sales_text", true);
+ panelp = (LLPanel*) getChildByName("group_money_sales_tab", true);
+
+ if ( !can_view )
+ {
+ textp->setText(mImplementationp->mCantViewAccountsText);
+ }
+ else
+ {
+ mImplementationp->mMoneySalesTabEHp =
+ new LLGroupMoneySalesTabEventHandler(earlierp,
+ laterp,
+ textp,
+ tabcp,
+ panelp,
+ mGroupID);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+BOOL LLPanelGroupLandMoney::isVisibleByAgent(LLAgent* agentp)
+{
+ return mAllowEdit && agentp->isInGroup(mGroupID);
+}
+
+void LLPanelGroupLandMoney::processPlacesReply(LLMessageSystem* msg, void**)
+{
+ LLUUID group_id;
+ msg->getUUID("AgentData", "QueryID", group_id);
+
+ LLPanelGroupLandMoney* selfp = sGroupIDs.getIfThere(group_id);
+ if(!selfp)
+ {
+ llinfos << "Group Panel Land Money " << group_id << " no longer in existence."
+ << llendl;
+ return;
+ }
+
+ selfp->mImplementationp->processGroupLand(msg);
+}
+
+//*************************************************
+//** LLGroupMoneyTabEventHandler::impl Functions **
+//*************************************************
+
+class LLGroupMoneyTabEventHandler::impl
+{
+public:
+ impl(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days);
+ ~impl();
+
+ bool getCanClickLater();
+ bool getCanClickEarlier();
+
+ void updateButtons();
+
+//member variables
+public:
+ LLUUID mGroupID;
+ LLUUID mPanelID;
+
+ LLPanel* mPanelp;
+
+ int mIntervalLength;
+ int mMaxInterval;
+ int mCurrentInterval;
+
+ LLTextEditor* mTextEditorp;
+ LLButton* mEarlierButtonp;
+ LLButton* mLaterButtonp;
+};
+
+LLGroupMoneyTabEventHandler::impl::impl(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days)
+{
+ mGroupID = group_id;
+ mPanelID.generate();
+
+ mIntervalLength = interval_length_days;
+ mMaxInterval = max_interval_days;
+ mCurrentInterval = 0;
+
+ mTextEditorp = text_editorp;
+ mEarlierButtonp = earlier_buttonp;
+ mLaterButtonp = later_buttonp;
+ mPanelp = panelp;
+}
+
+LLGroupMoneyTabEventHandler::impl::~impl()
+{
+}
+
+bool LLGroupMoneyTabEventHandler::impl::getCanClickEarlier()
+{
+ return (mCurrentInterval < mMaxInterval);
+}
+
+bool LLGroupMoneyTabEventHandler::impl::getCanClickLater()
+{
+ return ( mCurrentInterval > 0 );
+}
+
+void LLGroupMoneyTabEventHandler::impl::updateButtons()
+{
+ if ( mEarlierButtonp )
+ {
+ mEarlierButtonp->setEnabled(getCanClickEarlier());
+ }
+ if ( mLaterButtonp )
+ {
+ mLaterButtonp->setEnabled(getCanClickLater());
+ }
+}
+
+//*******************************************
+//** LLGroupMoneyTabEventHandler Functions **
+//*******************************************
+LLMap<LLUUID, LLGroupMoneyTabEventHandler*> LLGroupMoneyTabEventHandler::sInstanceIDs;
+std::map<LLPanel*, LLGroupMoneyTabEventHandler*> LLGroupMoneyTabEventHandler::sTabsToHandlers;
+
+LLGroupMoneyTabEventHandler::LLGroupMoneyTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days)
+{
+ mImplementationp = new impl(earlier_buttonp,
+ later_buttonp,
+ text_editorp,
+ panelp,
+ group_id,
+ interval_length_days,
+ max_interval_days);
+
+ if ( earlier_buttonp )
+ {
+ earlier_buttonp->setClickedCallback(LLGroupMoneyTabEventHandler::clickEarlierCallback,
+ this);
+ }
+
+ if ( later_buttonp )
+ {
+ later_buttonp->setClickedCallback(LLGroupMoneyTabEventHandler::clickLaterCallback,
+ this);
+ }
+
+ mImplementationp->updateButtons();
+
+ if ( tab_containerp && panelp )
+ {
+ tab_containerp->setTabChangeCallback(panelp,
+ LLGroupMoneyTabEventHandler::clickTabCallback);
+ tab_containerp->setTabUserData(panelp, this);
+ }
+
+ sInstanceIDs.addData(mImplementationp->mPanelID, this);
+ sTabsToHandlers[panelp] = this;
+}
+
+LLGroupMoneyTabEventHandler::~LLGroupMoneyTabEventHandler()
+{
+ sInstanceIDs.removeData(mImplementationp->mPanelID);
+ sTabsToHandlers.erase(mImplementationp->mPanelp);
+
+ delete mImplementationp;
+}
+
+
+void LLGroupMoneyTabEventHandler::onClickTab()
+{
+ requestData(gMessageSystem);
+}
+
+void LLGroupMoneyTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ //do nothing
+}
+
+void LLGroupMoneyTabEventHandler::processReply(LLMessageSystem* msg, void** data)
+{
+ //do nothing
+}
+
+void LLGroupMoneyTabEventHandler::onClickEarlier()
+{
+ if ( mImplementationp->mTextEditorp)
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+ mImplementationp->mCurrentInterval++;
+
+ mImplementationp->updateButtons();
+
+ requestData(gMessageSystem);
+}
+
+void LLGroupMoneyTabEventHandler::onClickLater()
+{
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+ mImplementationp->mCurrentInterval--;
+
+ mImplementationp->updateButtons();
+
+ requestData(gMessageSystem);
+}
+
+//static
+void LLGroupMoneyTabEventHandler::clickEarlierCallback(void* data)
+{
+ LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data;
+
+ if ( selfp ) selfp->onClickEarlier();
+}
+
+//static
+void LLGroupMoneyTabEventHandler::clickLaterCallback(void* data)
+{
+ LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data;
+ if ( selfp ) selfp->onClickLater();
+}
+
+//static
+void LLGroupMoneyTabEventHandler::clickTabCallback(void* data, bool from_click)
+{
+ LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data;
+ if ( selfp ) selfp->onClickTab();
+}
+
+//**************************************************
+//** LLGroupMoneyDetailsTabEventHandler Functions **
+//**************************************************
+
+LLGroupMoneyDetailsTabEventHandler::LLGroupMoneyDetailsTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id)
+ : LLGroupMoneyTabEventHandler(earlier_buttonp,
+ later_buttonp,
+ text_editorp,
+ tab_containerp,
+ panelp,
+ group_id,
+ SUMMARY_INTERVAL,
+ SUMMARY_MAX)
+{
+}
+
+LLGroupMoneyDetailsTabEventHandler::~LLGroupMoneyDetailsTabEventHandler()
+{
+}
+
+void LLGroupMoneyDetailsTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ msg->newMessageFast(_PREHASH_GroupAccountDetailsRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID );
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID );
+ msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength );
+ msg->addS32Fast(_PREHASH_CurrentInterval, mImplementationp->mCurrentInterval);
+
+ gAgent.sendReliableMessage();
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+
+ LLGroupMoneyTabEventHandler::requestData(msg);
+}
+
+void LLGroupMoneyDetailsTabEventHandler::processReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+ if (mImplementationp->mGroupID != group_id)
+ {
+ llwarns << "Group Account details not for this group!" << llendl;
+ return;
+ }
+
+ char line[MAX_STRING];
+ LLString text;
+
+ char start_date[MAX_STRING];
+ S32 interval_days;
+ S32 current_interval;
+
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, MAX_STRING, start_date);
+
+ if ( interval_days != mImplementationp->mIntervalLength ||
+ current_interval != mImplementationp->mCurrentInterval )
+ {
+ llinfos << "Out of date details packet " << interval_days << " "
+ << current_interval << llendl;
+ return;
+ }
+
+ sprintf(line, "%s\n\n", start_date);
+ text.append(line);
+
+ S32 total_amount = 0;
+ S32 transactions = msg->getNumberOfBlocksFast(_PREHASH_HistoryData);
+ for(S32 i = 0; i < transactions; i++)
+ {
+ S32 amount = 0;
+ char desc[MAX_STRING];
+
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Description, MAX_STRING, desc, i );
+ msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Amount, amount, i);
+
+ if (amount != 0)
+ {
+ sprintf(line, "%-24s %6d\n", desc, amount );
+ text.append(line);
+ }
+ else
+ {
+ // skip it
+ }
+
+ total_amount += amount;
+ }
+
+ text.append(1, '\n');
+
+ sprintf(line, "%-24s %6d\n", "Total", total_amount );
+ text.append(line);
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(text);
+ }
+}
+
+//static
+void LLGroupMoneyDetailsTabEventHandler::processGroupAccountDetailsReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group money history reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id );
+ LLGroupMoneyTabEventHandler* selfp = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id);
+ if (!selfp)
+ {
+ llwarns << "GroupAccountDetails recieved for non-existent group panel." << llendl;
+ return;
+ }
+
+ selfp->processReply(msg, data);
+}
+
+//************************************************
+//** LLGroupMoneySalesTabEventHandler Functions **
+//************************************************
+
+LLGroupMoneySalesTabEventHandler::LLGroupMoneySalesTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id)
+ : LLGroupMoneyTabEventHandler(earlier_buttonp,
+ later_buttonp,
+ text_editorp,
+ tab_containerp,
+ panelp,
+ group_id,
+ SUMMARY_INTERVAL,
+ SUMMARY_MAX)
+{
+}
+
+LLGroupMoneySalesTabEventHandler::~LLGroupMoneySalesTabEventHandler()
+{
+}
+
+void LLGroupMoneySalesTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ msg->newMessageFast(_PREHASH_GroupAccountTransactionsRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID );
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID );
+ msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength );
+ msg->addS32Fast(_PREHASH_CurrentInterval, mImplementationp->mCurrentInterval);
+
+ gAgent.sendReliableMessage();
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+
+ LLGroupMoneyTabEventHandler::requestData(msg);
+}
+
+void LLGroupMoneySalesTabEventHandler::processReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+ if (mImplementationp->mGroupID != group_id)
+ {
+ llwarns << "Group Account Transactions not for this group!" << llendl;
+ return;
+ }
+
+ char line[MAX_STRING];
+ std::string text = mImplementationp->mTextEditorp->getText();
+
+ char start_date[MAX_STRING];
+ S32 interval_days;
+ S32 current_interval;
+
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, MAX_STRING, start_date);
+
+ if (interval_days != mImplementationp->mIntervalLength ||
+ current_interval != mImplementationp->mCurrentInterval)
+ {
+ llinfos << "Out of date details packet " << interval_days << " "
+ << current_interval << llendl;
+ return;
+ }
+
+ // If this is the first packet, clear the text, don't append.
+ // Start with the date.
+ if (text == LOADING_STRING)
+ {
+ text.clear();
+
+ sprintf(line, "%s\n\n", start_date);
+ text.append(line);
+ }
+
+ S32 transactions = msg->getNumberOfBlocksFast(_PREHASH_HistoryData);
+ if (transactions == 0)
+ {
+ text.append("(none)");
+ }
+ else
+ {
+ for(S32 i = 0; i < transactions; i++)
+ {
+ const S32 SHORT_STRING = 64;
+ char time[SHORT_STRING];
+ S32 type = 0;
+ S32 amount = 0;
+ char user[SHORT_STRING];
+ char item[SHORT_STRING];
+
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Time, SHORT_STRING, time, i);
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_User, SHORT_STRING, user, i );
+ msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Type, type, i);
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Item, SHORT_STRING, item, i );
+ msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Amount, amount, i);
+
+ if (amount != 0)
+ {
+ char* verb;
+
+ switch(type)
+ {
+ case TRANS_OBJECT_SALE:
+ verb = "bought";
+ break;
+ case TRANS_GIFT:
+ verb = "paid you";
+ break;
+ case TRANS_PAY_OBJECT:
+ verb = "paid into";
+ break;
+ case TRANS_LAND_PASS_SALE:
+ verb = "bought pass to";
+ break;
+ case TRANS_EVENT_FEE:
+ verb = "paid fee for event";
+ break;
+ case TRANS_EVENT_PRIZE:
+ verb = "paid prize for event";
+ break;
+ default:
+ verb = "";
+ break;
+ }
+
+ sprintf(line, "%s %6d - %s %s %s\n", time, amount, user, verb, item);
+ text.append(line);
+ }
+ }
+ }
+
+ if ( mImplementationp->mTextEditorp)
+ {
+ mImplementationp->mTextEditorp->setText(text);
+ }
+}
+
+//static
+void LLGroupMoneySalesTabEventHandler::processGroupAccountTransactionsReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group money history reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id );
+
+ LLGroupMoneyTabEventHandler* self;
+
+ self = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id);
+ if (!self)
+ {
+ llwarns << "GroupAccountTransactions recieved for non-existent group panel." << llendl;
+ return;
+ }
+
+ self->processReply(msg, data);
+}
+
+//***************************************************
+//** LLGroupMoneyPlanningTabEventHandler Functions **
+//***************************************************
+
+LLGroupMoneyPlanningTabEventHandler::LLGroupMoneyPlanningTabEventHandler(LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id)
+ : LLGroupMoneyTabEventHandler(NULL,
+ NULL,
+ text_editorp,
+ tab_containerp,
+ panelp,
+ group_id,
+ SUMMARY_INTERVAL,
+ SUMMARY_MAX)
+{
+}
+
+LLGroupMoneyPlanningTabEventHandler::~LLGroupMoneyPlanningTabEventHandler()
+{
+}
+
+void LLGroupMoneyPlanningTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ msg->newMessageFast(_PREHASH_GroupAccountSummaryRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID );
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID );
+ msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength);
+ msg->addS32Fast(_PREHASH_CurrentInterval, 0); //planning has 0 interval
+
+ gAgent.sendReliableMessage();
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+
+ LLGroupMoneyTabEventHandler::requestData(msg);
+}
+
+void LLGroupMoneyPlanningTabEventHandler::processReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+ if (mImplementationp->mGroupID != group_id)
+ {
+ llwarns << "Group Account Summary received not for this group!" << llendl;
+ return;
+ }
+
+ char line[MAX_STRING];
+ LLString text;
+
+ char start_date[MAX_STRING];
+ char last_stipend_date[MAX_STRING];
+ char next_stipend_date[MAX_STRING];
+ S32 interval_days;
+ S32 current_interval;
+ S32 balance;
+ S32 total_credits;
+ S32 total_debits;
+ S32 cur_object_tax;
+ S32 cur_light_tax;
+ S32 cur_land_tax;
+ S32 cur_group_tax;
+ S32 cur_parcel_dir_fee;
+ S32 cur_total_tax;
+ S32 proj_object_tax;
+ S32 proj_light_tax;
+ S32 proj_land_tax;
+ S32 proj_group_tax;
+ S32 proj_parcel_dir_fee;
+ S32 proj_total_tax;
+ S32 non_exempt_members;
+
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_Balance, balance );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_TotalCredits, total_credits );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_TotalDebits, total_debits );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ObjectTaxCurrent, cur_object_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LightTaxCurrent, cur_light_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LandTaxCurrent, cur_land_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_GroupTaxCurrent, cur_group_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ParcelDirFeeCurrent, cur_parcel_dir_fee );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ObjectTaxEstimate, proj_object_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LightTaxEstimate, proj_light_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LandTaxEstimate, proj_land_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_GroupTaxEstimate, proj_group_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ParcelDirFeeEstimate, proj_parcel_dir_fee );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_NonExemptMembers, non_exempt_members );
+
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, MAX_STRING, start_date);
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_LastTaxDate, MAX_STRING, last_stipend_date);
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_TaxDate, MAX_STRING, next_stipend_date);
+
+ cur_total_tax = cur_object_tax + cur_light_tax + cur_land_tax + cur_group_tax + cur_parcel_dir_fee;
+ proj_total_tax = proj_object_tax + proj_light_tax + proj_land_tax + proj_group_tax + proj_parcel_dir_fee;
+
+ if (interval_days != mImplementationp->mIntervalLength ||
+ current_interval != mImplementationp->mCurrentInterval)
+ {
+ llinfos << "Out of date summary packet " << interval_days << " "
+ << current_interval << llendl;
+ return;
+ }
+
+ sprintf(line, "Summary for this week, beginning on %s\n", start_date);
+ text.append(line);
+
+ if (current_interval == 0)
+ {
+ sprintf(line, "The next stipend day is %s\n\n", next_stipend_date);
+ text.append(line);
+ sprintf(line, "%-24sL$%6d\n", "Balance", balance );
+ text.append(line);
+
+ text.append(1, '\n');
+ }
+
+ sprintf(line, " Group Individual Share\n");
+ text.append(line);
+ sprintf(line, "%-24s %6d %6d \n", "Credits", total_credits, (S32)floor((F32)total_credits/(F32)non_exempt_members));
+ text.append(line);
+ sprintf(line, "%-24s %6d %6d \n", "Debits", total_debits, (S32)floor((F32)total_debits/(F32)non_exempt_members));
+ text.append(line);
+ sprintf(line, "%-24s %6d %6d \n", "Total", total_credits + total_debits, (S32)floor((F32)(total_credits + total_debits)/(F32)non_exempt_members));
+ text.append(line);
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(text);
+ }
+}
+
+//static
+void LLGroupMoneyPlanningTabEventHandler::processGroupAccountSummaryReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group money history reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id );
+
+ LLGroupMoneyTabEventHandler* self;
+
+ self = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id);
+ if (!self)
+ {
+ llwarns << "GroupAccountSummary recieved for non-existent group money planning tab." << llendl;
+ return;
+ }
+
+ self->processReply(msg, data);
+}
diff --git a/indra/newview/llpanelgrouplandmoney.h b/indra/newview/llpanelgrouplandmoney.h
new file mode 100644
index 0000000000..b1cf44fe46
--- /dev/null
+++ b/indra/newview/llpanelgrouplandmoney.h
@@ -0,0 +1,127 @@
+/**
+ * @file llpanelgrouplandmoney.h
+ * @brief Panel for group land and money.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PANEL_GROUP_LAND_MONEY_H
+#define LL_PANEL_GROUP_LAND_MONEY_H
+
+#include "llpanelgroup.h"
+#include "llmap.h"
+#include "lluuid.h"
+
+#include "llbutton.h"
+#include "lltexteditor.h"
+#include "llpanel.h"
+
+class LLPanelGroupLandMoney : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupLandMoney(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupLandMoney();
+ virtual BOOL postBuild();
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ static void* createTab(void* data);
+
+ virtual void activate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ virtual void update(LLGroupChange gc);
+
+ static void processPlacesReply(LLMessageSystem* msg, void**);
+
+ static LLMap<LLUUID, LLPanelGroupLandMoney*> sGroupIDs;
+protected:
+ class impl;
+ impl* mImplementationp;
+};
+
+class LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneyTabEventHandler(LLButton* earlier_button,
+ LLButton* later_button,
+ LLTextEditor* text_editor,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days);
+ virtual ~LLGroupMoneyTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ virtual void onClickEarlier();
+ virtual void onClickLater();
+ virtual void onClickTab();
+
+ static void clickEarlierCallback(void* data);
+ static void clickLaterCallback(void* data);
+ static void clickTabCallback(void* user_data, bool from_click);
+
+ static LLMap<LLUUID, LLGroupMoneyTabEventHandler*> sInstanceIDs;
+ static std::map<LLPanel*, LLGroupMoneyTabEventHandler*> sTabsToHandlers;
+protected:
+ class impl;
+ impl* mImplementationp;
+};
+
+class LLGroupMoneyDetailsTabEventHandler : public LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneyDetailsTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id);
+ virtual ~LLGroupMoneyDetailsTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ static void processGroupAccountDetailsReply(LLMessageSystem* msg,
+ void** data);
+};
+
+class LLGroupMoneySalesTabEventHandler : public LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneySalesTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id);
+ virtual ~LLGroupMoneySalesTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ static void processGroupAccountTransactionsReply(LLMessageSystem* msg,
+ void** data);
+};
+
+class LLGroupMoneyPlanningTabEventHandler : public LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneyPlanningTabEventHandler(LLTextEditor* text_editor,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id);
+ virtual ~LLGroupMoneyPlanningTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ static void processGroupAccountSummaryReply(LLMessageSystem* msg,
+ void** data);
+};
+
+#endif // LL_PANEL_GROUP_LAND_MONEY_H
diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp
new file mode 100644
index 0000000000..9f774db2e9
--- /dev/null
+++ b/indra/newview/llpanelgroupnotices.cpp
@@ -0,0 +1,565 @@
+/**
+ * @file llpanelgroupnotices.cpp
+ * @brief A panel to display group notices.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupnotices.h"
+
+#include "llview.h"
+
+#include "llinventory.h"
+#include "llviewerinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llagent.h"
+#include "lltooldraganddrop.h"
+
+#include "lllineeditor.h"
+#include "lltexteditor.h"
+#include "llbutton.h"
+#include "lliconctrl.h"
+#include "llcheckboxctrl.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+
+#include "roles_constants.h"
+#include "llviewerwindow.h"
+#include "llviewermessage.h"
+
+/////////////////////////
+// LLPanelGroupNotices //
+/////////////////////////
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDropTarget
+//
+// This handy class is a simple way to drop something on another
+// view. It handles drop events, always setting itself to the size of
+// its parent.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLGroupDropTarget : public LLView
+{
+public:
+ LLGroupDropTarget(const std::string& name, const LLRect& rect, LLPanelGroupNotices* panel, const LLUUID& group_id);
+ ~LLGroupDropTarget() {};
+
+ void doDrop(EDragAndDropType cargo_type, void* cargo_data);
+
+ //
+ // LLView functionality
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+protected:
+ LLPanelGroupNotices* mGroupNoticesPanel;
+ LLUUID mGroupID;
+};
+
+LLGroupDropTarget::LLGroupDropTarget(const std::string& name, const LLRect& rect,
+ LLPanelGroupNotices* panel, const LLUUID& group_id) :
+ LLView(name, rect, NOT_MOUSE_OPAQUE, FOLLOWS_ALL),
+ mGroupNoticesPanel(panel),
+ mGroupID(group_id)
+{
+}
+
+EWidgetType LLGroupDropTarget::getWidgetType() const
+{
+ return WIDGET_TYPE_DONTCARE;
+}
+
+LLString LLGroupDropTarget::getWidgetTag() const
+{
+ return LL_GROUP_DROP_TARGET_TAG;
+}
+
+void LLGroupDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data)
+{
+ llinfos << "LLGroupDropTarget::doDrop()" << llendl;
+}
+
+BOOL LLGroupDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ if (!gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_SEND))
+ {
+ *accept = ACCEPT_NO;
+ return TRUE;
+ }
+
+ if(getParent())
+ {
+ // check if inside
+ //LLRect parent_rect = mParentView->getRect();
+ //mRect.set(0, parent_rect.getHeight(), parent_rect.getWidth(), 0);
+ handled = TRUE;
+
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLViewerInventoryItem* inv_item = (LLViewerInventoryItem*)cargo_data;
+ if(gInventory.getItem(inv_item->getUUID())
+ && LLToolDragAndDrop::isInventoryGroupGiveAcceptable(inv_item))
+ {
+ //FIXME: get multiple object transfers working
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if(drop)
+ {
+ mGroupNoticesPanel->setItem(inv_item);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably
+ // in an object's contents), so disallow dragging
+ // it here. You can't give something you don't
+ // yet have.
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ case DAD_CALLINGCARD:
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+ }
+ return handled;
+}
+
+//-----------------------------------------------------------------------------
+// LLPanelGroupNotices
+//-----------------------------------------------------------------------------
+char* build_notice_date(const time_t& the_time, char* buffer)
+{
+ time_t t = the_time;
+ if (!t) time(&t);
+ tm* lt = localtime(&t);
+ //for some reason, the month is off by 1. See other uses of
+ //"local" time in the code...
+ sprintf(buffer,"%i/%i/%i", lt->tm_mon + 1, lt->tm_mday, lt->tm_year + 1900);
+
+ return buffer;
+}
+
+LLPanelGroupNotices::LLPanelGroupNotices(const std::string& name,
+ const LLUUID& group_id) :
+ LLPanelGroupTab(name,group_id),
+ mInventoryItem(NULL),
+ mInventoryOffer(NULL)
+{
+ sInstances[group_id] = this;
+}
+
+LLPanelGroupNotices::~LLPanelGroupNotices()
+{
+ sInstances.erase(mGroupID);
+
+ if (mInventoryOffer)
+ {
+ // Cancel the inventory offer.
+ inventory_offer_callback( 1 , mInventoryOffer);
+ mInventoryOffer = NULL;
+ }
+}
+
+// static
+void* LLPanelGroupNotices::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupNotices("panel group notices", *group_id);
+}
+
+BOOL LLPanelGroupNotices::isVisibleByAgent(LLAgent* agentp)
+{
+ return mAllowEdit &&
+ agentp->hasPowerInGroup(mGroupID, GP_NOTICES_SEND | GP_NOTICES_RECEIVE);
+}
+
+BOOL LLPanelGroupNotices::postBuild()
+{
+ bool recurse = true;
+
+ mNoticesList = (LLScrollListCtrl*)getChildByName("notice_list",recurse);
+ mNoticesList->setCommitOnSelectionChange(TRUE);
+ mNoticesList->setCommitCallback(onSelectNotice);
+ mNoticesList->setCallbackUserData(this);
+
+ mBtnNewMessage = (LLButton*)getChildByName("create_new_notice",recurse);
+ mBtnNewMessage->setClickedCallback(onClickNewMessage);
+ mBtnNewMessage->setCallbackUserData(this);
+ mBtnNewMessage->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_NOTICES_SEND));
+
+ mBtnGetPastNotices = (LLButton*)getChildByName("refresh_notices",recurse);
+ mBtnGetPastNotices->setClickedCallback(onClickRefreshNotices);
+ mBtnGetPastNotices->setCallbackUserData(this);
+
+ // Create
+ mCreateSubject = (LLLineEditor*)getChildByName("create_subject",recurse);
+ mCreateMessage = (LLTextEditor*)getChildByName("create_message",recurse);
+
+ mCreateInventoryName = (LLLineEditor*)getChildByName("create_inventory_name",recurse);
+ mCreateInventoryName->setTabStop(FALSE);
+ mCreateInventoryName->setEnabled(FALSE);
+
+ mCreateInventoryIcon = (LLIconCtrl*)getChildByName("create_inv_icon",recurse);
+ mCreateInventoryIcon->setVisible(FALSE);
+
+ mBtnSendMessage = (LLButton*)getChildByName("send_notice",recurse);
+ mBtnSendMessage->setClickedCallback(onClickSendMessage);
+ mBtnSendMessage->setCallbackUserData(this);
+
+ mBtnRemoveAttachment = (LLButton*)getChildByName("remove_attachment",recurse);
+ mBtnRemoveAttachment->setClickedCallback(onClickRemoveAttachment);
+ mBtnRemoveAttachment->setCallbackUserData(this);
+ mBtnRemoveAttachment->setEnabled(FALSE);
+
+ // View
+ mViewSubject = (LLLineEditor*)getChildByName("view_subject",recurse);
+ mViewMessage = (LLTextEditor*)getChildByName("view_message",recurse);
+
+ mViewInventoryName = (LLLineEditor*)getChildByName("view_inventory_name",recurse);
+ mViewInventoryName->setTabStop(FALSE);
+ mViewInventoryName->setEnabled(FALSE);
+
+ mViewInventoryIcon = (LLIconCtrl*)getChildByName("view_inv_icon",recurse);
+ mViewInventoryIcon->setVisible(FALSE);
+
+ mBtnOpenAttachment = (LLButton*)getChildByName("open_attachment",recurse);
+ mBtnOpenAttachment->setClickedCallback(onClickOpenAttachment);
+ mBtnOpenAttachment->setCallbackUserData(this);
+
+ LLTextBox *txt = (LLTextBox*) getChildByName("no_notices_text",false);
+ if (txt)
+ {
+ mNoNoticesStr = txt->getText();
+ removeChild(txt);
+ }
+
+ mPanelCreateNotice = (LLPanel*) getChildByName("panel_create_new_notice",recurse);
+ mPanelViewNotice = (LLPanel*) getChildByName("panel_view_past_notice",recurse);
+
+ // Must be in front of all other UI elements.
+ LLPanel* dtv = (LLPanel*)getChildByName("drop_target",recurse);
+ LLGroupDropTarget* target = new LLGroupDropTarget("drop_target",
+ dtv->getRect(),
+ this, mGroupID);
+ target->setEnabled(TRUE);
+ target->setToolTip(dtv->getToolTip());
+
+ mPanelCreateNotice->addChild(target);
+ mPanelCreateNotice->removeChild(dtv);
+
+ arrangeNoticeView(VIEW_PAST_NOTICE);
+
+ return LLPanelGroupTab::postBuild();
+}
+
+void LLPanelGroupNotices::activate()
+{
+ BOOL can_send = gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_SEND);
+ BOOL can_receive = gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_RECEIVE);
+
+ mPanelViewNotice->setEnabled(can_receive);
+ mPanelCreateNotice->setEnabled(can_send);
+
+ // Always disabled to stop direct editing of attachment names
+ mCreateInventoryName->setEnabled(FALSE);
+ mViewInventoryName->setEnabled(FALSE);
+
+ // If we can receive notices, grab them right away.
+ if (can_receive)
+ {
+ onClickRefreshNotices(this);
+ }
+}
+
+void LLPanelGroupNotices::setItem(LLPointer<LLInventoryItem> inv_item)
+{
+ mInventoryItem = inv_item;
+
+ LLViewerImage* item_icon = get_item_icon(inv_item->getType(),
+ inv_item->getInventoryType(),
+ inv_item->getFlags());
+
+ mCreateInventoryIcon->setImage(item_icon->getID());
+ mCreateInventoryIcon->setVisible(TRUE);
+
+ std::stringstream ss;
+ ss << " " << mInventoryItem->getName();
+
+ mCreateInventoryName->setText(ss.str());
+ mBtnRemoveAttachment->setEnabled(TRUE);
+}
+
+void LLPanelGroupNotices::onClickRemoveAttachment(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+ self->mInventoryItem = NULL;
+ self->mCreateInventoryName->clear();
+ self->mCreateInventoryIcon->setVisible(FALSE);
+ self->mBtnRemoveAttachment->setEnabled(FALSE);
+}
+
+//static
+void LLPanelGroupNotices::onClickOpenAttachment(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ inventory_offer_callback( 0 , self->mInventoryOffer);
+ self->mInventoryOffer = NULL;
+ self->mBtnOpenAttachment->setEnabled(FALSE);
+}
+
+void LLPanelGroupNotices::onClickSendMessage(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ if (self->mCreateSubject->getText().empty())
+ {
+ // Must supply a subject
+ gViewerWindow->alertXml("MustSpecifyGroupNoticeSubject");
+ return;
+ }
+ send_group_notice(
+ self->mGroupID,
+ self->mCreateSubject->getText().c_str(),
+ self->mCreateMessage->getText().c_str(),
+ self->mInventoryItem);
+
+ self->mCreateMessage->clear();
+ self->mCreateSubject->clear();
+ onClickRemoveAttachment(data);
+
+ self->arrangeNoticeView(VIEW_PAST_NOTICE);
+ onClickRefreshNotices(self);
+
+}
+
+//static
+void LLPanelGroupNotices::onClickNewMessage(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ self->arrangeNoticeView(CREATE_NEW_NOTICE);
+
+ if (self->mInventoryOffer)
+ {
+ inventory_offer_callback( 1 , self->mInventoryOffer);
+ self->mInventoryOffer = NULL;
+ }
+
+ self->mCreateSubject->clear();
+ self->mCreateMessage->clear();
+ if (self->mInventoryItem) onClickRemoveAttachment(self);
+ self->mNoticesList->deselectAllItems(TRUE); // TRUE == don't commit on chnage
+}
+
+void LLPanelGroupNotices::onClickRefreshNotices(void* data)
+{
+ lldebugs << "LLPanelGroupNotices::onClickGetPastNotices" << llendl;
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ self->mNoticesList->deleteAllItems();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupNoticesListRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID",self->mGroupID);
+ gAgent.sendReliableMessage();
+}
+
+//static
+std::map<LLUUID,LLPanelGroupNotices*> LLPanelGroupNotices::sInstances;
+
+// static
+void LLPanelGroupNotices::processGroupNoticesListReply(LLMessageSystem* msg, void** data)
+{
+ LLUUID group_id;
+ msg->getUUID("AgentData", "GroupID", group_id);
+
+ std::map<LLUUID,LLPanelGroupNotices*>::iterator it = sInstances.find(group_id);
+ if (it == sInstances.end())
+ {
+ llinfos << "Group Panel Notices " << group_id << " no longer in existence."
+ << llendl;
+ return;
+ }
+
+ LLPanelGroupNotices* selfp = it->second;
+ if(!selfp)
+ {
+ llinfos << "Group Panel Notices " << group_id << " no longer in existence."
+ << llendl;
+ return;
+ }
+
+ selfp->processNotices(msg);
+}
+
+void LLPanelGroupNotices::processNotices(LLMessageSystem* msg)
+{
+ LLUUID id;
+ char subj[MAX_STRING];
+ char name[MAX_STRING];
+ U32 timestamp;
+ BOOL has_attachment;
+ U8 asset_type;
+
+ S32 i=0;
+ S32 count = msg->getNumberOfBlocks("Data");
+ for (;i<count;++i)
+ {
+ msg->getUUID("Data","NoticeID",id,i);
+ if (1 == count && id.isNull())
+ {
+ // Only one entry, the dummy entry.
+ mNoticesList->addSimpleItem(mNoNoticesStr,ADD_BOTTOM,FALSE);
+ mNoticesList->setEnabled(FALSE);
+ return;
+ }
+
+ msg->getString("Data","Subject",MAX_STRING,subj,i);
+ msg->getString("Data","FromName",MAX_STRING,name,i);
+ msg->getBOOL("Data","HasAttachment",has_attachment,i);
+ msg->getU8("Data","AssetType",asset_type,i);
+ msg->getU32("Data","Timestamp",timestamp,i);
+ time_t t = timestamp;
+
+ LLSD row;
+ row["id"] = id;
+
+ row["columns"][0]["column"] = "icon";
+ if (has_attachment)
+ {
+ LLUUID icon_id = get_item_icon_uuid(
+ (LLAssetType::EType)asset_type,
+ LLInventoryType::IT_NONE,FALSE);
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+ }
+
+ row["columns"][1]["column"] = "subject";
+ row["columns"][1]["value"] = subj;
+
+ row["columns"][2]["column"] = "from";
+ row["columns"][2]["value"] = name;
+
+ char buffer[30];
+ build_notice_date(t, buffer);
+ row["columns"][3]["column"] = "date";
+ row["columns"][3]["value"] = buffer;
+
+ snprintf(buffer, 30, "%u", timestamp);
+ row["columns"][4]["column"] = "sort";
+ row["columns"][4]["value"] = buffer;
+
+ mNoticesList->addElement(row, ADD_SORTED);
+ }
+}
+
+void LLPanelGroupNotices::onSelectNotice(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ if(!self) return;
+ LLScrollListItem* item = self->mNoticesList->getFirstSelected();
+ if (!item) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupNoticeRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("GroupNoticeID",item->getUUID());
+ gAgent.sendReliableMessage();
+
+ lldebugs << "Item " << item->getUUID().getString().c_str() << " selected." << llendl;
+}
+
+void LLPanelGroupNotices::showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer)
+{
+ arrangeNoticeView(VIEW_PAST_NOTICE);
+
+ if(mViewSubject) mViewSubject->setText(subject);
+ if(mViewMessage) mViewMessage->setText(message);
+
+ if (mInventoryOffer)
+ {
+ // Cancel the inventory offer for the previously viewed notice
+ inventory_offer_callback( 1 , mInventoryOffer);
+ mInventoryOffer = NULL;
+ }
+
+ if (inventory_offer)
+ {
+ mInventoryOffer = inventory_offer;
+
+ LLViewerImage* item_icon = get_item_icon(mInventoryOffer->mType,
+ LLInventoryType::IT_TEXTURE,
+ 0);
+
+ mViewInventoryIcon->setImage(item_icon->getID());
+ mViewInventoryIcon->setVisible(TRUE);
+
+ std::stringstream ss;
+ ss << " " << inventory_name;
+
+ mViewInventoryName->setText(ss.str());
+ mBtnOpenAttachment->setEnabled(TRUE);
+ }
+ else
+ {
+ mViewInventoryName->clear();
+ mViewInventoryIcon->setVisible(FALSE);
+ mBtnOpenAttachment->setEnabled(FALSE);
+ }
+}
+
+void LLPanelGroupNotices::arrangeNoticeView(ENoticeView view_type)
+{
+ if (CREATE_NEW_NOTICE == view_type)
+ {
+ mPanelCreateNotice->setVisible(TRUE);
+ mPanelViewNotice->setVisible(FALSE);
+ }
+ else
+ {
+ mPanelCreateNotice->setVisible(FALSE);
+ mPanelViewNotice->setVisible(TRUE);
+ mBtnOpenAttachment->setEnabled(FALSE);
+ }
+}
diff --git a/indra/newview/llpanelgroupnotices.h b/indra/newview/llpanelgroupnotices.h
new file mode 100644
index 0000000000..62c8f62162
--- /dev/null
+++ b/indra/newview/llpanelgroupnotices.h
@@ -0,0 +1,98 @@
+/**
+ * @file llpanelgroupnotices.h
+ * @brief A panel to display group notices.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPNOTICES_H
+#define LL_LLPANELGROUPNOTICES_H
+
+#include "llpanelgroup.h"
+#include "llmemory.h"
+#include "llinventory.h"
+
+class LLLineEditor;
+class LLTextEditor;
+class LLButton;
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLScrollListCtrl;
+
+class LLPanelGroupNotices : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupNotices(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupNotices();
+
+ // LLPanelGroupTab
+ static void* createTab(void* data);
+ virtual void activate();
+ //virtual bool needsApply(LLString& mesg);
+ //virtual bool apply(LLString& mesg);
+ //virtual void update();
+
+ virtual BOOL postBuild();
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ void setItem(LLPointer<LLInventoryItem> inv_item);
+
+ static void processGroupNoticesListReply(LLMessageSystem* msg, void** data);
+
+ void showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer);
+
+private:
+ static void onClickRemoveAttachment(void* data);
+ static void onClickOpenAttachment(void* data);
+ static void onClickSendMessage(void* data);
+ static void onClickNewMessage(void* data);
+ static void onClickRefreshNotices(void* data);
+
+ void processNotices(LLMessageSystem* msg);
+ static void onSelectNotice(LLUICtrl* ctrl, void* data);
+
+ enum ENoticeView
+ {
+ VIEW_PAST_NOTICE,
+ CREATE_NEW_NOTICE
+ };
+
+ void arrangeNoticeView(ENoticeView view_type);
+
+ LLPointer<LLInventoryItem> mInventoryItem;
+
+ LLLineEditor *mCreateSubject;
+ LLLineEditor *mCreateInventoryName;
+ LLTextEditor *mCreateMessage;
+
+ LLLineEditor *mViewSubject;
+ LLLineEditor *mViewInventoryName;
+ LLTextEditor *mViewMessage;
+
+ LLButton *mBtnSendMessage;
+ LLButton *mBtnNewMessage;
+ LLButton *mBtnRemoveAttachment;
+ LLButton *mBtnOpenAttachment;
+ LLButton *mBtnGetPastNotices;
+
+ LLPanel *mPanelCreateNotice;
+ LLPanel *mPanelViewNotice;
+
+ LLIconCtrl *mCreateInventoryIcon;
+ LLIconCtrl *mViewInventoryIcon;
+
+ LLScrollListCtrl *mNoticesList;
+
+ std::string mNoNoticesStr;
+
+ LLOfferInfo* mInventoryOffer;
+
+ static std::map<LLUUID,LLPanelGroupNotices*> sInstances;
+};
+
+#endif
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
new file mode 100644
index 0000000000..ccec55efce
--- /dev/null
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -0,0 +1,2642 @@
+/**
+ * @file llpanelgrouproles.cpp
+ * @brief Panel for roles information about a particular group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcheckboxctrl.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroupinvite.h"
+#include "lliconctrl.h"
+#include "lllineeditor.h"
+#include "llnamelistctrl.h"
+#include "llnotify.h"
+#include "llpanelgrouproles.h"
+#include "llscrolllistctrl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltexteditor.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llfocusmgr.h"
+
+#include "roles_constants.h"
+
+bool agentCanRemoveFromRole(const LLUUID& group_id,
+ const LLUUID& role_id)
+{
+ return gAgent.hasPowerInGroup(group_id, GP_ROLE_REMOVE_MEMBER);
+}
+
+bool agentCanAddToRole(const LLUUID& group_id,
+ const LLUUID& role_id)
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(group_id);
+ if (!gdatap)
+ {
+ llwarns << "agentCanAddToRole "
+ << "-- No group data!" << llendl;
+ return false;
+ }
+
+ //make sure the agent is in the group
+ LLGroupMgrGroupData::member_iter mi = gdatap->mMembers.find(gAgent.getID());
+ if (mi == gdatap->mMembers.end())
+ {
+ return false;
+ }
+
+ LLGroupMemberData* member_data = (*mi).second;
+
+ // Owners can add to any role.
+ if ( member_data->isInRole(gdatap->mOwnerRole) )
+ {
+ return true;
+ }
+
+ // 'Limited assign members' can add to roles the user is in.
+ if ( gAgent.hasPowerInGroup(group_id, GP_ROLE_ASSIGN_MEMBER_LIMITED) &&
+ member_data->isInRole(role_id) )
+ {
+ return true;
+ }
+
+ // 'assign members' can add to non-owner roles.
+ if ( gAgent.hasPowerInGroup(group_id, GP_ROLE_ASSIGN_MEMBER) &&
+ role_id != gdatap->mOwnerRole )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// static
+void* LLPanelGroupRoles::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupRoles("panel group roles", *group_id);
+}
+
+LLPanelGroupRoles::LLPanelGroupRoles(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupTab(name, group_id),
+ mCurrentTab(NULL),
+ mRequestedTab( NULL ),
+ mSubTabContainer( NULL ),
+ mFirstUse( TRUE ),
+ mIgnoreTransition( FALSE )
+{
+}
+
+LLPanelGroupRoles::~LLPanelGroupRoles()
+{
+ int i;
+ for (i = 0; i < mSubTabContainer->getTabCount(); ++i)
+ {
+ LLPanelGroupSubTab* subtabp = (LLPanelGroupSubTab*) mSubTabContainer->getPanelByIndex(i);
+
+ subtabp->removeObserver(this);
+ }
+}
+
+BOOL LLPanelGroupRoles::postBuild()
+{
+ lldebugs << "LLPanelGroupRoles::postBuild()" << llendl;
+
+ mSubTabContainer = (LLTabContainerCommon*) getChildByName("roles_tab_container");
+
+ if (!mSubTabContainer) return FALSE;
+
+ // Hook up each sub-tabs callback and widgets.
+ S32 i;
+ for (i = 0; i < mSubTabContainer->getTabCount(); ++i)
+ {
+ LLPanelGroupSubTab* subtabp = (LLPanelGroupSubTab*) mSubTabContainer->getPanelByIndex(i);
+
+ // Add click callbacks to all the tabs.
+ mSubTabContainer->setTabChangeCallback(subtabp, onClickSubTab);
+ mSubTabContainer->setTabUserData(subtabp, this);
+
+ // Hand the subtab a pointer to this LLPanelGroupRoles, so that it can
+ // look around for the widgets it is interested in.
+ if (!subtabp->postBuildSubTab(this)) return FALSE;
+
+ subtabp->addObserver(this);
+ }
+
+ // Set the current tab to whatever is currently being shown.
+ mCurrentTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!mCurrentTab)
+ {
+ // Need to select a tab.
+ mSubTabContainer->selectFirstTab();
+ mCurrentTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ }
+
+ if (!mCurrentTab) return FALSE;
+
+ // Act as though this tab was just activated.
+ mCurrentTab->activate();
+
+ // Read apply text from the xml file.
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*)getChildByName("default_needs_apply_text");
+ if (txt)
+ {
+ mDefaultNeedsApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+ txt = (LLTextBox*)getChildByName("want_apply_text");
+ if (txt)
+ {
+ mWantApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+BOOL LLPanelGroupRoles::isVisibleByAgent(LLAgent* agentp)
+{
+ /* This power was removed to make group roles simpler
+ return agentp->hasPowerInGroup(mGroupID,
+ GP_ROLE_CREATE |
+ GP_ROLE_DELETE |
+ GP_ROLE_PROPERTIES |
+ GP_ROLE_VIEW |
+ GP_ROLE_ASSIGN_MEMBER |
+ GP_ROLE_REMOVE_MEMBER |
+ GP_ROLE_CHANGE_ACTIONS |
+ GP_MEMBER_INVITE |
+ GP_MEMBER_EJECT |
+ GP_MEMBER_OPTIONS );
+ */
+ return mAllowEdit && agentp->isInGroup(mGroupID);
+
+}
+
+// static
+void LLPanelGroupRoles::onClickSubTab(void* user_data, bool from_click)
+{
+ LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
+ self->handleClickSubTab();
+}
+
+void LLPanelGroupRoles::handleClickSubTab()
+{
+ // If we are already handling a transition,
+ // ignore this.
+ if (mIgnoreTransition)
+ {
+ return;
+ }
+
+ mRequestedTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+
+ // Make sure they aren't just clicking the same tab...
+ if (mRequestedTab == mCurrentTab)
+ {
+ return;
+ }
+
+ // Try to switch from the current panel to the panel the user selected.
+ attemptTransition();
+}
+
+BOOL LLPanelGroupRoles::attemptTransition()
+{
+ // Check if the current tab needs to be applied.
+ LLString mesg;
+ if (mCurrentTab && mCurrentTab->needsApply(mesg))
+ {
+ // If no message was provided, give a generic one.
+ if (mesg.empty())
+ {
+ mesg = mDefaultNeedsApplyMesg;
+ }
+ // Create a notify box, telling the user about the unapplied tab.
+ LLString::format_map_t args;
+ args["[NEEDS_APPLY_MESSAGE]"] = mesg;
+ args["[WANT_APPLY_MESSAGE]"] = mWantApplyMesg;
+ gViewerWindow->alertXml("PanelGroupApply", args,
+ onNotifyCallback, (void*) this);
+ mHasModal = TRUE;
+ // We need to reselect the current tab, since it isn't finished.
+ if (mSubTabContainer)
+ {
+ mIgnoreTransition = TRUE;
+ mSubTabContainer->selectTabPanel( mCurrentTab );
+ mIgnoreTransition = FALSE;
+ }
+ // Returning FALSE will block a close action from finishing until
+ // we get a response back from the user.
+ return FALSE;
+ }
+ else
+ {
+ // The current panel didn't have anything it needed to apply.
+ if (mRequestedTab)
+ {
+ transitionToTab();
+ }
+ return TRUE;
+ }
+}
+
+void LLPanelGroupRoles::transitionToTab()
+{
+ // Tell the current panel that it is being deactivated.
+ if (mCurrentTab)
+ {
+ mCurrentTab->deactivate();
+ }
+
+ // Tell the new panel that it is being activated.
+ if (mRequestedTab)
+ {
+ // This is now the current tab;
+ mCurrentTab = mRequestedTab;
+ mCurrentTab->activate();
+ }
+}
+
+// static
+void LLPanelGroupRoles::onNotifyCallback(S32 option, void* user_data)
+{
+ LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
+ if (self)
+ {
+ self->handleNotifyCallback(option);
+ }
+}
+
+void LLPanelGroupRoles::handleNotifyCallback(S32 option)
+{
+ mHasModal = FALSE;
+ switch (option)
+ {
+ case 0: // "Apply Changes"
+ {
+ // Try to apply changes, and switch to the requested tab.
+ LLString apply_mesg;
+ if ( !apply( apply_mesg ) )
+ {
+ // There was a problem doing the apply.
+ if ( !apply_mesg.empty() )
+ {
+ mHasModal = TRUE;
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = apply_mesg;
+ gViewerWindow->alertXml("GenericAlert", args, onModalClose, (void*) this);
+ }
+ // Skip switching tabs.
+ break;
+ }
+
+ // This panel's info successfully applied.
+ // Switch to the next panel.
+ // No break! Continue into 'Ignore Changes' which just switches tabs.
+ mIgnoreTransition = TRUE;
+ mSubTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ }
+ case 1: // "Ignore Changes"
+ // Switch to the requested panel without applying changes
+ cancel();
+ mIgnoreTransition = TRUE;
+ mSubTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ case 2: // "Cancel"
+ default:
+ // Do nothing. The user is canceling the action.
+ break;
+ }
+}
+
+// static
+void LLPanelGroupRoles::onModalClose(S32 option, void* user_data)
+{
+ LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
+ if (self)
+ {
+ self->mHasModal = FALSE;
+ }
+}
+
+
+bool LLPanelGroupRoles::apply(LLString& mesg)
+{
+ // Pass this along to the currently visible sub tab.
+ if (!mSubTabContainer) return false;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return false;
+
+ // Ignore the needs apply message.
+ LLString ignore_mesg;
+ if ( !panelp->needsApply(ignore_mesg) )
+ {
+ // We don't need to apply anything.
+ // We're done.
+ return true;
+ }
+
+ // Try to do the actual apply.
+ return panelp->apply(mesg);
+}
+
+void LLPanelGroupRoles::cancel()
+{
+ // Pass this along to the currently visible sub tab.
+ if (!mSubTabContainer) return;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return;
+
+ panelp->cancel();
+}
+
+// Pass all of these messages to the currently visible sub tab.
+LLString LLPanelGroupRoles::getHelpText() const
+{
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp)
+ {
+ return panelp->getHelpText();
+ }
+ else
+ {
+ return mHelpText;
+ }
+}
+
+void LLPanelGroupRoles::update(LLGroupChange gc)
+{
+ if (mGroupID.isNull()) return;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp)
+ {
+ panelp->update(gc);
+ }
+ else
+ {
+ llwarns << "LLPanelGroupRoles::update() -- No subtab to update!" << llendl;
+ }
+}
+
+void LLPanelGroupRoles::activate()
+{
+ // Start requesting member and role data if needed.
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ //if (!gdatap || mFirstUse)
+ {
+ // Check member data.
+
+ if (!gdatap || !gdatap->isMemberDataComplete() )
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ // Check role data.
+ if (!gdatap || !gdatap->isRoleDataComplete() )
+ {
+ // Mildly hackish - clear all pending changes
+ cancel();
+
+ gGroupMgr->sendGroupRoleDataRequest(mGroupID);
+ }
+
+ // Check role-member mapping data.
+ if (!gdatap || !gdatap->isRoleMemberDataComplete() )
+ {
+ gGroupMgr->sendGroupRoleMembersRequest(mGroupID);
+ }
+
+ // Need this to get base group member powers
+ if (!gdatap || !gdatap->isGroupPropertiesDataComplete() )
+ {
+ gGroupMgr->sendGroupPropertiesRequest(mGroupID);
+ }
+
+ mFirstUse = FALSE;
+ }
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp) panelp->activate();
+}
+
+void LLPanelGroupRoles::deactivate()
+{
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp) panelp->deactivate();
+}
+
+bool LLPanelGroupRoles::needsApply(LLString& mesg)
+{
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return false;
+
+ return panelp->needsApply(mesg);
+}
+
+BOOL LLPanelGroupRoles::hasModal()
+{
+ if (mHasModal) return TRUE;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return FALSE;
+
+ return panelp->hasModal();
+}
+
+// PanelGroupTab observer trigger
+void LLPanelGroupRoles::tabChanged()
+{
+ notifyObservers();
+}
+
+////////////////////////////
+// LLPanelGroupSubTab
+////////////////////////////
+LLPanelGroupSubTab::LLPanelGroupSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupTab(name, group_id),
+ mHeader(NULL),
+ mFooter(NULL),
+ mSearchLineEditor(NULL),
+ mSearchButton(NULL),
+ mShowAllButton(NULL)
+{
+}
+
+LLPanelGroupSubTab::~LLPanelGroupSubTab()
+{
+}
+
+BOOL LLPanelGroupSubTab::postBuild()
+{
+ // Hook up the search widgets.
+ bool recurse = true;
+ mSearchLineEditor = (LLLineEditor*) getChildByName("search_text", recurse);
+
+ if (!mSearchLineEditor) return FALSE;
+ mSearchLineEditor->setKeystrokeCallback(onSearchKeystroke);
+ mSearchLineEditor->setCallbackUserData(this);
+
+ mSearchButton = (LLButton*) getChildByName("search_button", recurse);
+
+ if (!mSearchButton) return FALSE;
+ mSearchButton->setClickedCallback(onClickSearch);
+ mSearchButton->setCallbackUserData(this);
+ mSearchButton->setEnabled(FALSE);
+
+ mShowAllButton = (LLButton*) getChildByName("show_all_button", recurse);
+
+ if (!mShowAllButton) return FALSE;
+ mShowAllButton->setClickedCallback(onClickShowAll);
+ mShowAllButton->setCallbackUserData(this);
+ mShowAllButton->setEnabled(FALSE);
+
+ // Get icons for later use.
+ mActionIcons.clear();
+
+ bool no_recurse = false;
+
+ LLIconCtrl* icon = (LLIconCtrl*) getChildByName("power_folder_icon",no_recurse);
+ if (icon && icon->getImage().notNull())
+ {
+ mActionIcons["folder"] = icon->getImage();
+ removeChild(icon);
+ }
+
+ icon = (LLIconCtrl*) getChildByName("power_all_have_icon",no_recurse);
+ if (icon && icon->getImage().notNull())
+ {
+ mActionIcons["full"] = icon->getImage();
+ removeChild(icon);
+ }
+
+ icon = (LLIconCtrl*) getChildByName("power_partial_icon",no_recurse);
+ if (icon && icon->getImage().notNull())
+ {
+ mActionIcons["partial"] = icon->getImage();
+ removeChild(icon);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+// static
+void LLPanelGroupSubTab::onSearchKeystroke(LLLineEditor* caller, void* user_data)
+{
+ LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
+ self->handleSearchKeystroke(caller);
+}
+
+void LLPanelGroupSubTab::handleSearchKeystroke(LLLineEditor* caller)
+{
+ if (caller->getText().size())
+ {
+ setDefaultBtn( mSearchButton );
+ mSearchButton->setEnabled(TRUE);
+ }
+ else
+ {
+ setDefaultBtn( NULL );
+ mSearchButton->setEnabled(FALSE);
+ }
+}
+
+// static
+void LLPanelGroupSubTab::onClickSearch(void* user_data)
+{
+ LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
+ self->handleClickSearch();
+}
+
+void LLPanelGroupSubTab::handleClickSearch()
+{
+ lldebugs << "LLPanelGroupSubTab::handleClickSearch()" << llendl;
+
+ if (0 == mSearchLineEditor->getText().size())
+ {
+ // No search text. (This shouldn't happen... the search button should have been disabled).
+ llwarns << "handleClickSearch with no search text!" << llendl;
+ mSearchButton->setEnabled(FALSE);
+ return;
+ }
+
+ setSearchFilter( mSearchLineEditor->getText() );
+ mShowAllButton->setEnabled(TRUE);
+}
+
+// static
+void LLPanelGroupSubTab::onClickShowAll(void* user_data)
+{
+ LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
+ self->handleClickShowAll();
+}
+
+void LLPanelGroupSubTab::handleClickShowAll()
+{
+ lldebugs << "LLPanelGroupSubTab::handleClickShowAll()" << llendl;
+ setSearchFilter( LLString::null );
+ mShowAllButton->setEnabled(FALSE);
+}
+
+void LLPanelGroupSubTab::setSearchFilter(const LLString& filter)
+{
+ lldebugs << "LLPanelGroupSubTab::setSearchFilter() ==> '" << filter << "'" << llendl;
+ mSearchFilter = filter;
+ LLString::toLower(mSearchFilter);
+ update(GC_ALL);
+}
+
+void LLPanelGroupSubTab::activate()
+{
+ lldebugs << "LLPanelGroupSubTab::activate()" << llendl;
+ setOthersVisible(TRUE);
+}
+
+void LLPanelGroupSubTab::deactivate()
+{
+ lldebugs << "LLPanelGroupSubTab::deactivate()" << llendl;
+ setOthersVisible(FALSE);
+}
+
+void LLPanelGroupSubTab::setOthersVisible(BOOL b)
+{
+ if (mHeader)
+ {
+ mHeader->setVisible( b );
+ }
+ else
+ {
+ llwarns << "LLPanelGroupSubTab missing header!" << llendl;
+ }
+
+ if (mFooter)
+ {
+ mFooter->setVisible( b );
+ }
+ else
+ {
+ llwarns << "LLPanelGroupSubTab missing footer!" << llendl;
+ }
+}
+
+bool LLPanelGroupSubTab::matchesActionSearchFilter(std::string action)
+{
+ // If the search filter is empty, everything passes.
+ if (mSearchFilter.empty()) return true;
+
+ LLString::toLower(action);
+ std::string::size_type match = action.find(mSearchFilter);
+
+ if (std::string::npos == match)
+ {
+ // not found
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role)
+{
+ if (gGroupMgr->mRoleActionSets.empty())
+ {
+ llwarns << "Can't build action list - no actions found." << llendl;
+ return;
+ }
+
+ std::vector<LLRoleActionSet*>::iterator ras_it = gGroupMgr->mRoleActionSets.begin();
+ std::vector<LLRoleActionSet*>::iterator ras_end = gGroupMgr->mRoleActionSets.end();
+
+ for ( ; ras_it != ras_end; ++ras_it)
+ {
+ buildActionCategory(ctrl,
+ allowed_by_some,
+ allowed_by_all,
+ (*ras_it),
+ icons,
+ commit_callback,
+ show_all,
+ filter,
+ is_owner_role);
+ }
+}
+
+void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ LLRoleActionSet* action_set,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role)
+{
+ lldebugs << "Building role list for: " << action_set->mActionSetData->mName << llendl;
+ // See if the allow mask matches anything in this category.
+ if (show_all || (allowed_by_some & action_set->mActionSetData->mPowerBit))
+ {
+ // List all the actions in this category that at least some members have.
+ LLSD row;
+
+ row["columns"][0]["column"] = "icon";
+ icon_map_t::iterator iter = icons.find("folder");
+ if (iter != icons.end())
+ {
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = (*iter).second;
+ }
+
+ row["columns"][1]["column"] = "action";
+ row["columns"][1]["value"] = action_set->mActionSetData->mName;
+ row["columns"][1]["font-style"] = "BOLD";
+
+ LLScrollListItem* title_row = ctrl->addElement(row, ADD_BOTTOM, action_set->mActionSetData);
+
+ bool category_matches_filter = (filter) ? matchesActionSearchFilter(action_set->mActionSetData->mName) : true;
+
+ std::vector<LLRoleAction*>::iterator ra_it = action_set->mActions.begin();
+ std::vector<LLRoleAction*>::iterator ra_end = action_set->mActions.end();
+
+ bool items_match_filter = false;
+ BOOL can_change_actions = (!is_owner_role && gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CHANGE_ACTIONS));
+
+ for ( ; ra_it != ra_end; ++ra_it)
+ {
+ // See if anyone has these action.
+ if (!show_all && !(allowed_by_some & (*ra_it)->mPowerBit))
+ {
+ continue;
+ }
+
+ // See if we are filtering out these actions
+ // If we aren't using filters, category_matches_filter will be true.
+ if (!category_matches_filter
+ && !matchesActionSearchFilter((*ra_it)->mDescription))
+ {
+ continue;
+ }
+
+ items_match_filter = true;
+
+ // See if everyone has these actions.
+ bool show_full_strength = false;
+ if ( (allowed_by_some & (*ra_it)->mPowerBit) == (allowed_by_all & (*ra_it)->mPowerBit) )
+ {
+ show_full_strength = true;
+ }
+
+ LLSD row;
+
+ S32 column_index = 0;
+ row["columns"][column_index]["column"] = "icon";
+ ++column_index;
+
+
+ S32 check_box_index = -1;
+ if (commit_callback)
+ {
+ row["columns"][column_index]["column"] = "checkbox";
+ row["columns"][column_index]["type"] = "checkbox";
+ row["columns"][column_index]["value"] = (*ra_it)->mName;
+ check_box_index = column_index;
+ ++column_index;
+ }
+ else
+ {
+ if (show_full_strength)
+ {
+ icon_map_t::iterator iter = icons.find("full");
+ if (iter != icons.end())
+ {
+ row["columns"][column_index]["column"] = "checkbox";
+ row["columns"][column_index]["type"] = "icon";
+ row["columns"][column_index]["value"] = (*iter).second;
+ ++column_index;
+ }
+ }
+ else
+ {
+ icon_map_t::iterator iter = icons.find("partial");
+ if (iter != icons.end())
+ {
+ row["columns"][column_index]["column"] = "checkbox";
+ row["columns"][column_index]["type"] = "icon";
+ row["columns"][column_index]["value"] = (*iter).second;
+ ++column_index;
+ }
+ row["enabled"] = false;
+ }
+ }
+
+ row["columns"][column_index]["column"] = "action";
+ row["columns"][column_index]["value"] = (*ra_it)->mDescription;
+ row["columns"][column_index]["font"] = "SANSSERIFSMALL";
+
+ LLScrollListItem* item = ctrl->addElement(row, ADD_BOTTOM, (*ra_it));
+
+ if (-1 != check_box_index)
+ {
+ // Extract the checkbox that was created.
+ LLScrollListCheck* check_cell = (LLScrollListCheck*) item->getColumn(check_box_index);
+ LLCheckBoxCtrl* check = check_cell->getCheckBox();
+ check->setEnabled(can_change_actions);
+ check->setCommitCallback(commit_callback);
+ check->setCallbackUserData(ctrl->getCallbackUserData());
+ check->setToolTip( check->getLabel() );
+
+ if (show_all)
+ {
+ check->setTentative(FALSE);
+ if (allowed_by_some & (*ra_it)->mPowerBit)
+ {
+ check->set(TRUE);
+ }
+ else
+ {
+ check->set(FALSE);
+ }
+ }
+ else
+ {
+ check->set(TRUE);
+ if (show_full_strength)
+ {
+ check->setTentative(FALSE);
+ }
+ else
+ {
+ check->setTentative(TRUE);
+ }
+ }
+ }
+ }
+
+ if (!items_match_filter)
+ {
+ S32 title_index = ctrl->getItemIndex(title_row);
+ ctrl->deleteSingleItem(title_index);
+ }
+ }
+}
+
+void LLPanelGroupSubTab::setFooterEnabled(BOOL enable)
+{
+ if (mFooter)
+ {
+ mFooter->setAllChildrenEnabled(enable);
+ }
+}
+
+////////////////////////////
+// LLPanelGroupMembersSubTab
+////////////////////////////
+
+// static
+void* LLPanelGroupMembersSubTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupMembersSubTab("panel group members sub tab", *group_id);
+}
+
+LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupSubTab(name, group_id),
+ mMembersList(NULL),
+ mAssignedRolesList(NULL),
+ mAllowedActionsList(NULL),
+ mChanged(FALSE),
+ mPendingMemberUpdate(FALSE),
+ mHasMatch(FALSE),
+ mNumOwnerAdditions(0)
+{
+}
+
+LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab()
+{
+}
+
+BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root)
+{
+ // Upcast parent so we can ask it for sibling controls.
+ LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;
+
+ // Look recursively from the parent to find all our widgets.
+ bool recurse = true;
+ mHeader = (LLPanel*) parent->getChildByName("members_header", recurse);
+ mFooter = (LLPanel*) parent->getChildByName("members_footer", recurse);
+
+ mMembersList = (LLNameListCtrl*) parent->getChildByName("member_list", recurse);
+ mAssignedRolesList = (LLScrollListCtrl*) parent->getChildByName("member_assigned_roles", recurse);
+ mAllowedActionsList = (LLScrollListCtrl*) parent->getChildByName("member_allowed_actions", recurse);
+
+ if (!mMembersList || !mAssignedRolesList || !mAllowedActionsList) return FALSE;
+
+ // We want to be notified whenever a member is selected.
+ mMembersList->setCallbackUserData(this);
+ mMembersList->setCommitOnSelectionChange(TRUE);
+ mMembersList->setCommitCallback(onMemberSelect);
+ // Show the member's profile on double click.
+ mMembersList->setDoubleClickCallback(onMemberDoubleClick);
+
+ LLButton* button = (LLButton*) parent->getChildByName("member_invite", recurse);
+ if ( button )
+ {
+ button->setClickedCallback(onInviteMember);
+ button->setCallbackUserData(this);
+ button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE));
+ }
+
+ mEjectBtn = (LLButton*) parent->getChildByName("member_eject", recurse);
+ if ( mEjectBtn )
+ {
+ mEjectBtn->setClickedCallback(onEjectMembers);
+ mEjectBtn->setCallbackUserData(this);
+ mEjectBtn->setEnabled(FALSE);
+ }
+
+ return TRUE;
+}
+
+
+// static
+void LLPanelGroupMembersSubTab::onMemberSelect(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+ self->handleMemberSelect();
+}
+
+void LLPanelGroupMembersSubTab::handleMemberSelect()
+{
+ lldebugs << "LLPanelGroupMembersSubTab::handleMemberSelect" << llendl;
+
+ mAssignedRolesList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::handleMemberSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ // Check if there is anything selected.
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if (selection.empty()) return;
+
+ // Build a vector of all selected members, and gather allowed actions.
+ std::vector<LLUUID> selected_members;
+ U64 allowed_by_all = 0xffffffffffffLL;
+ U64 allowed_by_some = 0;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin();
+ itor != selection.end(); ++itor)
+ {
+ selected_members.push_back( (*itor)->getUUID() );
+ // Get this member's power mask including any unsaved changes
+
+ U64 powers = getAgentPowersBasedOnRoleChanges((*itor)->getUUID());
+
+ allowed_by_all &= powers;
+ allowed_by_some |= powers;
+ }
+ std::sort(selected_members.begin(), selected_members.end());
+
+ //////////////////////////////////
+ // Build the allowed actions list.
+ //////////////////////////////////
+ buildActionsList(mAllowedActionsList,
+ allowed_by_some,
+ allowed_by_all,
+ mActionIcons,
+ NULL,
+ FALSE,
+ FALSE,
+ FALSE);
+
+ //////////////////////////////////
+ // Build the assigned roles list.
+ //////////////////////////////////
+ // Add each role to the assigned roles list.
+ LLGroupMgrGroupData::role_iter iter = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+
+ BOOL can_eject_members = gAgent.hasPowerInGroup(mGroupID,
+ GP_MEMBER_EJECT);
+ BOOL member_is_owner = FALSE;
+
+ for( ; iter != end; ++iter)
+ {
+ // Count how many selected users are in this role.
+ const LLUUID& role_id = iter->first;
+ LLGroupRoleData* group_role_data = iter->second;
+
+ if (group_role_data)
+ {
+ const BOOL needs_sort = FALSE;
+ S32 count = group_role_data->getMembersInRole(
+ selected_members, needs_sort);
+ //check if the user has permissions to assign/remove
+ //members to/from the role (but the ability to add/remove
+ //should only be based on the "saved" changes to the role
+ //not in the temp/meta data. -jwolk
+ BOOL cb_enable = ( (count > 0) ?
+ agentCanRemoveFromRole(mGroupID, role_id) :
+ agentCanAddToRole(mGroupID, role_id) );
+
+
+ // Owner role has special enabling permissions for removal.
+ if (cb_enable && (count > 0) && role_id == gdatap->mOwnerRole)
+ {
+ // Check if any owners besides this agent are selected.
+ std::vector<LLUUID>::const_iterator member_iter;
+ std::vector<LLUUID>::const_iterator member_end =
+ selected_members.end();
+ for (member_iter = selected_members.begin();
+ member_iter != member_end;
+ ++member_iter)
+ {
+ // Don't count the agent.
+ if ((*member_iter) == gAgent.getID()) continue;
+
+ // Look up the member data.
+ LLGroupMgrGroupData::member_iter mi =
+ gdatap->mMembers.find((*member_iter));
+ if (mi == gdatap->mMembers.end()) continue;
+ LLGroupMemberData* member_data = (*mi).second;
+ // Is the member an owner?
+ if ( member_data->isInRole(gdatap->mOwnerRole) )
+ {
+ // Can't remove other owners.
+ cb_enable = FALSE;
+ break;
+ }
+ }
+ }
+
+ //now see if there are any role changes for the selected
+ //members and remember to include them
+ std::vector<LLUUID>::iterator sel_mem_iter = selected_members.begin();
+ for (; sel_mem_iter != selected_members.end(); sel_mem_iter++)
+ {
+ LLRoleMemberChangeType type;
+ if ( getRoleChangeType(*sel_mem_iter, role_id, type) )
+ {
+ if ( type == RMC_ADD ) count++;
+ else if ( type == RMC_REMOVE ) count--;
+ }
+ }
+
+ // If anyone selected is in any role besides 'Everyone' then they can't be ejected.
+ if (role_id.notNull() && (count > 0))
+ {
+ can_eject_members = FALSE;
+ if (role_id == gdatap->mOwnerRole)
+ {
+ member_is_owner = TRUE;
+ }
+ }
+
+ LLRoleData rd;
+ if (gdatap->getRoleData(role_id,rd))
+ {
+ std::ostringstream label;
+ label << rd.mRoleName;
+ // Don't bother showing a count, if there is only 0 or 1.
+ if (count > 1)
+ {
+ label << ": " << count ;
+ }
+
+ LLSD row;
+ row["id"] = role_id;
+
+ row["columns"][0]["column"] = "checkbox";
+ row["columns"][0]["type"] = "checkbox";
+
+ row["columns"][1]["column"] = "role";
+ row["columns"][1]["value"] = label.str();
+
+ if (row["id"].asUUID().isNull())
+ {
+ // This is the everyone role, you can't take people out of the everyone role!
+ row["enabled"] = false;
+ }
+
+ LLScrollListItem* item = mAssignedRolesList->addElement(row);
+
+ // Extract the checkbox that was created.
+ LLScrollListCheck* check_cell = (LLScrollListCheck*) item->getColumn(0);
+ LLCheckBoxCtrl* check = check_cell->getCheckBox();
+ check->setCommitCallback(onRoleCheck);
+ check->setCallbackUserData(this);
+ check->set( count > 0 );
+ check->setTentative(0 != count && selected_members.size() != count);
+
+ //NOTE: as of right now a user can break the group
+ //by removing himself from a role if he is the
+ //last owner. We should check for this special case
+ // -jwolk
+ check->setEnabled(cb_enable);
+ }
+ }
+ else
+ {
+ // This could happen if changes are not synced right on sub-panel change.
+ llwarns << "No group role data for " << iter->second << llendl;
+ }
+ }
+ mAssignedRolesList->setEnabled(TRUE);
+
+ if (!can_eject_members && !member_is_owner)
+ {
+ // Maybe we can eject them because we are an owner...
+ LLGroupMgrGroupData::member_iter mi = gdatap->mMembers.find(gAgent.getID());
+ if (mi != gdatap->mMembers.end())
+ {
+ LLGroupMemberData* member_data = (*mi).second;
+
+ if ( member_data && member_data->isInRole(gdatap->mOwnerRole) )
+ {
+ can_eject_members = TRUE;
+ }
+ }
+ }
+
+ mEjectBtn->setEnabled(can_eject_members);
+}
+
+// static
+void LLPanelGroupMembersSubTab::onMemberDoubleClick(void* user_data)
+{
+ LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+ self->handleMemberDoubleClick();
+}
+
+//static
+void LLPanelGroupMembersSubTab::onInviteMember(void *userdata)
+{
+ LLPanelGroupMembersSubTab* selfp = (LLPanelGroupMembersSubTab*) userdata;
+
+ if ( selfp )
+ {
+ selfp->handleInviteMember();
+ }
+}
+
+void LLPanelGroupMembersSubTab::handleInviteMember()
+{
+ LLFloaterGroupInvite::showForGroup(mGroupID);
+}
+
+void LLPanelGroupMembersSubTab::onEjectMembers(void *userdata)
+{
+ LLPanelGroupMembersSubTab* selfp = (LLPanelGroupMembersSubTab*) userdata;
+
+ if ( selfp )
+ {
+ selfp->handleEjectMembers();
+ }
+}
+
+void LLPanelGroupMembersSubTab::handleEjectMembers()
+{
+ //send down an eject message
+ std::vector<LLUUID> selected_members;
+
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if (selection.empty()) return;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
+ {
+ selected_members.push_back((*itor)->getUUID());
+ }
+
+ mMembersList->deleteSelectedItems();
+
+ gGroupMgr->sendGroupMemberEjects(mGroupID,
+ selected_members);
+}
+
+void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,
+ LLRoleMemberChangeType type)
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap) return;
+
+ //add that the user is requesting to change the roles for selected
+ //members
+ U64 powers_all_have = 0xffffffffffffLL;
+ U64 powers_some_have = 0;
+
+ BOOL is_owner_role = ( gdatap->mOwnerRole == role_id );
+ LLUUID member_id;
+
+
+ member_role_change_iter member_end = mMemberRoleChangeData.end();
+ member_role_change_iter member;
+ role_change_data_map_t *role_change_datap;
+ role_change_data_map_t::iterator role_end;
+ role_change_data_map_t::iterator role;
+
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if (selection.empty()) return;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
+ {
+ member_id = (*itor)->getUUID();
+
+ //see if we requested a change for this member before
+ member = mMemberRoleChangeData.find(member_id);
+ if ( member != member_end )
+ {
+ //this member had previously had their role data changed
+ //so grab it
+ role_change_datap = (*member).second;
+ }
+ else
+ {
+ role_change_datap = new role_change_data_map_t;
+ mMemberRoleChangeData[member_id] = role_change_datap;
+ }
+
+ //now check to see if the selected group member
+ //had changed his association with the selected role before
+ role_end = role_change_datap->end();
+ role = role_change_datap->find(role_id);
+
+ if ( role != role_end )
+ {
+ //see if the new change type cancels out the previous change
+ if (role->second != type)
+ {
+ role_change_datap->erase(role_id);
+ if ( is_owner_role ) mNumOwnerAdditions--;
+ }
+ //else do nothing
+
+ if ( role_change_datap->empty() )
+ {
+ //the current member now has no role changes
+ //so erase the role change and erase the member's entry
+ delete role_change_datap;
+ role_change_datap = NULL;
+
+ mMemberRoleChangeData.erase(member_id);
+ }
+ }
+ else
+ {
+ //a previously unchanged role is being changed
+ (*role_change_datap)[role_id] = type;
+ if ( is_owner_role && type == RMC_ADD ) mNumOwnerAdditions++;
+ }
+
+ //we need to calculate what powers the selected members
+ //have (including the role changes we're making)
+ //so that we can rebuild the action list
+ U64 new_powers = getAgentPowersBasedOnRoleChanges(member_id);
+
+ powers_all_have &= new_powers;
+ powers_some_have |= new_powers;
+ }
+
+
+ mChanged = !mMemberRoleChangeData.empty();
+ notifyObservers();
+
+ //alrighty now we need to update the actions list
+ //to reflect the changes
+ mAllowedActionsList->deleteAllItems();
+ buildActionsList(mAllowedActionsList,
+ powers_some_have,
+ powers_all_have,
+ mActionIcons,
+ NULL,
+ FALSE,
+ FALSE,
+ FALSE);
+}
+
+
+// static
+void LLPanelGroupMembersSubTab::onRoleCheck(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+ LLCheckBoxCtrl* check_box = static_cast<LLCheckBoxCtrl*>(ctrl);
+ if (!check_box || !self) return;
+
+
+ LLUUID role_id = self->mAssignedRolesList->getFirstSelected()->getUUID();
+ LLRoleMemberChangeType change_type = (check_box->get() ?
+ RMC_ADD :
+ RMC_REMOVE);
+
+ self->handleRoleCheck(role_id, change_type);
+}
+
+void LLPanelGroupMembersSubTab::handleMemberDoubleClick()
+{
+ LLScrollListItem* selected = mMembersList->getFirstSelected();
+ if (selected)
+ {
+ LLFloaterAvatarInfo::showFromDirectory( selected->getUUID() );
+ }
+}
+
+void LLPanelGroupMembersSubTab::activate()
+{
+ LLPanelGroupSubTab::activate();
+
+ update(GC_ALL);
+}
+
+void LLPanelGroupMembersSubTab::deactivate()
+{
+ LLPanelGroupSubTab::deactivate();
+}
+
+bool LLPanelGroupMembersSubTab::needsApply(LLString& mesg)
+{
+ return mChanged;
+}
+
+void LLPanelGroupMembersSubTab::cancel()
+{
+ if ( mChanged )
+ {
+ std::for_each(mMemberRoleChangeData.begin(),
+ mMemberRoleChangeData.end(),
+ DeletePairedPointer());
+ mMemberRoleChangeData.clear();
+
+ mChanged = FALSE;
+ notifyObservers();
+ }
+}
+
+bool LLPanelGroupMembersSubTab::apply(LLString& mesg)
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "Unable to get group data for group " << mGroupID << llendl;
+
+ mesg.assign("Unable to save member data. Try again later.");
+ return false;
+ }
+
+ if (mChanged)
+ {
+ //figure out if we are somehow adding an owner or not and alert
+ //the user...possibly make it ignorable
+ if ( mNumOwnerAdditions > 0 )
+ {
+ LLRoleData rd;
+ LLStringBase<char>::format_map_t args;
+
+ if ( gdatap->getRoleData(gdatap->mOwnerRole, rd) )
+ {
+ mHasModal = TRUE;
+ args["[ROLE_NAME]"] = rd.mRoleName;
+ gViewerWindow->alertXml("AddGroupOwnerWarning",
+ args,
+ addOwnerCB,
+ this);
+ }
+ else
+ {
+ llwarns << "Unable to get role information for the owner role in group " << mGroupID << llendl;
+
+ mesg.assign("Unable to retried specific group information. Try again later");
+ return false;
+ }
+
+ }
+ else
+ {
+ applyMemberChanges();
+ }
+ }
+
+ return true;
+}
+
+//static
+void LLPanelGroupMembersSubTab::addOwnerCB(S32 option, void* data)
+{
+ LLPanelGroupMembersSubTab* self = (LLPanelGroupMembersSubTab*) data;
+
+ if (!self) return;
+
+ self->mHasModal = FALSE;
+
+ if (0 == option)
+ {
+ // User clicked "Yes"
+ self->applyMemberChanges();
+ }
+}
+
+void LLPanelGroupMembersSubTab::applyMemberChanges()
+{
+ //sucks to do a find again here, but it is in constant time, so, could
+ //be worse
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "Unable to get group data for group " << mGroupID << llendl;
+ return;
+ }
+
+ //we need to add all of the changed roles data
+ //for each member whose role changed
+ member_role_change_iter member_end = mMemberRoleChangeData.end();
+ member_role_change_iter member = mMemberRoleChangeData.begin();
+
+ for (; member != member_end; member++)
+ {
+ role_change_data_map_t::iterator role_end = member->second->end();
+ role_change_data_map_t::iterator role = member->second->begin();
+
+ for (; role != role_end; role++)
+ {
+ gdatap->changeRoleMember(role->first, //role_id
+ member->first, //member_id
+ role->second); //add/remove
+ }
+
+ member->second->clear();
+ delete member->second;
+ }
+ mMemberRoleChangeData.clear();
+
+ gGroupMgr->sendGroupRoleMemberChanges(mGroupID);
+ //force a UI update
+ handleMemberSelect();
+
+ mChanged = FALSE;
+ mNumOwnerAdditions = 0;
+ notifyObservers();
+}
+
+bool LLPanelGroupMembersSubTab::matchesSearchFilter(char* first, char* last)
+{
+ // If the search filter is empty, everything passes.
+ if (mSearchFilter.empty()) return true;
+
+ // Create a full name, and compare it to the search filter.
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+ LLString::toLower(fullname);
+
+ std::string::size_type match = fullname.find(mSearchFilter);
+
+ if (std::string::npos == match)
+ {
+ // not found
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+U64 LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges(const LLUUID& agent_id)
+{
+ //we loop over all of the changes
+ //if we are adding a role, then we simply add the role's powers
+ //if we are removing a role, we store that role id away
+ //and then we have to build the powers up bases on the roles the agent
+ //is in
+ member_role_change_iter member_end = mMemberRoleChangeData.end();
+ member_role_change_iter member;
+ role_change_data_map_t *role_change_datap = NULL;
+ role_change_data_map_t::iterator role_end;
+ role_change_data_map_t::iterator role;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No group data!" << llendl;
+ return GP_NO_POWERS;
+ }
+
+ LLGroupMemberData* member_data = gdatap->mMembers[agent_id];
+ if ( !member_data )
+ {
+ llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No member data for member with UUID " << agent_id << llendl;
+ return GP_NO_POWERS;
+ }
+
+ //see if there are unsaved role changes for this agent
+ member = mMemberRoleChangeData.find(agent_id);
+ if ( member != member_end )
+ {
+ //this member has unsaved role changes
+ //so grab them
+ role_change_datap = (*member).second;
+ }
+
+ U64 new_powers = GP_NO_POWERS;
+
+ if ( role_change_datap )
+ {
+ std::vector<LLUUID> roles_to_be_removed;
+
+ role_end = role_change_datap->end();
+ role = role_change_datap->begin();
+
+ for (; role != role_end; role++)
+ {
+ if ( role->second == RMC_ADD )
+ new_powers |= gdatap->getRolePowers(role->first);
+ else
+ {
+ roles_to_be_removed.push_back(role->first);
+ }
+ }
+
+ //loop over the member's current roles, summing up
+ //the powers (not including the role we are removing)
+ std::map<LLUUID,LLGroupRoleData*>::iterator current_role =
+ member_data->roleBegin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator end_role =
+ member_data->roleEnd();
+
+ for (; current_role != end_role; current_role++)
+ {
+ bool role_in_remove_list =
+ (std::find(roles_to_be_removed.begin(),
+ roles_to_be_removed.end(),
+ current_role->second->getID()) !=
+ roles_to_be_removed.end());
+
+ if ( !role_in_remove_list )
+ {
+ new_powers |=
+ current_role->second->getRoleData().mRolePowers;
+ }
+ }
+ }
+ else
+ {
+ //there are no changes for this member
+ //the member's powers are just the ones stored in the group
+ //manager
+ new_powers = member_data->getAgentPowers();
+ }
+
+ return new_powers;
+}
+
+//If there is no change, returns false be sure to verify
+//that there is a role change before attempting to get it or else
+//the data will make no sense. Stores the role change type
+bool LLPanelGroupMembersSubTab::getRoleChangeType(const LLUUID& member_id,
+ const LLUUID& role_id,
+ LLRoleMemberChangeType& type)
+{
+ member_role_change_iter member_changes_iter;
+ role_change_data_map_t::iterator role_changes_iter;
+
+ member_changes_iter = mMemberRoleChangeData.find(member_id);
+ if ( member_changes_iter != mMemberRoleChangeData.end() )
+ {
+ role_changes_iter = member_changes_iter->second->find(role_id);
+ if ( role_changes_iter != member_changes_iter->second->end() )
+ {
+ type = role_changes_iter->second;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void LLPanelGroupMembersSubTab::draw()
+{
+ LLPanelGroupSubTab::draw();
+
+ if (mPendingMemberUpdate)
+ {
+ updateMembers();
+ }
+}
+
+void LLPanelGroupMembersSubTab::update(LLGroupChange gc)
+{
+ if (mGroupID.isNull()) return;
+
+ if ( GC_TITLES == gc || GC_PROPERTIES == gc )
+ {
+ // Don't care about title or general group properties updates.
+ return;
+ }
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::update() -- No group data!" << llendl;
+ return;
+ }
+
+ // Rebuild the members list.
+ mMembersList->deleteAllItems();
+
+ // Wait for both all data to be retrieved before displaying anything.
+ if ( gdatap->isMemberDataComplete()
+ && gdatap->isRoleDataComplete()
+ && gdatap->isRoleMemberDataComplete())
+ {
+ mMemberProgress = gdatap->mMembers.begin();
+ mPendingMemberUpdate = TRUE;
+ mHasMatch = FALSE;
+ }
+ else
+ {
+ // Build a string with info on retrieval progress.
+ std::ostringstream retrieved;
+ if ( !gdatap->isMemberDataComplete() )
+ {
+ // Still busy retreiving member list.
+ retrieved << "Retrieving member list (" << gdatap->mMembers.size()
+ << " / " << gdatap->mMemberCount << ")...";
+ }
+ else if( !gdatap->isRoleDataComplete() )
+ {
+ // Still busy retreiving role list.
+ retrieved << "Retrieving role list (" << gdatap->mRoles.size()
+ << " / " << gdatap->mRoleCount << ")...";
+ }
+ else // (!gdatap->isRoleMemberDataComplete())
+ {
+ // Still busy retreiving role/member mappings.
+ retrieved << "Retrieving role member mappings...";
+ }
+ mMembersList->setEnabled(FALSE);
+ mMembersList->addSimpleItem(retrieved.str());
+ }
+}
+
+void LLPanelGroupMembersSubTab::updateMembers()
+{
+ mPendingMemberUpdate = FALSE;
+
+ lldebugs << "LLPanelGroupMembersSubTab::updateMembers()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::updateMembers() -- No group data!" << llendl;
+ return;
+ }
+
+ // Make sure all data is still complete. Incomplete data
+ // may occur if we refresh.
+ if ( !gdatap->isMemberDataComplete()
+ || !gdatap->isRoleDataComplete()
+ || !gdatap->isRoleMemberDataComplete())
+ {
+ return;
+ }
+
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ S32 i = 0;
+ for( ; mMemberProgress != end && i<UPDATE_MEMBERS_PER_FRAME;
+ ++mMemberProgress, ++i)
+ {
+ if (!mMemberProgress->second)
+ continue;
+ // Do filtering on name if it is already in the cache.
+ bool add_member = true;
+
+ if (gCacheName->getName(mMemberProgress->first, first, last))
+ {
+ if ( !matchesSearchFilter(first, last) )
+ {
+ add_member = false;
+ }
+ }
+
+ if (add_member)
+ {
+ // Build the donated tier string.
+ std::ostringstream donated;
+ donated << mMemberProgress->second->getContribution() << " sq. m.";
+
+ LLSD row;
+ row["id"] = (*mMemberProgress).first;
+
+ row["columns"][0]["column"] = "name";
+ // value is filled in by name list control
+
+ row["columns"][1]["column"] = "donated";
+ row["columns"][1]["value"] = donated.str();
+
+ row["columns"][2]["column"] = "online";
+ row["columns"][2]["value"] = mMemberProgress->second->getOnlineStatus();
+ row["columns"][2]["font"] = "SANSSERIFSMALL";
+
+ mMembersList->addElement(row);//, ADD_SORTED);
+ mHasMatch = TRUE;
+ }
+ }
+
+ if (mMemberProgress == end)
+ {
+ if (mHasMatch)
+ {
+ mMembersList->setEnabled(TRUE);
+ }
+ else
+ {
+ mMembersList->setEnabled(FALSE);
+ mMembersList->addSimpleItem("No match.");
+ }
+ }
+ else
+ {
+ mPendingMemberUpdate = TRUE;
+ }
+
+ // This should clear the other two lists, since nothing is selected.
+ handleMemberSelect();
+}
+
+
+
+////////////////////////////
+// LLPanelGroupRolesSubTab
+////////////////////////////
+
+// static
+void* LLPanelGroupRolesSubTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupRolesSubTab("panel group roles sub tab", *group_id);
+}
+
+LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupSubTab(name, group_id), mHasRoleChange(FALSE)
+{
+}
+
+LLPanelGroupRolesSubTab::~LLPanelGroupRolesSubTab()
+{
+}
+
+BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root)
+{
+ // Upcast parent so we can ask it for sibling controls.
+ LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;
+
+ // Look recursively from the parent to find all our widgets.
+ bool recurse = true;
+ mHeader = (LLPanel*) parent->getChildByName("roles_header", recurse);
+ mFooter = (LLPanel*) parent->getChildByName("roles_footer", recurse);
+
+
+ mRolesList = (LLScrollListCtrl*) parent->getChildByName("role_list", recurse);
+ mAssignedMembersList = (LLNameListCtrl*) parent->getChildByName("role_assigned_members", recurse);
+ mAllowedActionsList = (LLScrollListCtrl*) parent->getChildByName("role_allowed_actions", recurse);
+
+ mRoleName = (LLLineEditor*) parent->getChildByName("role_name", recurse);
+ mRoleTitle = (LLLineEditor*) parent->getChildByName("role_title", recurse);
+ mRoleDescription = (LLTextEditor*) parent->getChildByName("role_description", recurse);
+
+ mMemberVisibleCheck = (LLCheckBoxCtrl*) parent->getChildByName("role_visible_in_list", recurse);
+
+ if (!mRolesList || !mAssignedMembersList || !mAllowedActionsList
+ || !mRoleName || !mRoleTitle || !mRoleDescription || !mMemberVisibleCheck)
+ {
+ llwarns << "ARG! element not found." << llendl;
+ return FALSE;
+ }
+
+ LLTextBox* txt = (LLTextBox*) parent->getChildByName("cant_delete_role", FALSE);
+ if (txt)
+ {
+ mRemoveEveryoneTxt = txt->getText();
+ parent->removeChild(txt);
+ }
+
+ mCreateRoleButton =
+ (LLButton*) parent->getChildByName("role_create", recurse);
+ if ( mCreateRoleButton )
+ {
+ mCreateRoleButton->setCallbackUserData(this);
+ mCreateRoleButton->setClickedCallback(onCreateRole);
+ mCreateRoleButton->setEnabled(FALSE);
+ }
+
+ mDeleteRoleButton =
+ (LLButton*) parent->getChildByName("role_delete", recurse);
+ if ( mDeleteRoleButton )
+ {
+ mDeleteRoleButton->setCallbackUserData(this);
+ mDeleteRoleButton->setClickedCallback(onDeleteRole);
+ mDeleteRoleButton->setEnabled(FALSE);
+ }
+
+ mRolesList->setCommitOnSelectionChange(TRUE);
+ mRolesList->setCallbackUserData(this);
+ mRolesList->setCommitCallback(onRoleSelect);
+
+ mMemberVisibleCheck->setCallbackUserData(this);
+ mMemberVisibleCheck->setCommitCallback(onMemberVisibilityChange);
+
+ mAllowedActionsList->setCommitOnSelectionChange(TRUE);
+ mAllowedActionsList->setCallbackUserData(this);
+
+ mRoleName->setCommitOnFocusLost(TRUE);
+ mRoleName->setCallbackUserData(this);
+ mRoleName->setKeystrokeCallback(onPropertiesKey);
+
+ mRoleTitle->setCommitOnFocusLost(TRUE);
+ mRoleTitle->setCallbackUserData(this);
+ mRoleTitle->setKeystrokeCallback(onPropertiesKey);
+
+ mRoleDescription->setCommitOnFocusLost(TRUE);
+ mRoleDescription->setCallbackUserData(this);
+ mRoleDescription->setCommitCallback(onDescriptionCommit);
+ mRoleDescription->setFocusReceivedCallback(onDescriptionCommit);
+
+ setFooterEnabled(FALSE);
+
+ return TRUE;
+}
+
+void LLPanelGroupRolesSubTab::activate()
+{
+ LLPanelGroupSubTab::activate();
+
+ mRolesList->deselectAllItems();
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+ mRoleName->clear();
+ mRoleDescription->clear();
+ mRoleTitle->clear();
+
+ setFooterEnabled(FALSE);
+
+ mHasRoleChange = FALSE;
+ update(GC_ALL);
+}
+
+void LLPanelGroupRolesSubTab::deactivate()
+{
+ lldebugs << "LLPanelGroupRolesSubTab::deactivate()" << llendl;
+
+ LLPanelGroupSubTab::deactivate();
+}
+
+bool LLPanelGroupRolesSubTab::needsApply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::needsApply()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ return (mHasRoleChange // Text changed in current role
+ || (gdatap && gdatap->pendingRoleChanges())); // Pending role changes in the group
+}
+
+bool LLPanelGroupRolesSubTab::apply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::apply()" << llendl;
+
+ saveRoleChanges();
+ gGroupMgr->sendGroupRoleChanges(mGroupID);
+
+ notifyObservers();
+
+ return true;
+}
+
+void LLPanelGroupRolesSubTab::cancel()
+{
+ mHasRoleChange = FALSE;
+ gGroupMgr->cancelGroupRoleChanges(mGroupID);
+
+ notifyObservers();
+}
+
+LLSD LLPanelGroupRolesSubTab::createRoleItem(const LLUUID& role_id,
+ std::string name,
+ std::string title,
+ S32 members)
+{
+ LLSD row;
+ row["id"] = role_id;
+
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["value"] = name;
+
+ row["columns"][1]["column"] = "title";
+ row["columns"][1]["value"] = title;
+
+ row["columns"][2]["column"] = "members";
+ row["columns"][2]["value"] = members;
+
+ return row;
+}
+
+bool LLPanelGroupRolesSubTab::matchesSearchFilter(std::string rolename, std::string roletitle)
+{
+ // If the search filter is empty, everything passes.
+ if (mSearchFilter.empty()) return true;
+
+ LLString::toLower(rolename);
+ LLString::toLower(roletitle);
+ std::string::size_type match_name = rolename.find(mSearchFilter);
+ std::string::size_type match_title = roletitle.find(mSearchFilter);
+
+ if ( (std::string::npos == match_name)
+ && (std::string::npos == match_title))
+ {
+ // not found
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void LLPanelGroupRolesSubTab::update(LLGroupChange gc)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::update()" << llendl;
+
+ if (mGroupID.isNull()) return;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap || !gdatap->isRoleDataComplete())
+ {
+ gGroupMgr->sendGroupRoleDataRequest(mGroupID);
+ }
+ else
+ {
+ bool had_selection = false;
+ LLUUID last_selected;
+ if (mRolesList->getFirstSelected())
+ {
+ last_selected = mRolesList->getFirstSelected()->getUUID();
+ had_selection = true;
+ }
+ mRolesList->deleteAllItems();
+
+ LLScrollListItem* item = NULL;
+
+ LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+
+ for ( ; rit != end; ++rit)
+ {
+ LLRoleData rd;
+ if (gdatap->getRoleData((*rit).first,rd))
+ {
+ if (matchesSearchFilter(rd.mRoleName, rd.mRoleTitle))
+ {
+ // If this is the everyone role, then EVERYONE is in it.
+ S32 members_in_role = (*rit).first.isNull() ? gdatap->mMembers.size() : (*rit).second->getTotalMembersInRole();
+ LLSD row = createRoleItem((*rit).first,rd.mRoleName, rd.mRoleTitle, members_in_role);
+ item = mRolesList->addElement(row, ((*rit).first.isNull()) ? ADD_TOP : ADD_BOTTOM, this);
+ if (had_selection && ((*rit).first == last_selected))
+ {
+ item->setSelected(TRUE);
+ }
+ }
+ }
+ else
+ {
+ llwarns << "LLPanelGroupRolesSubTab::update() No role data for role " << (*rit).first << llendl;
+ }
+ }
+
+ mRolesList->sortByColumn("name", TRUE);
+
+ if ( (gdatap->mRoles.size() < MAX_ROLES)
+ && gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CREATE) )
+ {
+ mCreateRoleButton->setEnabled(TRUE);
+ }
+ else
+ {
+ mCreateRoleButton->setEnabled(FALSE);
+ }
+
+ if (had_selection)
+ {
+ handleRoleSelect();
+ }
+ else
+ {
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+ mRoleName->clear();
+ mRoleDescription->clear();
+ mRoleTitle->clear();
+ setFooterEnabled(FALSE);
+ mDeleteRoleButton->setEnabled(FALSE);
+ }
+ }
+
+ if (!gdatap || !gdatap->isMemberDataComplete())
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ if (!gdatap || !gdatap->isRoleMemberDataComplete())
+ {
+ gGroupMgr->sendGroupRoleMembersRequest(mGroupID);
+ }
+
+ if ((GC_ROLE_MEMBER_DATA == gc || GC_MEMBER_DATA == gc)
+ && gdatap->isMemberDataComplete()
+ && gdatap->isRoleMemberDataComplete())
+ {
+ buildMembersList();
+ }
+}
+
+// static
+void LLPanelGroupRolesSubTab::onRoleSelect(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ self->handleRoleSelect();
+}
+
+void LLPanelGroupRolesSubTab::handleRoleSelect()
+{
+ BOOL can_delete = TRUE;
+ lldebugs << "LLPanelGroupRolesSubTab::handleRoleSelect()" << llendl;
+
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ saveRoleChanges();
+
+ // Check if there is anything selected.
+ LLScrollListItem* item = mRolesList->getFirstSelected();
+ if (!item)
+ {
+ setFooterEnabled(FALSE);
+ return;
+ }
+
+ setFooterEnabled(TRUE);
+
+ LLRoleData rd;
+ if (gdatap->getRoleData(item->getUUID(),rd))
+ {
+ BOOL is_owner_role = ( gdatap->mOwnerRole == item->getUUID() );
+ mRoleName->setText(rd.mRoleName);
+ mRoleTitle->setText(rd.mRoleTitle);
+ mRoleDescription->setText(rd.mRoleDescription);
+
+ mAllowedActionsList->setEnabled(gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_CHANGE_ACTIONS));
+ buildActionsList(mAllowedActionsList,
+ rd.mRolePowers,
+ 0LL,
+ mActionIcons,
+ onActionCheck,
+ TRUE,
+ FALSE,
+ is_owner_role);
+
+
+ mMemberVisibleCheck->set((rd.mRolePowers & GP_MEMBER_VISIBLE_IN_DIR) == GP_MEMBER_VISIBLE_IN_DIR);
+ mRoleName->setEnabled(!is_owner_role &&
+ gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+ mRoleTitle->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+ mRoleDescription->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+ mMemberVisibleCheck->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+
+ if (item->getUUID().isNull())
+ {
+ // Everyone role, can't edit description or name or delete
+ mRoleDescription->setEnabled(FALSE);
+ mRoleName->setEnabled(FALSE);
+ can_delete = FALSE;
+ }
+ //you can't delete the owner role
+ if ( is_owner_role ) can_delete = FALSE;
+ }
+ else
+ {
+ mRolesList->deselectAllItems();
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+ mRoleName->clear();
+ mRoleDescription->clear();
+ mRoleTitle->clear();
+ setFooterEnabled(FALSE);
+
+ can_delete = FALSE;
+ }
+ mSelectedRole = item->getUUID();
+ buildMembersList();
+
+ can_delete = can_delete && gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_DELETE);
+ mDeleteRoleButton->setEnabled(can_delete);
+}
+
+void LLPanelGroupRolesSubTab::buildMembersList()
+{
+ mAssignedMembersList->deleteAllItems();
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ // Check if there is anything selected.
+ LLScrollListItem* item = mRolesList->getFirstSelected();
+ if (!item) return;
+
+ if (item->getUUID().isNull())
+ {
+ // Special cased 'Everyone' role
+ LLGroupMgrGroupData::member_iter mit = gdatap->mMembers.begin();
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+ for ( ; mit != end; ++mit)
+ {
+ mAssignedMembersList->addNameItem((*mit).first);
+ }
+ }
+ else
+ {
+ LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.find(item->getUUID());
+ if (rit != gdatap->mRoles.end())
+ {
+ LLGroupRoleData* rdatap = (*rit).second;
+ if (rdatap)
+ {
+ std::vector<LLUUID>::const_iterator mit = rdatap->getMembersBegin();
+ std::vector<LLUUID>::const_iterator end = rdatap->getMembersEnd();
+ for ( ; mit != end; ++mit)
+ {
+ mAssignedMembersList->addNameItem((*mit));
+ }
+ }
+ }
+ }
+}
+
+// static
+void LLPanelGroupRolesSubTab::onActionCheck(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ LLCheckBoxCtrl* check = static_cast<LLCheckBoxCtrl*>(ctrl);
+ if (!check || !self) return;
+
+ self->handleActionCheck(check);
+}
+
+struct ActionCBData
+{
+ LLPanelGroupRolesSubTab* mSelf;
+ LLCheckBoxCtrl* mCheck;
+};
+
+void LLPanelGroupRolesSubTab::handleActionCheck(LLCheckBoxCtrl* check, bool force)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::handleActionSelect()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ LLScrollListItem* action_item = mAllowedActionsList->getFirstSelected();
+ if (!action_item)
+ {
+ return;
+ }
+
+ LLScrollListItem* role_item = mRolesList->getFirstSelected();
+ if (!role_item)
+ {
+ return;
+ }
+ LLUUID role_id = role_item->getUUID();
+
+ LLRoleAction* rap = (LLRoleAction*)action_item->getUserdata();
+ U64 power = rap->mPowerBit;
+
+ if (check->get())
+ {
+ if (!force && ( (GP_ROLE_ASSIGN_MEMBER == power)
+ || (GP_ROLE_CHANGE_ACTIONS == power) ))
+ {
+ // Uncheck the item, for now. It will be
+ // checked if they click 'Yes', below.
+ check->set(FALSE);
+
+ LLRoleData rd;
+ LLStringBase<char>::format_map_t args;
+
+ if ( gdatap->getRoleData(role_id, rd) )
+ {
+ args["[ACTION_NAME]"] = rap->mDescription;
+ args["[ROLE_NAME]"] = rd.mRoleName;
+ struct ActionCBData* cb_data = new ActionCBData;
+ cb_data->mSelf = this;
+ cb_data->mCheck = check;
+ mHasModal = TRUE;
+ LLString warning = "AssignDangerousActionWarning";
+ if (GP_ROLE_CHANGE_ACTIONS == power)
+ {
+ warning = "AssignDangerousAbilityWarning";
+ }
+ gViewerWindow->alertXml(warning, args, addActionCB, cb_data);
+ }
+ else
+ {
+ llwarns << "Unable to look up role information for role id: "
+ << role_id << llendl;
+ }
+ }
+ else
+ {
+ gdatap->addRolePower(role_id,power);
+ }
+ }
+ else
+ {
+ gdatap->removeRolePower(role_id,power);
+ }
+
+ mHasRoleChange = TRUE;
+ notifyObservers();
+}
+
+//static
+void LLPanelGroupRolesSubTab::addActionCB(S32 option, void* data)
+{
+ struct ActionCBData* cb_data = (struct ActionCBData*) data;
+
+ if (!cb_data || !cb_data->mSelf || !cb_data->mCheck) return;
+
+ cb_data->mSelf->mHasModal = FALSE;
+
+ if (0 == option)
+ {
+ // User clicked "Yes"
+ cb_data->mCheck->set(TRUE);
+ const bool force_add = true;
+ cb_data->mSelf->handleActionCheck(cb_data->mCheck, force_add);
+ }
+}
+
+
+// static
+void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->mHasRoleChange = TRUE;
+ self->notifyObservers();
+}
+
+// static
+void LLPanelGroupRolesSubTab::onDescriptionCommit(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->mHasRoleChange = TRUE;
+ self->notifyObservers();
+}
+
+// static
+void LLPanelGroupRolesSubTab::onMemberVisibilityChange(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ LLCheckBoxCtrl* check = static_cast<LLCheckBoxCtrl*>(ctrl);
+ if (!check || !self) return;
+
+ self->handleMemberVisibilityChange(check->get());
+}
+
+void LLPanelGroupRolesSubTab::handleMemberVisibilityChange(bool value)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::handleMemberVisibilityChange()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ LLScrollListItem* role_item = mRolesList->getFirstSelected();
+ if (!role_item)
+ {
+ return;
+ }
+
+ if (value)
+ {
+ gdatap->addRolePower(role_item->getUUID(),GP_MEMBER_VISIBLE_IN_DIR);
+ }
+ else
+ {
+ gdatap->removeRolePower(role_item->getUUID(),GP_MEMBER_VISIBLE_IN_DIR);
+ }
+}
+
+// static
+void LLPanelGroupRolesSubTab::onCreateRole(void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->handleCreateRole();
+}
+
+void LLPanelGroupRolesSubTab::handleCreateRole()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ LLUUID new_role_id;
+ new_role_id.generate();
+
+ LLRoleData rd;
+ rd.mRoleName = "New Role";
+ gdatap->createRole(new_role_id,rd);
+
+ mRolesList->deselectAllItems(TRUE);
+ LLSD row;
+ row["id"] = new_role_id;
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["value"] = rd.mRoleName;
+ mRolesList->addElement(row, ADD_BOTTOM, this);
+ mRolesList->selectByID(new_role_id);
+
+ // put focus on name field and select its contents
+ if(mRoleName)
+ {
+ mRoleName->setFocus(TRUE);
+ mRoleName->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+
+ notifyObservers();
+}
+
+// static
+void LLPanelGroupRolesSubTab::onDeleteRole(void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->handleDeleteRole();
+}
+
+void LLPanelGroupRolesSubTab::handleDeleteRole()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ LLScrollListItem* role_item = mRolesList->getFirstSelected();
+ if (!role_item)
+ {
+ return;
+ }
+
+ if (role_item->getUUID().isNull() || role_item->getUUID() == gdatap->mOwnerRole)
+ {
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = mRemoveEveryoneTxt;
+ LLNotifyBox::showXml("GenericNotify", args);
+ return;
+ }
+
+ gdatap->deleteRole(role_item->getUUID());
+ mRolesList->deleteSingleItem(mRolesList->getFirstSelectedIndex());
+ mRolesList->selectFirstItem();
+
+ notifyObservers();
+}
+
+void LLPanelGroupRolesSubTab::saveRoleChanges()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ if (mHasRoleChange)
+ {
+ LLRoleData rd;
+ if (!gdatap->getRoleData(mSelectedRole,rd)) return;
+
+ rd.mRoleName = mRoleName->getText();
+ rd.mRoleDescription = mRoleDescription->getText();
+ rd.mRoleTitle = mRoleTitle->getText();
+
+ gdatap->setRoleData(mSelectedRole,rd);
+
+ mRolesList->deleteSingleItem(mRolesList->getItemIndex(mSelectedRole));
+
+ LLSD row = createRoleItem(mSelectedRole,rd.mRoleName,rd.mRoleTitle,0);
+ LLScrollListItem* item = mRolesList->addElement(row, ADD_BOTTOM, this);
+ item->setSelected(TRUE);
+
+ mHasRoleChange = FALSE;
+ }
+}
+////////////////////////////
+// LLPanelGroupActionsSubTab
+////////////////////////////
+
+// static
+void* LLPanelGroupActionsSubTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupActionsSubTab("panel group actions sub tab", *group_id);
+}
+
+LLPanelGroupActionsSubTab::LLPanelGroupActionsSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupSubTab(name, group_id)
+{
+}
+
+LLPanelGroupActionsSubTab::~LLPanelGroupActionsSubTab()
+{
+}
+
+BOOL LLPanelGroupActionsSubTab::postBuildSubTab(LLView* root)
+{
+ // Upcast parent so we can ask it for sibling controls.
+ LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;
+
+ // Look recursively from the parent to find all our widgets.
+ bool recurse = true;
+ mHeader = (LLPanel*) parent->getChildByName("actions_header", recurse);
+ mFooter = (LLPanel*) parent->getChildByName("actions_footer", recurse);
+
+ mActionDescription = (LLTextEditor*) parent->getChildByName("action_description", recurse);
+
+ mActionList = (LLScrollListCtrl*) parent->getChildByName("action_list",recurse);
+ mActionRoles = (LLScrollListCtrl*) parent->getChildByName("action_roles",recurse);
+ mActionMembers = (LLNameListCtrl*) parent->getChildByName("action_members",recurse);
+
+ if (!mActionList || !mActionDescription || !mActionRoles || !mActionMembers) return FALSE;
+
+ mActionList->setCallbackUserData(this);
+ mActionList->setCommitOnSelectionChange(TRUE);
+ mActionList->setCommitCallback(onActionSelect);
+
+ mActionMembers->setCallbackUserData(this);
+ mActionRoles->setCallbackUserData(this);
+
+ update(GC_ALL);
+
+ return TRUE;
+}
+
+void LLPanelGroupActionsSubTab::activate()
+{
+ LLPanelGroupSubTab::activate();
+ lldebugs << "LLPanelGroupActionsSubTab::activate()" << llendl;
+
+ mActionList->deselectAllItems();
+ mActionMembers->deleteAllItems();
+ mActionRoles->deleteAllItems();
+ mActionDescription->clear();
+}
+
+void LLPanelGroupActionsSubTab::deactivate()
+{
+ lldebugs << "LLPanelGroupActionsSubTab::deactivate()" << llendl;
+
+ LLPanelGroupSubTab::deactivate();
+}
+
+bool LLPanelGroupActionsSubTab::needsApply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupActionsSubTab::needsApply()" << llendl;
+
+ return false;
+}
+
+bool LLPanelGroupActionsSubTab::apply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupActionsSubTab::apply()" << llendl;
+ return true;
+}
+
+void LLPanelGroupActionsSubTab::update(LLGroupChange gc)
+{
+ lldebugs << "LLPanelGroupActionsSubTab::update()" << llendl;
+
+ if (mGroupID.isNull()) return;
+
+ mActionList->deselectAllItems();
+ mActionMembers->deleteAllItems();
+ mActionRoles->deleteAllItems();
+ mActionDescription->clear();
+
+ mActionList->deleteAllItems();
+ buildActionsList(mActionList,
+ GP_ALL_POWERS,
+ GP_ALL_POWERS,
+ mActionIcons,
+ NULL,
+ FALSE,
+ TRUE,
+ FALSE);
+}
+
+// static
+void LLPanelGroupActionsSubTab::onActionSelect(LLUICtrl* scroll, void* data)
+{
+ LLPanelGroupActionsSubTab* self = static_cast<LLPanelGroupActionsSubTab*>(data);
+ self->handleActionSelect();
+}
+
+void LLPanelGroupActionsSubTab::handleActionSelect()
+{
+ mActionMembers->deleteAllItems();
+ mActionRoles->deleteAllItems();
+
+ U64 power_mask = GP_NO_POWERS;
+ std::vector<LLScrollListItem*> selection =
+ mActionList->getAllSelected();
+ if (selection.empty()) return;
+
+ LLRoleAction* rap;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
+ {
+ rap = (LLRoleAction*)( (*itor)->getUserdata() );
+ power_mask |= rap->mPowerBit;
+ }
+
+ if (selection.size() == 1)
+ {
+ LLScrollListItem* item = selection[0];
+ rap = (LLRoleAction*)(item->getUserdata());
+
+ if (rap->mLongDescription.empty())
+ {
+ mActionDescription->setText(rap->mDescription);
+ }
+ else
+ {
+ mActionDescription->setText(rap->mLongDescription);
+ }
+ }
+ else
+ {
+ mActionDescription->clear();
+ }
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ if (gdatap->isMemberDataComplete())
+ {
+ LLGroupMgrGroupData::member_iter it = gdatap->mMembers.begin();
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+ LLGroupMemberData* gmd;
+
+ for ( ; it != end; ++it)
+ {
+ gmd = (*it).second;
+ if (!gmd) continue;
+ if ((gmd->getAgentPowers() & power_mask) == power_mask)
+ {
+ mActionMembers->addNameItem(gmd->getID());
+ }
+ }
+ }
+ else
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ if (gdatap->isRoleDataComplete())
+ {
+ LLGroupMgrGroupData::role_iter it = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+ LLGroupRoleData* rmd;
+
+ for ( ; it != end; ++it)
+ {
+ rmd = (*it).second;
+ if (!rmd) continue;
+ if ((rmd->getRoleData().mRolePowers & power_mask) == power_mask)
+ {
+ mActionRoles->addSimpleItem(rmd->getRoleData().mRoleName);
+ }
+ }
+ }
+ else
+ {
+ gGroupMgr->sendGroupRoleDataRequest(mGroupID);
+ }
+}
diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h
new file mode 100644
index 0000000000..58a81b1daa
--- /dev/null
+++ b/indra/newview/llpanelgrouproles.h
@@ -0,0 +1,297 @@
+/**
+ * @file llpanelgrouproles.h
+ * @brief Panel for roles information about a particular group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPROLES_H
+#define LL_LLPANELGROUPROLES_H
+
+#include "llpanelgroup.h"
+
+class LLNameListCtrl;
+class LLPanelGroupSubTab;
+class LLPanelGroupMembersSubTab;
+class LLPanelGroupRolesSubTab;
+class LLPanelGroupActionsSubTab;
+class LLScrollListCtrl;
+class LLScrollListItem;
+
+// Forward declare for friend usage.
+//virtual BOOL LLPanelGroupSubTab::postBuildSubTab(LLView*);
+
+typedef std::map<std::string,LLUUID> icon_map_t;
+
+class LLPanelGroupRoles : public LLPanelGroupTab,
+ public LLPanelGroupTabObserver
+{
+public:
+ LLPanelGroupRoles(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupRoles();
+
+ // Allow sub tabs to ask for sibling controls.
+ friend class LLPanelGroupMembersSubTab;
+ friend class LLPanelGroupRolesSubTab;
+ friend class LLPanelGroupActionsSubTab;
+
+ virtual BOOL postBuild();
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ static void* createTab(void* data);
+ static void onClickSubTab(void*,bool);
+ void handleClickSubTab();
+
+ // Checks if the current tab needs to be applied, and tries to switch to the requested tab.
+ BOOL attemptTransition();
+
+ // Switches to the requested tab (will close() if requested is NULL)
+ void transitionToTab();
+
+ // Used by attemptTransition to query the user's response to a tab that needs to apply.
+ static void onNotifyCallback(S32 option, void* user_data);
+ void handleNotifyCallback(S32 option);
+ static void onModalClose(S32 option, void* user_data);
+
+ // Most of these messages are just passed on to the current sub-tab.
+ virtual LLString getHelpText() const;
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool needsApply(LLString& mesg);
+ virtual BOOL hasModal();
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ virtual void update(LLGroupChange gc);
+
+ // PanelGroupTab observer trigger
+ virtual void tabChanged();
+
+protected:
+ LLPanelGroupTab* mCurrentTab;
+ LLPanelGroupTab* mRequestedTab;
+ LLTabContainerCommon* mSubTabContainer;
+ BOOL mFirstUse;
+ BOOL mIgnoreTransition;
+
+ LLString mDefaultNeedsApplyMesg;
+ LLString mWantApplyMesg;
+};
+
+class LLPanelGroupSubTab : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupSubTab();
+
+ virtual BOOL postBuild();
+
+ // This allows sub-tabs to collect child widgets from a higher level in the view hierarchy.
+ virtual BOOL postBuildSubTab(LLView* root) { return TRUE; }
+
+ static void onSearchKeystroke(LLLineEditor* caller, void* user_data);
+ void handleSearchKeystroke(LLLineEditor* caller);
+
+ static void onClickSearch(void*);
+ void handleClickSearch();
+ static void onClickShowAll(void*);
+ void handleClickShowAll();
+
+ virtual void setSearchFilter( const LLString& filter );
+
+ virtual void activate();
+ virtual void deactivate();
+
+ // Helper functions
+ bool matchesActionSearchFilter(std::string action);
+ void buildActionsList(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role);
+ void buildActionCategory(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ LLRoleActionSet* action_set,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role);
+
+ void setFooterEnabled(BOOL enable);
+protected:
+ LLPanel* mHeader;
+ LLPanel* mFooter;
+
+ LLLineEditor* mSearchLineEditor;
+ LLButton* mSearchButton;
+ LLButton* mShowAllButton;
+
+ LLString mSearchFilter;
+
+ icon_map_t mActionIcons;
+
+ void setOthersVisible(BOOL b);
+};
+
+class LLPanelGroupMembersSubTab : public LLPanelGroupSubTab
+{
+public:
+ LLPanelGroupMembersSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupMembersSubTab();
+
+ virtual BOOL postBuildSubTab(LLView* root);
+
+ static void* createTab(void* data);
+
+ static void onMemberSelect(LLUICtrl*, void*);
+ void handleMemberSelect();
+
+ static void onMemberDoubleClick(void*);
+ void handleMemberDoubleClick();
+
+ static void onInviteMember(void*);
+ void handleInviteMember();
+
+ static void onEjectMembers(void*);
+ void handleEjectMembers();
+
+ static void onRoleCheck(LLUICtrl* check, void* user_data);
+ void handleRoleCheck(const LLUUID& role_id,
+ LLRoleMemberChangeType type);
+
+ void applyMemberChanges();
+ static void addOwnerCB(S32 option, void* data);
+
+ virtual void activate();
+ virtual void deactivate();
+ virtual void cancel();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void update(LLGroupChange gc);
+ void updateMembers();
+
+ virtual void draw();
+
+protected:
+ typedef std::map<LLUUID, LLRoleMemberChangeType> role_change_data_map_t;
+ typedef std::map<LLUUID, role_change_data_map_t*>::iterator member_role_change_iter;
+ typedef std::map<LLUUID, role_change_data_map_t*> member_role_changes_map_t;
+
+ bool matchesSearchFilter(char* first, char* last);
+
+ U64 getAgentPowersBasedOnRoleChanges(const LLUUID& agent_id);
+ bool getRoleChangeType(const LLUUID& member_id,
+ const LLUUID& role_id,
+ LLRoleMemberChangeType& type);
+
+ LLNameListCtrl* mMembersList;
+ LLScrollListCtrl* mAssignedRolesList;
+ LLScrollListCtrl* mAllowedActionsList;
+ LLButton* mEjectBtn;
+
+ BOOL mChanged;
+ BOOL mPendingMemberUpdate;
+ BOOL mHasMatch;
+
+ member_role_changes_map_t mMemberRoleChangeData;
+ U32 mNumOwnerAdditions;
+
+ LLGroupMgrGroupData::member_iter mMemberProgress;
+};
+
+class LLPanelGroupRolesSubTab : public LLPanelGroupSubTab
+{
+public:
+ LLPanelGroupRolesSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupRolesSubTab();
+
+ virtual BOOL postBuildSubTab(LLView* root);
+
+ static void* createTab(void* data);
+
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ bool matchesSearchFilter(std::string rolename, std::string roletitle);
+ virtual void update(LLGroupChange gc);
+
+ static void onRoleSelect(LLUICtrl*, void*);
+ void handleRoleSelect();
+ void buildMembersList();
+
+ static void onActionCheck(LLUICtrl*, void*);
+ void handleActionCheck(LLCheckBoxCtrl*, bool force=false);
+ static void addActionCB(S32 option, void* data);
+
+ static void onPropertiesKey(LLLineEditor*, void*);
+
+ static void onDescriptionCommit(LLUICtrl*, void*);
+
+ static void onMemberVisibilityChange(LLUICtrl*, void*);
+ void handleMemberVisibilityChange(bool value);
+
+ static void onCreateRole(void*);
+ void handleCreateRole();
+
+ static void onDeleteRole(void*);
+ void handleDeleteRole();
+
+ void saveRoleChanges();
+protected:
+ LLSD createRoleItem(const LLUUID& role_id,
+ std::string name,
+ std::string title,
+ S32 members);
+
+ LLScrollListCtrl* mRolesList;
+ LLNameListCtrl* mAssignedMembersList;
+ LLScrollListCtrl* mAllowedActionsList;
+
+ LLLineEditor* mRoleName;
+ LLLineEditor* mRoleTitle;
+ LLTextEditor* mRoleDescription;
+
+ LLCheckBoxCtrl* mMemberVisibleCheck;
+ LLButton* mDeleteRoleButton;
+ LLButton* mCreateRoleButton;
+
+ LLUUID mSelectedRole;
+ BOOL mHasRoleChange;
+ std::string mRemoveEveryoneTxt;
+};
+
+class LLPanelGroupActionsSubTab : public LLPanelGroupSubTab
+{
+public:
+ LLPanelGroupActionsSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupActionsSubTab();
+
+ virtual BOOL postBuildSubTab(LLView* root);
+
+ static void* createTab(void* data);
+
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void update(LLGroupChange gc);
+
+ static void onActionSelect(LLUICtrl*, void*);
+ void handleActionSelect();
+protected:
+ LLScrollListCtrl* mActionList;
+ LLScrollListCtrl* mActionRoles;
+ LLNameListCtrl* mActionMembers;
+
+ LLTextEditor* mActionDescription;
+};
+
+
+#endif // LL_LLPANELGROUPROLES_H
diff --git a/indra/newview/llpanelland.cpp b/indra/newview/llpanelland.cpp
new file mode 100644
index 0000000000..1a8885d05b
--- /dev/null
+++ b/indra/newview/llpanelland.cpp
@@ -0,0 +1,236 @@
+/**
+ * @file llpanelland.cpp
+ * @brief Land information in the tool floater, NOT the "About Land" floater
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <time.h>
+
+#include "llpanelland.h"
+
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloaterland.h"
+#include "lltextbox.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "roles_constants.h"
+
+#include "llvieweruictrlfactory.h"
+
+LLPanelLandSelectObserver* LLPanelLandInfo::sObserver = NULL;
+LLPanelLandInfo* LLPanelLandInfo::sInstance = NULL;
+
+class LLPanelLandSelectObserver : public LLParcelObserver
+{
+public:
+ LLPanelLandSelectObserver() {}
+ virtual ~LLPanelLandSelectObserver() {}
+ virtual void changed() { LLPanelLandInfo::refreshAll(); }
+};
+
+
+BOOL LLPanelLandInfo::postBuild()
+{
+
+ childSetAction("button buy land",onClickClaim,this);
+ childSetAction("button abandon land",onClickRelease,this);
+ childSetAction("button subdivide land",onClickDivide,this);
+ childSetAction("button join land",onClickJoin,this);
+ childSetAction("button about land",onClickAbout,this);
+
+ return TRUE;
+}
+//
+// Methods
+//
+LLPanelLandInfo::LLPanelLandInfo(const std::string& name)
+: LLPanel(name)
+{
+ if (!sInstance)
+ {
+ sInstance = this;
+ }
+ if (!sObserver)
+ {
+ sObserver = new LLPanelLandSelectObserver();
+ gParcelMgr->addObserver( sObserver );
+ }
+
+}
+
+
+// virtual
+LLPanelLandInfo::~LLPanelLandInfo()
+{
+ gParcelMgr->removeObserver( sObserver );
+ delete sObserver;
+ sObserver = NULL;
+
+ sInstance = NULL;
+}
+
+
+// static
+void LLPanelLandInfo::refreshAll()
+{
+ if (sInstance)
+ {
+ sInstance->refresh();
+ }
+}
+
+
+// public
+void LLPanelLandInfo::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ LLViewerRegion *regionp = gParcelMgr->getSelectionRegion();
+
+ if (!parcel || !regionp)
+ {
+ // nothing selected, disable panel
+ childSetVisible("label_area_price",false);
+ childSetVisible("label_area",false);
+
+ //mTextPrice->setText("");
+ childSetText("textbox price","");
+
+ childSetEnabled("button buy land",FALSE);
+ childSetEnabled("button abandon land",FALSE);
+ childSetEnabled("button subdivide land",FALSE);
+ childSetEnabled("button join land",FALSE);
+ childSetEnabled("button about land",FALSE);
+ }
+ else
+ {
+ // something selected, hooray!
+ const LLUUID& owner_id = parcel->getOwnerID();
+ const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID();
+
+ BOOL is_public = parcel->isPublic();
+ BOOL is_for_sale = parcel->getForSale()
+ && ((parcel->getSalePrice() > 0) || (auth_buyer_id.notNull()));
+ BOOL can_buy = (is_for_sale
+ && (owner_id != gAgent.getID())
+ && ((gAgent.getID() == auth_buyer_id)
+ || (auth_buyer_id.isNull())));
+
+ if (is_public)
+ {
+ childSetEnabled("button buy land",TRUE);
+ }
+ else
+ {
+ childSetEnabled("button buy land",can_buy);
+ }
+
+ BOOL owner_release = LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_RELEASE);
+ BOOL owner_divide = LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_DIVIDE_JOIN);
+
+ BOOL manager_releaseable = ( gAgent.canManageEstate()
+ && (parcel->getOwnerID() == regionp->getOwner()) );
+
+ BOOL manager_divideable = ( gAgent.canManageEstate()
+ && ((parcel->getOwnerID() == regionp->getOwner()) || owner_divide) );
+
+ childSetEnabled("button abandon land",owner_release || manager_releaseable || gAgent.isGodlike());
+
+ // only mainland sims are subdividable by owner
+ if (regionp->getRegionFlags() && REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ childSetEnabled("button subdivide land",owner_divide || manager_divideable || gAgent.isGodlike());
+ }
+ else
+ {
+ childSetEnabled("button subdivide land",manager_divideable || gAgent.isGodlike());
+ }
+
+ // To join land, must have something selected,
+ // not just a single unit of land,
+ // you must own part of it,
+ // and it must not be a whole parcel.
+ if (gParcelMgr->getSelectedArea() > PARCEL_UNIT_AREA
+ //&& gParcelMgr->getSelfCount() > 1
+ && !gParcelMgr->getWholeParcelSelected())
+ {
+ childSetEnabled("button join land",TRUE);
+ }
+ else
+ {
+ lldebugs << "Invalid selection for joining land" << llendl;
+ childSetEnabled("button join land",FALSE);
+ }
+
+ childSetEnabled("button about land",TRUE);
+
+ // show pricing information
+ S32 area;
+ S32 claim_price;
+ S32 rent_price;
+ BOOL for_sale;
+ F32 dwell;
+ gParcelMgr->getDisplayInfo(&area,
+ &claim_price,
+ &rent_price,
+ &for_sale,
+ &dwell);
+ if(is_public || (is_for_sale && gParcelMgr->getWholeParcelSelected()))
+ {
+ childSetTextArg("label_area_price","[PRICE]", llformat("%d",claim_price));
+ childSetTextArg("label_area_price","[AREA]", llformat("%d",area));
+ childSetVisible("label_area_price",true);
+ childSetVisible("label_area",false);
+ }
+ else
+ {
+ childSetVisible("label_area_price",false);
+ childSetTextArg("label_area","[AREA]", llformat("%d",area));
+ childSetVisible("label_area",true);
+ }
+ }
+}
+
+
+//static
+void LLPanelLandInfo::onClickClaim(void*)
+{
+ gParcelMgr->startBuyLand();
+}
+
+
+//static
+void LLPanelLandInfo::onClickRelease(void*)
+{
+ gParcelMgr->startReleaseLand();
+}
+
+// static
+void LLPanelLandInfo::onClickDivide(void*)
+{
+ gParcelMgr->startDivideLand();
+}
+
+// static
+void LLPanelLandInfo::onClickJoin(void*)
+{
+ gParcelMgr->startJoinLand();
+}
+
+//static
+void LLPanelLandInfo::onClickAbout(void*)
+{
+ // Promote the rectangle selection to a parcel selection
+ if (!gParcelMgr->getWholeParcelSelected())
+ {
+ gParcelMgr->selectParcelInRectangle();
+ }
+
+ LLFloaterLand::show();
+}
diff --git a/indra/newview/llpanelland.h b/indra/newview/llpanelland.h
new file mode 100644
index 0000000000..aee3e65329
--- /dev/null
+++ b/indra/newview/llpanelland.h
@@ -0,0 +1,54 @@
+/**
+ * @file llpanelland.h
+ * @brief Land information in the tool floater, NOT the "About Land" floater
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELLAND_H
+#define LL_LLPANELLAND_H
+
+#include "llpanel.h"
+
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLButton;
+class LLSpinCtrl;
+class LLLineEditor;
+class LLPanelLandSelectObserver;
+
+class LLPanelLandInfo
+: public LLPanel
+{
+public:
+ LLPanelLandInfo(const std::string& name);
+ virtual ~LLPanelLandInfo();
+
+ void refresh();
+ static void refreshAll();
+
+protected:
+ static void onClickClaim(void*);
+ static void onClickRelease(void*);
+ static void onClickDivide(void*);
+ static void onClickJoin(void*);
+ static void onClickAbout(void*);
+
+protected:
+ //LLTextBox* mTextPriceLabel;
+ //LLTextBox* mTextPrice;
+
+ //LLButton* mBtnClaimLand;
+ //LLButton* mBtnReleaseLand;
+ //LLButton* mBtnDivideLand;
+ //LLButton* mBtnJoinLand;
+ //LLButton* mBtnAbout;
+
+ virtual BOOL postBuild();
+
+ static LLPanelLandSelectObserver* sObserver;
+ static LLPanelLandInfo* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
new file mode 100644
index 0000000000..e1ed7a0df7
--- /dev/null
+++ b/indra/newview/llpanellogin.cpp
@@ -0,0 +1,748 @@
+/**
+ * @file llpanellogin.cpp
+ * @brief Login dialog and logo display
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanellogin.h"
+
+#include "indra_constants.h" // for key and mask constants
+#include "llfontgl.h"
+#include "llmd5.h"
+#include "llsecondlifeurls.h"
+#include "llwindow.h" // shell_open()
+#include "llversion.h"
+#include "v4color.h"
+
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "llfloaterabout.h"
+#include "llfloaterpreference.h"
+#include "llfocusmgr.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llviewerbuild.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h" // for handle_preferences()
+#include "llviewernetwork.h"
+#include "llviewerwindow.h" // to link into child list
+#include "llmozlib.h"
+#include "llnotify.h"
+#include "viewer.h" // for gHideLinks
+#include "llvieweruictrlfactory.h"
+#include "llhttpclient.h"
+#include "llweb.h"
+#include "llwebbrowserctrl.h"
+
+#include "llfloaterhtmlhelp.h"
+#include "llfloatertos.h"
+
+#include "llglheaders.h"
+
+const S32 BLACK_BORDER_HEIGHT = 160;
+const S32 MAX_PASSWORD = 16;
+
+LLPanelLogin *LLPanelLogin::sInstance = NULL;
+BOOL LLPanelLogin::sCapslockDidNotification = FALSE;
+
+// helper class that trys to download a URL from a web site and calls a method
+// on parent class indicating if the web server is working or not
+class LLIamHereLogin : public LLHTTPClient::Responder
+{
+ private:
+ LLIamHereLogin( LLPanelLogin* parent ) :
+ mParent( parent )
+ {}
+
+ LLPanelLogin* mParent;
+
+ public:
+ static boost::intrusive_ptr< LLIamHereLogin > build( LLPanelLogin* parent )
+ {
+ return boost::intrusive_ptr< LLIamHereLogin >( new LLIamHereLogin( parent ) );
+ };
+
+ virtual void setParent( LLPanelLogin* parentIn )
+ {
+ mParent = parentIn;
+ };
+
+ virtual void result( const LLSD& content )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( true );
+ };
+
+ virtual void error( U32 status, const std::string& reason )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( false );
+ };
+};
+
+// this is global and not a class member to keep crud out of the header file
+namespace {
+ boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0;
+};
+
+//---------------------------------------------------------------------------
+// Public methods
+//---------------------------------------------------------------------------
+LLPanelLogin::LLPanelLogin(const LLRect &rect,
+ BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void *cb_data)
+: LLPanel("panel_login", LLRect(0,600,800,0), FALSE), // not bordered
+ mLogoImage(),
+ mCallback(callback),
+ mCallbackData(cb_data),
+ mHtmlAvailable( TRUE )
+{
+ mIsFocusRoot = TRUE;
+ mMungedPassword[0] = '\0';
+ mIncomingPassword[0] = '\0';
+
+ setBackgroundVisible(FALSE);
+ setBackgroundOpaque(TRUE);
+
+ // instance management
+ if (LLPanelLogin::sInstance)
+ {
+ llwarns << "Duplicate instance of login view deleted" << llendl;
+ delete LLPanelLogin::sInstance;
+ }
+
+ LLPanelLogin::sInstance = this;
+
+ // add to front so we are the bottom-most child
+ gViewerWindow->getRootView()->addChildAtEnd(this);
+
+ // Logo
+ mLogoImage = gImageList.getImage("startup_logo.tga", LLUUID::null, MIPMAP_FALSE, TRUE);
+
+ gUICtrlFactory->buildPanel(this, "panel_login.xml");
+ setRect(rect);
+ reshape(rect.getWidth(), rect.getHeight());
+
+ childSetPrevalidate("first_name_edit", LLLineEditor::prevalidatePrintableNoSpace);
+ childSetPrevalidate("last_name_edit", LLLineEditor::prevalidatePrintableNoSpace);
+
+ childSetCommitCallback("password_edit", mungePassword);
+ childSetKeystrokeCallback("password_edit", onPassKey, this);
+ childSetUserData("password_edit", this);
+
+ LLLineEditor* edit = LLUICtrlFactory::getLineEditorByName(this, "password_edit");
+ if (edit) edit->setDrawAsterixes(TRUE);
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "start_location_combo");
+ if (combo)
+ {
+ combo->setAllowTextEntry(TRUE, 128, FALSE);
+
+ // The XML file loads the combo with the following labels:
+ // 0 - "My Home"
+ // 1 - "My Last Location"
+ // 2 - "<Type region name>"
+
+ BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation");
+ LLString sim_string = LLURLSimString::sInstance.mSimString;
+ if (!sim_string.empty())
+ {
+ // Replace "<Type region name>" with this region name
+ combo->remove(2);
+ combo->add( sim_string );
+ combo->setTextEntry(sim_string);
+ combo->setCurrentByIndex( 2 );
+ }
+ else if (login_last)
+ {
+ combo->setCurrentByIndex( 1 );
+ }
+ else
+ {
+ combo->setCurrentByIndex( 0 );
+ }
+ }
+
+ // Specific servers added later.
+ childSetVisible("server_combo", show_server);
+
+ childSetAction("new_account_btn", onClickNewAccount, this);
+ childSetVisible("new_account_btn", !gHideLinks);
+
+ childSetAction("connect_btn", onClickConnect, this);
+
+ setDefaultBtn("connect_btn");
+
+ childSetAction("preferences_btn", LLFloaterPreference::show, this);
+
+ childSetAction("quit_btn", onClickQuit, this);
+
+ LLTextBox* text = LLUICtrlFactory::getTextBoxByName(this, "version_text");
+ if (text)
+ {
+ LLString version = llformat("%d.%d.%d (%d)",
+ LL_VERSION_MAJOR,
+ LL_VERSION_MINOR,
+ LL_VERSION_PATCH,
+ LL_VIEWER_BUILD );
+ text->setText(version);
+ text->setClickedCallback(onClickVersion);
+ text->setCallbackUserData(this);
+
+ // HACK
+ S32 right = getRect().mRight;
+ LLRect r = text->getRect();
+ const S32 PAD = 2;
+ r.setOriginAndSize( right - r.getWidth() - PAD, PAD,
+ r.getWidth(), r.getHeight() );
+ text->setRect(r);
+ }
+
+ // get the web browser control
+ #if LL_LIBXUL_ENABLED
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "login_html");
+ if ( web_browser )
+ {
+ // don't make it a tab stop until SL-27594 is fixed
+ web_browser->setTabStop(FALSE);
+
+ // painfully build the path to the loading screen
+ std::string loading_path( gDirUtilp->getExpandedFilename( LL_PATH_SKINS, "" ) );
+ loading_path.append( gDirUtilp->getDirDelimiter() );
+ loading_path.append( "html" );
+ loading_path.append( gDirUtilp->getDirDelimiter() );
+ loading_path.append( "loading" );
+ loading_path.append( gDirUtilp->getDirDelimiter() );
+ loading_path.append( "loading.html" );
+ web_browser->navigateTo( loading_path.c_str() );
+
+ // make links open in external browser
+ web_browser->setOpenInExternalBrowser( true );
+
+ // force the size to be correct (XML doesn't seem to be sufficient to do this)
+ LLRect htmlRect = mRect;
+ htmlRect.setCenterAndSize( mRect.getCenterX(), mRect.getCenterY() + 40, mRect.getWidth(), mRect.getHeight() - 80 );
+ web_browser->setRect( htmlRect );
+ web_browser->reshape( htmlRect.getWidth(), htmlRect.getHeight(), TRUE );
+ reshape( mRect.getWidth(), mRect.getHeight(), 1 );
+
+ // kick off a request to grab the url manually
+ gResponsePtr = LLIamHereLogin::build( this );
+ LLHTTPClient::get( childGetValue( "real_url" ).asString(), gResponsePtr );
+ };
+ #else
+ mHtmlAvailable = FALSE;
+ #endif
+
+ // Initialize visibility (and don't force visibility - use prefs)
+ refreshLocation( false );
+}
+
+void LLPanelLogin::setSiteIsAlive( bool alive )
+{
+#if LL_LIBXUL_ENABLED
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "login_html");
+ // if the contents of the site was retrieved
+ if ( alive )
+ {
+ if ( web_browser )
+ {
+ // navigate to the "real" page
+ web_browser->navigateTo( childGetValue( "real_url" ).asString() );
+
+ // mark as available
+ mHtmlAvailable = TRUE;
+ };
+ }
+ else
+ // the site is not available (missing page, server down, other badness)
+ {
+ if ( web_browser )
+ {
+ // hide browser control (revealing default one)
+ web_browser->setVisible( FALSE );
+
+ // mark as unavailable
+ mHtmlAvailable = FALSE;
+ };
+ };
+#else
+ mHtmlAvailable = FALSE;
+#endif
+}
+
+void LLPanelLogin::mungePassword(LLUICtrl* caller, void* user_data)
+{
+ LLPanelLogin* self = (LLPanelLogin*)user_data;
+ LLLineEditor* editor = (LLLineEditor*)caller;
+ std::string password = editor->getText();
+
+ // Re-md5 if we've changed at all
+ if (password != self->mIncomingPassword)
+ {
+ LLMD5 pass((unsigned char *)password.c_str());
+ pass.hex_digest(self->mMungedPassword);
+ }
+}
+
+LLPanelLogin::~LLPanelLogin()
+{
+ LLPanelLogin::sInstance = NULL;
+
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ // We know we're done with the image, so be rid of it.
+ gImageList.deleteImage( mLogoImage );
+}
+
+// virtual
+void LLPanelLogin::draw()
+{
+ if (!getVisible()) return;
+
+ BOOL target_fullscreen;
+ S32 target_width;
+ S32 target_height;
+ gViewerWindow->getTargetWindow(target_fullscreen, target_width, target_height);
+
+ childSetVisible("full_screen_text", target_fullscreen);
+
+ glPushMatrix();
+ {
+ F32 image_aspect = 1.333333f;
+ F32 view_aspect = (F32)mRect.getWidth() / (F32)mRect.getHeight();
+ // stretch image to maintain aspect ratio
+ if (image_aspect > view_aspect)
+ {
+ glTranslatef(-0.5f * (image_aspect / view_aspect - 1.f) * mRect.getWidth(), 0.f, 0.f);
+ glScalef(image_aspect / view_aspect, 1.f, 1.f);
+ }
+ // Don't maintain aspect ratio if screen wider than image. This results in the
+ // hand being partially cut off. JC
+ //else
+ //{
+ // glTranslatef(0.f, -0.5f * (view_aspect / image_aspect - 1.f) * (F32)BLACK_BORDER_HEIGHT, 0.f);
+ // glScalef(1.f, view_aspect / image_aspect, 1.f);
+ //}
+
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+
+ if ( mHtmlAvailable )
+ {
+ // draw a background box in black
+ gl_rect_2d( 0, height - 264, width, 264, LLColor4( 0.0f, 0.0f, 0.0f, 1.f ) );
+
+ // draw the bottom part of the background image - just the blue background to the native client UI
+ gl_draw_scaled_image(0, -264, width + 8, mLogoImage->getHeight(), mLogoImage);
+ }
+ else
+ {
+ // the HTML login page is not available so default to the original screen
+ S32 offscreen_part = height / 3;
+ gl_draw_scaled_image(0, -offscreen_part, width, height+offscreen_part, mLogoImage);
+ };
+ }
+ glPopMatrix();
+
+ LLPanel::draw();
+}
+
+// virtual
+BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (getVisible() && getEnabled())
+ {
+ if (( KEY_RETURN == key ) && (MASK_ALT == mask))
+ {
+ gViewerWindow->toggleFullscreen(FALSE);
+ return TRUE;
+ }
+
+ if (('P' == key) && (MASK_CONTROL == mask))
+ {
+ LLFloaterPreference::show(NULL);
+ return TRUE;
+ }
+
+ #if LL_LIBXUL_ENABLED
+ if ( KEY_F1 == key )
+ {
+ llinfos << "Spawning HTML help window" << llendl;
+ LLHtmlHelp::show( );
+ return TRUE;
+ };
+ #if ! LL_RELEASE_FOR_DOWNLOAD
+ if ( KEY_F2 == key )
+ {
+ llinfos << "Spawning floater TOS window" << llendl;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,"");
+ tos_dialog->startModal();
+ return TRUE;
+ };
+ #endif
+ #endif
+
+ if (!called_from_parent)
+ {
+ if (KEY_RETURN == key && MASK_NONE == mask)
+ {
+ // let the panel handle UICtrl processing: calls onClickConnect()
+ return LLPanel::handleKeyHere(key, mask, called_from_parent);
+ }
+ }
+ }
+
+ return LLPanel::handleKeyHere(key, mask, called_from_parent);
+}
+
+// virtual
+void LLPanelLogin::setFocus(BOOL b)
+{
+ if(b != hasFocus())
+ {
+ if(b)
+ {
+ LLPanelLogin::giveFocus();
+ }
+ else
+ {
+ LLPanel::setFocus(b);
+ }
+ }
+}
+
+// static
+void LLPanelLogin::giveFocus()
+{
+ if( sInstance )
+ {
+ // Grab focus and move cursor to first blank input field
+ std::string first = sInstance->childGetText("first_name_edit");
+ std::string pass = sInstance->childGetText("password_edit");
+
+ BOOL have_first = !first.empty();
+ BOOL have_pass = !pass.empty();
+
+ LLLineEditor* edit = NULL;
+ if (have_first && !have_pass)
+ {
+ // User saved his name but not his password. Move
+ // focus to password field.
+ edit = LLUICtrlFactory::getLineEditorByName(sInstance, "password_edit");
+ }
+ else
+ {
+ // User doesn't have a name, so start there.
+ edit = LLUICtrlFactory::getLineEditorByName(sInstance, "first_name_edit");
+ }
+
+ if (edit)
+ {
+ edit->setFocus(TRUE);
+ edit->selectAll();
+ }
+ }
+}
+
+
+// static
+void LLPanelLogin::show(const LLRect &rect,
+ BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void* callback_data)
+{
+ new LLPanelLogin(rect, show_server, callback, callback_data);
+
+ if( !gFocusMgr.getKeyboardFocus() )
+ {
+ // Grab focus and move cursor to first enabled control
+ sInstance->setFocus(TRUE);
+ // make sure that focus always goes here
+ gFocusMgr.setDefaultKeyboardFocus(sInstance);
+ }
+}
+
+// static
+void LLPanelLogin::setFields(const std::string& firstname, const std::string& lastname, const std::string& password,
+ BOOL remember)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted fillFields with no login view shown" << llendl;
+ return;
+ }
+
+ sInstance->childSetText("first_name_edit", firstname);
+ sInstance->childSetText("last_name_edit", lastname);
+
+ // Max "actual" password length is 16 characters.
+ // Hex digests are always 32 characters.
+ if (password.length() == 32)
+ {
+ // This is a MD5 hex digest of a password.
+ // We don't actually use the password input field,
+ // fill it with MAX_PASSWORD characters so we get a
+ // nice row of asterixes.
+ const char* filler = "123456789!123456";
+ sInstance->childSetText("password_edit", filler);
+ strcpy(sInstance->mIncomingPassword, filler);
+ strcpy(sInstance->mMungedPassword, password.c_str());
+ }
+ else
+ {
+ // this is a normal text password
+ sInstance->childSetText("password_edit", password);
+ strcpy(sInstance->mIncomingPassword, password.c_str());
+ LLMD5 pass((unsigned char *)password.c_str());
+ pass.hex_digest(sInstance->mMungedPassword);
+ }
+
+ sInstance->childSetValue("remember_check", remember);
+}
+
+
+// static
+void LLPanelLogin::addServer(const char *server, S32 domain_name)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted addServer with no login view shown" << llendl;
+ return;
+ }
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "server_combo");
+ if (combo)
+ {
+ combo->add(server, LLSD(domain_name) );
+ combo->setCurrentByIndex(0);
+ }
+}
+
+// static
+void LLPanelLogin::getFields(LLString &firstname, LLString &lastname, LLString &password,
+ BOOL &remember)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted getFields with no login view shown" << llendl;
+ return;
+ }
+
+ firstname = sInstance->childGetText("first_name_edit");
+ LLString::trim(firstname);
+
+ lastname = sInstance->childGetText("last_name_edit");
+ LLString::trim(lastname);
+
+ password.assign( sInstance->mMungedPassword );
+ remember = sInstance->childGetValue("remember_check");
+}
+
+
+// static
+void LLPanelLogin::getServer(LLString &server, S32 &domain_name)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted getServer with no login view shown" << llendl;
+ return;
+ }
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "server_combo");
+ if (combo)
+ {
+ LLSD combo_val = combo->getValue();
+ if (LLSD::TypeInteger == combo_val.type())
+ {
+ domain_name = combo->getValue().asInteger();
+
+ if ((S32)USERSERVER_OTHER == domain_name)
+ {
+ server = gUserServerName;
+ }
+ }
+ else
+ {
+ // no valid selection, return other
+ domain_name = (S32)USERSERVER_OTHER;
+ server = combo_val.asString();
+ }
+ }
+}
+
+// static
+void LLPanelLogin::getLocation(LLString &location)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted getLocation with no login view shown" << llendl;
+ return;
+ }
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "start_location_combo");
+ if (combo)
+ {
+ location = combo->getValue().asString();
+ }
+}
+
+// static
+void LLPanelLogin::refreshLocation( bool force_visible )
+{
+ if (!sInstance) return;
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "start_location_combo");
+ if (!combo) return;
+
+ LLString sim_string = LLURLSimString::sInstance.mSimString;
+ if (!sim_string.empty())
+ {
+ combo->setCurrentByIndex( 3 ); // BUG? Maybe 2?
+ combo->setTextEntry(sim_string);
+ }
+ else
+ {
+ BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation");
+ combo->setCurrentByIndex( login_last ? 1 : 0 );
+ }
+
+ BOOL show_start = TRUE;
+
+ if ( ! force_visible )
+ show_start = gSavedSettings.getBOOL("ShowStartLocation");
+
+ sInstance->childSetVisible("start_location_combo", show_start);
+ sInstance->childSetVisible("start_location_text", show_start);
+}
+
+// static
+void LLPanelLogin::close()
+{
+ if (sInstance)
+ {
+ gViewerWindow->getRootView()->removeChild( LLPanelLogin::sInstance );
+
+ gFocusMgr.setDefaultKeyboardFocus(NULL);
+
+ delete sInstance;
+ sInstance = NULL;
+ }
+}
+
+
+//---------------------------------------------------------------------------
+// Protected methods
+//---------------------------------------------------------------------------
+
+// static
+void LLPanelLogin::onClickConnect(void *)
+{
+ if (sInstance && sInstance->mCallback)
+ {
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ // JC - Make sure the fields all get committed.
+ sInstance->setFocus(FALSE);
+
+ LLString first = sInstance->childGetText("first_name_edit");
+ LLString last = sInstance->childGetText("last_name_edit");
+ if (!first.empty() && !last.empty())
+ {
+ // has both first and last name typed
+
+ // store off custom server entry, if currently selected
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "server_combo");
+ if (combo)
+ {
+ S32 selected_server = combo->getValue();
+ if (selected_server == USERSERVER_NONE)
+ {
+ LLString custom_server = combo->getValue().asString();
+ gSavedSettings.setString("CustomServer", custom_server);
+ }
+ }
+ sInstance->mCallback(0, sInstance->mCallbackData);
+ }
+ else
+ {
+ // empty first or last name
+ // same as clicking new account
+ onClickNewAccount(NULL);
+ }
+ }
+}
+
+
+// static
+void LLPanelLogin::newAccountAlertCallback(S32 option, void*)
+{
+ if (0 == option)
+ {
+ llinfos << "Going to account creation URL" << llendl;
+ LLWeb::loadURL( CREATE_ACCOUNT_URL );
+ }
+ else
+ {
+ sInstance->setFocus(TRUE);
+ }
+}
+
+
+// static
+void LLPanelLogin::onClickNewAccount(void*)
+{
+ if (gHideLinks)
+ {
+ gViewerWindow->alertXml("MustHaveAccountToLogInNoLinks");
+ }
+ else
+ {
+ gViewerWindow->alertXml("MustHaveAccountToLogIn",
+ LLPanelLogin::newAccountAlertCallback);
+ }
+}
+
+
+// static
+void LLPanelLogin::onClickQuit(void*)
+{
+ if (sInstance && sInstance->mCallback)
+ {
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ sInstance->mCallback(1, sInstance->mCallbackData);
+ }
+}
+
+
+// static
+void LLPanelLogin::onClickVersion(void*)
+{
+ LLFloaterAbout::show(NULL);
+}
+
+// static
+void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data)
+{
+ if (gKeyboard->getKeyDown(KEY_CAPSLOCK) && sCapslockDidNotification == FALSE)
+ {
+ LLNotifyBox::showXml("CapsKeyOn");
+ sCapslockDidNotification = TRUE;
+ }
+}
diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h
new file mode 100644
index 0000000000..060f2f6732
--- /dev/null
+++ b/indra/newview/llpanellogin.h
@@ -0,0 +1,83 @@
+/**
+ * @file llpanellogin.h
+ * @brief Login username entry fields.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELLOGIN_H
+#define LL_LLPANELLOGIN_H
+
+#include "llpanel.h"
+
+#include "lldbstrings.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+#include "llstring.h"
+
+class LLTextBox;
+class LLLineEditor;
+class LLCheckBoxCtrl;
+class LLButton;
+class LLComboBox;
+
+class LLPanelLogin
+: public LLPanel
+{
+public:
+ LLPanelLogin(const LLRect &rect, BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void *callback_data);
+ ~LLPanelLogin();
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual void draw();
+ virtual void setFocus( BOOL b );
+
+ static void show(const LLRect &rect, BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void* callback_data);
+
+ static void setFields(const std::string& firstname, const std::string& lastname,
+ const std::string& password, BOOL remember);
+
+ static void addServer(const char *server, S32 domain_name);
+ static void refreshLocation( bool force_visible );
+
+ static void getFields(LLString &firstname, LLString &lastname,
+ LLString &password, BOOL &remember);
+
+ static void getServer(LLString &server, S32& domain_name);
+ static void getLocation(LLString &location);
+
+ static void close();
+
+ void setSiteIsAlive( bool alive );
+
+ static void giveFocus();
+ static void mungePassword(LLUICtrl* caller, void* user_data);
+
+private:
+ static void onClickConnect(void*);
+ static void onClickNewAccount(void*);
+ static void newAccountAlertCallback(S32 option, void*);
+ static void onClickQuit(void*);
+ static void onClickVersion(void*);
+ static void onPassKey(LLLineEditor* caller, void* user_data);
+
+private:
+ LLPointer<LLViewerImage> mLogoImage;
+
+ void (*mCallback)(S32 option, void *userdata);
+ void* mCallbackData;
+
+ char mIncomingPassword[DB_USER_PASSWORD_BUF_SIZE];
+ char mMungedPassword[MD5HEX_STR_SIZE];
+
+ static LLPanelLogin* sInstance;
+ static BOOL sCapslockDidNotification;
+ BOOL mHtmlAvailable;
+};
+
+#endif
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
new file mode 100644
index 0000000000..71eeef97ff
--- /dev/null
+++ b/indra/newview/llpanelobject.cpp
@@ -0,0 +1,1679 @@
+/**
+ * @file llpanelobject.cpp
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelobject.h"
+
+// linden library includes
+#include "lleconomy.h"
+#include "llerror.h"
+#include "llfontgl.h"
+#include "llpermissionsflags.h"
+#include "llstring.h"
+#include "llvolume.h"
+#include "m3math.h"
+
+// project includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcolorswatch.h"
+#include "llcombobox.h"
+#include "llfocusmgr.h"
+#include "llmanipscale.h"
+#include "llpanelinventory.h"
+#include "llpreviewscript.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltool.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+
+#include "lldrawpool.h"
+
+//
+// Constants
+//
+enum {
+ MI_BOX,
+ MI_CYLINDER,
+ MI_PRISM,
+ MI_SPHERE,
+ MI_TORUS,
+ MI_TUBE,
+ MI_RING,
+ MI_NONE,
+ MI_VOLUME_COUNT
+};
+
+enum {
+ MI_HOLE_SAME,
+ MI_HOLE_CIRCLE,
+ MI_HOLE_SQUARE,
+ MI_HOLE_TRIANGLE,
+ MI_HOLE_COUNT
+};
+
+//XUI:translate (depricated, so very low priority)
+static const LLString LEGACY_FULLBRIGHT_DESC("Fullbright (Legacy)");
+
+BOOL LLPanelObject::postBuild()
+{
+ setMouseOpaque(FALSE);
+
+ //--------------------------------------------------------
+ // Top
+ //--------------------------------------------------------
+
+ // Lock checkbox
+ mCheckLock = gUICtrlFactory->getCheckBoxByName(this,"checkbox locked");
+ childSetCommitCallback("checkbox locked",onCommitLock,this);
+
+ // Physical checkbox
+ mCheckPhysics = gUICtrlFactory->getCheckBoxByName(this,"Physical Checkbox Ctrl");
+ childSetCommitCallback("Physical Checkbox Ctrl",onCommitPhysics,this);
+
+ // Temporary checkbox
+ mCheckTemporary = gUICtrlFactory->getCheckBoxByName(this,"Temporary Checkbox Ctrl");
+ childSetCommitCallback("Temporary Checkbox Ctrl",onCommitTemporary,this);
+
+ // Phantom checkbox
+ mCheckPhantom = gUICtrlFactory->getCheckBoxByName(this,"Phantom Checkbox Ctrl");
+ childSetCommitCallback("Phantom Checkbox Ctrl",onCommitPhantom,this);
+
+ // Position
+ mLabelPosition = gUICtrlFactory->getTextBoxByName(this,"label position");
+ mCtrlPosX = gUICtrlFactory->getSpinnerByName(this,"Pos X");
+ childSetCommitCallback("Pos X",onCommitPosition,this);
+ mCtrlPosY = gUICtrlFactory->getSpinnerByName(this,"Pos Y");
+ childSetCommitCallback("Pos Y",onCommitPosition,this);
+ mCtrlPosZ = gUICtrlFactory->getSpinnerByName(this,"Pos Z");
+ childSetCommitCallback("Pos Z",onCommitPosition,this);
+
+ // Scale
+ mLabelSize = gUICtrlFactory->getTextBoxByName(this,"label size");
+ mCtrlScaleX = gUICtrlFactory->getSpinnerByName(this,"Scale X");
+ childSetCommitCallback("Scale X",onCommitScale,this);
+
+ // Scale Y
+ mCtrlScaleY = gUICtrlFactory->getSpinnerByName(this,"Scale Y");
+ childSetCommitCallback("Scale Y",onCommitScale,this);
+
+ // Scale Z
+ mCtrlScaleZ = gUICtrlFactory->getSpinnerByName(this,"Scale Z");
+ childSetCommitCallback("Scale Z",onCommitScale,this);
+
+ // Rotation
+ mLabelRotation = gUICtrlFactory->getTextBoxByName(this,"label rotation");
+ mCtrlRotX = gUICtrlFactory->getSpinnerByName(this,"Rot X");
+ childSetCommitCallback("Rot X",onCommitRotation,this);
+ mCtrlRotY = gUICtrlFactory->getSpinnerByName(this,"Rot Y");
+ childSetCommitCallback("Rot Y",onCommitRotation,this);
+ mCtrlRotZ = gUICtrlFactory->getSpinnerByName(this,"Rot Z");
+ childSetCommitCallback("Rot Z",onCommitRotation,this);
+
+ //--------------------------------------------------------
+
+ // material type popup
+ mLabelMaterial = gUICtrlFactory->getTextBoxByName(this,"label material");
+ mComboMaterial = gUICtrlFactory->getComboBoxByName(this,"material");
+ childSetCommitCallback("material",onCommitMaterial,this);
+ mComboMaterial->removeall();
+ // XUI:translate
+ LLMaterialInfo *minfop;
+ for (minfop = LLMaterialTable::basic.mMaterialInfoList.getFirstData();
+ minfop != NULL;
+ minfop = LLMaterialTable::basic.mMaterialInfoList.getNextData())
+ {
+ if (minfop->mMCode != LL_MCODE_LIGHT)
+ {
+ mComboMaterial->add(minfop->mName);
+ }
+ }
+ mComboMaterialItemCount = mComboMaterial->getItemCount();
+
+ // Base Type
+ mLabelBaseType = gUICtrlFactory->getTextBoxByName(this,"label basetype");
+ mComboBaseType = gUICtrlFactory->getComboBoxByName(this,"comboBaseType");
+ childSetCommitCallback("comboBaseType",onCommitParametric,this);
+
+ // Cut
+ mLabelCut = gUICtrlFactory->getTextBoxByName(this,"text cut");
+ mSpinCutBegin = gUICtrlFactory->getSpinnerByName(this,"cut begin");
+ childSetCommitCallback("cut begin",onCommitParametric,this);
+ mSpinCutBegin->setValidateBeforeCommit( precommitValidate );
+ mSpinCutEnd = gUICtrlFactory->getSpinnerByName(this,"cut end");
+ childSetCommitCallback("cut end",onCommitParametric,this);
+ mSpinCutEnd->setValidateBeforeCommit( &precommitValidate );
+
+ // Hollow / Skew
+ mLabelHollow = gUICtrlFactory->getTextBoxByName(this,"text hollow");
+ mLabelSkew = gUICtrlFactory->getTextBoxByName(this,"text skew");
+ mSpinHollow = gUICtrlFactory->getSpinnerByName(this,"Scale 1");
+ childSetCommitCallback("Scale 1",onCommitParametric,this);
+ mSpinHollow->setValidateBeforeCommit( &precommitValidate );
+ mSpinSkew = gUICtrlFactory->getSpinnerByName(this,"Skew");
+ childSetCommitCallback("Skew",onCommitParametric,this);
+ mSpinSkew->setValidateBeforeCommit( &precommitValidate );
+ mLabelHoleType = gUICtrlFactory->getTextBoxByName(this,"Hollow Shape");
+
+ // Hole Type
+ mComboHoleType = gUICtrlFactory->getComboBoxByName(this,"hole");
+ childSetCommitCallback("hole",onCommitParametric,this);
+
+ // Twist
+ mLabelTwist = gUICtrlFactory->getTextBoxByName(this,"text twist");
+ mSpinTwistBegin = gUICtrlFactory->getSpinnerByName(this,"Twist Begin");
+ childSetCommitCallback("Twist Begin",onCommitParametric,this);
+ mSpinTwistBegin->setValidateBeforeCommit( precommitValidate );
+ mSpinTwist = gUICtrlFactory->getSpinnerByName(this,"Twist End");
+ childSetCommitCallback("Twist End",onCommitParametric,this);
+ mSpinTwist->setValidateBeforeCommit( &precommitValidate );
+
+ // Scale
+ mSpinScaleX = gUICtrlFactory->getSpinnerByName(this,"Taper Scale X");
+ childSetCommitCallback("Taper Scale X",onCommitParametric,this);
+ mSpinScaleX->setValidateBeforeCommit( &precommitValidate );
+ mSpinScaleY = gUICtrlFactory->getSpinnerByName(this,"Taper Scale Y");
+ childSetCommitCallback("Taper Scale Y",onCommitParametric,this);
+ mSpinScaleY->setValidateBeforeCommit( &precommitValidate );
+
+ // Shear
+ mLabelShear = gUICtrlFactory->getTextBoxByName(this,"text topshear");
+ mSpinShearX = gUICtrlFactory->getSpinnerByName(this,"Shear X");
+ childSetCommitCallback("Shear X",onCommitParametric,this);
+ mSpinShearX->setValidateBeforeCommit( &precommitValidate );
+ mSpinShearY = gUICtrlFactory->getSpinnerByName(this,"Shear Y");
+ childSetCommitCallback("Shear Y",onCommitParametric,this);
+ mSpinShearY->setValidateBeforeCommit( &precommitValidate );
+
+ // Path / Profile
+ mCtrlPathBegin = gUICtrlFactory->getSpinnerByName(this,"Path Limit Begin");
+ childSetCommitCallback("Path Limit Begin",onCommitParametric,this);
+ mCtrlPathBegin->setValidateBeforeCommit( &precommitValidate );
+ mCtrlPathEnd = gUICtrlFactory->getSpinnerByName(this,"Path Limit End");
+ childSetCommitCallback("Path Limit End",onCommitParametric,this);
+ mCtrlPathEnd->setValidateBeforeCommit( &precommitValidate );
+
+ // Taper
+ mLabelTaper = gUICtrlFactory->getTextBoxByName(this,"text taper2");
+ mSpinTaperX = gUICtrlFactory->getSpinnerByName(this,"Taper X");
+ childSetCommitCallback("Taper X",onCommitParametric,this);
+ mSpinTaperX->setValidateBeforeCommit( precommitValidate );
+ mSpinTaperY = gUICtrlFactory->getSpinnerByName(this,"Taper Y");
+ childSetCommitCallback("Taper Y",onCommitParametric,this);
+ mSpinTaperY->setValidateBeforeCommit( precommitValidate );
+
+ // Radius Offset / Revolutions
+ mLabelRadiusOffset = gUICtrlFactory->getTextBoxByName(this,"text radius delta");
+ mLabelRevolutions = gUICtrlFactory->getTextBoxByName(this,"text revolutions");
+ mSpinRadiusOffset = gUICtrlFactory->getSpinnerByName(this,"Radius Offset");
+ childSetCommitCallback("Radius Offset",onCommitParametric,this);
+ mSpinRadiusOffset->setValidateBeforeCommit( &precommitValidate );
+ mSpinRevolutions = gUICtrlFactory->getSpinnerByName(this,"Revolutions");
+ childSetCommitCallback("Revolutions",onCommitParametric,this);
+ mSpinRevolutions->setValidateBeforeCommit( &precommitValidate );
+
+ // Start with everyone disabled
+ clearCtrls();
+
+ return TRUE;
+}
+
+LLPanelObject::LLPanelObject(const std::string& name)
+: LLPanel(name),
+ mIsPhysical(FALSE),
+ mIsTemporary(FALSE),
+ mIsPhantom(FALSE),
+ mCastShadows(TRUE),
+ mSelectedType(MI_BOX)
+{
+}
+
+
+LLPanelObject::~LLPanelObject()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+void LLPanelObject::getState( )
+{
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ LLViewerObject* root_objectp = objectp;
+ if(!objectp)
+ {
+ objectp = gSelectMgr->getFirstObject();
+ //FIXME: shouldn't we just keep the child?
+ if (objectp)
+ {
+ LLViewerObject* parentp = objectp->getSubParent();
+
+ if (parentp)
+ {
+ root_objectp = parentp;
+ }
+ else
+ {
+ root_objectp = objectp;
+ }
+ }
+ }
+
+ LLVOVolume *volobjp = NULL;
+ if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
+ {
+ volobjp = (LLVOVolume *)objectp;
+ }
+
+ if( !objectp )
+ {
+ //forfeit focus
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ // Disable all text input fields
+ clearCtrls();
+ return;
+ }
+
+ // can move or rotate only linked group with move permissions, or sub-object with move and modify perms
+ BOOL enable_move = objectp->permMove() && !objectp->isAttachment() && (objectp->permModify() || gSavedSettings.getBOOL("SelectLinkedSet"));
+ BOOL enable_scale = objectp->permMove() && objectp->permModify();
+ BOOL enable_rotate = objectp->permMove() && ( (objectp->permModify() && !objectp->isAttachment()) || gSavedSettings.getBOOL("SelectLinkedSet"));
+
+ LLVector3 vec;
+ if (enable_move)
+ {
+ vec = objectp->getPositionEdit();
+ mCtrlPosX->set( vec.mV[VX] );
+ mCtrlPosY->set( vec.mV[VY] );
+ mCtrlPosZ->set( vec.mV[VZ] );
+ }
+ else
+ {
+ mCtrlPosX->clear();
+ mCtrlPosY->clear();
+ mCtrlPosZ->clear();
+ }
+
+
+ mLabelPosition->setEnabled( enable_move );
+ mCtrlPosX->setEnabled(enable_move);
+ mCtrlPosY->setEnabled(enable_move);
+ mCtrlPosZ->setEnabled(enable_move);
+
+ if (enable_scale)
+ {
+ vec = objectp->getScale();
+ mCtrlScaleX->set( vec.mV[VX] );
+ mCtrlScaleY->set( vec.mV[VY] );
+ mCtrlScaleZ->set( vec.mV[VZ] );
+ }
+ else
+ {
+ mCtrlScaleX->clear();
+ mCtrlScaleY->clear();
+ mCtrlScaleZ->clear();
+ }
+
+ mLabelSize->setEnabled( enable_scale );
+ mCtrlScaleX->setEnabled( enable_scale );
+ mCtrlScaleY->setEnabled( enable_scale );
+ mCtrlScaleZ->setEnabled( enable_scale );
+
+ LLQuaternion object_rot = objectp->getRotationEdit();
+ object_rot.getEulerAngles(&(mCurEulerDegrees.mV[VX]), &(mCurEulerDegrees.mV[VY]), &(mCurEulerDegrees.mV[VZ]));
+ mCurEulerDegrees *= RAD_TO_DEG;
+ mCurEulerDegrees.mV[VX] = fmod(llround(mCurEulerDegrees.mV[VX], OBJECT_ROTATION_PRECISION) + 360.f, 360.f);
+ mCurEulerDegrees.mV[VY] = fmod(llround(mCurEulerDegrees.mV[VY], OBJECT_ROTATION_PRECISION) + 360.f, 360.f);
+ mCurEulerDegrees.mV[VZ] = fmod(llround(mCurEulerDegrees.mV[VZ], OBJECT_ROTATION_PRECISION) + 360.f, 360.f);
+
+ if (enable_rotate)
+ {
+ mCtrlRotX->set( mCurEulerDegrees.mV[VX] );
+ mCtrlRotY->set( mCurEulerDegrees.mV[VY] );
+ mCtrlRotZ->set( mCurEulerDegrees.mV[VZ] );
+ }
+ else
+ {
+ mCtrlRotX->clear();
+ mCtrlRotY->clear();
+ mCtrlRotZ->clear();
+ }
+
+ mLabelRotation->setEnabled( enable_rotate );
+ mCtrlRotX->setEnabled( enable_rotate );
+ mCtrlRotY->setEnabled( enable_rotate );
+ mCtrlRotZ->setEnabled( enable_rotate );
+
+ BOOL owners_identical;
+ LLUUID owner_id;
+ LLString owner_name;
+ owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+
+ // BUG? Check for all objects being editable?
+ S32 roots_selected = gSelectMgr->getRootObjectCount();
+ BOOL editable = root_objectp->permModify();
+ S32 selected_count = gSelectMgr->getObjectCount();
+ BOOL single_volume = (gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME ))
+ && (selected_count == 1);
+
+ // Select Single Message
+ childSetVisible("select_single", FALSE);
+ childSetVisible("edit_object", FALSE);
+ if (!editable || single_volume || selected_count <= 1)
+ {
+ childSetVisible("edit_object", TRUE);
+ childSetEnabled("edit_object", TRUE);
+ }
+ else
+ {
+ childSetVisible("select_single", TRUE);
+ childSetEnabled("select_single", TRUE);
+ }
+ // Lock checkbox - only modifiable if you own the object.
+ BOOL self_owned = (gAgent.getID() == owner_id);
+ mCheckLock->setEnabled( roots_selected > 0 && self_owned );
+
+ // More lock and debit checkbox - get the values
+ BOOL valid;
+ U32 owner_mask_on;
+ U32 owner_mask_off;
+ valid = gSelectMgr->selectGetPerm(PERM_OWNER, &owner_mask_on, &owner_mask_off);
+
+ if(valid)
+ {
+ if(owner_mask_on & PERM_MOVE)
+ {
+ // owner can move, so not locked
+ mCheckLock->set(FALSE);
+ mCheckLock->setTentative(FALSE);
+ }
+ else if(owner_mask_off & PERM_MOVE)
+ {
+ // owner can't move, so locked
+ mCheckLock->set(TRUE);
+ mCheckLock->setTentative(FALSE);
+ }
+ else
+ {
+ // some locked, some not locked
+ mCheckLock->set(FALSE);
+ mCheckLock->setTentative(TRUE);
+ }
+ }
+
+ BOOL is_flexible = volobjp && volobjp->isFlexible();
+
+ // Physics checkbox
+ mIsPhysical = root_objectp->usePhysics();
+ mCheckPhysics->set( mIsPhysical );
+ mCheckPhysics->setEnabled( roots_selected==1
+ && (editable || gAgent.isGodlike())
+ && !is_flexible);
+
+ mIsTemporary = root_objectp->flagTemporaryOnRez();
+ mCheckTemporary->set( mIsTemporary );
+ mCheckTemporary->setEnabled( roots_selected==1 && editable );
+
+ mIsPhantom = root_objectp->flagPhantom();
+ mCheckPhantom->set( mIsPhantom );
+ mCheckPhantom->setEnabled( roots_selected==1 && editable && !is_flexible );
+
+#if 0 // 1.9.2
+ mCastShadows = root_objectp->flagCastShadows();
+ mCheckCastShadows->set( mCastShadows );
+ mCheckCastShadows->setEnabled( roots_selected==1 && editable );
+#endif
+
+ // Update material part
+ U8 material_code;
+ BOOL material_same = gSelectMgr->selectionGetMaterial(&material_code);
+ if (editable && single_volume && material_same)
+ {
+ mComboMaterial->setEnabled( TRUE );
+ mLabelMaterial->setEnabled( TRUE );
+ if (material_code == LL_MCODE_LIGHT)
+ {
+ if (mComboMaterial->getItemCount() == mComboMaterialItemCount)
+ {
+ mComboMaterial->add(LEGACY_FULLBRIGHT_DESC);
+ }
+ mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC);
+ }
+ else
+ {
+ if (mComboMaterial->getItemCount() != mComboMaterialItemCount)
+ {
+ mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC);
+ }
+ mComboMaterial->setSimple(LLMaterialTable::basic.getName(material_code));
+ }
+ }
+ else
+ {
+ mComboMaterial->setEnabled( FALSE );
+ mLabelMaterial->setEnabled( FALSE );
+ }
+ //----------------------------------------------------------------------------
+
+ S32 selected_item = MI_BOX;
+ S32 selected_hole = MI_HOLE_SAME;
+ BOOL enabled = FALSE;
+ BOOL hole_enabled = FALSE;
+ F32 scale_x=1.f, scale_y=1.f;
+
+ if( !objectp || !objectp->getVolume() || !editable || !single_volume)
+ {
+ // Clear out all geometry fields.
+ mComboBaseType->clear();
+ mSpinHollow->clear();
+ mSpinCutBegin->clear();
+ mSpinCutEnd->clear();
+ mCtrlPathBegin->clear();
+ mCtrlPathEnd->clear();
+ mSpinScaleX->clear();
+ mSpinScaleY->clear();
+ mSpinTwist->clear();
+ mSpinTwistBegin->clear();
+ mComboHoleType->clear();
+ mSpinShearX->clear();
+ mSpinShearY->clear();
+ mSpinTaperX->clear();
+ mSpinTaperY->clear();
+ mSpinRadiusOffset->clear();
+ mSpinRevolutions->clear();
+ mSpinSkew->clear();
+
+ mSelectedType = MI_NONE;
+ }
+ else
+ {
+ // Only allowed to change these parameters for objects
+ // that you have permissions on AND are not attachments.
+ enabled = root_objectp->permModify();
+
+ const LLVolumeParams &volume_params = objectp->getVolume()->getParams();
+
+ // Volume type
+ U8 path = volume_params.getPathParams().getCurveType();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ U8 profile = profile_and_hole & LL_PCODE_PROFILE_MASK;
+ U8 hole = profile_and_hole & LL_PCODE_HOLE_MASK;
+
+ // Scale goes first so we can differentiate between a sphere and a torus,
+ // which have the same profile and path types.
+
+ // Scale
+ scale_x = volume_params.getRatioX();
+ scale_y = volume_params.getRatioY();
+
+ BOOL linear_path = (path == LL_PCODE_PATH_LINE) || (path == LL_PCODE_PATH_FLEXIBLE);
+ if ( linear_path && profile == LL_PCODE_PROFILE_CIRCLE )
+ {
+ selected_item = MI_CYLINDER;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_SQUARE )
+ {
+ selected_item = MI_BOX;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_ISOTRI )
+ {
+ selected_item = MI_PRISM;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_EQUALTRI )
+ {
+ selected_item = MI_PRISM;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_RIGHTTRI )
+ {
+ selected_item = MI_PRISM;
+ }
+ else if (path == LL_PCODE_PATH_FLEXIBLE) // shouldn't happen
+ {
+ selected_item = MI_CYLINDER; // reasonable default
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE && scale_y > 0.75f)
+ {
+ selected_item = MI_SPHERE;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE && scale_y <= 0.75f)
+ {
+ selected_item = MI_TORUS;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE_HALF)
+ {
+ selected_item = MI_SPHERE;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE2 && profile == LL_PCODE_PROFILE_CIRCLE )
+ {
+ // Spirals aren't supported. Make it into a sphere. JC
+ selected_item = MI_SPHERE;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_EQUALTRI )
+ {
+ selected_item = MI_RING;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_SQUARE && scale_y <= 0.75f)
+ {
+ selected_item = MI_TUBE;
+ }
+ else
+ {
+ llinfos << "Unknown path " << (S32) path << " profile " << (S32) profile << " in getState" << llendl;
+ selected_item = MI_BOX;
+ }
+ mComboBaseType ->setCurrentByIndex( selected_item );
+ mSelectedType = selected_item;
+
+ // Grab S path
+ F32 begin_s = volume_params.getBeginS();
+ F32 end_s = volume_params.getEndS();
+
+ // Compute cut and advanced cut from S and T
+ F32 begin_t = volume_params.getBeginT();
+ F32 end_t = volume_params.getEndT();
+
+ // Hollowness
+ F32 hollow = volume_params.getHollow();
+ mSpinHollow->set( 100.f * hollow );
+
+ // All hollow objects allow a shape to be selected.
+ if (hollow > 0.f)
+ {
+ switch (hole)
+ {
+ case LL_PCODE_HOLE_CIRCLE:
+ selected_hole = MI_HOLE_CIRCLE;
+ break;
+ case LL_PCODE_HOLE_SQUARE:
+ selected_hole = MI_HOLE_SQUARE;
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ selected_hole = MI_HOLE_TRIANGLE;
+ break;
+ case LL_PCODE_HOLE_SAME:
+ default:
+ selected_hole = MI_HOLE_SAME;
+ break;
+ }
+ mComboHoleType->setCurrentByIndex( selected_hole );
+ hole_enabled = enabled;
+ }
+ else
+ {
+ mComboHoleType->setCurrentByIndex( MI_HOLE_SAME );
+ hole_enabled = FALSE;
+ }
+
+ // Cut interpretation varies based on base object type
+ F32 cut_begin, cut_end, adv_cut_begin, adv_cut_end;
+
+ if ( selected_item == MI_SPHERE || selected_item == MI_TORUS ||
+ selected_item == MI_TUBE || selected_item == MI_RING )
+ {
+ cut_begin = begin_t;
+ cut_end = end_t;
+ adv_cut_begin = begin_s;
+ adv_cut_end = end_s;
+ }
+ else
+ {
+ cut_begin = begin_s;
+ cut_end = end_s;
+ adv_cut_begin = begin_t;
+ adv_cut_end = end_t;
+ }
+
+ mSpinCutBegin ->set( cut_begin );
+ mSpinCutEnd ->set( cut_end );
+ mCtrlPathBegin ->set( adv_cut_begin );
+ mCtrlPathEnd ->set( adv_cut_end );
+
+ // Twist
+ F32 twist = volume_params.getTwist();
+ F32 twist_begin = volume_params.getTwistBegin();
+ // Check the path type for conversion.
+ if (path == LL_PCODE_PATH_LINE || path == LL_PCODE_PATH_FLEXIBLE)
+ {
+ twist *= OBJECT_TWIST_LINEAR_MAX;
+ twist_begin *= OBJECT_TWIST_LINEAR_MAX;
+ }
+ else
+ {
+ twist *= OBJECT_TWIST_MAX;
+ twist_begin *= OBJECT_TWIST_MAX;
+ }
+
+ mSpinTwist ->set( twist );
+ mSpinTwistBegin ->set( twist_begin );
+
+ // Shear
+ F32 shear_x = volume_params.getShearX();
+ F32 shear_y = volume_params.getShearY();
+ mSpinShearX->set( shear_x );
+ mSpinShearY->set( shear_y );
+
+ // Taper
+ F32 taper_x = volume_params.getTaperX();
+ F32 taper_y = volume_params.getTaperY();
+ mSpinTaperX->set( taper_x );
+ mSpinTaperY->set( taper_y );
+
+ // Radius offset.
+ F32 radius_offset = volume_params.getRadiusOffset();
+ // Limit radius offset, based on taper and hole size y.
+ F32 radius_mag = fabs(radius_offset);
+ F32 hole_y_mag = fabs(scale_y);
+ F32 taper_y_mag = fabs(taper_y);
+ // Check to see if the taper effects us.
+ if ( (radius_offset > 0.f && taper_y < 0.f) ||
+ (radius_offset < 0.f && taper_y > 0.f) )
+ {
+ // The taper does not help increase the radius offset range.
+ taper_y_mag = 0.f;
+ }
+ F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+ // Enforce the maximum magnitude.
+ if (radius_mag > max_radius_mag)
+ {
+ // Check radius offset sign.
+ if (radius_offset < 0.f)
+ {
+ radius_offset = -max_radius_mag;
+ }
+ else
+ {
+ radius_offset = max_radius_mag;
+ }
+ }
+ mSpinRadiusOffset->set( radius_offset);
+
+ // Revolutions
+ F32 revolutions = volume_params.getRevolutions();
+ mSpinRevolutions->set( revolutions );
+
+ // Skew
+ F32 skew = volume_params.getSkew();
+ // Limit skew, based on revolutions hole size x.
+ F32 skew_mag= fabs(skew);
+ F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+ // Discontinuity; A revolution of 1 allows skews below 0.5.
+ if ( fabs(revolutions - 1.0f) < 0.001)
+ min_skew_mag = 0.0f;
+
+ // Clip skew.
+ if (skew_mag < min_skew_mag)
+ {
+ // Check skew sign.
+ if (skew < 0.0f)
+ {
+ skew = -min_skew_mag;
+ }
+ else
+ {
+ skew = min_skew_mag;
+ }
+ }
+ mSpinSkew->set( skew );
+ }
+
+ // Compute control visibility, label names, and twist range.
+ // Start with defaults.
+ BOOL top_size_x_visible = TRUE;
+ BOOL top_size_y_visible = TRUE;
+ BOOL top_shear_x_visible = TRUE;
+ BOOL top_shear_y_visible = TRUE;
+ BOOL twist_visible = TRUE;
+ BOOL advanced_cut_visible = FALSE;
+ BOOL taper_visible = FALSE;
+ BOOL skew_visible = FALSE;
+ BOOL radius_offset_visible = FALSE;
+ BOOL revolutions_visible = FALSE;
+ F32 twist_min = OBJECT_TWIST_LINEAR_MIN;
+ F32 twist_max = OBJECT_TWIST_LINEAR_MAX;
+ F32 twist_inc = OBJECT_TWIST_LINEAR_INC;
+
+ BOOL advanced_is_dimple = FALSE;
+ BOOL size_is_hole = FALSE;
+
+ // Tune based on overall volume type
+ switch (selected_item)
+ {
+ case MI_SPHERE:
+ top_size_x_visible = FALSE;
+ top_size_y_visible = FALSE;
+ top_shear_x_visible = FALSE;
+ top_shear_y_visible = FALSE;
+ //twist_visible = FALSE;
+ advanced_cut_visible = TRUE;
+ advanced_is_dimple = TRUE;
+ twist_min = OBJECT_TWIST_MIN;
+ twist_max = OBJECT_TWIST_MAX;
+ twist_inc = OBJECT_TWIST_INC;
+ break;
+
+ case MI_TORUS:
+ case MI_TUBE:
+ case MI_RING:
+ //top_size_x_visible = FALSE;
+ //top_size_y_visible = FALSE;
+ size_is_hole = TRUE;
+ skew_visible = TRUE;
+ advanced_cut_visible = TRUE;
+ taper_visible = TRUE;
+ radius_offset_visible = TRUE;
+ revolutions_visible = TRUE;
+ twist_min = OBJECT_TWIST_MIN;
+ twist_max = OBJECT_TWIST_MAX;
+ twist_inc = OBJECT_TWIST_INC;
+
+ break;
+
+ case MI_BOX:
+ case MI_CYLINDER:
+ case MI_PRISM:
+ default:
+ break;
+ }
+
+ // Check if we need to change top size/hole size params.
+ switch (selected_item)
+ {
+ case MI_SPHERE:
+ case MI_TORUS:
+ case MI_TUBE:
+ case MI_RING:
+ mSpinScaleX->set( scale_x );
+ mSpinScaleY->set( scale_y );
+ mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE);
+ mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X);
+ mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE);
+ mSpinScaleY->setMaxValue(OBJECT_MAX_HOLE_SIZE_Y);
+ break;
+ default:
+ mSpinScaleX->set( 1.f - scale_x );
+ mSpinScaleY->set( 1.f - scale_y );
+ mSpinScaleX->setMinValue(-1.f);
+ mSpinScaleX->setMaxValue(1.f);
+ mSpinScaleY->setMinValue(-1.f);
+ mSpinScaleY->setMaxValue(1.f);
+ break;
+ }
+
+ // Check if we need to limit the hollow based on the hole type.
+ if ( selected_hole == MI_HOLE_SQUARE &&
+ ( selected_item == MI_CYLINDER || selected_item == MI_TORUS ||
+ selected_item == MI_PRISM || selected_item == MI_RING ||
+ selected_item == MI_SPHERE ) )
+ {
+ mSpinHollow->setMinValue(0.f);
+ mSpinHollow->setMaxValue(70.f);
+ }
+ else
+ {
+ mSpinHollow->setMinValue(0.f);
+ mSpinHollow->setMaxValue(95.f);
+ }
+
+ // Update field enablement
+ mLabelBaseType ->setEnabled( enabled );
+ mComboBaseType ->setEnabled( enabled );
+
+ mLabelCut ->setEnabled( enabled );
+ mSpinCutBegin ->setEnabled( enabled );
+ mSpinCutEnd ->setEnabled( enabled );
+
+ mLabelHollow ->setEnabled( enabled );
+ mSpinHollow ->setEnabled( enabled );
+ mLabelHoleType ->setEnabled( hole_enabled );
+ mComboHoleType ->setEnabled( hole_enabled );
+
+ mLabelTwist ->setEnabled( enabled );
+ mSpinTwist ->setEnabled( enabled );
+ mSpinTwistBegin ->setEnabled( enabled );
+
+ mLabelSkew ->setEnabled( enabled );
+ mSpinSkew ->setEnabled( enabled );
+
+ childSetVisible("scale_hole", FALSE);
+ childSetVisible("scale_taper", FALSE);
+ if (top_size_x_visible || top_size_y_visible)
+ {
+ if (size_is_hole)
+ {
+ childSetVisible("scale_hole", TRUE);
+ childSetEnabled("scale_hole", enabled);
+ }
+ else
+ {
+ childSetVisible("scale_taper", TRUE);
+ childSetEnabled("scale_taper", enabled);
+ }
+ }
+
+ mSpinScaleX ->setEnabled( enabled );
+ mSpinScaleY ->setEnabled( enabled );
+
+ mLabelShear ->setEnabled( enabled );
+ mSpinShearX ->setEnabled( enabled );
+ mSpinShearY ->setEnabled( enabled );
+
+ childSetVisible("advanced_cut", FALSE);
+ childSetVisible("advanced_dimple", FALSE);
+ if (advanced_cut_visible)
+ {
+ if (advanced_is_dimple)
+ {
+ childSetVisible("advanced_dimple", TRUE);
+ childSetEnabled("advanced_dimple", enabled);
+ }
+ else
+ {
+ childSetVisible("advanced_cut", TRUE);
+ childSetEnabled("advanced_cut", enabled);
+ }
+ }
+
+ mCtrlPathBegin ->setEnabled( enabled );
+ mCtrlPathEnd ->setEnabled( enabled );
+
+ mLabelTaper ->setEnabled( enabled );
+ mSpinTaperX ->setEnabled( enabled );
+ mSpinTaperY ->setEnabled( enabled );
+
+ mLabelRadiusOffset->setEnabled( enabled );
+ mSpinRadiusOffset ->setEnabled( enabled );
+
+ mLabelRevolutions->setEnabled( enabled );
+ mSpinRevolutions ->setEnabled( enabled );
+
+ // Update field visibility
+ mLabelTwist ->setVisible( twist_visible );
+ mSpinTwist ->setVisible( twist_visible );
+ mSpinTwistBegin ->setVisible( twist_visible );
+ mSpinTwist ->setMinValue( twist_min );
+ mSpinTwist ->setMaxValue( twist_max );
+ mSpinTwist ->setIncrement( twist_inc );
+ mSpinTwistBegin ->setMinValue( twist_min );
+ mSpinTwistBegin ->setMaxValue( twist_max );
+ mSpinTwistBegin ->setIncrement( twist_inc );
+
+ mSpinScaleX ->setVisible( top_size_x_visible );
+ mSpinScaleY ->setVisible( top_size_y_visible );
+
+ mLabelSkew ->setVisible( skew_visible );
+ mSpinSkew ->setVisible( skew_visible );
+
+ mLabelShear ->setVisible( top_shear_x_visible || top_shear_y_visible );
+ mSpinShearX ->setVisible( top_shear_x_visible );
+ mSpinShearY ->setVisible( top_shear_y_visible );
+
+ mCtrlPathBegin ->setVisible( advanced_cut_visible );
+ mCtrlPathEnd ->setVisible( advanced_cut_visible );
+
+ mLabelTaper ->setVisible( taper_visible );
+ mSpinTaperX ->setVisible( taper_visible );
+ mSpinTaperY ->setVisible( taper_visible );
+
+ mLabelRadiusOffset->setVisible( radius_offset_visible );
+ mSpinRadiusOffset ->setVisible( radius_offset_visible );
+
+ mLabelRevolutions->setVisible( revolutions_visible );
+ mSpinRevolutions ->setVisible( revolutions_visible );
+
+ //----------------------------------------------------------------------------
+
+ mObject = objectp;
+ mRootObject = root_objectp;
+}
+
+// static
+BOOL LLPanelObject::precommitValidate( LLUICtrl* ctrl, void* userdata )
+{
+ // TODO: Richard will fill this in later.
+ return TRUE; // FALSE means that validation failed and new value should not be commited.
+}
+
+void LLPanelObject::sendIsPhysical()
+{
+ BOOL value = mCheckPhysics->get();
+ if( mIsPhysical != value )
+ {
+ gSelectMgr->selectionUpdatePhysics(value);
+ mIsPhysical = value;
+
+ llinfos << "update physics sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update physics not changed" << llendl;
+ }
+}
+
+void LLPanelObject::sendIsTemporary()
+{
+ BOOL value = mCheckTemporary->get();
+ if( mIsTemporary != value )
+ {
+ gSelectMgr->selectionUpdateTemporary(value);
+ mIsTemporary = value;
+
+ llinfos << "update temporary sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update temporary not changed" << llendl;
+ }
+}
+
+
+void LLPanelObject::sendIsPhantom()
+{
+ BOOL value = mCheckPhantom->get();
+ if( mIsPhantom != value )
+ {
+ gSelectMgr->selectionUpdatePhantom(value);
+ mIsPhantom = value;
+
+ llinfos << "update phantom sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update phantom not changed" << llendl;
+ }
+}
+
+void LLPanelObject::sendCastShadows()
+{
+ BOOL value = mCheckCastShadows->get();
+ if( mCastShadows != value )
+ {
+ gSelectMgr->selectionUpdateCastShadows(value);
+ mCastShadows = value;
+
+ llinfos << "update cast shadows sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update cast shadows not changed" << llendl;
+ }
+}
+
+// static
+void LLPanelObject::onCommitMaterial( LLUICtrl* ctrl, void* userdata )
+{
+ //LLPanelObject* self = (LLPanelObject*) userdata;
+ LLComboBox* box = (LLComboBox*) ctrl;
+
+ if (box)
+ {
+ // apply the currently selected material to the object
+ const LLString& material_name = box->getSimple();
+ if (material_name != LEGACY_FULLBRIGHT_DESC)
+ {
+ U8 material_code = LLMaterialTable::basic.getMCode(material_name.c_str());
+ gSelectMgr->selectionSetMaterial(material_code);
+ }
+ }
+}
+
+// static
+void LLPanelObject::onCommitParametric( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+
+ if (self->mObject.isNull())
+ {
+ return;
+ }
+
+ if (self->mObject->getPCode() != LL_PCODE_VOLUME)
+ {
+ // Don't allow modification of non-volume objects.
+ return;
+ }
+
+ LLVolume *volume = self->mObject->getVolume();
+ if (!volume)
+ {
+ return;
+ }
+
+ LLVolumeParams volume_params;
+ self->getVolumeParams(volume_params);
+
+ // Update the volume, if necessary.
+ self->mObject->updateVolume(volume_params);
+
+ // This was added to make sure thate when changes are made, the UI
+ // adjusts to present valid options.
+ // *FIX: only some changes, ie, hollow or primitive type changes,
+ // require a refresh.
+ self->refresh();
+
+}
+
+void LLPanelObject::getVolumeParams(LLVolumeParams& volume_params)
+{
+ // Figure out what type of volume to make
+ S32 was_selected_type = mSelectedType;
+ S32 selected_type = mComboBaseType->getCurrentIndex();
+ U8 profile;
+ U8 path;
+ switch ( selected_type )
+ {
+ case MI_CYLINDER:
+ profile = LL_PCODE_PROFILE_CIRCLE;
+ path = LL_PCODE_PATH_LINE;
+ break;
+
+ case MI_BOX:
+ profile = LL_PCODE_PROFILE_SQUARE;
+ path = LL_PCODE_PATH_LINE;
+ break;
+
+ case MI_PRISM:
+ profile = LL_PCODE_PROFILE_EQUALTRI;
+ path = LL_PCODE_PATH_LINE;
+ break;
+
+ case MI_SPHERE:
+ profile = LL_PCODE_PROFILE_CIRCLE_HALF;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ case MI_TORUS:
+ profile = LL_PCODE_PROFILE_CIRCLE;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ case MI_TUBE:
+ profile = LL_PCODE_PROFILE_SQUARE;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ case MI_RING:
+ profile = LL_PCODE_PROFILE_EQUALTRI;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ default:
+ llwarns << "Unknown base type " << selected_type
+ << " in getVolumeParams()" << llendl;
+ // assume a box
+ selected_type = MI_BOX;
+ profile = LL_PCODE_PROFILE_SQUARE;
+ path = LL_PCODE_PATH_LINE;
+ break;
+ }
+
+ if (path == LL_PCODE_PATH_LINE)
+ {
+ LLVOVolume *volobjp = (LLVOVolume *)(LLViewerObject*)(mObject);
+ if (volobjp->isFlexible())
+ {
+ path = LL_PCODE_PATH_FLEXIBLE;
+ }
+ }
+
+ S32 selected_hole = mComboHoleType->getCurrentIndex();
+ U8 hole;
+ switch (selected_hole)
+ {
+ case MI_HOLE_CIRCLE:
+ hole = LL_PCODE_HOLE_CIRCLE;
+ break;
+ case MI_HOLE_SQUARE:
+ hole = LL_PCODE_HOLE_SQUARE;
+ break;
+ case MI_HOLE_TRIANGLE:
+ hole = LL_PCODE_HOLE_TRIANGLE;
+ break;
+ case MI_HOLE_SAME:
+ default:
+ hole = LL_PCODE_HOLE_SAME;
+ break;
+ }
+
+ volume_params.setType(profile | hole, path);
+ mSelectedType = selected_type;
+
+ // Compute cut start/end
+ F32 cut_begin = mSpinCutBegin->get();
+ F32 cut_end = mSpinCutEnd->get();
+
+ // Make sure at least OBJECT_CUT_INC of the object survives
+ if (cut_begin > cut_end - OBJECT_MIN_CUT_INC)
+ {
+ cut_begin = cut_end - OBJECT_MIN_CUT_INC;
+ mSpinCutBegin->set(cut_begin);
+ }
+
+ F32 adv_cut_begin = mCtrlPathBegin->get();
+ F32 adv_cut_end = mCtrlPathEnd->get();
+
+ // Make sure at least OBJECT_CUT_INC of the object survives
+ if (adv_cut_begin > adv_cut_end - OBJECT_MIN_CUT_INC)
+ {
+ adv_cut_begin = adv_cut_end - OBJECT_MIN_CUT_INC;
+ mCtrlPathBegin->set(adv_cut_begin);
+ }
+
+ F32 begin_s, end_s;
+ F32 begin_t, end_t;
+
+ if (selected_type == MI_SPHERE || selected_type == MI_TORUS ||
+ selected_type == MI_TUBE || selected_type == MI_RING)
+ {
+ begin_s = adv_cut_begin;
+ end_s = adv_cut_end;
+
+ begin_t = cut_begin;
+ end_t = cut_end;
+ }
+ else
+ {
+ begin_s = cut_begin;
+ end_s = cut_end;
+
+ begin_t = adv_cut_begin;
+ end_t = adv_cut_end;
+ }
+
+ volume_params.setBeginAndEndS(begin_s, end_s);
+ volume_params.setBeginAndEndT(begin_t, end_t);
+
+ // Hollowness
+ F32 hollow = mSpinHollow->get() / 100.f;
+
+ if ( selected_hole == MI_HOLE_SQUARE &&
+ ( selected_type == MI_CYLINDER || selected_type == MI_TORUS ||
+ selected_type == MI_PRISM || selected_type == MI_RING ||
+ selected_type == MI_SPHERE ) )
+ {
+ if (hollow > 0.7f) hollow = 0.7f;
+ }
+
+ volume_params.setHollow( hollow );
+
+ // Twist Begin,End
+ F32 twist_begin = mSpinTwistBegin->get();
+ F32 twist = mSpinTwist->get();
+ // Check the path type for twist conversion.
+ if (path == LL_PCODE_PATH_LINE || path == LL_PCODE_PATH_FLEXIBLE)
+ {
+ twist_begin /= OBJECT_TWIST_LINEAR_MAX;
+ twist /= OBJECT_TWIST_LINEAR_MAX;
+ }
+ else
+ {
+ twist_begin /= OBJECT_TWIST_MAX;
+ twist /= OBJECT_TWIST_MAX;
+ }
+
+ volume_params.setTwistBegin(twist_begin);
+ volume_params.setTwist(twist);
+
+ // Scale X,Y
+ F32 scale_x = mSpinScaleX->get();
+ F32 scale_y = mSpinScaleY->get();
+ if ( was_selected_type == MI_BOX || was_selected_type == MI_CYLINDER || was_selected_type == MI_PRISM)
+ {
+ scale_x = 1.f - scale_x;
+ scale_y = 1.f - scale_y;
+ }
+
+ // Skew
+ F32 skew = mSpinSkew->get();
+
+ // Taper X,Y
+ F32 taper_x = mSpinTaperX->get();
+ F32 taper_y = mSpinTaperY->get();
+
+ // Radius offset
+ F32 radius_offset = mSpinRadiusOffset->get();
+
+ // Revolutions
+ F32 revolutions = mSpinRevolutions->get();
+
+ if ( selected_type == MI_SPHERE )
+ {
+ // Snap values to valid sphere parameters.
+ scale_x = 1.0f;
+ scale_y = 1.0f;
+ skew = 0.0f;
+ taper_x = 0.0f;
+ taper_y = 0.0f;
+ radius_offset = 0.0f;
+ revolutions = 1.0f;
+ }
+ else if ( selected_type == MI_TORUS || selected_type == MI_TUBE ||
+ selected_type == MI_RING )
+ {
+ scale_x = llclamp(
+ scale_x,
+ OBJECT_MIN_HOLE_SIZE,
+ OBJECT_MAX_HOLE_SIZE_X);
+ scale_y = llclamp(
+ scale_y,
+ OBJECT_MIN_HOLE_SIZE,
+ OBJECT_MAX_HOLE_SIZE_Y);
+
+ // Limit radius offset, based on taper and hole size y.
+ F32 radius_mag = fabs(radius_offset);
+ F32 hole_y_mag = fabs(scale_y);
+ F32 taper_y_mag = fabs(taper_y);
+ // Check to see if the taper effects us.
+ if ( (radius_offset > 0.f && taper_y < 0.f) ||
+ (radius_offset < 0.f && taper_y > 0.f) )
+ {
+ // The taper does not help increase the radius offset range.
+ taper_y_mag = 0.f;
+ }
+ F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+ // Enforce the maximum magnitude.
+ if (radius_mag > max_radius_mag)
+ {
+ // Check radius offset sign.
+ if (radius_offset < 0.f)
+ {
+ radius_offset = -max_radius_mag;
+ }
+ else
+ {
+ radius_offset = max_radius_mag;
+ }
+ }
+
+ // Check the skew value against the revolutions.
+ F32 skew_mag= fabs(skew);
+ F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+ // Discontinuity; A revolution of 1 allows skews below 0.5.
+ if ( fabs(revolutions - 1.0f) < 0.001)
+ min_skew_mag = 0.0f;
+
+ // Clip skew.
+ if (skew_mag < min_skew_mag)
+ {
+ // Check skew sign.
+ if (skew < 0.0f)
+ {
+ skew = -min_skew_mag;
+ }
+ else
+ {
+ skew = min_skew_mag;
+ }
+ }
+ }
+
+ volume_params.setRatio( scale_x, scale_y );
+ volume_params.setSkew(skew);
+ volume_params.setTaper( taper_x, taper_y );
+ volume_params.setRadiusOffset(radius_offset);
+ volume_params.setRevolutions(revolutions);
+
+ // Shear X,Y
+ F32 shear_x = mSpinShearX->get();
+ F32 shear_y = mSpinShearY->get();
+ volume_params.setShear( shear_x, shear_y );
+}
+
+// BUG: Make work with multiple objects
+void LLPanelObject::sendRotation()
+{
+ if (mObject.isNull()) return;
+
+ LLVector3 new_rot(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get());
+ new_rot.mV[VX] = llround(new_rot.mV[VX], OBJECT_ROTATION_PRECISION);
+ new_rot.mV[VY] = llround(new_rot.mV[VY], OBJECT_ROTATION_PRECISION);
+ new_rot.mV[VZ] = llround(new_rot.mV[VZ], OBJECT_ROTATION_PRECISION);
+
+ // Note: must compare before conversion to radians
+ LLVector3 delta = new_rot - mCurEulerDegrees;
+
+ if (delta.magVec() >= 0.0005f)
+ {
+ mCurEulerDegrees = new_rot;
+ new_rot *= DEG_TO_RAD;
+
+ LLQuaternion rotation;
+ rotation.setQuat(new_rot.mV[VX], new_rot.mV[VY], new_rot.mV[VZ]);
+
+ if (mRootObject != mObject)
+ {
+ rotation = rotation * ~mRootObject->getRotationRegion();
+ }
+
+ mObject->setRotation(rotation, TRUE );
+
+ gSelectMgr->sendMultipleUpdate(UPD_ROTATION);
+ }
+}
+
+
+// BUG: Make work with multiple objects
+void LLPanelObject::sendScale()
+{
+ if (mObject.isNull()) return;
+
+ LLVector3 newscale(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get());
+
+ LLVector3 delta = newscale - mObject->getScale();
+ if (delta.magVec() >= 0.0005f)
+ {
+ // scale changed by more than 1/2 millimeter
+
+ // check to see if we aren't scaling the textures
+ // (in which case the tex coord's need to be recomputed)
+ BOOL dont_stretch_textures = !LLManipScale::getStretchTextures();
+ if (dont_stretch_textures)
+ {
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE);
+ }
+
+ mObject->setScale(newscale, TRUE);
+ gSelectMgr->sendMultipleUpdate(UPD_SCALE | UPD_POSITION);
+
+ gSelectMgr->adjustTexturesByScale(TRUE, !dont_stretch_textures);
+// llinfos << "scale sent" << llendl;
+ }
+ else
+ {
+// llinfos << "scale not changed" << llendl;
+ }
+}
+
+
+void LLPanelObject::sendPosition()
+{
+ if (mObject.isNull()) return;
+
+ LLVector3 newpos(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get());
+ LLViewerRegion* regionp = mObject->getRegion();
+
+ // Clamp the Z height
+ const F32 height = newpos.mV[VZ];
+ const F32 min_height = gWorldp->getMinAllowedZ(mObject);
+ const F32 max_height = gWorldPointer->getRegionMaxHeight();
+
+ if (!mObject->isAttachment())
+ {
+ if ( height < min_height)
+ {
+ newpos.mV[VZ] = min_height;
+ mCtrlPosZ->set( min_height );
+ }
+ else if ( height > max_height )
+ {
+ newpos.mV[VZ] = max_height;
+ mCtrlPosZ->set( max_height );
+ }
+
+ // Grass is always drawn on the ground, so clamp its position to the ground
+ if (mObject->getPCode() == LL_PCODE_LEGACY_GRASS)
+ {
+ mCtrlPosZ->set(gWorldp->resolveLandHeightAgent(newpos) + 1.f);
+ }
+ }
+
+ // Make sure new position is in a valid region, so the object
+ // won't get dumped by the simulator.
+ LLVector3d new_pos_global = regionp->getPosGlobalFromRegion(newpos);
+
+ if ( gWorldPointer->positionRegionValidGlobal(new_pos_global) )
+ {
+ // send only if the position is changed, that is, the delta vector is not zero
+ LLVector3d old_pos_global = mObject->getPositionGlobal();
+ LLVector3d delta = new_pos_global - old_pos_global;
+ // moved more than 1/2 millimeter
+ if (delta.magVec() >= 0.0005f)
+ {
+ if (mRootObject != mObject)
+ {
+ newpos = newpos - mRootObject->getPositionRegion();
+ newpos = newpos * ~mRootObject->getRotationRegion();
+ mObject->setPositionParent(newpos);
+ }
+ else
+ {
+ mObject->setPositionEdit(newpos);
+ }
+ gSelectMgr->sendMultipleUpdate(UPD_POSITION);
+ //mRootObject->sendPositionUpdate();
+
+ gSelectMgr->updateSelectionCenter();
+
+// llinfos << "position sent" << llendl;
+ }
+ else
+ {
+// llinfos << "position not changed" << llendl;
+ }
+ }
+ else
+ {
+ // move failed, so we update the UI with the correct values
+ LLVector3 vec = mRootObject->getPositionRegion();
+ mCtrlPosX->set(vec.mV[VX]);
+ mCtrlPosY->set(vec.mV[VY]);
+ mCtrlPosZ->set(vec.mV[VZ]);
+ }
+}
+
+
+void LLPanelObject::refresh()
+{
+ getState();
+ if (mObject.notNull() && mObject->isDead())
+ {
+ mObject = NULL;
+ }
+
+ if (mRootObject.notNull() && mRootObject->isDead())
+ {
+ mRootObject = NULL;
+ }
+}
+
+
+void LLPanelObject::draw()
+{
+ const LLColor4 white( 1.0f, 1.0f, 1.0f, 1);
+ const LLColor4 red( 1.0f, 0.25f, 0.f, 1);
+ const LLColor4 green( 0.f, 1.0f, 0.f, 1);
+ const LLColor4 blue( 0.f, 0.5f, 1.0f, 1);
+
+ // Tune the colors of the labels
+ LLTool* tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+
+ if (tool == gToolTranslate)
+ {
+ mCtrlPosX ->setLabelColor(red);
+ mCtrlPosY ->setLabelColor(green);
+ mCtrlPosZ ->setLabelColor(blue);
+
+ mCtrlScaleX ->setLabelColor(white);
+ mCtrlScaleY ->setLabelColor(white);
+ mCtrlScaleZ ->setLabelColor(white);
+
+ mCtrlRotX ->setLabelColor(white);
+ mCtrlRotY ->setLabelColor(white);
+ mCtrlRotZ ->setLabelColor(white);
+ }
+ else if ( tool == gToolStretch )
+ {
+ mCtrlPosX ->setLabelColor(white);
+ mCtrlPosY ->setLabelColor(white);
+ mCtrlPosZ ->setLabelColor(white);
+
+ mCtrlScaleX ->setLabelColor(red);
+ mCtrlScaleY ->setLabelColor(green);
+ mCtrlScaleZ ->setLabelColor(blue);
+
+ mCtrlRotX ->setLabelColor(white);
+ mCtrlRotY ->setLabelColor(white);
+ mCtrlRotZ ->setLabelColor(white);
+ }
+ else if ( tool == gToolRotate )
+ {
+ mCtrlPosX ->setLabelColor(white);
+ mCtrlPosY ->setLabelColor(white);
+ mCtrlPosZ ->setLabelColor(white);
+
+ mCtrlScaleX ->setLabelColor(white);
+ mCtrlScaleY ->setLabelColor(white);
+ mCtrlScaleZ ->setLabelColor(white);
+
+ mCtrlRotX ->setLabelColor(red);
+ mCtrlRotY ->setLabelColor(green);
+ mCtrlRotZ ->setLabelColor(blue);
+ }
+ else
+ {
+ mCtrlPosX ->setLabelColor(white);
+ mCtrlPosY ->setLabelColor(white);
+ mCtrlPosZ ->setLabelColor(white);
+
+ mCtrlScaleX ->setLabelColor(white);
+ mCtrlScaleY ->setLabelColor(white);
+ mCtrlScaleZ ->setLabelColor(white);
+
+ mCtrlRotX ->setLabelColor(white);
+ mCtrlRotY ->setLabelColor(white);
+ mCtrlRotZ ->setLabelColor(white);
+ }
+
+ LLPanel::draw();
+}
+
+// virtual
+void LLPanelObject::clearCtrls()
+{
+ LLPanel::clearCtrls();
+
+ mCheckLock ->set(FALSE);
+ mCheckLock ->setEnabled( FALSE );
+ mCheckPhysics ->set(FALSE);
+ mCheckPhysics ->setEnabled( FALSE );
+ mCheckTemporary ->set(FALSE);
+ mCheckTemporary ->setEnabled( FALSE );
+ mCheckPhantom ->set(FALSE);
+ mCheckPhantom ->setEnabled( FALSE );
+#if 0 // 1.9.2
+ mCheckCastShadows->set(FALSE);
+ mCheckCastShadows->setEnabled( FALSE );
+#endif
+ mComboMaterial ->setEnabled( FALSE );
+ mLabelMaterial ->setEnabled( FALSE );
+ // Disable text labels
+ mLabelPosition ->setEnabled( FALSE );
+ mLabelSize ->setEnabled( FALSE );
+ mLabelRotation ->setEnabled( FALSE );
+ mLabelBaseType ->setEnabled( FALSE );
+ mLabelCut ->setEnabled( FALSE );
+ mLabelHollow ->setEnabled( FALSE );
+ mLabelHoleType ->setEnabled( FALSE );
+ mLabelTwist ->setEnabled( FALSE );
+ mLabelSkew ->setEnabled( FALSE );
+ mLabelShear ->setEnabled( FALSE );
+ mLabelTaper ->setEnabled( FALSE );
+ mLabelRadiusOffset->setEnabled( FALSE );
+ mLabelRevolutions->setEnabled( FALSE );
+
+ childSetVisible("select_single", TRUE);
+ childSetVisible("edit_object", TRUE);
+ childSetEnabled("edit_object", FALSE);
+
+ childSetEnabled("scale_hole", FALSE);
+ childSetEnabled("scale_taper", FALSE);
+ childSetEnabled( "advanced_cut", FALSE );
+ childSetEnabled( "advanced_dimple", FALSE );
+}
+
+//
+// Static functions
+//
+
+// static
+void LLPanelObject::onCommitLock(LLUICtrl *ctrl, void *data)
+{
+ // Checkbox will have toggled itself
+ LLPanelObject *self = (LLPanelObject *)data;
+
+ if(self->mRootObject.isNull()) return;
+
+ BOOL new_state = self->mCheckLock->get();
+
+ gSelectMgr->setObjectPermissions(PERM_OWNER, !new_state, PERM_MOVE | PERM_MODIFY);
+}
+
+// static
+void LLPanelObject::onCommitPosition( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendPosition();
+}
+
+// static
+void LLPanelObject::onCommitScale( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendScale();
+}
+
+// static
+void LLPanelObject::onCommitRotation( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendRotation();
+}
+
+// static
+void LLPanelObject::onCommitPhysics( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendIsPhysical();
+}
+
+// static
+void LLPanelObject::onCommitTemporary( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendIsTemporary();
+}
+
+// static
+void LLPanelObject::onCommitPhantom( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendIsPhantom();
+}
+
+// static
+void LLPanelObject::onCommitCastShadows( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendCastShadows();
+}
diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h
new file mode 100644
index 0000000000..07b65b6372
--- /dev/null
+++ b/indra/newview/llpanelobject.h
@@ -0,0 +1,147 @@
+/**
+ * @file llpanelobject.h
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELOBJECT_H
+#define LL_LLPANELOBJECT_H
+
+#include "v3math.h"
+#include "llpanel.h"
+#include "llmemory.h"
+#include "llvolume.h"
+
+class LLSpinCtrl;
+class LLCheckBoxCtrl;
+class LLTextBox;
+class LLUICtrl;
+class LLButton;
+class LLViewerObject;
+class LLComboBox;
+class LLPanelInventory;
+class LLColorSwatchCtrl;
+
+class LLPanelObject : public LLPanel
+{
+public:
+ LLPanelObject(const std::string& name);
+ virtual ~LLPanelObject();
+
+ virtual BOOL postBuild();
+ virtual void draw();
+ virtual void clearCtrls();
+
+ void refresh();
+
+ static BOOL precommitValidate(LLUICtrl* ctrl,void* userdata);
+
+ static void onCommitLock(LLUICtrl *ctrl, void *data);
+ static void onCommitPosition( LLUICtrl* ctrl, void* userdata);
+ static void onCommitScale( LLUICtrl* ctrl, void* userdata);
+ static void onCommitRotation( LLUICtrl* ctrl, void* userdata);
+ static void onCommitPhysics( LLUICtrl* ctrl, void* userdata);
+ static void onCommitTemporary( LLUICtrl* ctrl, void* userdata);
+ static void onCommitPhantom( LLUICtrl* ctrl, void* userdata);
+ static void onCommitCastShadows( LLUICtrl* ctrl, void* userdata);
+
+ static void onCommitParametric(LLUICtrl* ctrl, void* userdata);
+
+ static void onCommitMaterial( LLUICtrl* ctrl, void* userdata);
+
+protected:
+ void getState();
+
+ void sendRotation();
+ void sendScale();
+ void sendPosition();
+ void sendIsPhysical();
+ void sendIsTemporary();
+ void sendIsPhantom();
+ void sendCastShadows();
+
+ void getVolumeParams(LLVolumeParams& volume_params);
+
+protected:
+ S32 mComboMaterialItemCount;
+
+ LLTextBox* mLabelMaterial;
+ LLComboBox* mComboMaterial;
+
+ // Per-object options
+ LLTextBox* mLabelBaseType;
+ LLComboBox* mComboBaseType;
+
+ LLTextBox* mLabelCut;
+ LLSpinCtrl* mSpinCutBegin;
+ LLSpinCtrl* mSpinCutEnd;
+
+ LLTextBox* mLabelHollow;
+ LLSpinCtrl* mSpinHollow;
+
+ LLTextBox* mLabelHoleType;
+ LLComboBox* mComboHoleType;
+
+ LLTextBox* mLabelTwist;
+ LLSpinCtrl* mSpinTwist;
+ LLSpinCtrl* mSpinTwistBegin;
+
+ LLSpinCtrl* mSpinScaleX;
+ LLSpinCtrl* mSpinScaleY;
+
+ LLTextBox* mLabelSkew;
+ LLSpinCtrl* mSpinSkew;
+
+ LLTextBox* mLabelShear;
+ LLSpinCtrl* mSpinShearX;
+ LLSpinCtrl* mSpinShearY;
+
+ // Advanced Path
+ LLSpinCtrl* mCtrlPathBegin;
+ LLSpinCtrl* mCtrlPathEnd;
+
+ LLTextBox* mLabelTaper;
+ LLSpinCtrl* mSpinTaperX;
+ LLSpinCtrl* mSpinTaperY;
+
+ LLTextBox* mLabelRadiusOffset;
+ LLSpinCtrl* mSpinRadiusOffset;
+
+ LLTextBox* mLabelRevolutions;
+ LLSpinCtrl* mSpinRevolutions;
+
+ LLTextBox* mLabelPosition;
+ LLSpinCtrl* mCtrlPosX;
+ LLSpinCtrl* mCtrlPosY;
+ LLSpinCtrl* mCtrlPosZ;
+
+ LLTextBox* mLabelSize;
+ LLSpinCtrl* mCtrlScaleX;
+ LLSpinCtrl* mCtrlScaleY;
+ LLSpinCtrl* mCtrlScaleZ;
+
+ LLTextBox* mLabelRotation;
+ LLSpinCtrl* mCtrlRotX;
+ LLSpinCtrl* mCtrlRotY;
+ LLSpinCtrl* mCtrlRotZ;
+
+ LLCheckBoxCtrl *mCheckLock;
+ LLCheckBoxCtrl *mCheckPhysics;
+ LLCheckBoxCtrl *mCheckTemporary;
+ LLCheckBoxCtrl *mCheckPhantom;
+ LLCheckBoxCtrl *mCheckCastShadows;
+
+ LLVector3 mCurEulerDegrees; // to avoid sending rotation when not changed
+ BOOL mIsPhysical; // to avoid sending "physical" when not changed
+ BOOL mIsTemporary; // to avoid sending "temporary" when not changed
+ BOOL mIsPhantom; // to avoid sending "phantom" when not changed
+ BOOL mCastShadows; // to avoid sending "cast shadows" when not changed
+ S32 mSelectedType; // So we know what selected type we last were
+
+ LLPointer<LLViewerObject> mObject;
+ LLPointer<LLViewerObject> mRootObject;
+};
+
+#endif
diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp
new file mode 100644
index 0000000000..9e23b977d9
--- /dev/null
+++ b/indra/newview/llpanelpermissions.cpp
@@ -0,0 +1,1028 @@
+/**
+ * @file llpanelpermissions.cpp
+ * @brief LLPanelPermissions class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+//
+// This class represents the panel in the build view for
+// viewing/editing object names, owners, permissions, etc.
+//
+//*****************************************************************************
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelpermissions.h"
+
+#include "lluuid.h"
+#include "llpermissions.h"
+#include "llcategory.h"
+#include "llclickaction.h"
+#include "llfocusmgr.h"
+#include "llstring.h"
+
+#include "llviewerwindow.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llviewerobject.h"
+#include "llselectmgr.h"
+#include "llagent.h"
+#include "llstatusbar.h" // for getBalance()
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llcombobox.h"
+#include "llfloateravatarinfo.h"
+#include "lluiconstants.h"
+#include "lldbstrings.h"
+#include "llfloatergroupinfo.h"
+#include "llfloatergroups.h"
+#include "llnamebox.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "roles_constants.h"
+
+///----------------------------------------------------------------------------
+/// Class llpanelpermissions
+///----------------------------------------------------------------------------
+
+// Default constructor
+
+BOOL LLPanelPermissions::postBuild()
+{
+ this->childSetCommitCallback("Object Name",LLPanelPermissions::onCommitName,this);
+ this->childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe);
+ this->childSetCommitCallback("Object Description",LLPanelPermissions::onCommitDesc,this);
+ this->childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe);
+
+
+ this->childSetAction("button owner profile",LLPanelPermissions::onClickOwner,this);
+ this->childSetAction("button creator profile",LLPanelPermissions::onClickCreator,this);
+
+ this->childSetAction("button set group",LLPanelPermissions::onClickGroup,this);
+
+ this->childSetCommitCallback("checkbox share with group",LLPanelPermissions::onCommitGroupShare,this);
+
+ this->childSetAction("button deed",LLPanelPermissions::onClickDeedToGroup,this);
+
+ this->childSetCommitCallback("checkbox allow everyone move",LLPanelPermissions::onCommitEveryoneMove,this);
+
+ this->childSetCommitCallback("checkbox allow everyone copy",LLPanelPermissions::onCommitEveryoneCopy,this);
+
+ this->childSetCommitCallback("checkbox for sale",LLPanelPermissions::onCommitSaleInfo,this);
+
+ this->childSetCommitCallback("EdCost",LLPanelPermissions::onCommitSaleInfo,this);
+ this->childSetPrevalidate("EdCost",LLLineEditor::prevalidateNonNegativeS32);
+
+ this->childSetCommitCallback("sale type",LLPanelPermissions::onCommitSaleType,this);
+
+ this->childSetCommitCallback("checkbox next owner can modify",LLPanelPermissions::onCommitNextOwnerModify,this);
+ this->childSetCommitCallback("checkbox next owner can copy",LLPanelPermissions::onCommitNextOwnerCopy,this);
+ this->childSetCommitCallback("checkbox next owner can transfer",LLPanelPermissions::onCommitNextOwnerTransfer,this);
+ this->childSetCommitCallback("clickaction",LLPanelPermissions::onCommitClickAction,this);
+
+ LLTextBox* LabelGroupNameRectProxy = gUICtrlFactory->getTextBoxByName(this,"Group Name Proxy");
+ if(LabelGroupNameRectProxy )
+ {
+ mLabelGroupName = new LLNameBox("Group Name",LabelGroupNameRectProxy->getRect());
+ addChild(mLabelGroupName);
+ }else
+ mLabelGroupName = NULL;
+
+ return TRUE;
+}
+
+LLPanelPermissions::LLPanelPermissions(const std::string& title) :
+ LLPanel(title)
+{
+ setMouseOpaque(FALSE);
+}
+
+// Destroys the object
+LLPanelPermissions::~LLPanelPermissions()
+{
+ // base class will take care of everything
+}
+
+
+void LLPanelPermissions::refresh()
+{
+
+ LLButton* BtnDeedToGroup = gUICtrlFactory->getButtonByName(this,"button deed");
+ if(BtnDeedToGroup)
+ {
+ LLString deedText;
+ if (gSavedSettings.getWarning("DeedObject"))
+ {
+ deedText = this->childGetText("text deed continued");
+ }
+ else
+ {
+ deedText = this->childGetText("text deed");
+ }
+ BtnDeedToGroup->setLabelSelected(deedText);
+ BtnDeedToGroup->setLabelUnselected(deedText);
+ }
+ BOOL root_selected = TRUE;
+ LLSelectNode* nodep = gSelectMgr->getFirstRootNode();
+ S32 object_count = gSelectMgr->getRootObjectCount();
+ if(!nodep || 0 == object_count)
+ {
+ nodep = gSelectMgr->getFirstNode();
+ object_count = gSelectMgr->getObjectCount();
+ root_selected = FALSE;
+ }
+
+ BOOL attachment_selected = gSelectMgr->selectionIsAttachment();
+
+ LLViewerObject* objectp = NULL;
+ if(nodep) objectp = nodep->getObject();
+ if(!nodep || !objectp || attachment_selected)
+ {
+ // ...nothing selected
+ childSetEnabled("perm_modify",false);
+ childSetText("perm_modify","");
+
+ childSetEnabled("Creator:",false);
+ childSetText("Creator Name","");
+ childSetEnabled("Creator Name",false);
+ childSetEnabled("button creator profile",false);
+
+ childSetEnabled("Owner:",false);
+ childSetText("Owner Name","");
+ childSetEnabled("Owner Name",false);
+ childSetEnabled("button owner profile",false);
+
+ childSetEnabled("Group:",false);
+ childSetText("Group Name","");
+ childSetEnabled("Group Name",false);
+ childSetEnabled("button set group",false);
+
+ childSetText("Object Name","");
+ childSetEnabled("Object Name",false);
+ childSetEnabled("Name:",false);
+ childSetText("Group Name","");
+ childSetEnabled("Group Name",false);
+ childSetEnabled("Description:",false);
+ childSetText("Object Description","");
+ childSetEnabled("Object Description",false);
+
+ childSetText("prim info","");
+ childSetEnabled("prim info",false);
+
+ childSetEnabled("Permissions:",false);
+
+ childSetValue("checkbox share with group",FALSE);
+ childSetEnabled("checkbox share with group",false);
+ childSetEnabled("button deed",false);
+
+ childSetValue("checkbox allow everyone move",FALSE);
+ childSetEnabled("checkbox allow everyone move",false);
+ childSetValue("checkbox allow everyone copy",FALSE);
+ childSetEnabled("checkbox allow everyone copy",false);
+
+ //Next owner can:
+ childSetEnabled("Next owner can:",false);
+ childSetValue("checkbox next owner can modify",FALSE);
+ childSetEnabled("checkbox next owner can modify",false);
+ childSetValue("checkbox next owner can copy",FALSE);
+ childSetEnabled("checkbox next owner can copy",false);
+ childSetValue("checkbox next owner can transfer",FALSE);
+ childSetEnabled("checkbox next owner can transfer",false);
+
+ //checkbox for sale
+ childSetValue("checkbox for sale",FALSE);
+ childSetEnabled("checkbox for sale",false);
+
+ LLRadioGroup* RadioSaleType = gUICtrlFactory->getRadioGroupByName(this,"sale type");
+ if(RadioSaleType)
+ {
+ RadioSaleType->setSelectedIndex(-1);
+ RadioSaleType->setEnabled(FALSE);
+ }
+
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost",false);
+ childSetEnabled("EdCost",false);
+
+ childSetEnabled("label click action",false);
+ LLComboBox* ComboClickAction = gUICtrlFactory->getComboBoxByName(this,"clickaction");
+ if(ComboClickAction)
+ {
+ ComboClickAction->setEnabled(FALSE);
+ ComboClickAction->clear();
+ }
+ childSetVisible("B:",false);
+ childSetVisible("O:",false);
+ childSetVisible("G:",false);
+ childSetVisible("E:",false);
+ childSetVisible("N:",false);
+ childSetVisible("F:",false);
+
+ return;
+ }
+
+ // figure out a few variables
+ BOOL is_one_object = (object_count == 1);
+
+ // BUG: fails if a root and non-root are both single-selected.
+ BOOL is_perm_modify = (gSelectMgr->getFirstRootNode()
+ && gSelectMgr->selectGetRootsModify())
+ || gSelectMgr->selectGetModify();
+ const LLView* keyboard_focus_view = gFocusMgr.getKeyboardFocus();
+ S32 string_index = 0;
+ LLString MODIFY_INFO_STRINGS[] =
+ {
+ childGetText("text modify info 1"),
+ childGetText("text modify info 2"),
+ childGetText("text modify info 3"),
+ childGetText("text modify info 4")
+ };
+ if(!is_perm_modify)
+ {
+ string_index += 2;
+ }
+ if(!is_one_object)
+ {
+ ++string_index;
+ }
+ childSetEnabled("perm_modify",true);
+ childSetText("perm_modify",MODIFY_INFO_STRINGS[string_index]);
+
+ childSetEnabled("Permissions:",true);
+
+ // Update creator text field
+ childSetEnabled("Creator:",true);
+ BOOL creators_identical;
+ LLString creator_name;
+ creators_identical = gSelectMgr->selectGetCreator(mCreatorID,
+ creator_name);
+
+ childSetText("Creator Name",creator_name);
+ childSetEnabled("Creator Name",TRUE);
+ childSetEnabled("button creator profile", creators_identical && mCreatorID.notNull() );
+
+ // Update owner text field
+ childSetEnabled("Owner:",true);
+
+ BOOL owners_identical;
+ LLString owner_name;
+ owners_identical = gSelectMgr->selectGetOwner(mOwnerID, owner_name);
+
+// llinfos << "owners_identical " << (owners_identical ? "TRUE": "FALSE") << llendl;
+
+ if (mOwnerID.isNull())
+ {
+ if(gSelectMgr->selectIsGroupOwned())
+ {
+ // Group owned already displayed by selectGetOwner
+ }
+ else
+ {
+ // Display last owner if public
+ LLString last_owner_name;
+ gSelectMgr->selectGetLastOwner(mLastOwnerID, last_owner_name);
+
+ // It should never happen that the last owner is null and the owner
+ // is null, but it seems to be a bug in the simulator right now. JC
+ if (!mLastOwnerID.isNull() && !last_owner_name.empty())
+ {
+ owner_name.append(", last ");
+ owner_name.append( last_owner_name );
+ }
+ }
+ }
+
+ childSetText("Owner Name",owner_name);
+ childSetEnabled("Owner Name",TRUE);
+ childSetEnabled("button owner profile",owners_identical && (mOwnerID.notNull() || gSelectMgr->selectIsGroupOwned()));
+
+ // update group text field
+ childSetEnabled("Group:",true);
+ LLUUID group_id;
+ BOOL groups_identical = gSelectMgr->selectGetGroup(group_id);
+ if (groups_identical)
+ {
+ if(mLabelGroupName)
+ {
+ mLabelGroupName->setNameID(group_id, TRUE);
+ mLabelGroupName->setEnabled(TRUE);
+ }
+ }
+ childSetEnabled("button set group",owners_identical && (mOwnerID == gAgent.getID()));
+
+ // figure out the contents of the name, description, & category
+ BOOL edit_name_desc = FALSE;
+ if(is_one_object && objectp->permModify())
+ {
+ edit_name_desc = TRUE;
+ }
+ if(is_one_object)
+ {
+ childSetEnabled("Name:",true);
+ LLLineEditor* LineEditorObjectName = gUICtrlFactory->getLineEditorByName(this,"Object Name");
+ if(keyboard_focus_view != LineEditorObjectName)
+ {
+ childSetText("Object Name",nodep->mName);
+ }
+
+ childSetEnabled("Description:",true);
+ LLLineEditor* LineEditorObjectDesc = gUICtrlFactory->getLineEditorByName(this,"Object Description");
+ if(LineEditorObjectDesc)
+ {
+ if(keyboard_focus_view != LineEditorObjectDesc)
+ {
+ LineEditorObjectDesc->setText(nodep->mDescription);
+ }
+ }
+ }
+
+ if(edit_name_desc)
+ {
+ childSetEnabled("Object Name",true);
+ childSetEnabled("Object Description",true);
+ }
+ else
+ {
+ childSetEnabled("Object Name",false);
+ childSetEnabled("Object Description",false);
+ }
+
+
+ // Pre-compute object info string
+ S32 prim_count = gSelectMgr->getObjectCount();
+ S32 obj_count = gSelectMgr->getRootObjectCount();
+
+ LLString object_info_string;
+ if (1 == obj_count)
+ {
+ object_info_string.assign("1 Object, ");
+ }
+ else
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d Objects, ", obj_count);
+ object_info_string.assign(buffer);
+ }
+ if (1 == prim_count)
+ {
+ object_info_string.append("1 Primitive");
+ }
+ else
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d Primitives", prim_count);
+ object_info_string.append(buffer);
+ }
+ childSetText("prim info",object_info_string);
+ childSetEnabled("prim info",true);
+
+ S32 price;
+ BOOL is_for_sale = gSelectMgr->selectIsForSale(price);
+ if (!is_for_sale)
+ {
+ price = DEFAULT_PRICE;
+ }
+
+ BOOL self_owned = (gAgent.getID() == mOwnerID);
+ BOOL group_owned = gSelectMgr->selectIsGroupOwned() ;
+ BOOL public_owned = (mOwnerID.isNull() && !gSelectMgr->selectIsGroupOwned());
+ BOOL can_transfer = gSelectMgr->selectGetRootsTransfer();
+ BOOL can_copy = gSelectMgr->selectGetRootsCopy();
+
+ if(!owners_identical)
+ {
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost","");
+ childSetEnabled("EdCost",false);
+ }
+ else if(self_owned || (group_owned && gAgent.hasPowerInGroup(group_id,GP_OBJECT_SET_SALE)))
+ {
+ LLLineEditor* EditPrice = gUICtrlFactory->getLineEditorByName(this,"EdCost");
+ if(keyboard_focus_view != EditPrice)
+ {
+ childSetText("EdCost",llformat("%d",price));
+ }
+ if(is_for_sale && is_one_object && can_transfer)
+ {
+ childSetEnabled("Price: L$",true);
+ childSetEnabled("EdCost",true);
+ }
+ else
+ {
+ childSetEnabled("Price: L$",false);
+ childSetEnabled("EdCost",false);
+ }
+ }
+ else if(!public_owned)
+ {
+ // ...someone, not you, owns it
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost",llformat("%d",price));
+ childSetEnabled("EdCost",false);
+ }
+ else
+ {
+ // ...public object
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost","");
+ childSetEnabled("EdCost",false);
+ }
+
+ // Enable and disable the permissions checkboxes
+ // based on who owns the object.
+ // TODO: Creator permissions
+
+ BOOL valid_base_perms = FALSE;
+ BOOL valid_owner_perms = FALSE;
+ BOOL valid_group_perms = FALSE;
+ BOOL valid_everyone_perms = FALSE;
+ BOOL valid_next_perms = FALSE;
+
+ U32 base_mask_on;
+ U32 base_mask_off;
+ U32 owner_mask_on;
+ U32 owner_mask_off;
+ U32 group_mask_on;
+ U32 group_mask_off;
+ U32 everyone_mask_on;
+ U32 everyone_mask_off;
+ U32 next_owner_mask_on = 0;
+ U32 next_owner_mask_off = 0;
+
+ valid_base_perms = gSelectMgr->selectGetPerm(PERM_BASE,
+ &base_mask_on,
+ &base_mask_off);
+
+ valid_owner_perms = gSelectMgr->selectGetPerm(PERM_OWNER,
+ &owner_mask_on,
+ &owner_mask_off);
+
+ valid_group_perms = gSelectMgr->selectGetPerm(PERM_GROUP,
+ &group_mask_on,
+ &group_mask_off);
+
+ valid_everyone_perms = gSelectMgr->selectGetPerm(PERM_EVERYONE,
+ &everyone_mask_on,
+ &everyone_mask_off);
+
+ valid_next_perms = gSelectMgr->selectGetPerm(PERM_NEXT_OWNER,
+ &next_owner_mask_on,
+ &next_owner_mask_off);
+
+
+ if( gSavedSettings.getBOOL("DebugPermissions") )
+ {
+ char perm_string[10];
+ if (valid_base_perms)
+ {
+
+ strcpy(perm_string, "B: ");
+ mask_to_string(base_mask_on, perm_string+3);
+ childSetText("B:",perm_string);
+ childSetVisible("B:",true);
+
+ strcpy(perm_string, "O: ");
+ mask_to_string(owner_mask_on, perm_string+3);
+ childSetText("O:",perm_string);
+ childSetVisible("O:",true);
+
+ strcpy(perm_string, "G: ");
+ mask_to_string(group_mask_on, perm_string+3);
+ childSetText("G:",perm_string);
+ childSetVisible("G:",true);
+
+ strcpy(perm_string, "E: ");
+ mask_to_string(everyone_mask_on, perm_string+3);
+ childSetText("E:",perm_string);
+ childSetVisible("E:",true);
+
+ strcpy(perm_string, "N: ");
+ mask_to_string(next_owner_mask_on, perm_string+3);
+ childSetText("N:",perm_string);
+ childSetVisible("N:",true);
+ }
+ strcpy(perm_string, "F: ");
+ U32 flag_mask = 0x0;
+ if (objectp->permMove())
+ flag_mask |= PERM_MOVE;
+ if (objectp->permModify())
+ flag_mask |= PERM_MODIFY;
+ if (objectp->permCopy())
+ flag_mask |= PERM_COPY;
+ if (objectp->permTransfer())
+ flag_mask |= PERM_TRANSFER;
+ mask_to_string(flag_mask, perm_string+3);
+ childSetText("F:",perm_string);
+ childSetVisible("F:",true);
+ }
+ else
+ {
+ childSetVisible("B:",false);
+ childSetVisible("O:",false);
+ childSetVisible("G:",false);
+ childSetVisible("E:",false);
+ childSetVisible("N:",false);
+ childSetVisible("F:",false);
+ }
+
+ bool has_change_perm_ability = false;
+ bool has_change_sale_ability = false;
+
+ if(valid_base_perms
+ && (self_owned
+ || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_MANIPULATE))))
+ {
+ has_change_perm_ability = true;
+ }
+ if(valid_base_perms
+ && (self_owned
+ || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_SET_SALE))))
+ {
+ has_change_sale_ability = true;
+ }
+
+ if (!has_change_perm_ability && !has_change_sale_ability && !root_selected)
+ {
+ // XUI:translate
+ // ...must select root to choose permissions
+ childSetValue("perm_modify", "Must select entire object to set permissions.");
+ }
+
+ if (has_change_perm_ability)
+ {
+ childSetEnabled("checkbox share with group",true);
+ childSetEnabled("checkbox allow everyone move",owner_mask_on & PERM_MOVE);
+ childSetEnabled("checkbox allow everyone copy",owner_mask_on & PERM_COPY && owner_mask_on & PERM_TRANSFER);
+ }
+ else
+ {
+ childSetEnabled("checkbox share with group", FALSE);
+ childSetEnabled("checkbox allow everyone move", FALSE);
+ childSetEnabled("checkbox allow everyone copy", FALSE);
+ }
+
+ if (has_change_sale_ability && (owner_mask_on & PERM_TRANSFER))
+ {
+ childSetEnabled("checkbox for sale", can_transfer || (!can_transfer && is_for_sale));
+ childSetEnabled("sale type",is_for_sale && can_transfer);
+
+ childSetEnabled("Next owner can:", TRUE);
+ childSetEnabled("checkbox next owner can modify",base_mask_on & PERM_MODIFY);
+ childSetEnabled("checkbox next owner can copy",base_mask_on & PERM_COPY);
+ childSetEnabled("checkbox next owner can transfer",next_owner_mask_on & PERM_COPY);
+ }
+ else
+ {
+ childSetEnabled("checkbox for sale",FALSE);
+ childSetEnabled("sale type",FALSE);
+
+ childSetEnabled("Next owner can:",FALSE);
+ childSetEnabled("checkbox next owner can modify",FALSE);
+ childSetEnabled("checkbox next owner can copy",FALSE);
+ childSetEnabled("checkbox next owner can transfer",FALSE);
+ }
+
+ if(valid_group_perms)
+ {
+ if((group_mask_on & PERM_COPY) && (group_mask_on & PERM_MODIFY) && (group_mask_on & PERM_MOVE))
+ {
+ childSetValue("checkbox share with group",TRUE);
+ childSetTentative("checkbox share with group",FALSE);
+ childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (owner_mask_on & PERM_TRANSFER) && !group_owned);
+ }
+ else if((group_mask_off & PERM_COPY) && (group_mask_off & PERM_MODIFY) && (group_mask_off & PERM_MOVE))
+ {
+ childSetValue("checkbox share with group",FALSE);
+ childSetTentative("checkbox share with group",false);
+ childSetEnabled("button deed",false);
+ }
+ else
+ {
+ childSetValue("checkbox share with group",TRUE);
+ childSetTentative("checkbox share with group",true);
+ childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (group_mask_on & PERM_MOVE) && (owner_mask_on & PERM_TRANSFER) && !group_owned);
+ }
+ }
+
+ if(valid_everyone_perms)
+ {
+ // Move
+ if(everyone_mask_on & PERM_MOVE)
+ {
+ childSetValue("checkbox allow everyone move",TRUE);
+ childSetTentative("checkbox allow everyone move",false);
+ }
+ else if(everyone_mask_off & PERM_MOVE)
+ {
+ childSetValue("checkbox allow everyone move",FALSE);
+ childSetTentative("checkbox allow everyone move",false);
+ }
+ else
+ {
+ childSetValue("checkbox allow everyone move",TRUE);
+ childSetTentative("checkbox allow everyone move",true);
+ }
+
+ // Copy == everyone can't copy
+ if(everyone_mask_on & PERM_COPY)
+ {
+ childSetValue("checkbox allow everyone copy",TRUE);
+ childSetTentative("checkbox allow everyone copy",!can_copy || !can_transfer);
+ }
+ else if(everyone_mask_off & PERM_COPY)
+ {
+ childSetValue("checkbox allow everyone copy",FALSE);
+ childSetTentative("checkbox allow everyone copy",false);
+ }
+ else
+ {
+ childSetValue("checkbox allow everyone copy",TRUE);
+ childSetTentative("checkbox allow everyone copy",true);
+ }
+ }
+
+ if(valid_next_perms)
+ {
+ // Modify == next owner canot modify
+ if(next_owner_mask_on & PERM_MODIFY)
+ {
+ childSetValue("checkbox next owner can modify",TRUE);
+ childSetTentative("checkbox next owner can modify",false);
+ }
+ else if(next_owner_mask_off & PERM_MODIFY)
+ {
+ childSetValue("checkbox next owner can modify",FALSE);
+ childSetTentative("checkbox next owner can modify",false);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can modify",TRUE);
+ childSetTentative("checkbox next owner can modify",true);
+ }
+
+ // Copy == next owner cannot copy
+ if(next_owner_mask_on & PERM_COPY)
+ {
+ childSetValue("checkbox next owner can copy",TRUE);
+ childSetTentative("checkbox next owner can copy",!can_copy);
+ }
+ else if(next_owner_mask_off & PERM_COPY)
+ {
+ childSetValue("checkbox next owner can copy",FALSE);
+ childSetTentative("checkbox next owner can copy",FALSE);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can copy",TRUE);
+ childSetTentative("checkbox next owner can copy",TRUE);
+ }
+
+ // Transfer == next owner cannot transfer
+ if(next_owner_mask_on & PERM_TRANSFER)
+ {
+ childSetValue("checkbox next owner can transfer",TRUE);
+ childSetTentative("checkbox next owner can transfer",!can_transfer);
+ }
+ else if(next_owner_mask_off & PERM_TRANSFER)
+ {
+ childSetValue("checkbox next owner can transfer",FALSE);
+ childSetTentative("checkbox next owner can transfer",FALSE);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can transfer",TRUE);
+ childSetTentative("checkbox next owner can transfer",TRUE);
+ }
+ }
+
+ // reflect sale information
+ LLSaleInfo sale_info;
+ BOOL valid_sale_info = gSelectMgr->selectGetSaleInfo(sale_info);
+ LLSaleInfo::EForSale sale_type = sale_info.getSaleType();
+
+ LLRadioGroup* RadioSaleType = gUICtrlFactory->getRadioGroupByName(this,"sale type");
+ if(RadioSaleType && valid_sale_info)
+ {
+ RadioSaleType->setSelectedIndex((S32)sale_type - 1);
+ }
+
+ if (is_for_sale)
+ {
+ childSetValue("checkbox for sale",TRUE);
+ childSetTentative("checkbox for sale",!can_transfer || (!can_copy && sale_type == LLSaleInfo::FS_COPY));
+ }
+ else
+ {
+ childSetValue("checkbox for sale",FALSE);
+ childSetTentative("checkbox for sale",false);
+ }
+
+ // Click action (touch, sit, buy)
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+ U8 click_action = 0;
+ if (gSelectMgr->selectionGetClickAction(&click_action))
+ {
+ LLComboBox* ComboClickAction = gUICtrlFactory->getComboBoxByName(this,"clickaction");
+ if(ComboClickAction)
+ {
+ ComboClickAction->setCurrentByIndex((S32)click_action);
+ }
+ }
+ childSetEnabled("label click action",is_perm_modify && all_volume);
+ childSetEnabled("clickaction",is_perm_modify && all_volume);
+}
+
+
+// static
+void LLPanelPermissions::onClickClaim(void*)
+{
+ // try to claim ownership
+ gSelectMgr->sendOwner(gAgent.getID(), gAgent.getGroupID());
+}
+
+// static
+void LLPanelPermissions::onClickRelease(void*)
+{
+ // try to release ownership
+ gSelectMgr->sendOwner(LLUUID::null, LLUUID::null);
+}
+
+// static
+void LLPanelPermissions::onClickCreator(void *data)
+{
+ LLPanelPermissions *self = (LLPanelPermissions *)data;
+
+ LLFloaterAvatarInfo::showFromObject(self->mCreatorID);
+}
+
+// static
+void LLPanelPermissions::onClickOwner(void *data)
+{
+ LLPanelPermissions *self = (LLPanelPermissions *)data;
+
+ if (gSelectMgr->selectIsGroupOwned())
+ {
+ LLUUID group_id;
+ gSelectMgr->selectGetGroup(group_id);
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+ else
+ {
+ LLFloaterAvatarInfo::showFromObject(self->mOwnerID);
+ }
+}
+
+void LLPanelPermissions::onClickGroup(void* data)
+{
+ LLUUID owner_id;
+ LLString name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, name);
+ if(owners_identical && (owner_id == gAgent.getID()))
+ {
+ LLFloaterGroups* fg;
+ fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ fg->setOkCallback( cbGroupID, data );
+ }
+}
+
+// static
+void LLPanelPermissions::cbGroupID(LLUUID group_id, void* userdata)
+{
+ LLPanelPermissions* self = (LLPanelPermissions*)userdata;
+ if(self->mLabelGroupName)
+ {
+ self->mLabelGroupName->setNameID(group_id, TRUE);
+ }
+ gSelectMgr->sendGroup(group_id);
+}
+
+void callback_deed_to_group(S32 option, void*)
+{
+ if (0 == option)
+ {
+ LLUUID group_id;
+ BOOL groups_identical = gSelectMgr->selectGetGroup(group_id);
+ if(groups_identical && (gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED)))
+ {
+ gSelectMgr->sendOwner(LLUUID::null, group_id, FALSE);
+// gViewerStats->incStat(LLViewerStats::ST_RELEASE_COUNT);
+ }
+ }
+}
+
+void LLPanelPermissions::onClickDeedToGroup(void* data)
+{
+ gViewerWindow->alertXml( "DeedObjectToGroup",
+ callback_deed_to_group, NULL);
+}
+
+///----------------------------------------------------------------------------
+/// Permissions checkboxes
+///----------------------------------------------------------------------------
+
+// static
+void LLPanelPermissions::onCommitPerm(LLUICtrl *ctrl, void *data, U8 field, U32 perm)
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object) return;
+
+ // Checkbox will have toggled itself
+ // LLPanelPermissions* self = (LLPanelPermissions*)data;
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ BOOL new_state = check->get();
+
+ gSelectMgr->setObjectPermissions(field, new_state, perm);
+}
+
+// static
+void LLPanelPermissions::onCommitGroupShare(LLUICtrl *ctrl, void *data)
+{
+ onCommitPerm(ctrl, data, PERM_GROUP, PERM_MODIFY | PERM_MOVE | PERM_COPY);
+}
+
+// static
+void LLPanelPermissions::onCommitEveryoneMove(LLUICtrl *ctrl, void *data)
+{
+ onCommitPerm(ctrl, data, PERM_EVERYONE, PERM_MOVE);
+}
+
+
+// static
+void LLPanelPermissions::onCommitEveryoneCopy(LLUICtrl *ctrl, void *data)
+{
+ onCommitPerm(ctrl, data, PERM_EVERYONE, PERM_COPY);
+}
+
+// static
+void LLPanelPermissions::onCommitNextOwnerModify(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitNextOwnerModify" << llendl;
+ onCommitPerm(ctrl, data, PERM_NEXT_OWNER, PERM_MODIFY);
+}
+
+// static
+void LLPanelPermissions::onCommitNextOwnerCopy(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitNextOwnerCopy" << llendl;
+ onCommitPerm(ctrl, data, PERM_NEXT_OWNER, PERM_COPY);
+}
+
+// static
+void LLPanelPermissions::onCommitNextOwnerTransfer(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitNextOwnerTransfer" << llendl;
+ onCommitPerm(ctrl, data, PERM_NEXT_OWNER, PERM_TRANSFER);
+}
+
+// static
+void LLPanelPermissions::onCommitName(LLUICtrl*, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitName()" << llendl;
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ LLLineEditor* tb = gUICtrlFactory->getLineEditorByName(self,"Object Name");
+ if(tb)
+ {
+ gSelectMgr->setObjectName(tb->getText());
+// gSelectMgr->setObjectName(self->mLabelObjectName->getText());
+ }
+}
+
+
+// static
+void LLPanelPermissions::onCommitDesc(LLUICtrl*, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitDesc()" << llendl;
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ LLLineEditor* le = gUICtrlFactory->getLineEditorByName(self,"Object Description");
+ if(le)
+ {
+ gSelectMgr->setObjectDescription(le->getText());
+ }
+}
+
+// static
+void LLPanelPermissions::onCommitSaleInfo(LLUICtrl*, void* data)
+{
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ self->setAllSaleInfo();
+}
+
+// static
+void LLPanelPermissions::onCommitSaleType(LLUICtrl*, void* data)
+{
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ self->setAllSaleInfo();
+}
+
+void LLPanelPermissions::setAllSaleInfo()
+{
+ llinfos << "LLPanelPermissions::setAllSaleInfo()" << llendl;
+ LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_NOT;
+
+ LLCheckBoxCtrl* mCheckPurchase = gUICtrlFactory->getCheckBoxByName(this,"checkbox for sale");
+
+ if(mCheckPurchase && mCheckPurchase->get())
+ {
+ LLRadioGroup* RadioSaleType = gUICtrlFactory->getRadioGroupByName(this,"sale type");
+ if(RadioSaleType)
+ {
+ switch(RadioSaleType->getSelectedIndex())
+ {
+ case 0:
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ break;
+ case 1:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ case 2:
+ sale_type = LLSaleInfo::FS_CONTENTS;
+ break;
+ default:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ }
+ }
+ }
+ LLLineEditor* mEditPrice = gUICtrlFactory->getLineEditorByName(this,"EdCost");
+
+ S32 price = -1;
+ if(mEditPrice)
+ {
+ price = atoi(mEditPrice->getText().c_str());
+ }
+ // Invalid data - turn off the sale
+ if (price < 0)
+ {
+ sale_type = LLSaleInfo::FS_NOT;
+ price = 0;
+ }
+
+ LLSaleInfo sale_info(sale_type, price);
+ gSelectMgr->setObjectSaleInfo(sale_info);
+
+ // If turned off for-sale, make sure click-action buy is turned
+ // off as well
+ if (sale_type == LLSaleInfo::FS_NOT)
+ {
+ U8 click_action = 0;
+ gSelectMgr->selectionGetClickAction(&click_action);
+ if (click_action == CLICK_ACTION_BUY)
+ {
+ gSelectMgr->selectionSetClickAction(CLICK_ACTION_TOUCH);
+ }
+ }
+}
+
+class LLSelectionPayable : public LLSelectedObjectFunctor
+{
+public:
+ virtual bool apply(LLViewerObject* obj)
+ {
+ // can pay if you or your parent has money() event in script
+ LLViewerObject* parent = (LLViewerObject*)obj->getParent();
+ return (obj->flagTakesMoney()
+ || (parent && parent->flagTakesMoney()));
+ }
+};
+
+// static
+void LLPanelPermissions::onCommitClickAction(LLUICtrl* ctrl, void*)
+{
+ LLComboBox* box = (LLComboBox*)ctrl;
+ if (!box) return;
+
+ U8 click_action = (U8)box->getCurrentIndex();
+ if (click_action == CLICK_ACTION_BUY)
+ {
+ LLSaleInfo sale_info;
+ gSelectMgr->selectGetSaleInfo(sale_info);
+ if (!sale_info.isForSale())
+ {
+ gViewerWindow->alertXml("CantSetBuyObject");
+
+ // Set click action back to its old value
+ U8 click_action = 0;
+ gSelectMgr->selectionGetClickAction(&click_action);
+ box->setCurrentByIndex((S32)click_action);
+
+ return;
+ }
+ }
+ else if (click_action == CLICK_ACTION_PAY)
+ {
+ // Verify object has script with money() handler
+ LLSelectionPayable payable;
+ bool can_pay = gSelectMgr->applyToObjects(&payable);
+ if (!can_pay)
+ {
+ // Warn, but do it anyway.
+ gViewerWindow->alertXml("ClickActionNotPayable");
+ }
+ }
+ gSelectMgr->selectionSetClickAction(click_action);
+}
diff --git a/indra/newview/llpanelpermissions.h b/indra/newview/llpanelpermissions.h
new file mode 100644
index 0000000000..286a66f397
--- /dev/null
+++ b/indra/newview/llpanelpermissions.h
@@ -0,0 +1,92 @@
+/**
+ * @file llpanelpermissions.h
+ * @brief LLPanelPermissions class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELPERMISSIONS_H
+#define LL_LLPANELPERMISSIONS_H
+
+#ifndef LL_LLPANEL_H
+#include "llpanel.h"
+#endif
+
+#include "lluuid.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class llpanelpermissions
+//
+// Panel for permissions of an object.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLCheckBoxCtrl;
+class LLTextBox;
+class LLButton;
+class LLLineEditor;
+class LLRadioGroup;
+class LLComboBox;
+class LLNameBox;
+
+class LLPanelPermissions : public LLPanel
+{
+public:
+ LLPanelPermissions(const std::string& title);
+ virtual ~LLPanelPermissions();
+
+ virtual BOOL postBuild();
+
+ // MANIPULATORS
+ void refresh(); // refresh all labels as needed
+// void setPermCheckboxes(U32 mask_on, U32 mask_off,
+// LLCheckBoxCtrl* move, LLCheckboxCtrl* edit,
+// LLCheckBoxCtrl* copy);
+protected:
+ // statics
+ static void onClickClaim(void*);
+ static void onClickRelease(void*);
+ static void onClickCreator(void*);
+ static void onClickOwner(void*);
+ static void onClickGroup(void*);
+ static void cbGroupID(LLUUID group_id, void* userdata);
+ static void onClickDeedToGroup(void*);
+
+ static void onCommitPerm(LLUICtrl *ctrl, void *data, U8 field, U32 perm);
+
+// static void onCommitGroupMove(LLUICtrl *ctrl, void *data);
+// static void onCommitGroupCopy(LLUICtrl *ctrl, void *data);
+// static void onCommitGroupModify(LLUICtrl *ctrl, void *data);
+ static void onCommitGroupShare(LLUICtrl *ctrl, void *data);
+
+ static void onCommitEveryoneMove(LLUICtrl *ctrl, void *data);
+ static void onCommitEveryoneCopy(LLUICtrl *ctrl, void *data);
+ //static void onCommitEveryoneModify(LLUICtrl *ctrl, void *data);
+
+ static void onCommitNextOwnerModify(LLUICtrl* ctrl, void* data);
+ static void onCommitNextOwnerCopy(LLUICtrl* ctrl, void* data);
+ static void onCommitNextOwnerTransfer(LLUICtrl* ctrl, void* data);
+
+ static void onCommitName(LLUICtrl* ctrl, void* data);
+ static void onCommitDesc(LLUICtrl* ctrl, void* data);
+
+ static void onCommitSaleInfo(LLUICtrl* ctrl, void* data);
+ static void onCommitSaleType(LLUICtrl* ctrl, void* data);
+ void setAllSaleInfo();
+
+ static void onCommitClickAction(LLUICtrl* ctrl, void*);
+
+protected:
+ LLNameBox* mLabelGroupName; // group name
+
+ //LLTextBox* mBuyerLabel;
+ //LLCheckBoxCtrl* mCheckBuyerModify;
+ //LLCheckBoxCtrl* mCheckBuyerCopy;
+
+ LLUUID mCreatorID;
+ LLUUID mOwnerID;
+ LLUUID mLastOwnerID;
+};
+
+
+#endif // LL_LLPANELPERMISSIONS_H
diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp
new file mode 100644
index 0000000000..9ad039d01b
--- /dev/null
+++ b/indra/newview/llpanelpick.cpp
@@ -0,0 +1,494 @@
+/**
+ * @file llpanelpick.cpp
+ * @brief LLPanelPick class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a "Top Pick" used both for the global top picks in the
+// Find directory, and also for each individual user's picks in their
+// profile.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelpick.h"
+
+#include "lldir.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llviewercontrol.h"
+#include "lllineeditor.h"
+#include "lltabcontainervertical.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "lluiconstants.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerparcelmgr.h"
+#include "llworldmap.h"
+#include "llfloaterworldmap.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+
+//static
+LLLinkedList<LLPanelPick> LLPanelPick::sAllPanels;
+
+LLPanelPick::LLPanelPick(BOOL top_pick)
+: LLPanel("Top Picks Panel"),
+ mTopPick(top_pick),
+ mPickID(),
+ mCreatorID(),
+ mParcelID(),
+ mDataRequested(FALSE),
+ mDataReceived(FALSE),
+ mPosGlobal(),
+ mSnapshotCtrl(NULL),
+ mNameEditor(NULL),
+ mDescEditor(NULL),
+ mLocationEditor(NULL),
+ mTeleportBtn(NULL),
+ mMapBtn(NULL),
+ //mLandmarkBtn(NULL),
+ mSortOrderText(NULL),
+ mSortOrderEditor(NULL),
+ mEnabledCheck(NULL),
+ mSetBtn(NULL)
+{
+ sAllPanels.addData(this);
+
+ std::string pick_def_file;
+ if (top_pick)
+ {
+ gUICtrlFactory->buildPanel(this, "panel_top_pick.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildPanel(this, "panel_avatar_pick.xml");
+ }
+}
+
+
+LLPanelPick::~LLPanelPick()
+{
+ sAllPanels.removeData(this);
+}
+
+
+void LLPanelPick::reset()
+{
+ mPickID.setNull();
+ mCreatorID.setNull();
+ mParcelID.setNull();
+
+ // Don't request data, this isn't valid
+ mDataRequested = TRUE;
+ mDataReceived = FALSE;
+
+ mPosGlobal.clearVec();
+
+ clearCtrls();
+}
+
+
+BOOL LLPanelPick::postBuild()
+{
+ mSnapshotCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setCommitCallback(onCommitAny);
+ mSnapshotCtrl->setCallbackUserData(this);
+
+ mNameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "given_name_editor");
+ mNameEditor->setCommitOnFocusLost(TRUE);
+ mNameEditor->setCommitCallback(onCommitAny);
+ mNameEditor->setCallbackUserData(this);
+
+ mDescEditor = LLUICtrlFactory::getTextEditorByName(this, "desc_editor");
+ mDescEditor->setCommitOnFocusLost(TRUE);
+ mDescEditor->setCommitCallback(onCommitAny);
+ mDescEditor->setCallbackUserData(this);
+ mDescEditor->setTabToNextField(TRUE);
+
+ mLocationEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "location_editor");
+
+ mSetBtn = LLViewerUICtrlFactory::getButtonByName(this, "set_location_btn");
+ mSetBtn->setClickedCallback(onClickSet);
+ mSetBtn->setCallbackUserData(this);
+
+ mTeleportBtn = LLViewerUICtrlFactory::getButtonByName(this, "pick_teleport_btn");
+ mTeleportBtn->setClickedCallback(onClickTeleport);
+ mTeleportBtn->setCallbackUserData(this);
+
+ mMapBtn = LLViewerUICtrlFactory::getButtonByName(this, "pick_map_btn");
+ mMapBtn->setClickedCallback(onClickMap);
+ mMapBtn->setCallbackUserData(this);
+
+ mSortOrderText = LLViewerUICtrlFactory::getTextBoxByName(this, "sort_order_text");
+
+ mSortOrderEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "sort_order_editor");
+ mSortOrderEditor->setPrevalidate(LLLineEditor::prevalidateInt);
+ mSortOrderEditor->setCommitOnFocusLost(TRUE);
+ mSortOrderEditor->setCommitCallback(onCommitAny);
+ mSortOrderEditor->setCallbackUserData(this);
+
+ mEnabledCheck = LLViewerUICtrlFactory::getCheckBoxByName(this, "enabled_check");
+ mEnabledCheck->setCommitCallback(onCommitAny);
+ mEnabledCheck->setCallbackUserData(this);
+
+ return TRUE;
+}
+
+
+// Fill in some reasonable defaults for a new pick.
+void LLPanelPick::initNewPick()
+{
+ mPickID.generate();
+
+ mCreatorID = gAgent.getID();
+
+ mPosGlobal = gAgent.getPositionGlobal();
+
+ // Try to fill in the current parcel
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
+ {
+ mNameEditor->setText(parcel->getName());
+ mDescEditor->setText(parcel->getDesc());
+ mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
+ }
+
+ // Commit to the database, since we've got "new" values.
+ sendPickInfoUpdate();
+}
+
+
+void LLPanelPick::setPickID(const LLUUID& id)
+{
+ mPickID = id;
+}
+
+
+// Schedules the panel to request data
+// from the server next time it is drawn.
+void LLPanelPick::markForServerRequest()
+{
+ mDataRequested = FALSE;
+ mDataReceived = FALSE;
+}
+
+
+std::string LLPanelPick::getPickName()
+{
+ return mNameEditor->getText();
+}
+
+
+void LLPanelPick::sendPickInfoRequest()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessage("PickInfoRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("PickID", mPickID);
+ gAgent.sendReliableMessage();
+
+ mDataRequested = TRUE;
+}
+
+
+void LLPanelPick::sendPickInfoUpdate()
+{
+ // If we don't have a pick id yet, we'll need to generate one,
+ // otherwise we'll keep overwriting pick_id 00000 in the database.
+ if (mPickID.isNull())
+ {
+ mPickID.generate();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessage("PickInfoUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("PickID", mPickID);
+ msg->addUUID("CreatorID", mCreatorID);
+ msg->addBOOL("TopPick", mTopPick);
+ // fills in on simulator if null
+ msg->addUUID("ParcelID", mParcelID);
+ msg->addString("Name", mNameEditor->getText());
+ msg->addString("Desc", mDescEditor->getText());
+ msg->addUUID("SnapshotID", mSnapshotCtrl->getImageAssetID());
+ msg->addVector3d("PosGlobal", mPosGlobal);
+
+ // Only top picks have a sort order
+ S32 sort_order;
+ if (mTopPick)
+ {
+ sort_order = atoi(mSortOrderEditor->getText().c_str());
+ }
+ else
+ {
+ sort_order = 0;
+ }
+ msg->addS32("SortOrder", sort_order);
+ msg->addBOOL("Enabled", mEnabledCheck->get());
+ gAgent.sendReliableMessage();
+}
+
+
+//static
+void LLPanelPick::processPickInfoReply(LLMessageSystem *msg, void **)
+{
+ // Extract the agent id and verify the message is for this
+ // client.
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id );
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Agent ID mismatch in processPickInfoReply"
+ << llendl;
+ return;
+ }
+
+ LLUUID pick_id;
+ msg->getUUID("Data", "PickID", pick_id);
+
+ LLUUID creator_id;
+ msg->getUUID("Data", "CreatorID", creator_id);
+
+ BOOL top_pick;
+ msg->getBOOL("Data", "TopPick", top_pick);
+
+ LLUUID parcel_id;
+ msg->getUUID("Data", "ParcelID", parcel_id);
+
+ char name[DB_PARCEL_NAME_SIZE];
+ msg->getString("Data", "Name", DB_PARCEL_NAME_SIZE, name);
+
+ char desc[DB_PICK_DESC_SIZE];
+ msg->getString("Data", "Desc", DB_PICK_DESC_SIZE, desc);
+
+ LLUUID snapshot_id;
+ msg->getUUID("Data", "SnapshotID", snapshot_id);
+
+ // "Location text" is actually the owner name, the original
+ // name that owner gave the parcel, and the location.
+ char buffer[256];
+ LLString location_text;
+
+ msg->getString("Data", "User", 256, buffer);
+ location_text.assign(buffer);
+ location_text.append(", ");
+
+ msg->getString("Data", "OriginalName", 256, buffer);
+ if (buffer[0] != '\0')
+ {
+ location_text.append(buffer);
+ location_text.append(", ");
+ }
+
+ char sim_name[256];
+ msg->getString("Data", "SimName", 256, sim_name);
+
+ LLVector3d pos_global;
+ msg->getVector3d("Data", "PosGlobal", pos_global);
+
+ S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)pos_global.mdV[VZ]);
+
+ sprintf(buffer, "%s (%d, %d, %d)", sim_name, region_x, region_y, region_z);
+ location_text.append(buffer);
+
+ S32 sort_order;
+ msg->getS32("Data", "SortOrder", sort_order);
+
+ BOOL enabled;
+ msg->getBOOL("Data", "Enabled", enabled);
+
+ // Look up the panel to fill in
+ LLPanelPick *self = NULL;
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ // For top picks, must match pick id
+ if (self->mPickID != pick_id)
+ {
+ continue;
+ }
+
+ self->mDataReceived = TRUE;
+
+ // Found the panel, now fill in the information
+ self->mPickID = pick_id;
+ self->mCreatorID = creator_id;
+ self->mParcelID = parcel_id;
+ self->mSimName.assign(sim_name);
+ self->mPosGlobal = pos_global;
+
+ // Update UI controls
+ self->mNameEditor->setText(name);
+ self->mDescEditor->setText(desc);
+ self->mSnapshotCtrl->setImageAssetID(snapshot_id);
+ self->mLocationEditor->setText(location_text);
+ self->mEnabledCheck->set(enabled);
+
+ sprintf(buffer, "%d", sort_order);
+ self->mSortOrderEditor->setText(buffer);
+ }
+}
+
+void LLPanelPick::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelPick::refresh()
+{
+ if (!mDataRequested)
+ {
+ sendPickInfoRequest();
+ }
+
+ // Check for god mode
+ BOOL godlike = gAgent.isGodlike();
+ BOOL is_self = (gAgent.getID() == mCreatorID);
+
+ // Set button visibility/enablement appropriately
+ if (mTopPick)
+ {
+ mSnapshotCtrl->setEnabled(godlike);
+ mNameEditor->setEnabled(godlike);
+ mDescEditor->setEnabled(godlike);
+
+ mSortOrderText->setVisible(godlike);
+
+ mSortOrderEditor->setVisible(godlike);
+ mSortOrderEditor->setEnabled(godlike);
+
+ mEnabledCheck->setVisible(godlike);
+ mEnabledCheck->setEnabled(godlike);
+
+ mSetBtn->setVisible(godlike);
+ mSetBtn->setEnabled(godlike);
+ }
+ else
+ {
+ mSnapshotCtrl->setEnabled(is_self);
+ mNameEditor->setEnabled(is_self);
+ mDescEditor->setEnabled(is_self);
+
+ mSortOrderText->setVisible(FALSE);
+
+ mSortOrderEditor->setVisible(FALSE);
+ mSortOrderEditor->setEnabled(FALSE);
+
+ mEnabledCheck->setVisible(FALSE);
+ mEnabledCheck->setEnabled(FALSE);
+
+ mSetBtn->setVisible(is_self);
+ mSetBtn->setEnabled(is_self);
+ }
+}
+
+
+// static
+void LLPanelPick::onClickTeleport(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gAgent.teleportViaLocation(self->mPosGlobal);
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ }
+}
+
+
+// static
+void LLPanelPick::onClickMap(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+// static
+/*
+void LLPanelPick::onClickLandmark(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+ create_landmark(self->mNameEditor->getText().c_str(), "", self->mPosGlobal);
+}
+*/
+
+// static
+void LLPanelPick::onClickSet(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+
+ // Save location for later.
+ self->mPosGlobal = gAgent.getPositionGlobal();
+
+ LLString location_text;
+ location_text.assign("(will update after save)");
+ location_text.append(", ");
+
+ S32 region_x = llround((F32)self->mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)self->mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)self->mPosGlobal.mdV[VZ]);
+
+ location_text.append(self->mSimName);
+ location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z));
+
+ // if sim name in pick is different from current sim name
+ // make sure it's clear that all that's being changed
+ // is the location and nothing else
+ if ( gAgent.getRegion ()->getName () != self->mSimName )
+ {
+ gViewerWindow->alertXml("SetPickLocation");
+ };
+
+ self->mLocationEditor->setText(location_text);
+
+ onCommitAny(NULL, data);
+}
+
+
+// static
+void LLPanelPick::onCommitAny(LLUICtrl* ctrl, void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+
+ // have we received up to date data for this pick?
+ if (self->mDataReceived)
+ {
+ self->sendPickInfoUpdate();
+
+ // Big hack - assume that top picks are always in a browser,
+ // and non-top-picks are always in a tab container.
+ /*if (self->mTopPick)
+ {
+ LLPanelDirPicks* panel = (LLPanelDirPicks*)self->getParent();
+ panel->renamePick(self->mPickID, self->mNameEditor->getText().c_str());
+ }
+ else
+ {*/
+ LLTabContainerVertical* tab = (LLTabContainerVertical*)self->getParent();
+ tab->setCurrentTabName(self->mNameEditor->getText());
+ //}
+ }
+}
diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h
new file mode 100644
index 0000000000..bdc7ef1f3e
--- /dev/null
+++ b/indra/newview/llpanelpick.h
@@ -0,0 +1,99 @@
+/**
+ * @file llpanelpick.h
+ * @brief LLPanelPick class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a "Top Pick" used both for the global top picks in the
+// Find directory, and also for each individual user's picks in their
+// profile.
+
+#ifndef LL_LLPANELPICK_H
+#define LL_LLPANELPICK_H
+
+#include "llpanel.h"
+#include "v3dmath.h"
+#include "lluuid.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLIconCtrl;
+class LLLineEditor;
+class LLTextBox;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLMessageSystem;
+
+class LLPanelPick : public LLPanel
+{
+public:
+ LLPanelPick(BOOL top_pick);
+ /*virtual*/ ~LLPanelPick();
+
+ void reset();
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ // Setup a new pick, including creating an id, giving a sane
+ // initial position, etc.
+ void initNewPick();
+
+ void setPickID(const LLUUID& id);
+
+ // Schedules the panel to request data
+ // from the server next time it is drawn.
+ void markForServerRequest();
+
+ std::string getPickName();
+ const LLUUID& getPickID() const { return mPickID; }
+
+ void sendPickInfoRequest();
+ void sendPickInfoUpdate();
+
+ static void processPickInfoReply(LLMessageSystem* msg, void**);
+
+protected:
+ static void onClickTeleport(void* data);
+ static void onClickMap(void* data);
+ //static void onClickLandmark(void* data);
+ static void onClickSet(void* data);
+
+ static void onCommitAny(LLUICtrl* ctrl, void* data);
+
+protected:
+ BOOL mTopPick;
+ LLUUID mPickID;
+ LLUUID mCreatorID;
+ LLUUID mParcelID;
+
+ // Data will be requested on first draw
+ BOOL mDataRequested;
+ BOOL mDataReceived;
+
+ LLString mSimName;
+ LLVector3d mPosGlobal;
+
+ LLTextureCtrl* mSnapshotCtrl;
+ LLLineEditor* mNameEditor;
+ LLTextEditor* mDescEditor;
+ LLLineEditor* mLocationEditor;
+
+ LLButton* mTeleportBtn;
+ LLButton* mMapBtn;
+
+ LLTextBox* mSortOrderText;
+ LLLineEditor* mSortOrderEditor;
+ LLCheckBoxCtrl* mEnabledCheck;
+ LLButton* mSetBtn;
+
+ static LLLinkedList<LLPanelPick> sAllPanels;
+};
+
+#endif // LL_LLPANELPICK_H
diff --git a/indra/newview/llpanelplace.cpp b/indra/newview/llpanelplace.cpp
new file mode 100644
index 0000000000..45ca1b0871
--- /dev/null
+++ b/indra/newview/llpanelplace.cpp
@@ -0,0 +1,271 @@
+/**
+ * @file llpanelplace.cpp
+ * @brief Display of a place in the Find directory.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelplace.h"
+
+#include "llviewercontrol.h"
+#include "llqueryflags.h"
+#include "message.h"
+#include "llui.h"
+#include "llsecondlifeurls.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llfloaterworldmap.h"
+#include "lllineeditor.h"
+#include "lluiconstants.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "llworldmap.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+//#include "llviewermenu.h" // create_landmark()
+#include "llweb.h"
+
+//static
+LLLinkedList<LLPanelPlace> LLPanelPlace::sAllPanels;
+
+LLPanelPlace::LLPanelPlace()
+: LLPanel("Places Panel"),
+ mParcelID(),
+ mPosGlobal(),
+ mAuctionID(0)
+{
+ sAllPanels.addData(this);
+}
+
+
+LLPanelPlace::~LLPanelPlace()
+{
+ sAllPanels.removeData(this);
+}
+
+
+BOOL LLPanelPlace::postBuild()
+{
+ // Since this is only used in the directory browser, always
+ // disable the snapshot control. Otherwise clicking on it will
+ // open a texture picker.
+ mSnapshotCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setEnabled(FALSE);
+
+ mNameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "name_editor");
+
+ mDescEditor = LLUICtrlFactory::getTextEditorByName(this, "desc_editor");
+
+ mInfoEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "info_editor");
+
+ mLocationEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "location_editor");
+
+ mTeleportBtn = LLViewerUICtrlFactory::getButtonByName(this, "teleport_btn");
+ mTeleportBtn->setClickedCallback(onClickTeleport);
+ mTeleportBtn->setCallbackUserData(this);
+
+ mMapBtn = LLViewerUICtrlFactory::getButtonByName(this, "map_btn");
+ mMapBtn->setClickedCallback(onClickMap);
+ mMapBtn->setCallbackUserData(this);
+
+ //mLandmarkBtn = LLViewerUICtrlFactory::getButtonByName(this, "landmark_btn");
+ //mLandmarkBtn->setClickedCallback(onClickLandmark);
+ //mLandmarkBtn->setCallbackUserData(this);
+
+ mAuctionBtn = LLViewerUICtrlFactory::getButtonByName(this, "auction_btn");
+ mAuctionBtn->setClickedCallback(onClickAuction);
+ mAuctionBtn->setCallbackUserData(this);
+
+ // Default to no auction button. We'll show it if we get an auction id
+ mAuctionBtn->setVisible(FALSE);
+
+ return TRUE;
+}
+
+
+
+void LLPanelPlace::setParcelID(const LLUUID& parcel_id)
+{
+ mParcelID = parcel_id;
+}
+
+
+void LLPanelPlace::sendParcelInfoRequest()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ if (mParcelID != mRequestedID)
+ {
+ msg->newMessage("ParcelInfoRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("ParcelID", mParcelID);
+ gAgent.sendReliableMessage();
+ mRequestedID = mParcelID;
+ }
+}
+
+
+//static
+void LLPanelPlace::processParcelInfoReply(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+ LLUUID parcel_id;
+ LLUUID owner_id;
+ char name[MAX_STRING];
+ char desc[MAX_STRING];
+ S32 actual_area;
+ S32 billable_area;
+ U8 flags;
+ F32 global_x;
+ F32 global_y;
+ F32 global_z;
+ char sim_name[MAX_STRING];
+ LLUUID snapshot_id;
+ F32 dwell;
+ S32 sale_price;
+ S32 auction_id;
+ char buffer[256];
+
+ msg->getUUID("AgentData", "AgentID", agent_id );
+ msg->getUUID("Data", "ParcelID", parcel_id);
+
+ // look up all panels which have this avatar
+ LLPanelPlace *self = NULL;
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mParcelID != parcel_id)
+ {
+ continue;
+ }
+
+ msg->getUUID ("Data", "OwnerID", owner_id);
+ msg->getString ("Data", "Name", MAX_STRING, name);
+ msg->getString ("Data", "Desc", MAX_STRING, desc);
+ msg->getS32 ("Data", "ActualArea", actual_area);
+ msg->getS32 ("Data", "BillableArea", billable_area);
+ msg->getU8 ("Data", "Flags", flags);
+ msg->getF32 ("Data", "GlobalX", global_x);
+ msg->getF32 ("Data", "GlobalY", global_y);
+ msg->getF32 ("Data", "GlobalZ", global_z);
+ msg->getString ("Data", "SimName", MAX_STRING, sim_name);
+ msg->getUUID ("Data", "SnapshotID", snapshot_id);
+ msg->getF32 ("Data", "Dwell", dwell);
+ msg->getS32 ("Data", "SalePrice", sale_price);
+ msg->getS32 ("Data", "AuctionID", auction_id);
+
+ self->mPosGlobal.setVec(global_x, global_y, global_z);
+
+ self->mAuctionID = auction_id;
+
+ self->mSnapshotCtrl->setImageAssetID(snapshot_id);
+
+ self->mNameEditor->setText(name);
+
+ self->mDescEditor->setText(desc);
+
+ LLString info;
+ sprintf(buffer, "Traffic: %.0f, Area: %d sq. m.", dwell, actual_area);
+ info.append(buffer);
+ if (flags & DFQ_FOR_SALE)
+ {
+ sprintf(buffer, ", For Sale for L$%d", sale_price);
+ info.append(buffer);
+ }
+ if (auction_id != 0)
+ {
+ sprintf(buffer, ", Auction ID %010d", auction_id);
+ info.append(buffer);
+ }
+ self->mInfoEditor->setText(info);
+
+ S32 region_x = llround(global_x) % REGION_WIDTH_UNITS;
+ S32 region_y = llround(global_y) % REGION_WIDTH_UNITS;
+ S32 region_z = llround(global_z);
+
+ // HACK: Flag 0x1 == mature region, otherwise assume PG
+ const char* rating = LLViewerRegion::accessToString(SIM_ACCESS_PG);
+ if (flags & 0x1)
+ {
+ rating = LLViewerRegion::accessToString(SIM_ACCESS_MATURE);
+ }
+
+ sprintf(buffer, "%s %d, %d, %d (%s)",
+ sim_name, region_x, region_y, region_z, rating);
+ self->mLocationEditor->setText(buffer);
+
+ BOOL show_auction = (auction_id > 0);
+ self->mAuctionBtn->setVisible(show_auction);
+ }
+}
+
+
+// static
+void LLPanelPlace::onClickTeleport(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gAgent.teleportViaLocation(self->mPosGlobal);
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ }
+}
+
+// static
+void LLPanelPlace::onClickMap(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }
+}
+
+// static
+/*
+void LLPanelPlace::onClickLandmark(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ create_landmark(self->mNameEditor->getText(), "", self->mPosGlobal);
+}
+*/
+
+
+// static
+void LLPanelPlace::onClickAuction(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ gViewerWindow->alertXml("GoToAuctionPage",
+ callbackAuctionWebPage,
+ self);
+}
+
+// static
+void LLPanelPlace::callbackAuctionWebPage(S32 option, void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ if (0 == option)
+ {
+ char url[256];
+ sprintf(url, "%s%010d", AUCTION_URL, self->mAuctionID);
+
+ llinfos << "Loading auction page " << url << llendl;
+
+ LLWeb::loadURL(url);
+ }
+}
diff --git a/indra/newview/llpanelplace.h b/indra/newview/llpanelplace.h
new file mode 100644
index 0000000000..b45aef3e87
--- /dev/null
+++ b/indra/newview/llpanelplace.h
@@ -0,0 +1,70 @@
+/**
+ * @file llpanelplace.h
+ * @brief Display of a place in the Find directory.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELPLACE_H
+#define LL_LLPANELPLACE_H
+
+#include "llpanel.h"
+
+#include "v3dmath.h"
+#include "lluuid.h"
+
+class LLButton;
+class LLTextBox;
+class LLLineEditor;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLMessageSystem;
+
+class LLPanelPlace : public LLPanel
+{
+public:
+ LLPanelPlace();
+ /*virtual*/ ~LLPanelPlace();
+
+ /*virtual*/ BOOL postBuild();
+
+
+ void setParcelID(const LLUUID& parcel_id);
+
+ void sendParcelInfoRequest();
+
+ static void processParcelInfoReply(LLMessageSystem* msg, void**);
+
+protected:
+ static void onClickTeleport(void* data);
+ static void onClickMap(void* data);
+ //static void onClickLandmark(void* data);
+ static void onClickAuction(void* data);
+
+ // Go to auction web page if user clicked OK
+ static void callbackAuctionWebPage(S32 option, void* data);
+
+protected:
+ LLUUID mParcelID;
+ LLUUID mRequestedID;
+ LLVector3d mPosGlobal;
+ // Zero if this is not an auction
+ S32 mAuctionID;
+
+ LLTextureCtrl* mSnapshotCtrl;
+
+ LLLineEditor* mNameEditor;
+ LLTextEditor* mDescEditor;
+ LLLineEditor* mInfoEditor;
+ LLLineEditor* mLocationEditor;
+
+ LLButton* mTeleportBtn;
+ LLButton* mMapBtn;
+ //LLButton* mLandmarkBtn;
+ LLButton* mAuctionBtn;
+
+ static LLLinkedList<LLPanelPlace> sAllPanels;
+};
+
+#endif // LL_LLPANELPLACE_H
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
new file mode 100644
index 0000000000..cd942db7e1
--- /dev/null
+++ b/indra/newview/llpanelvolume.cpp
@@ -0,0 +1,499 @@
+/**
+ * @file llpanelvolume.cpp
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelvolume.h"
+
+// linden library includes
+#include "llclickaction.h"
+#include "lleconomy.h"
+#include "llerror.h"
+#include "llfontgl.h"
+#include "llflexibleobject.h"
+#include "llmaterialtable.h"
+#include "llpermissionsflags.h"
+#include "llstring.h"
+#include "llvolume.h"
+#include "m3math.h"
+#include "material_codes.h"
+
+// project includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcolorswatch.h"
+#include "llcombobox.h"
+#include "llfirstuse.h"
+#include "llfocusmgr.h"
+#include "llmanipscale.h"
+#include "llpanelinventory.h"
+#include "llpreviewscript.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltool.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+#include "lldrawpool.h"
+#include "llvieweruictrlfactory.h"
+
+// "Features" Tab
+
+BOOL LLPanelVolume::postBuild()
+{
+ // Flexible Objects Parameters
+ {
+ childSetCommitCallback("Flexible1D Checkbox Ctrl",onCommitIsFlexible,this);
+ childSetCommitCallback("FlexNumSections",onCommitFlexible,this);
+ childSetValidate("FlexNumSections",precommitValidate);
+ childSetCommitCallback("FlexGravity",onCommitFlexible,this);
+ childSetValidate("FlexGravity",precommitValidate);
+ childSetCommitCallback("FlexFriction",onCommitFlexible,this);
+ childSetValidate("FlexFriction",precommitValidate);
+ childSetCommitCallback("FlexWind",onCommitFlexible,this);
+ childSetValidate("FlexWind",precommitValidate);
+ childSetCommitCallback("FlexTension",onCommitFlexible,this);
+ childSetValidate("FlexTension",precommitValidate);
+ childSetCommitCallback("FlexForceX",onCommitFlexible,this);
+ childSetValidate("FlexForceX",precommitValidate);
+ childSetCommitCallback("FlexForceY",onCommitFlexible,this);
+ childSetValidate("FlexForceY",precommitValidate);
+ childSetCommitCallback("FlexForceZ",onCommitFlexible,this);
+ childSetValidate("FlexForceZ",precommitValidate);
+ }
+
+ // LIGHT Parameters
+ {
+ childSetCommitCallback("Light Checkbox Ctrl",onCommitIsLight,this);
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch){
+ LightColorSwatch->setOnCancelCallback(onLightCancelColor);
+ LightColorSwatch->setOnSelectCallback(onLightSelectColor);
+ childSetCommitCallback("colorswatch",onCommitLight,this);
+ }
+ childSetCommitCallback("Light Intensity",onCommitLight,this);
+ childSetValidate("Light Intensity",precommitValidate);
+ childSetCommitCallback("Light Radius",onCommitLight,this);
+ childSetValidate("Light Radius",precommitValidate);
+ childSetCommitCallback("Light Falloff",onCommitLight,this);
+ childSetValidate("Light Falloff",precommitValidate);
+ }
+
+ // Start with everyone disabled
+ clearCtrls();
+
+ return TRUE;
+}
+
+LLPanelVolume::LLPanelVolume(const std::string& name)
+ : LLPanel(name)
+{
+ setMouseOpaque(FALSE);
+
+}
+
+
+LLPanelVolume::~LLPanelVolume()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+void LLPanelVolume::getState( )
+{
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ LLViewerObject* root_objectp = objectp;
+ if(!objectp)
+ {
+ objectp = gSelectMgr->getFirstObject();
+ //FIXME: shouldn't we just keep the child?
+ if (objectp)
+ {
+ LLViewerObject* parentp = objectp->getSubParent();
+
+ if (parentp)
+ {
+ root_objectp = parentp;
+ }
+ else
+ {
+ root_objectp = objectp;
+ }
+ }
+ }
+
+ LLVOVolume *volobjp = NULL;
+ if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
+ {
+ volobjp = (LLVOVolume *)objectp;
+ }
+
+ if( !objectp )
+ {
+ //forfeit focus
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ // Disable all text input fields
+ clearCtrls();
+
+ return;
+ }
+
+ BOOL owners_identical;
+ LLUUID owner_id;
+ LLString owner_name;
+ owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+
+ // BUG? Check for all objects being editable?
+ BOOL editable = root_objectp->permModify();
+ BOOL single_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME )
+ && gSelectMgr->getObjectCount() == 1;
+
+ // Select Single Message
+ if (single_volume)
+ {
+ childSetVisible("edit_object",true);
+ childSetEnabled("edit_object",true);
+ childSetVisible("select_single",false);
+ }
+ else
+ {
+ childSetVisible("edit_object",false);
+ childSetVisible("select_single",true);
+ childSetEnabled("select_single",true);
+ }
+
+ // Light properties
+ BOOL is_light = volobjp && volobjp->getIsLight();
+ childSetValue("Light Checkbox Ctrl",is_light);
+ childSetEnabled("Light Checkbox Ctrl",editable && single_volume && volobjp);
+
+ if (is_light && editable && single_volume)
+ {
+ childSetEnabled("label color",true);
+ //mLabelColor ->setEnabled( TRUE );
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setEnabled( TRUE );
+ LightColorSwatch->setValid( TRUE );
+ LightColorSwatch->set(volobjp->getLightBaseColor());
+ }
+ childSetEnabled("Light Intensity",true);
+ childSetEnabled("Light Radius",true);
+ childSetEnabled("Light Falloff",true);
+
+ childSetValue("Light Intensity",volobjp->getLightIntensity());
+ childSetValue("Light Radius",volobjp->getLightRadius());
+ childSetValue("Light Falloff",volobjp->getLightFalloff());
+
+ mLightSavedColor = volobjp->getLightColor();
+ }
+ else
+ {
+ childSetEnabled("label color",false);
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setEnabled( FALSE );
+ LightColorSwatch->setValid( FALSE );
+ }
+ childSetEnabled("Light Intensity",false);
+ childSetEnabled("Light Radius",false);
+ childSetEnabled("Light Falloff",false);
+ }
+
+ // Flexible properties
+ BOOL is_flexible = volobjp && volobjp->isFlexible();
+ childSetValue("Flexible1D Checkbox Ctrl",is_flexible);
+ if (is_flexible || (volobjp && volobjp->canBeFlexible()))
+ {
+ childSetEnabled("Flexible1D Checkbox Ctrl", editable && single_volume && volobjp);
+ }
+ else
+ {
+ childSetEnabled("Flexible1D Checkbox Ctrl", false);
+ }
+ if (is_flexible && editable && single_volume)
+ {
+ childSetVisible("FlexNumSections",true);
+ childSetVisible("FlexGravity",true);
+ childSetVisible("FlexTension",true);
+ childSetVisible("FlexFriction",true);
+ childSetVisible("FlexWind",true);
+ childSetVisible("FlexForceX",true);
+ childSetVisible("FlexForceY",true);
+ childSetVisible("FlexForceZ",true);
+
+ childSetEnabled("FlexNumSections",true);
+ childSetEnabled("FlexGravity",true);
+ childSetEnabled("FlexTension",true);
+ childSetEnabled("FlexFriction",true);
+ childSetEnabled("FlexWind",true);
+ childSetEnabled("FlexForceX",true);
+ childSetEnabled("FlexForceY",true);
+ childSetEnabled("FlexForceZ",true);
+
+ LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
+
+ childSetValue("FlexNumSections",(F32)attributes->getSimulateLOD());
+ childSetValue("FlexGravity",attributes->getGravity());
+ childSetValue("FlexTension",attributes->getTension());
+ childSetValue("FlexFriction",attributes->getAirFriction());
+ childSetValue("FlexWind",attributes->getWindSensitivity());
+ childSetValue("FlexForceX",attributes->getUserForce().mV[VX]);
+ childSetValue("FlexForceY",attributes->getUserForce().mV[VY]);
+ childSetValue("FlexForceZ",attributes->getUserForce().mV[VZ]);
+ }
+ else
+ {
+ childSetEnabled("FlexNumSections",false);
+ childSetEnabled("FlexGravity",false);
+ childSetEnabled("FlexTension",false);
+ childSetEnabled("FlexFriction",false);
+ childSetEnabled("FlexWind",false);
+ childSetEnabled("FlexForceX",false);
+ childSetEnabled("FlexForceY",false);
+ childSetEnabled("FlexForceZ",false);
+ }
+
+ mObject = objectp;
+ mRootObject = root_objectp;
+}
+
+// static
+BOOL LLPanelVolume::precommitValidate( LLUICtrl* ctrl, void* userdata )
+{
+ // TODO: Richard will fill this in later.
+ return TRUE; // FALSE means that validation failed and new value should not be commited.
+}
+
+
+void LLPanelVolume::refresh()
+{
+ getState();
+ if (mObject.notNull() && mObject->isDead())
+ {
+ mObject = NULL;
+ }
+
+ if (mRootObject.notNull() && mRootObject->isDead())
+ {
+ mRootObject = NULL;
+ }
+}
+
+
+void LLPanelVolume::draw()
+{
+ LLPanel::draw();
+}
+
+// virtual
+void LLPanelVolume::clearCtrls()
+{
+ LLPanel::clearCtrls();
+
+ childSetEnabled("select_single",false);
+ childSetVisible("select_single",true);
+ childSetEnabled("edit_object",false);
+ childSetVisible("edit_object",false);
+ childSetEnabled("Light Checkbox Ctrl",false);
+ childSetEnabled("label color",false);
+ childSetEnabled("label color",false);
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setEnabled( FALSE );
+ LightColorSwatch->setValid( FALSE );
+ }
+ childSetEnabled("Light Intensity",false);
+ childSetEnabled("Light Radius",false);
+ childSetEnabled("Light Falloff",false);
+
+ childSetEnabled("Flexible1D Checkbox Ctrl",false);
+ childSetEnabled("FlexNumSections",false);
+ childSetEnabled("FlexGravity",false);
+ childSetEnabled("FlexTension",false);
+ childSetEnabled("FlexFriction",false);
+ childSetEnabled("FlexWind",false);
+ childSetEnabled("FlexForceX",false);
+ childSetEnabled("FlexForceY",false);
+ childSetEnabled("FlexForceZ",false);
+}
+
+//
+// Static functions
+//
+
+void LLPanelVolume::sendIsLight()
+{
+ LLViewerObject* objectp = mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+ BOOL value = childGetValue("Light Checkbox Ctrl");
+ volobjp->setIsLight(value);
+ llinfos << "update light sent" << llendl;
+}
+
+void LLPanelVolume::sendIsFlexible()
+{
+ LLViewerObject* objectp = mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+ BOOL is_flexible = childGetValue("Flexible1D Checkbox Ctrl");
+ //BOOL is_flexible = mCheckFlexible1D->get();
+
+ if (is_flexible)
+ {
+ LLFirstUse::useFlexible();
+
+ if (objectp->getClickAction() == CLICK_ACTION_SIT)
+ {
+ gSelectMgr->selectionSetClickAction(CLICK_ACTION_NONE);
+ }
+
+ }
+
+ if (volobjp->setIsFlexible(is_flexible))
+ {
+ mObject->sendShapeUpdate();
+ gSelectMgr->selectionUpdatePhantom(volobjp->flagPhantom());
+ }
+
+ llinfos << "update flexible sent" << llendl;
+}
+
+void LLPanelVolume::onLightCancelColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(self,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setColor(self->mLightSavedColor);
+ }
+ onLightSelectColor(NULL, userdata);
+}
+
+void LLPanelVolume::onLightSelectColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLViewerObject* objectp = self->mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(self,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LLColor4 clr = LightColorSwatch->get();
+ LLColor3 clr3( clr );
+ volobjp->setLightColor(clr3);
+ self->mLightSavedColor = clr;
+ }
+}
+
+// static
+void LLPanelVolume::onCommitLight( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLViewerObject* objectp = self->mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+
+ volobjp->setLightIntensity((F32)self->childGetValue("Light Intensity").asReal());
+ volobjp->setLightRadius((F32)self->childGetValue("Light Radius").asReal());
+ volobjp->setLightFalloff((F32)self->childGetValue("Light Falloff").asReal());
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(self,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LLColor4 clr = LightColorSwatch->get();
+ volobjp->setLightColor(LLColor3(clr));
+ }
+}
+
+// static
+void LLPanelVolume::onCommitIsLight( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ self->sendIsLight();
+}
+
+//----------------------------------------------------------------------------
+
+// static
+void LLPanelVolume::onCommitFlexible( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLViewerObject* objectp = self->mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+
+ LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
+ if (attributes)
+ {
+ LLFlexibleObjectData new_attributes;
+ new_attributes = *attributes;
+
+
+ new_attributes.setSimulateLOD(self->childGetValue("FlexNumSections").asInteger());//(S32)self->mSpinSections->get());
+ new_attributes.setGravity((F32)self->childGetValue("FlexGravity").asReal());
+ new_attributes.setTension((F32)self->childGetValue("FlexTension").asReal());
+ new_attributes.setAirFriction((F32)self->childGetValue("FlexFriction").asReal());
+ new_attributes.setWindSensitivity((F32)self->childGetValue("FlexWind").asReal());
+ F32 fx = (F32)self->childGetValue("FlexForceX").asReal();
+ F32 fy = (F32)self->childGetValue("FlexForceY").asReal();
+ F32 fz = (F32)self->childGetValue("FlexForceZ").asReal();
+ LLVector3 force(fx,fy,fz);
+
+ new_attributes.setUserForce(force);
+ objectp->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, new_attributes, true);
+ }
+
+ // Values may fail validation
+ self->refresh();
+}
+
+// static
+void LLPanelVolume::onCommitIsFlexible( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ self->sendIsFlexible();
+}
+
diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h
new file mode 100644
index 0000000000..c8f4486521
--- /dev/null
+++ b/indra/newview/llpanelvolume.h
@@ -0,0 +1,82 @@
+/**
+ * @file llpanelvolume.h
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELVOLUME_H
+#define LL_LLPANELVOLUME_H
+
+#include "v3math.h"
+#include "llpanel.h"
+#include "llmemory.h"
+#include "llvolume.h"
+
+class LLSpinCtrl;
+class LLCheckBoxCtrl;
+class LLTextBox;
+class LLUICtrl;
+class LLButton;
+class LLViewerObject;
+class LLComboBox;
+class LLPanelInventory;
+class LLColorSwatchCtrl;
+
+class LLPanelVolume : public LLPanel
+{
+public:
+ LLPanelVolume(const std::string& name);
+ virtual ~LLPanelVolume();
+
+ virtual void draw();
+ virtual void clearCtrls();
+
+ virtual BOOL postBuild();
+
+ void refresh();
+
+ void sendIsLight();
+ void sendIsFlexible();
+
+ static BOOL precommitValidate(LLUICtrl* ctrl,void* userdata);
+
+ static void onCommitIsLight( LLUICtrl* ctrl, void* userdata);
+ static void onCommitLight( LLUICtrl* ctrl, void* userdata);
+ static void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata);
+ static void onCommitFlexible( LLUICtrl* ctrl, void* userdata);
+
+ static void onLightCancelColor(LLUICtrl* ctrl, void* userdata);
+ static void onLightSelectColor(LLUICtrl* ctrl, void* userdata);
+
+protected:
+ void getState();
+
+protected:
+/*
+ LLTextBox* mLabelSelectSingleMessage;
+ // Light
+ LLCheckBoxCtrl* mCheckLight;
+ LLCheckBoxCtrl* mCheckFlexible1D;
+ LLTextBox* mLabelColor;
+ LLColorSwatchCtrl* mLightColorSwatch;
+ LLSpinCtrl* mLightIntensity;
+ LLSpinCtrl* mLightRadius;
+ LLSpinCtrl* mLightFalloff;
+ LLSpinCtrl* mLightCutoff;
+ // Flexibile
+ LLSpinCtrl* mSpinSections;
+ LLSpinCtrl* mSpinGravity;
+ LLSpinCtrl* mSpinTension;
+ LLSpinCtrl* mSpinFriction;
+ LLSpinCtrl* mSpinWind;
+ LLSpinCtrl* mSpinForce[3];
+*/
+
+ LLColor4 mLightSavedColor;
+ LLPointer<LLViewerObject> mObject;
+ LLPointer<LLViewerObject> mRootObject;
+};
+
+#endif
diff --git a/indra/newview/llpatchvertexarray.cpp b/indra/newview/llpatchvertexarray.cpp
new file mode 100644
index 0000000000..62f8ac87bb
--- /dev/null
+++ b/indra/newview/llpatchvertexarray.cpp
@@ -0,0 +1,231 @@
+/**
+ * @file llpatchvertexarray.cpp
+ * @brief Implementation of the LLSurfaceVertexArray class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpatchvertexarray.h"
+#include "llsurfacepatch.h"
+
+// constructors
+
+LLPatchVertexArray::LLPatchVertexArray() :
+ mSurfaceWidth(0),
+ mPatchWidth(0),
+ mPatchOrder(0),
+ mRenderLevelp(NULL),
+ mRenderStridep(NULL)
+{
+}
+
+LLPatchVertexArray::LLPatchVertexArray(U32 surface_width, U32 patch_width, F32 meters_per_grid) :
+ mRenderLevelp(NULL),
+ mRenderStridep(NULL)
+{
+ create(surface_width, patch_width, meters_per_grid);
+}
+
+
+LLPatchVertexArray::~LLPatchVertexArray()
+{
+ destroy();
+}
+
+
+void LLPatchVertexArray::create(U32 surface_width, U32 patch_width, F32 meters_per_grid)
+{
+ // PART 1 -- Make sure the arguments are good...
+ // Make sure patch_width is not greater than surface_width
+ if (patch_width > surface_width)
+ {
+ U32 temp = patch_width;
+ patch_width = surface_width;
+ surface_width = temp;
+ }
+
+ // Make sure (surface_width-1) is equal to a power_of_two.
+ // (The -1 is there because an LLSurface has a buffer of 1 on
+ // its East and North edges).
+ U32 power_of_two = 1;
+ U32 surface_order = 0;
+ while (power_of_two < (surface_width-1))
+ {
+ power_of_two *= 2;
+ surface_order += 1;
+ }
+
+ if (power_of_two == (surface_width-1))
+ {
+ mSurfaceWidth = surface_width;
+
+ // Make sure patch_width is a factor of (surface_width - 1)
+ U32 ratio = (surface_width - 1) / patch_width;
+ F32 fratio = ((float)(surface_width - 1)) / ((float)(patch_width));
+ if ( fratio == (float)(ratio))
+ {
+ // Make sure patch_width is a power of two
+ power_of_two = 1;
+ U32 patch_order = 0;
+ while (power_of_two < patch_width)
+ {
+ power_of_two *= 2;
+ patch_order += 1;
+ }
+ if (power_of_two == patch_width)
+ {
+ mPatchWidth = patch_width;
+ mPatchOrder = patch_order;
+ }
+ else // patch_width is not a power of two...
+ {
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+ }
+ }
+ else // patch_width is not a factor of (surface_width - 1)...
+ {
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+ }
+ }
+ else // surface_width is not a power of two...
+ {
+ mSurfaceWidth = 0;
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+ }
+
+ // PART 2 -- Allocate memory for the render level table
+ if (mPatchWidth > 0)
+ {
+ mRenderLevelp = new U32 [2*mPatchWidth + 1];
+ mRenderStridep = new U32 [mPatchOrder + 1];
+ }
+
+
+ // Now that we've allocated memory, we can initialize the arrays...
+ init();
+}
+
+
+void LLPatchVertexArray::destroy()
+{
+ if (mPatchWidth == 0)
+ {
+ return;
+ }
+
+ delete [] mRenderLevelp;
+ delete [] mRenderStridep;
+ mSurfaceWidth = 0;
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+}
+
+
+void LLPatchVertexArray::init()
+// Initializes the triangle strip arrays.
+{
+ U32 j;
+ U32 level, stride;
+ U32 k;
+
+ // We need to build two look-up tables...
+
+ // render_level -> render_stride
+ // A 16x16 patch has 5 render levels : 2^0 to 2^4
+ // render_level render_stride
+ // 4 1
+ // 3 2
+ // 2 4
+ // 1 8
+ // 0 16
+ stride = mPatchWidth;
+ for (level=0; level<mPatchOrder + 1; level++)
+ {
+ mRenderStridep[level] = stride;
+ stride /= 2;
+ }
+
+ // render_level <- render_stride.
+/*
+ // For a 16x16 patch we'll clamp the render_strides to 0 through 16
+ // and enter the nearest render_level in the table. Of course, only
+ // power-of-two render strides are actually used.
+ //
+ // render_stride render_level
+ // 0 4
+ // 1 4 *
+ // 2 3 *
+ // 3 3
+ // 4 2 *
+ // 5 2
+ // 6 2
+ // 7 1
+ // 8 1 *
+ // 9 1
+ // 10 1
+ // 11 1
+ // 12 1
+ // 13 0
+ // 14 0
+ // 15 0
+ // 16 Always 0
+
+ level = mPatchOrder;
+ for (stride=0; stride<mPatchWidth; stride++)
+ {
+ if ((F32) stride > 2.1f * mRenderStridep[level])
+ {
+ level--;
+ };
+ mRenderLevelp[stride] = level;
+ }
+ */
+
+ // This method is more agressive about putting triangles onscreen
+ level = mPatchOrder;
+ k = 2;
+ mRenderLevelp[0] = mPatchOrder;
+ mRenderLevelp[1] = mPatchOrder;
+ stride = 2;
+ while(stride < 2*mPatchWidth)
+ {
+ for (j=0; j<k && stride<2*mPatchWidth; j++)
+ {
+ mRenderLevelp[stride++] = level;
+ }
+ k *= 2;
+ level--;
+ }
+ mRenderLevelp[2*mPatchWidth] = 0;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPatchVertexArray &va)
+{
+ U32 i;
+ s << "{ \n";
+ s << " mSurfaceWidth = " << va.mSurfaceWidth << "\n";
+ s << " mPatchWidth = " << va.mPatchWidth << "\n";
+ s << " mPatchOrder = " << va.mPatchOrder << "\n";
+ s << " mRenderStridep = \n";
+ for (i=0; i<va.mPatchOrder+1; i++)
+ {
+ s << " " << i << " " << va.mRenderStridep[i] << "\n";
+ }
+ s << " mRenderLevelp = \n";
+ for (i=0; i < 2*va.mPatchWidth + 1; i++)
+ {
+ s << " " << i << " " << va.mRenderLevelp[i] << "\n";
+ }
+ s << "}";
+ return s;
+}
+
+
+// EOF
diff --git a/indra/newview/llpatchvertexarray.h b/indra/newview/llpatchvertexarray.h
new file mode 100644
index 0000000000..5addfa57de
--- /dev/null
+++ b/indra/newview/llpatchvertexarray.h
@@ -0,0 +1,51 @@
+/**
+ * @file llpatchvertexarray.h
+ * @brief description of Surface class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPATCHVERTEXARRAY_H
+#define LL_LLPATCHVERTEXARRAY_H
+
+// A LLPatchVertexArray is really just a structure of vertex arrays for
+// rendering a "patch" of a certain size.
+//
+// A "patch" is currently a sub-square of a larger square array of data
+// we call a "surface".
+class LLPatchVertexArray
+{
+public:
+ LLPatchVertexArray();
+ LLPatchVertexArray(U32 surface_width, U32 patch_width, F32 meters_per_grid);
+ virtual ~LLPatchVertexArray();
+
+ void create(U32 surface_width, U32 patch_width, F32 meters_per_grid);
+ void destroy();
+ void init();
+
+public:
+ U32 mSurfaceWidth; // grid points on one side of a LLSurface
+ U32 mPatchWidth; // grid points on one side of a LLPatch
+ U32 mPatchOrder; // 2^mPatchOrder >= mPatchWidth
+
+ U32 *mRenderLevelp; // Look-up table : render_stride -> render_level
+ U32 *mRenderStridep; // Look-up table : render_level -> render_stride
+
+ // We want to be able to render a patch from multiple resolutions.
+ // The lowest resolution has two triangles, and the highest has
+ // 2*mPatchWidth*mPatchWidth triangles.
+ //
+ // mPatchWidth is not hard-coded, so we don't know how much memory
+ // to allocate to the vertex arrays until it is set. Once it is
+ // set, we will calculate how much total memory to allocate for the
+ // vertex arrays, and then keep track of their lengths and locations
+ // in the memory bank.
+ //
+ // A Patch has three regions that need vertex arrays: middle, north,
+ // and east. For each region there are three items that must be
+ // kept track of: data, offset, and length.
+};
+
+#endif
diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp
new file mode 100644
index 0000000000..593a502b37
--- /dev/null
+++ b/indra/newview/llpolymesh.cpp
@@ -0,0 +1,1117 @@
+/**
+ * @file llpolymesh.cpp
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llpolymesh.h"
+
+#include "llviewercontrol.h"
+#include "llxmltree.h"
+#include "llvoavatar.h"
+#include "lldir.h"
+#include "llvolume.h"
+#include "llendianswizzle.h"
+
+#include "llfasttimer.h"
+
+#define HEADER_ASCII "Linden Mesh 1.0"
+#define HEADER_BINARY "Linden Binary Mesh 1.0"
+
+extern LLControlGroup gSavedSettings; // read only
+
+//-----------------------------------------------------------------------------
+// Global table of loaded LLPolyMeshes
+//-----------------------------------------------------------------------------
+LLPolyMesh::LLPolyMeshSharedDataTable LLPolyMesh::sGlobalSharedMeshList;
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData()
+//-----------------------------------------------------------------------------
+LLPolyMeshSharedData::LLPolyMeshSharedData()
+{
+ mNumVertices = 0;
+ mBaseCoords = NULL;
+ mBaseNormals = NULL;
+ mBaseBinormals = NULL;
+ mTexCoords = NULL;
+ mDetailTexCoords = NULL;
+ mWeights = NULL;
+ mHasWeights = FALSE;
+ mHasDetailTexCoords = FALSE;
+
+ mNumFaces = 0;
+ mFaces = NULL;
+
+ mNumJointNames = 0;
+ mJointNames = NULL;
+
+ mTriangleIndices = NULL;
+ mNumTriangleIndices = 0;
+
+ mReferenceData = NULL;
+
+ mLastIndexOffset = -1;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMeshSharedData()
+//-----------------------------------------------------------------------------
+LLPolyMeshSharedData::~LLPolyMeshSharedData()
+{
+ freeMeshData();
+ mMorphData.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// setupLOD()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::setupLOD(LLPolyMeshSharedData* reference_data)
+{
+ mReferenceData = reference_data;
+
+ if (reference_data)
+ {
+ mBaseCoords = reference_data->mBaseCoords;
+ mBaseNormals = reference_data->mBaseNormals;
+ mBaseBinormals = reference_data->mBaseBinormals;
+ mTexCoords = reference_data->mTexCoords;
+ mDetailTexCoords = reference_data->mDetailTexCoords;
+ mWeights = reference_data->mWeights;
+ mHasWeights = reference_data->mHasWeights;
+ mHasDetailTexCoords = reference_data->mHasDetailTexCoords;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::freeMeshData()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::freeMeshData()
+{
+ if (!mReferenceData)
+ {
+ mNumVertices = 0;
+
+ delete [] mBaseCoords;
+ mBaseCoords = NULL;
+
+ delete [] mBaseNormals;
+ mBaseNormals = NULL;
+
+ delete [] mBaseBinormals;
+ mBaseBinormals = NULL;
+
+ delete [] mTexCoords;
+ mTexCoords = NULL;
+
+ delete [] mDetailTexCoords;
+ mDetailTexCoords = NULL;
+
+ delete [] mWeights;
+ mWeights = NULL;
+ }
+
+ mNumFaces = 0;
+ delete [] mFaces;
+ mFaces = NULL;
+
+ mNumJointNames = 0;
+ delete [] mJointNames;
+ mJointNames = NULL;
+
+ delete [] mTriangleIndices;
+ mTriangleIndices = NULL;
+
+// mVertFaceMap.deleteAllData();
+}
+
+// compate_int is used by the qsort function to sort the index array
+int compare_int(const void *a, const void *b);
+
+//-----------------------------------------------------------------------------
+// genIndices()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::genIndices(S32 index_offset)
+{
+ if (index_offset == mLastIndexOffset)
+ {
+ return;
+ }
+
+ delete []mTriangleIndices;
+ mTriangleIndices = new U32[mNumTriangleIndices];
+
+ S32 cur_index = 0;
+ for (S32 i = 0; i < mNumFaces; i++)
+ {
+ mTriangleIndices[cur_index] = mFaces[i][0] + index_offset;
+ cur_index++;
+ mTriangleIndices[cur_index] = mFaces[i][1] + index_offset;
+ cur_index++;
+ mTriangleIndices[cur_index] = mFaces[i][2] + index_offset;
+ cur_index++;
+ }
+
+ mLastIndexOffset = index_offset;
+}
+
+//--------------------------------------------------------------------
+// LLPolyMeshSharedData::getNumKB()
+//--------------------------------------------------------------------
+U32 LLPolyMeshSharedData::getNumKB()
+{
+ U32 num_kb = sizeof(LLPolyMesh);
+
+ if (!isLOD())
+ {
+ num_kb += mNumVertices *
+ ( sizeof(LLVector3) + // coords
+ sizeof(LLVector3) + // normals
+ sizeof(LLVector2) ); // texCoords
+ }
+
+ if (mHasDetailTexCoords && !isLOD())
+ {
+ num_kb += mNumVertices * sizeof(LLVector2); // detailTexCoords
+ }
+
+ if (mHasWeights && !isLOD())
+ {
+ num_kb += mNumVertices * sizeof(float); // weights
+ }
+
+ num_kb += mNumFaces * sizeof(LLPolyFace); // faces
+
+ num_kb /= 1024;
+ return num_kb;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateVertexData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateVertexData( U32 numVertices )
+{
+ U32 i;
+ mBaseCoords = new LLVector3[ numVertices ];
+ mBaseNormals = new LLVector3[ numVertices ];
+ mBaseBinormals = new LLVector3[ numVertices ];
+ mTexCoords = new LLVector2[ numVertices ];
+ mDetailTexCoords = new LLVector2[ numVertices ];
+ mWeights = new F32[ numVertices ];
+ for (i = 0; i < numVertices; i++)
+ {
+ mWeights[i] = 0.f;
+ }
+ mNumVertices = numVertices;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateFaceData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateFaceData( U32 numFaces )
+{
+ mFaces = new LLPolyFace[ numFaces ];
+ mNumFaces = numFaces;
+ mNumTriangleIndices = mNumFaces * 3;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateJointNames()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateJointNames( U32 numJointNames )
+{
+ mJointNames = new std::string[ numJointNames ];
+ mNumJointNames = numJointNames;
+ return TRUE;
+}
+
+//--------------------------------------------------------------------
+// LLPolyMeshSharedData::loadMesh()
+//--------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::loadMesh( const char *fileName )
+{
+ //-------------------------------------------------------------------------
+ // Open the file
+ //-------------------------------------------------------------------------
+ FILE *fp = LLFile::fopen(fileName, "rb");
+ if (!fp)
+ {
+ llerrs << "can't open: " << fileName << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // Read a chunk
+ //-------------------------------------------------------------------------
+ char header[128];
+ fread(header, sizeof(char), 128, fp);
+
+ //-------------------------------------------------------------------------
+ // Check for proper binary header
+ //-------------------------------------------------------------------------
+ BOOL status = FALSE;
+ if ( strncmp(header, HEADER_BINARY, strlen(HEADER_BINARY)) == 0 )
+ {
+ lldebugs << "Loading " << fileName << llendl;
+
+ //----------------------------------------------------------------
+ // File Header (seek past it)
+ //----------------------------------------------------------------
+ fseek(fp, 24, SEEK_SET);
+
+ //----------------------------------------------------------------
+ // HasWeights
+ //----------------------------------------------------------------
+ U8 hasWeights;
+ size_t numRead = fread(&hasWeights, sizeof(U8), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read HasWeights flag from " << fileName << llendl;
+ }
+ if (!isLOD())
+ {
+ mHasWeights = (hasWeights==0) ? FALSE : TRUE;
+ }
+
+ //----------------------------------------------------------------
+ // HasDetailTexCoords
+ //----------------------------------------------------------------
+ U8 hasDetailTexCoords;
+ numRead = fread(&hasDetailTexCoords, sizeof(U8), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read HasDetailTexCoords flag from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Position
+ //----------------------------------------------------------------
+ LLVector3 position;
+ numRead = fread(position.mV, sizeof(float), 3, fp);
+ llendianswizzle(position.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Position from " << fileName << llendl;
+ }
+ setPosition( position );
+
+ //----------------------------------------------------------------
+ // Rotation
+ //----------------------------------------------------------------
+ LLVector3 rotationAngles;
+ numRead = fread(rotationAngles.mV, sizeof(float), 3, fp);
+ llendianswizzle(rotationAngles.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read RotationAngles from " << fileName << llendl;
+ }
+
+ U8 rotationOrder;
+ numRead = fread(&rotationOrder, 1, 1, fp);
+
+ if (numRead != 1)
+ {
+ llerrs << "can't read RotationOrder from " << fileName << llendl;
+ }
+
+ rotationOrder = 0;
+
+ setRotation( mayaQ( rotationAngles.mV[0],
+ rotationAngles.mV[1],
+ rotationAngles.mV[2],
+ (LLQuaternion::Order)rotationOrder ) );
+
+ //----------------------------------------------------------------
+ // Scale
+ //----------------------------------------------------------------
+ LLVector3 scale;
+ numRead = fread(scale.mV, sizeof(float), 3, fp);
+ llendianswizzle(scale.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Scale from " << fileName << llendl;
+ }
+ setScale( scale );
+
+ //-------------------------------------------------------------------------
+ // Release any existing mesh geometry
+ //-------------------------------------------------------------------------
+ freeMeshData();
+
+ U16 numVertices = 0;
+
+ //----------------------------------------------------------------
+ // NumVertices
+ //----------------------------------------------------------------
+ if (!isLOD())
+ {
+ numRead = fread(&numVertices, sizeof(U16), 1, fp);
+ llendianswizzle(&numVertices, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumVertices from " << fileName << llendl;
+ }
+
+ allocateVertexData( numVertices );
+
+ //----------------------------------------------------------------
+ // Coords
+ //----------------------------------------------------------------
+ numRead = fread(mBaseCoords, 3*sizeof(float), numVertices, fp);
+ llendianswizzle(mBaseCoords, sizeof(float), 3*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read Coordinates from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Normals
+ //----------------------------------------------------------------
+ numRead = fread(mBaseNormals, 3*sizeof(float), numVertices, fp);
+ llendianswizzle(mBaseNormals, sizeof(float), 3*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << " can't read Normals from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Binormals
+ //----------------------------------------------------------------
+ numRead = fread(mBaseBinormals, 3*sizeof(float), numVertices, fp);
+ llendianswizzle(mBaseBinormals, sizeof(float), 3*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << " can't read Binormals from " << fileName << llendl;
+ }
+
+
+ //----------------------------------------------------------------
+ // TexCoords
+ //----------------------------------------------------------------
+ numRead = fread(mTexCoords, 2*sizeof(float), numVertices, fp);
+ llendianswizzle(mTexCoords, sizeof(float), 2*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read TexCoords from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // DetailTexCoords
+ //----------------------------------------------------------------
+ if (mHasDetailTexCoords)
+ {
+ numRead = fread(mDetailTexCoords, 2*sizeof(float), numVertices, fp);
+ llendianswizzle(mDetailTexCoords, sizeof(float), 2*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read DetailTexCoords from " << fileName << llendl;
+ }
+ }
+
+ //----------------------------------------------------------------
+ // Weights
+ //----------------------------------------------------------------
+ if (mHasWeights)
+ {
+ numRead = fread(mWeights, sizeof(float), numVertices, fp);
+ llendianswizzle(mWeights, sizeof(float), numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read Weights from " << fileName << llendl;
+ }
+ }
+ }
+
+ //----------------------------------------------------------------
+ // NumFaces
+ //----------------------------------------------------------------
+ U16 numFaces;
+ numRead = fread(&numFaces, sizeof(U16), 1, fp);
+ llendianswizzle(&numFaces, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumFaces from " << fileName << llendl;
+ }
+ allocateFaceData( numFaces );
+
+
+ //----------------------------------------------------------------
+ // Faces
+ //----------------------------------------------------------------
+ U32 i;
+ U32 numTris = 0;
+ for (i = 0; i < numFaces; i++)
+ {
+ S16 face[3];
+ numRead = fread(face, sizeof(U16), 3, fp);
+ llendianswizzle(face, sizeof(U16), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Face[" << i << "] from " << fileName << llendl;
+ }
+ if (mReferenceData)
+ {
+ llassert(face[0] < mReferenceData->mNumVertices);
+ llassert(face[1] < mReferenceData->mNumVertices);
+ llassert(face[2] < mReferenceData->mNumVertices);
+ }
+
+ if (isLOD())
+ {
+ // store largest index in case of LODs
+ for (S32 j = 0; j < 3; j++)
+ {
+ if (face[j] > mNumVertices - 1)
+ {
+ mNumVertices = face[j] + 1;
+ }
+ }
+ }
+ mFaces[i][0] = face[0];
+ mFaces[i][1] = face[1];
+ mFaces[i][2] = face[2];
+
+// S32 j;
+// for(j = 0; j < 3; j++)
+// {
+// LLDynamicArray<S32> *face_list = mVertFaceMap.getIfThere(face[j]);
+// if (!face_list)
+// {
+// face_list = new LLDynamicArray<S32>;
+// mVertFaceMap.addData(face[j], face_list);
+// }
+// face_list->put(i);
+// }
+
+ numTris++;
+ }
+
+ lldebugs << "verts: " << numVertices
+ << ", faces: " << numFaces
+ << ", tris: " << numTris
+ << llendl;
+
+ //----------------------------------------------------------------
+ // NumSkinJoints
+ //----------------------------------------------------------------
+ if (!isLOD())
+ {
+ U16 numSkinJoints = 0;
+ if ( mHasWeights )
+ {
+ numRead = fread(&numSkinJoints, sizeof(U16), 1, fp);
+ llendianswizzle(&numSkinJoints, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumSkinJoints from " << fileName << llendl;
+ }
+ allocateJointNames( numSkinJoints );
+ }
+
+ //----------------------------------------------------------------
+ // SkinJoints
+ //----------------------------------------------------------------
+ for (i=0; i < numSkinJoints; i++)
+ {
+ char jointName[64];
+ numRead = fread(jointName, sizeof(jointName), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read Skin[" << i << "].Name from " << fileName << llendl;
+ }
+
+ std::string *jn = &mJointNames[i];
+ *jn = jointName;
+ }
+
+ //-------------------------------------------------------------------------
+ // look for morph section
+ //-------------------------------------------------------------------------
+ char morphName[64];
+ while(fread(&morphName, sizeof(char), 64, fp) == 64)
+ {
+ if (!strcmp(morphName, "End Morphs"))
+ {
+ // we reached the end of the morphs
+ break;
+ }
+ LLPolyMorphData* morph_data = new LLPolyMorphData(morphName);
+
+ BOOL result = morph_data->loadBinary(fp, this);
+
+ if (!result)
+ {
+ delete morph_data;
+ continue;
+ }
+
+ mMorphData.addData(morph_data);
+ }
+
+ S32 numRemaps;
+ if (fread(&numRemaps, sizeof(S32), 1, fp) == 1)
+ {
+ llendianswizzle(&numRemaps, sizeof(S32), 1);
+ for (S32 i = 0; i < numRemaps; i++)
+ {
+ S32 remapSrc;
+ S32 remapDst;
+ if (fread(&remapSrc, sizeof(S32), 1, fp) != 1)
+ {
+ llerrs << "can't read source vertex in vertex remap data" << llendl;
+ break;
+ }
+ if (fread(&remapDst, sizeof(S32), 1, fp) != 1)
+ {
+ llerrs << "can't read destination vertex in vertex remap data" << llendl;
+ break;
+ }
+ llendianswizzle(&remapSrc, sizeof(S32), 1);
+ llendianswizzle(&remapDst, sizeof(S32), 1);
+
+ mSharedVerts[remapSrc] = remapDst;
+ }
+ }
+ }
+
+ status = TRUE;
+ }
+ else
+ {
+ llerrs << "invalid mesh file header: " << fileName << llendl;
+ status = FALSE;
+ }
+
+ if (0 == mNumJointNames)
+ {
+ allocateJointNames(1);
+ }
+
+ fclose( fp );
+
+ return status;
+}
+
+//-----------------------------------------------------------------------------
+// getSharedVert()
+//-----------------------------------------------------------------------------
+const S32 *LLPolyMeshSharedData::getSharedVert(S32 vert)
+{
+ if (mSharedVerts.count(vert) > 0)
+ {
+ return &mSharedVerts[vert];
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getUV()
+//-----------------------------------------------------------------------------
+const LLVector2 &LLPolyMeshSharedData::getUVs(U32 index)
+{
+ // TODO: convert all index variables to S32
+ llassert((S32)index < mNumVertices);
+
+ return mTexCoords[index];
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh::LLPolyMesh(LLPolyMeshSharedData *shared_data, LLPolyMesh *reference_mesh)
+{
+ llassert(shared_data);
+
+ mSharedData = shared_data;
+ mReferenceMesh = reference_mesh;
+ mAvatarp = NULL;
+ mVertexData = NULL;
+
+ if (shared_data->isLOD() && reference_mesh)
+ {
+ mCoords = reference_mesh->mCoords;
+ mNormals = reference_mesh->mNormals;
+ mScaledNormals = reference_mesh->mScaledNormals;
+ mBinormals = reference_mesh->mBinormals;
+ mScaledBinormals = reference_mesh->mScaledBinormals;
+ mTexCoords = reference_mesh->mTexCoords;
+ mClothingWeights = reference_mesh->mClothingWeights;
+ }
+ else
+ {
+#if 1 // Allocate memory without initializing every vector
+ // NOTE: This makes asusmptions about the size of LLVector[234]
+ int nverts = mSharedData->mNumVertices;
+ int nfloats = nverts * (3*5 + 2 + 4);
+ mVertexData = new F32[nfloats];
+ int offset = 0;
+ mCoords = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mNormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mScaledNormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mBinormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mScaledBinormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mTexCoords = (LLVector2*)(mVertexData + offset); offset += 2*nverts;
+ mClothingWeights = (LLVector4*)(mVertexData + offset); offset += 4*nverts;
+#else
+ mCoords = new LLVector3[mSharedData->mNumVertices];
+ mNormals = new LLVector3[mSharedData->mNumVertices];
+ mScaledNormals = new LLVector3[mSharedData->mNumVertices];
+ mBinormals = new LLVector3[mSharedData->mNumVertices];
+ mScaledBinormals = new LLVector3[mSharedData->mNumVertices];
+ mTexCoords = new LLVector2[mSharedData->mNumVertices];
+ mClothingWeights = new LLVector4[mSharedData->mNumVertices];
+ memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
+#endif
+ initializeForMorph();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh::~LLPolyMesh()
+{
+ S32 i;
+ for (i = 0; i < mJointRenderData.count(); i++)
+ {
+ delete mJointRenderData[i];
+ mJointRenderData[i] = NULL;
+ }
+#if 0 // These are now allocated as one big uninitialized chunk
+ delete [] mCoords;
+ delete [] mNormals;
+ delete [] mScaledNormals;
+ delete [] mBinormals;
+ delete [] mScaledBinormals;
+ delete [] mClothingWeights;
+ delete [] mTexCoords;
+#else
+ delete [] mVertexData;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh::getMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh *LLPolyMesh::getMesh(const LLString &name, LLPolyMesh* reference_mesh)
+{
+ //-------------------------------------------------------------------------
+ // search for an existing mesh by this name
+ //-------------------------------------------------------------------------
+ LLPolyMeshSharedData **meshSharedData = sGlobalSharedMeshList.getValue(name);
+ if (meshSharedData)
+ {
+// llinfos << "Polymesh " << name << " found in global mesh table." << llendl;
+ LLPolyMesh *poly_mesh = new LLPolyMesh(*meshSharedData, reference_mesh);
+ return poly_mesh;
+ }
+
+ //-------------------------------------------------------------------------
+ // if not found, create a new one, add it to the list
+ //-------------------------------------------------------------------------
+ char full_path[LL_MAX_PATH];
+ sprintf(full_path, "%s", (gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,name.c_str())).c_str());
+
+ LLPolyMeshSharedData *mesh_data = new LLPolyMeshSharedData();
+ if (reference_mesh)
+ {
+ mesh_data->setupLOD(reference_mesh->getSharedData());
+ }
+ if ( ! mesh_data->loadMesh( full_path ) )
+ {
+ delete mesh_data;
+ return NULL;
+ }
+
+ LLPolyMesh *poly_mesh = new LLPolyMesh(mesh_data, reference_mesh);
+
+// llinfos << "Polymesh " << name << " added to global mesh table." << llendl;
+ sGlobalSharedMeshList.addToTail(name, poly_mesh->mSharedData);
+
+ return poly_mesh;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh::freeAllMeshes()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::freeAllMeshes()
+{
+ U32 i;
+
+ // delete each item in the global lists
+ for (i=0; i<sGlobalSharedMeshList.length(); i++)
+ {
+ // returns a pointer to the value, which is the pointer
+ // to the mesh
+ LLPolyMeshSharedData **shared_mesh_pp = sGlobalSharedMeshList.getValueAt(i);
+
+ // delete the mesh
+ delete *shared_mesh_pp;
+ }
+
+ // empty the lists
+ sGlobalSharedMeshList.removeAll();
+}
+
+LLPolyMeshSharedData *LLPolyMesh::getSharedData() const
+{
+ return mSharedData;
+}
+
+
+//--------------------------------------------------------------------
+// LLPolyMesh::dumpDiagInfo()
+//--------------------------------------------------------------------
+void LLPolyMesh::dumpDiagInfo()
+{
+ // keep track of totals
+ U32 total_verts = 0;
+ U32 total_faces = 0;
+ U32 total_kb = 0;
+
+ char buf[1024];
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ llinfos << " Global PolyMesh Table (DEBUG only)" << llendl;
+ llinfos << " Verts Faces Mem(KB) Name" << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+
+ // print each loaded mesh, and it's memory usage
+ for (U32 i=0; i<sGlobalSharedMeshList.length(); i++)
+ {
+ std::string *mesh_name_p = sGlobalSharedMeshList.getIndexAt(i);
+
+ LLPolyMeshSharedData **mesh_pp = sGlobalSharedMeshList.getValueAt(i);
+ LLPolyMeshSharedData &mesh = **mesh_pp;
+
+ S32 num_verts = mesh.mNumVertices;
+ S32 num_faces = mesh.mNumFaces;
+ U32 num_kb = mesh.getNumKB();
+
+ sprintf(buf, "%8d %8d %8d %s", num_verts, num_faces, num_kb, mesh_name_p->c_str());
+ llinfos << buf << llendl;
+
+ total_verts += num_verts;
+ total_faces += num_faces;
+ total_kb += num_kb;
+ }
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ sprintf(buf, "%8d %8d %8d TOTAL", total_verts, total_faces, total_kb );
+ llinfos << buf << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// getCoords()
+//-----------------------------------------------------------------------------
+const LLVector3 *LLPolyMesh::getCoords() const
+{
+ return mCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableCoords()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getWritableCoords()
+{
+ return mCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableNormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getWritableNormals()
+{
+ return mNormals;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableBinormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getWritableBinormals()
+{
+ return mBinormals;
+}
+
+
+//-----------------------------------------------------------------------------
+// getWritableClothingWeights()
+//-----------------------------------------------------------------------------
+LLVector4 *LLPolyMesh::getWritableClothingWeights()
+{
+ return mClothingWeights;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableTexCoords()
+//-----------------------------------------------------------------------------
+LLVector2 *LLPolyMesh::getWritableTexCoords()
+{
+ return mTexCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getScaledNormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getScaledNormals()
+{
+ return mScaledNormals;
+}
+
+//-----------------------------------------------------------------------------
+// getScaledBinormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getScaledBinormals()
+{
+ return mScaledBinormals;
+}
+
+
+//-----------------------------------------------------------------------------
+// initializeForMorph()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::initializeForMorph()
+{
+ if (!mSharedData)
+ return;
+
+ memcpy(mCoords, mSharedData->mBaseCoords, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mNormals, mSharedData->mBaseNormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mScaledNormals, mSharedData->mBaseNormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mScaledBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mTexCoords, mSharedData->mTexCoords, sizeof(LLVector2) * mSharedData->mNumVertices);
+ memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
+}
+
+//-----------------------------------------------------------------------------
+// getMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData* LLPolyMesh::getMorphData(const char *morph_name)
+{
+ if (!mSharedData) return NULL;
+ for (LLPolyMorphData *morph_data = mSharedData->mMorphData.getFirstData();
+ morph_data;
+ morph_data = mSharedData->mMorphData.getNextData())
+ {
+ if (!strcmp(morph_data->getName(), morph_name))
+ {
+ return morph_data;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// removeMorphData()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::removeMorphData(LLPolyMorphData *morph_target)
+{
+ if (!mSharedData) return;
+
+ mSharedData->mMorphData.removeData(morph_target);
+}
+
+//-----------------------------------------------------------------------------
+// deleteAllMorphData()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::deleteAllMorphData()
+{
+ if (!mSharedData) return;
+
+ mSharedData->mMorphData.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// getWeights()
+//-----------------------------------------------------------------------------
+const F32* LLPolyMesh::getWeights() const
+{
+ return mSharedData->mWeights;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableWeights()
+//-----------------------------------------------------------------------------
+F32* LLPolyMesh::getWritableWeights() const
+{
+ return mSharedData->mWeights;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDistortionInfo()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortionInfo::LLPolySkeletalDistortionInfo()
+{
+}
+
+BOOL LLPolySkeletalDistortionInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_skeleton" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ LLXmlTreeNode* skeletalParam = node->getChildByName("param_skeleton");
+
+ for( LLXmlTreeNode* bone = skeletalParam->getFirstChild(); bone; bone = skeletalParam->getNextChild() )
+ {
+ if (bone->hasName("bone"))
+ {
+ LLString name;
+ LLVector3 scale;
+ LLVector3 pos;
+ BOOL haspos = FALSE;
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!bone->getFastAttributeString(name_string, name))
+ {
+ llwarns << "No bone name specified for skeletal param." << llendl;
+ continue;
+ }
+
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ if (!bone->getFastAttributeVector3(scale_string, scale))
+ {
+ llwarns << "No scale specified for bone " << name << "." << llendl;
+ continue;
+ }
+
+ // optional offset deformation (translation)
+ static LLStdStringHandle offset_string = LLXmlTree::addAttributeString("offset");
+ if (bone->getFastAttributeVector3(offset_string, pos))
+ {
+ haspos = TRUE;
+ }
+ mBoneInfoList.push_back(LLPolySkeletalBoneInfo(name, scale, pos, haspos));
+ }
+ else
+ {
+ llwarns << "Unrecognized element " << bone->getName() << " in skeletal distortion" << llendl;
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDistortion()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortion::LLPolySkeletalDistortion(LLVOAvatar *avatarp)
+{
+ mAvatar = avatarp;
+ mDefaultVec.setVec(0.001f, 0.001f, 0.001f);
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolySkeletalDistortion()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortion::~LLPolySkeletalDistortion()
+{
+}
+
+BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
+ for (iter = getInfo()->mBoneInfoList.begin(); iter != getInfo()->mBoneInfoList.end(); iter++)
+ {
+ LLPolySkeletalBoneInfo *bone_info = &(*iter);
+ LLJoint* joint = mAvatar->getJoint(bone_info->mBoneName);
+ if (!joint)
+ {
+ llwarns << "Joint " << bone_info->mBoneName << " not found." << llendl;
+ continue;
+ }
+
+ if (mJointScales.find(joint) != mJointScales.end())
+ {
+ llwarns << "Scale deformation already supplied for joint " << joint->getName() << "." << llendl;
+ }
+
+ // store it
+ mJointScales[joint] = bone_info->mScaleDeformation;
+
+ // apply to children that need to inherit it
+ for ( LLViewerJoint *child_joint = (LLViewerJoint *)joint->mChildren.getFirstData();
+ child_joint != NULL;
+ child_joint = (LLViewerJoint *)joint->mChildren.getNextData() )
+ {
+ if (child_joint->inheritScale())
+ {
+ LLVector3 childDeformation = LLVector3(child_joint->getScale());
+ childDeformation.scaleVec(bone_info->mScaleDeformation);
+ mJointScales[child_joint] = childDeformation;
+ }
+ }
+
+ if (bone_info->mHasPositionDeformation)
+ {
+ if (mJointOffsets.find(joint) != mJointOffsets.end())
+ {
+ llwarns << "Offset deformation already supplied for joint " << joint->getName() << "." << llendl;
+ }
+ mJointOffsets[joint] = bone_info->mPositionDeformation;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// apply()
+//-----------------------------------------------------------------------------
+void LLPolySkeletalDistortion::apply( ESex avatar_sex )
+{
+ F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();
+
+ LLJoint* joint;
+ joint_vec_map_t::iterator iter;
+
+ for (iter = mJointScales.begin();
+ iter != mJointScales.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newScale = joint->getScale();
+ LLVector3 scaleDelta = iter->second;
+ newScale = newScale + (effective_weight * scaleDelta) - (mLastWeight * scaleDelta);
+ joint->setScale(newScale);
+ }
+
+ for (iter = mJointOffsets.begin();
+ iter != mJointOffsets.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newPosition = joint->getPosition();
+ LLVector3 positionDelta = iter->second;
+ newPosition = newPosition + (effective_weight * positionDelta) - (mLastWeight * positionDelta);
+ joint->setPosition(newPosition);
+ }
+
+ if (mLastWeight != mCurWeight && !mIsAnimating)
+ {
+ mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
+ }
+ mLastWeight = mCurWeight;
+}
+
+// End
diff --git a/indra/newview/llpolymesh.h b/indra/newview/llpolymesh.h
new file mode 100644
index 0000000000..cefda92288
--- /dev/null
+++ b/indra/newview/llpolymesh.h
@@ -0,0 +1,412 @@
+/**
+ * @file llpolymesh.h
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPOLYMESH_H
+#define LL_LLPOLYMESH_H
+
+#include <string>
+#include <map>
+#include "llstl.h"
+
+#include "v3math.h"
+#include "v2math.h"
+#include "llquaternion.h"
+#include "llskipmap.h"
+#include "llassoclist.h"
+#include "llpolymorph.h"
+#include "llptrskipmap.h"
+#include "lljoint.h"
+//#include "lldarray.h"
+
+class LLSkinJoint;
+class LLVOAvatar;
+
+//#define USE_STRIPS // Use tri-strips for rendering.
+
+//-----------------------------------------------------------------------------
+// LLPolyFace
+// A set of 4 vertex indices.
+// An LLPolyFace can represent either a triangle or quad.
+// If the last index is -1, it's a triangle.
+//-----------------------------------------------------------------------------
+typedef S32 LLPolyFace[3];
+
+//struct PrimitiveGroup;
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh
+// A polyhedra consisting of any number of triangles and quads.
+// All instances contain a set of faces, and optionally may include
+// faces grouped into named face sets.
+//-----------------------------------------------------------------------------
+class LLPolyMorphTarget;
+
+class LLPolyMeshSharedData
+{
+ friend class LLPolyMesh;
+private:
+ // transform data
+ LLVector3 mPosition;
+ LLQuaternion mRotation;
+ LLVector3 mScale;
+
+ // vertex data
+ S32 mNumVertices;
+ LLVector3 *mBaseCoords;
+ LLVector3 *mBaseNormals;
+ LLVector3 *mBaseBinormals;
+ LLVector2 *mTexCoords;
+ LLVector2 *mDetailTexCoords;
+ F32 *mWeights;
+
+ BOOL mHasWeights;
+ BOOL mHasDetailTexCoords;
+
+ // face data
+ S32 mNumFaces;
+ LLPolyFace *mFaces;
+
+ // face set data
+ U32 mNumJointNames;
+ std::string* mJointNames;
+
+ // morph targets
+ typedef LLLinkedList<LLPolyMorphData> LLPolyMorphDataList;
+ LLPolyMorphDataList mMorphData;
+
+ std::map<S32, S32> mSharedVerts;
+
+ LLPolyMeshSharedData* mReferenceData;
+ S32 mLastIndexOffset;
+
+public:
+ // Temporarily...
+ // Triangle indices
+ U32 mNumTriangleIndices;
+ U32 *mTriangleIndices;
+
+private:
+ LLPolyMeshSharedData();
+
+ ~LLPolyMeshSharedData();
+
+ void setupLOD(LLPolyMeshSharedData* reference_data);
+
+ // Frees all mesh memory resources
+ void freeMeshData();
+
+ void setPosition( const LLVector3 &pos ) { mPosition = pos; }
+ void setRotation( const LLQuaternion &rot ) { mRotation = rot; }
+ void setScale( const LLVector3 &scale ) { mScale = scale; }
+
+ BOOL allocateVertexData( U32 numVertices );
+
+ BOOL allocateFaceData( U32 numFaces );
+
+ BOOL allocateJointNames( U32 numJointNames );
+
+ // Retrieve the number of KB of memory used by this instance
+ U32 getNumKB();
+
+ // Load mesh data from file
+ BOOL loadMesh( const char *fileName );
+
+public:
+ void genIndices(S32 offset);
+
+ const LLVector2 &getUVs(U32 index);
+
+ const S32 *getSharedVert(S32 vert);
+
+ BOOL isLOD() { return (mReferenceData != NULL); }
+};
+
+
+class LLJointRenderData
+{
+public:
+ LLJointRenderData(const LLMatrix4* world_matrix, LLSkinJoint* skin_joint) : mWorldMatrix(world_matrix), mSkinJoint(skin_joint){}
+ ~LLJointRenderData(){}
+
+ const LLMatrix4* mWorldMatrix;
+ LLSkinJoint* mSkinJoint;
+};
+
+
+class LLPolyMesh
+{
+public:
+
+ // Constructor
+ LLPolyMesh(LLPolyMeshSharedData *shared_data, LLPolyMesh *reference_mesh);
+
+ // Destructor
+ ~LLPolyMesh();
+
+ // Requests a mesh by name.
+ // If the mesh already exists in the global mesh table, it is returned,
+ // otherwise it is loaded from file, added to the table, and returned.
+ static LLPolyMesh *getMesh( const LLString &name, LLPolyMesh* reference_mesh = NULL);
+
+ // Frees all loaded meshes.
+ // This should only be called once you know there are no outstanding
+ // references to these objects. Generally, upon exit of the application.
+ static void freeAllMeshes();
+
+ //--------------------------------------------------------------------
+ // Transform Data Access
+ //--------------------------------------------------------------------
+ // Get position
+ const LLVector3 &getPosition() {
+ llassert (mSharedData);
+ return mSharedData->mPosition;
+ }
+
+ // Get rotation
+ const LLQuaternion &getRotation() {
+ llassert (mSharedData);
+ return mSharedData->mRotation;
+ }
+
+ // Get scale
+ const LLVector3 &getScale() {
+ llassert (mSharedData);
+ return mSharedData->mScale;
+ }
+
+ //--------------------------------------------------------------------
+ // Vertex Data Access
+ //--------------------------------------------------------------------
+ // Get number of vertices
+ U32 getNumVertices() {
+ llassert (mSharedData);
+ return mSharedData->mNumVertices;
+ }
+
+ // Returns whether or not the mesh has detail texture coords
+ BOOL hasDetailTexCoords() {
+ llassert (mSharedData);
+ return mSharedData->mHasDetailTexCoords;
+ }
+
+ // Returns whether or not the mesh has vertex weights
+ BOOL hasWeights() const{
+ llassert (mSharedData);
+ return mSharedData->mHasWeights;
+ }
+
+ // Get coords
+ const LLVector3 *getCoords() const;
+
+ // non const version
+ LLVector3 *getWritableCoords();
+
+ // Get normals
+ const LLVector3 *getNormals() const{
+ return mNormals;
+ }
+
+ // Get normals
+ const LLVector3 *getBinormals() const{
+ return mBinormals;
+ }
+
+ // Get base mesh normals
+ const LLVector3 *getBaseNormals() const{
+ llassert(mSharedData);
+ return mSharedData->mBaseNormals;
+ }
+
+ // Get base mesh normals
+ const LLVector3 *getBaseBinormals() const{
+ llassert(mSharedData);
+ return mSharedData->mBaseBinormals;
+ }
+
+ // intermediate morphed normals and output normals
+ LLVector3 *getWritableNormals();
+ LLVector3 *getScaledNormals();
+
+ LLVector3 *getWritableBinormals();
+ LLVector3 *getScaledBinormals();
+
+ // Get texCoords
+ const LLVector2 *getTexCoords() const {
+ return mTexCoords;
+ }
+
+ // non const version
+ LLVector2 *getWritableTexCoords();
+
+ // Get detailTexCoords
+ const LLVector2 *getDetailTexCoords() const {
+ llassert (mSharedData);
+ return mSharedData->mDetailTexCoords;
+ }
+
+ // Get weights
+ const F32 *getWeights() const;
+
+ F32 *getWritableWeights() const;
+
+ LLVector4 *getWritableClothingWeights();
+
+ const LLVector4 *getClothingWeights()
+ {
+ return mClothingWeights;
+ }
+
+ //--------------------------------------------------------------------
+ // Face Data Access
+ //--------------------------------------------------------------------
+ // Get number of faces
+ S32 getNumFaces() {
+ llassert (mSharedData);
+ return mSharedData->mNumFaces;
+ }
+
+ // Get faces
+ LLPolyFace *getFaces() {
+ llassert (mSharedData);
+ return mSharedData->mFaces;
+ }
+
+ U32 getNumJointNames() {
+ llassert (mSharedData);
+ return mSharedData->mNumJointNames;
+ }
+
+ std::string *getJointNames() {
+ llassert (mSharedData);
+ return mSharedData->mJointNames;
+ }
+
+ LLPolyMorphData* getMorphData(const char *morph_name);
+ void removeMorphData(LLPolyMorphData *morph_target);
+ void deleteAllMorphData();
+
+ LLPolyMeshSharedData *getSharedData() const;
+ LLPolyMesh *getReferenceMesh() { return mReferenceMesh ? mReferenceMesh : this; }
+
+ // Get indices
+ U32* getIndices() { return mSharedData ? mSharedData->mTriangleIndices : NULL; }
+
+ BOOL isLOD() { return mSharedData && mSharedData->isLOD(); }
+
+ void setAvatar(LLVOAvatar* avatarp) { mAvatarp = avatarp; }
+ LLVOAvatar* getAvatar() { return mAvatarp; }
+
+ LLDynamicArray<LLJointRenderData*> mJointRenderData;
+
+ U32 mFaceVertexOffset;
+ U32 mFaceVertexCount;
+ U32 mFaceIndexCount;
+ U32 mCurVertexCount;
+private:
+ void initializeForMorph();
+
+ // Dumps diagnostic information about the global mesh table
+ static void dumpDiagInfo();
+
+protected:
+ // mesh data shared across all instances of a given mesh
+ LLPolyMeshSharedData *mSharedData;
+ // Single array of floats for allocation / deletion
+ F32 *mVertexData;
+ // deformed vertices (resulting from application of morph targets)
+ LLVector3 *mCoords;
+ // deformed normals (resulting from application of morph targets)
+ LLVector3 *mScaledNormals;
+ // output normals (after normalization)
+ LLVector3 *mNormals;
+ // deformed binormals (resulting from application of morph targets)
+ LLVector3 *mScaledBinormals;
+ // output binormals (after normalization)
+ LLVector3 *mBinormals;
+ // weight values that mark verts as clothing/skin
+ LLVector4 *mClothingWeights;
+ // output texture coordinates
+ LLVector2 *mTexCoords;
+
+ LLPolyMesh *mReferenceMesh;
+
+ // global mesh list
+ typedef LLAssocList<std::string, LLPolyMeshSharedData*> LLPolyMeshSharedDataTable;
+ static LLPolyMeshSharedDataTable sGlobalSharedMeshList;
+
+ LLVOAvatar* mAvatarp;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDeformationInfo
+// Shared information for LLPolySkeletalDeformations
+//-----------------------------------------------------------------------------
+struct LLPolySkeletalBoneInfo
+{
+ LLPolySkeletalBoneInfo(LLString &name, LLVector3 &scale, LLVector3 &pos, BOOL haspos)
+ : mBoneName(name),
+ mScaleDeformation(scale),
+ mPositionDeformation(pos),
+ mHasPositionDeformation(haspos) {}
+ LLString mBoneName;
+ LLVector3 mScaleDeformation;
+ LLVector3 mPositionDeformation;
+ BOOL mHasPositionDeformation;
+};
+
+class LLPolySkeletalDistortionInfo : public LLViewerVisualParamInfo
+{
+ friend class LLPolySkeletalDistortion;
+public:
+ LLPolySkeletalDistortionInfo();
+ /*virtual*/ ~LLPolySkeletalDistortionInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ typedef std::vector<LLPolySkeletalBoneInfo> bone_info_list_t;
+ bone_info_list_t mBoneInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDeformation
+// A set of joint scale data for deforming the avatar mesh
+//-----------------------------------------------------------------------------
+class LLPolySkeletalDistortion : public LLViewerVisualParam
+{
+public:
+ LLPolySkeletalDistortion(LLVOAvatar *avatarp);
+ ~LLPolySkeletalDistortion();
+
+ // Special: These functions are overridden by child classes
+ LLPolySkeletalDistortionInfo* getInfo() const { return (LLPolySkeletalDistortionInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLPolySkeletalDistortionInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex );
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 0.1f; }
+ /*virtual*/ const LLVector3& getAvgDistortion() { return mDefaultVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 0.1f; }
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh){return LLVector3(0.001f, 0.001f, 0.001f);}
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh){index = 0; poly_mesh = NULL; return &mDefaultVec;};
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh){index = 0; poly_mesh = NULL; return NULL;};
+
+protected:
+ typedef std::map<LLJoint*, LLVector3> joint_vec_map_t;
+ joint_vec_map_t mJointScales;
+ joint_vec_map_t mJointOffsets;
+ LLVector3 mDefaultVec;
+ LLVOAvatar* mAvatar;
+};
+
+#endif // LL_LLPOLYMESH_H
+
diff --git a/indra/newview/llpolymorph.cpp b/indra/newview/llpolymorph.cpp
new file mode 100644
index 0000000000..317550a383
--- /dev/null
+++ b/indra/newview/llpolymorph.cpp
@@ -0,0 +1,639 @@
+/**
+ * @file llpolymorph.cpp
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llpolymorph.h"
+#include "linked_lists.h"
+#include "llvoavatar.h"
+#include "llxmltree.h"
+#include "llendianswizzle.h"
+
+//#include "../tools/imdebug/imdebug.h"
+
+const F32 NORMAL_SOFTEN_FACTOR = 0.65f;
+
+LLLinkedList<LLPolyMorphData> gLoadedMorphs;
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData::LLPolyMorphData(char *morph_name)
+{
+ llassert (morph_name);
+
+ mName = new char[strlen(morph_name) + 1];
+ strcpy(mName, morph_name);
+
+ mNumIndices = 0;
+ mCurrentIndex = 0;
+ mTotalDistortion = 0.f;
+ mAvgDistortion.zeroVec();
+ mMaxDistortion = 0.f;
+ mVertexIndices = NULL;
+ mCoords = NULL;
+ mNormals = NULL;
+ mBinormals = NULL;
+ mTexCoords = NULL;
+
+ mMesh = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData::~LLPolyMorphData()
+{
+ delete [] mName;
+ delete [] mVertexIndices;
+ delete [] mCoords;
+ delete [] mNormals;
+ delete [] mBinormals;
+ delete [] mTexCoords;
+}
+
+//-----------------------------------------------------------------------------
+// loadBinary()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphData::loadBinary(FILE *fp, LLPolyMeshSharedData *mesh)
+{
+ S32 numVertices;
+ S32 numRead;
+
+ numRead = fread(&numVertices, sizeof(S32), 1, fp);
+ llendianswizzle(&numVertices, sizeof(S32), 1);
+ if (numRead != 1)
+ {
+ llwarns << "Can't read number of morph target vertices" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // allocate vertices
+ //-------------------------------------------------------------------------
+ mCoords = new LLVector3[numVertices];
+ mNormals = new LLVector3[numVertices];
+ mBinormals = new LLVector3[numVertices];
+ mTexCoords = new LLVector2[numVertices];
+ // Actually, we are allocating more space than we need for the skiplist
+ mVertexIndices = new U32[numVertices];
+ mNumIndices = 0;
+ mTotalDistortion = 0.f;
+ mMaxDistortion = 0.f;
+ mAvgDistortion.zeroVec();
+ mMesh = mesh;
+
+ //-------------------------------------------------------------------------
+ // read vertices
+ //-------------------------------------------------------------------------
+ for(S32 v = 0; v < numVertices; v++)
+ {
+ numRead = fread(&mVertexIndices[v], sizeof(U32), 1, fp);
+ llendianswizzle(&mVertexIndices[v], sizeof(U32), 1);
+ if (numRead != 1)
+ {
+ llwarns << "Can't read morph target vertex number" << llendl;
+ return FALSE;
+ }
+
+ if (mVertexIndices[v] > 10000)
+ {
+ llerrs << "Bad morph index: " << mVertexIndices[v] << llendl;
+ }
+
+
+ numRead = fread(&mCoords[v].mV, sizeof(F32), 3, fp);
+ llendianswizzle(&mCoords[v].mV, sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target vertex coordinates" << llendl;
+ return FALSE;
+ }
+
+ F32 magnitude = mCoords[v].magVec();
+
+ mTotalDistortion += magnitude;
+ mAvgDistortion.mV[VX] += fabs(mCoords[v].mV[VX]);
+ mAvgDistortion.mV[VY] += fabs(mCoords[v].mV[VY]);
+ mAvgDistortion.mV[VZ] += fabs(mCoords[v].mV[VZ]);
+
+ if (magnitude > mMaxDistortion)
+ {
+ mMaxDistortion = magnitude;
+ }
+
+ numRead = fread(&mNormals[v].mV, sizeof(F32), 3, fp);
+ llendianswizzle(&mNormals[v].mV, sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target normal" << llendl;
+ return FALSE;
+ }
+
+ numRead = fread(&mBinormals[v].mV, sizeof(F32), 3, fp);
+ llendianswizzle(&mBinormals[v].mV, sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target binormal" << llendl;
+ return FALSE;
+ }
+
+
+ numRead = fread(&mTexCoords[v].mV, sizeof(F32), 2, fp);
+ llendianswizzle(&mTexCoords[v].mV, sizeof(F32), 2);
+ if (numRead != 2)
+ {
+ llwarns << "Can't read morph target uv" << llendl;
+ return FALSE;
+ }
+
+ mNumIndices++;
+ }
+
+ mAvgDistortion = mAvgDistortion * (1.f/(F32)mNumIndices);
+ mAvgDistortion.normVec();
+
+ gLoadedMorphs.addData(this);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTargetInfo()
+//-----------------------------------------------------------------------------
+LLPolyMorphTargetInfo::LLPolyMorphTargetInfo()
+ : mIsClothingMorph(FALSE)
+{
+}
+
+BOOL LLPolyMorphTargetInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_morph" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ // Get mixed-case name
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mMorphName ) )
+ {
+ llwarns << "Avatar file: <param> is missing name attribute" << llendl;
+ return FALSE; // Continue, ignoring this tag
+ }
+
+ static LLStdStringHandle clothing_morph_string = LLXmlTree::addAttributeString("clothing_morph");
+ node->getFastAttributeBOOL(clothing_morph_string, mIsClothingMorph);
+
+ LLXmlTreeNode *paramNode = node->getChildByName("param_morph");
+
+ for (LLXmlTreeNode* child_node = paramNode->getFirstChild();
+ child_node;
+ child_node = paramNode->getNextChild())
+ {
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (child_node->hasName("volume_morph"))
+ {
+ LLString volume_name;
+ if (child_node->getFastAttributeString(name_string, volume_name))
+ {
+ LLVector3 scale;
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ child_node->getFastAttributeVector3(scale_string, scale);
+
+ LLVector3 pos;
+ static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
+ child_node->getFastAttributeVector3(pos_string, pos);
+
+ mVolumeInfoList.push_back(LLPolyVolumeMorphInfo(volume_name,scale,pos));
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget()
+//-----------------------------------------------------------------------------
+LLPolyMorphTarget::LLPolyMorphTarget(LLPolyMesh *poly_mesh)
+ : mMorphData(NULL), mMesh(poly_mesh),
+ mVertMask(NULL),
+ mLastSex(SEX_FEMALE),
+ mNumMorphMasksPending(0)
+{
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMorphTarget()
+//-----------------------------------------------------------------------------
+LLPolyMorphTarget::~LLPolyMorphTarget()
+{
+ delete mVertMask;
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLVOAvatar* avatarp = mMesh->getAvatar();
+ LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
+ for (iter = getInfo()->mVolumeInfoList.begin(); iter != getInfo()->mVolumeInfoList.end(); iter++)
+ {
+ LLPolyVolumeMorphInfo *volume_info = &(*iter);
+ std::string vol_string(volume_info->mName);
+ for (S32 i = 0; i < avatarp->mNumCollisionVolumes; i++)
+ {
+ if (avatarp->mCollisionVolumes[i].getName() == vol_string)
+ {
+ mVolumeMorphs.push_back(LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
+ volume_info->mScale,
+ volume_info->mPos));
+ break;
+ }
+ }
+ }
+
+ mMorphData = mMesh->getMorphData(getInfo()->mMorphName.c_str());
+ if (!mMorphData)
+ {
+ llwarns << "No morph target named " << getInfo()->mMorphName << " found in mesh." << llendl;
+ return FALSE; // Continue, ignoring this tag
+ }
+ return TRUE;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphTarget::parseData(LLXmlTreeNode* node)
+{
+ LLPolyMorphTargetInfo* info = new LLPolyMorphTargetInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// getVertexDistortion()
+//-----------------------------------------------------------------------------
+LLVector3 LLPolyMorphTarget::getVertexDistortion(S32 requested_index, LLPolyMesh *mesh)
+{
+ if (!mMorphData || mMesh != mesh) return LLVector3::zero;
+
+ for(U32 index = 0; index < mMorphData->mNumIndices; index++)
+ {
+ if (mMorphData->mVertexIndices[index] == (U32)requested_index)
+ {
+ return mMorphData->mCoords[index];
+ }
+ }
+
+ return LLVector3::zero;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstDistortion()
+//-----------------------------------------------------------------------------
+const LLVector3 *LLPolyMorphTarget::getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ if (!mMorphData) return &LLVector3::zero;
+
+ LLVector3* resultVec;
+ mMorphData->mCurrentIndex = 0;
+ if (mMorphData->mNumIndices)
+ {
+ resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
+ if (index != NULL)
+ {
+ *index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
+ }
+ if (poly_mesh != NULL)
+ {
+ *poly_mesh = mMesh;
+ }
+
+ return resultVec;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getNextDistortion()
+//-----------------------------------------------------------------------------
+const LLVector3 *LLPolyMorphTarget::getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ if (!mMorphData) return &LLVector3::zero;
+
+ LLVector3* resultVec;
+ mMorphData->mCurrentIndex++;
+ if (mMorphData->mCurrentIndex < mMorphData->mNumIndices)
+ {
+ resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
+ if (index != NULL)
+ {
+ *index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
+ }
+ if (poly_mesh != NULL)
+ {
+ *poly_mesh = mMesh;
+ }
+ return resultVec;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getTotalDistortion()
+//-----------------------------------------------------------------------------
+F32 LLPolyMorphTarget::getTotalDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mTotalDistortion;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getAvgDistortion()
+//-----------------------------------------------------------------------------
+const LLVector3& LLPolyMorphTarget::getAvgDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mAvgDistortion;
+ }
+ else
+ {
+ return LLVector3::zero;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getMaxDistortion()
+//-----------------------------------------------------------------------------
+F32 LLPolyMorphTarget::getMaxDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mMaxDistortion;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// apply()
+//-----------------------------------------------------------------------------
+void LLPolyMorphTarget::apply( ESex avatar_sex )
+{
+ if (!mMorphData || mNumMorphMasksPending > 0)
+ {
+ return;
+ }
+
+ mLastSex = avatar_sex;
+
+ // perform differential update of morph
+ F32 delta_weight = ( getSex() & avatar_sex ) ? (mCurWeight - mLastWeight) : (getDefaultWeight() - mLastWeight);
+ // store last weight
+ mLastWeight += delta_weight;
+
+ if (delta_weight != 0.f)
+ {
+ llassert(!mMesh->isLOD());
+ LLVector3 *coords = mMesh->getWritableCoords();
+
+ LLVector3 *scaled_normals = mMesh->getScaledNormals();
+ LLVector3 *normals = mMesh->getWritableNormals();
+
+ LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
+ LLVector3 *binormals = mMesh->getWritableBinormals();
+
+ LLVector4 *clothing_weights = mMesh->getWritableClothingWeights();
+ LLVector2 *tex_coords = mMesh->getWritableTexCoords();
+
+ F32 *maskWeightArray = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
+
+ for(U32 vert_index_morph = 0; vert_index_morph < mMorphData->mNumIndices; vert_index_morph++)
+ {
+ S32 vert_index_mesh = mMorphData->mVertexIndices[vert_index_morph];
+
+ F32 maskWeight = 1.f;
+ if (maskWeightArray)
+ {
+ maskWeight = maskWeightArray[vert_index_morph];
+ }
+
+ coords[vert_index_mesh] += mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
+ if (getInfo()->mIsClothingMorph && clothing_weights)
+ {
+ LLVector3 clothing_offset = mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
+ LLVector4* clothing_weight = &clothing_weights[vert_index_mesh];
+ clothing_weight->mV[VX] += clothing_offset.mV[VX];
+ clothing_weight->mV[VY] += clothing_offset.mV[VY];
+ clothing_weight->mV[VZ] += clothing_offset.mV[VZ];
+ clothing_weight->mV[VW] = maskWeight;
+ }
+
+ // calculate new normals based on half angles
+ scaled_normals[vert_index_mesh] += mMorphData->mNormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
+ LLVector3 normalized_normal = scaled_normals[vert_index_mesh];
+ normalized_normal.normVec();
+ normals[vert_index_mesh] = normalized_normal;
+
+ // calculate new binormals
+ scaled_binormals[vert_index_mesh] += mMorphData->mBinormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
+ LLVector3 tangent = scaled_binormals[vert_index_mesh] % normalized_normal;
+ LLVector3 normalized_binormal = normalized_normal % tangent;
+ normalized_binormal.normVec();
+ binormals[vert_index_mesh] = normalized_binormal;
+
+ tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
+ }
+
+ // now apply volume changes
+ for( volume_list_t::iterator iter = mVolumeMorphs.begin(); iter != mVolumeMorphs.end(); iter++ )
+ {
+ LLPolyVolumeMorph* volume_morph = &(*iter);
+ LLVector3 scale_delta = volume_morph->mScale * delta_weight;
+ LLVector3 pos_delta = volume_morph->mPos * delta_weight;
+
+ volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
+ volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
+ }
+ }
+
+ if (mNext)
+ {
+ mNext->apply(avatar_sex);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// applyMask()
+//-----------------------------------------------------------------------------
+void LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert)
+{
+ LLVector4 *clothing_weights = getInfo()->mIsClothingMorph ? mMesh->getWritableClothingWeights() : NULL;
+
+ if (!mVertMask)
+ {
+ mVertMask = new LLPolyVertexMask(mMorphData);
+ mNumMorphMasksPending--;
+ }
+ else
+ {
+ // remove effect of previous mask
+ F32 *maskWeights = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
+
+ if (maskWeights)
+ {
+ LLVector3 *coords = mMesh->getWritableCoords();
+ LLVector3 *scaled_normals = mMesh->getScaledNormals();
+ LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
+ LLVector2 *tex_coords = mMesh->getWritableTexCoords();
+
+ for(U32 vert = 0; vert < mMorphData->mNumIndices; vert++)
+ {
+ F32 lastMaskWeight = mLastWeight * maskWeights[vert];
+ S32 out_vert = mMorphData->mVertexIndices[vert];
+
+ // remove effect of existing masked morph
+ coords[out_vert] -= mMorphData->mCoords[vert] * lastMaskWeight;
+ scaled_normals[out_vert] -= mMorphData->mNormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
+ scaled_binormals[out_vert] -= mMorphData->mBinormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
+ tex_coords[out_vert] -= mMorphData->mTexCoords[vert] * lastMaskWeight;
+
+ if (clothing_weights)
+ {
+ LLVector3 clothing_offset = mMorphData->mCoords[vert] * lastMaskWeight;
+ LLVector4* clothing_weight = &clothing_weights[out_vert];
+ clothing_weight->mV[VX] -= clothing_offset.mV[VX];
+ clothing_weight->mV[VY] -= clothing_offset.mV[VY];
+ clothing_weight->mV[VZ] -= clothing_offset.mV[VZ];
+ }
+ }
+ }
+ }
+
+ // set last weight to 0, since we've removed the effect of this morph
+ mLastWeight = 0.f;
+
+ mVertMask->generateMask(maskTextureData, width, height, num_components, invert, clothing_weights);
+
+ apply(mLastSex);
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+LLPolyVertexMask::LLPolyVertexMask(LLPolyMorphData* morph_data)
+{
+ mWeights = new F32[morph_data->mNumIndices];
+ mMorphData = morph_data;
+ mWeightsGenerated = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+LLPolyVertexMask::~LLPolyVertexMask()
+{
+ delete[] mWeights;
+}
+
+//-----------------------------------------------------------------------------
+// generateMask()
+//-----------------------------------------------------------------------------
+void LLPolyVertexMask::generateMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert, LLVector4 *clothing_weights)
+{
+// RN debug output that uses Image Debugger (http://www.cs.unc.edu/~baxter/projects/imdebug/)
+// BOOL debugImg = FALSE;
+// if (debugImg)
+// {
+// if (invert)
+// {
+// imdebug("lum rbga=rgba b=8 w=%d h=%d *-1 %p", width, height, maskTextureData);
+// }
+// else
+// {
+// imdebug("lum rbga=rgba b=8 w=%d h=%d %p", width, height, maskTextureData);
+// }
+// }
+ for (U32 index = 0; index < mMorphData->mNumIndices; index++)
+ {
+ S32 vertIndex = mMorphData->mVertexIndices[index];
+ const S32 *sharedVertIndex = mMorphData->mMesh->getSharedVert(vertIndex);
+ LLVector2 uvCoords;
+
+ if (sharedVertIndex)
+ {
+ uvCoords = mMorphData->mMesh->getUVs(*sharedVertIndex);
+ }
+ else
+ {
+ uvCoords = mMorphData->mMesh->getUVs(vertIndex);
+ }
+ U32 s = llclamp((U32)(uvCoords.mV[VX] * (F32)(width - 1)), (U32)0, (U32)width - 1);
+ U32 t = llclamp((U32)(uvCoords.mV[VY] * (F32)(height - 1)), (U32)0, (U32)height - 1);
+
+ mWeights[index] = ((F32) maskTextureData[((t * width + s) * num_components) + (num_components - 1)]) / 255.f;
+
+ if (invert)
+ {
+ mWeights[index] = 1.f - mWeights[index];
+ }
+
+ // now apply step function
+ // mWeights[index] = mWeights[index] > 0.95f ? 1.f : 0.f;
+
+ if (clothing_weights)
+ {
+ clothing_weights[vertIndex].mV[VW] = mWeights[index];
+ }
+ }
+ mWeightsGenerated = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// getMaskForMorphIndex()
+//-----------------------------------------------------------------------------
+F32* LLPolyVertexMask::getMorphMaskWeights()
+{
+ if (!mWeightsGenerated)
+ {
+ return NULL;
+ }
+
+ return mWeights;
+}
diff --git a/indra/newview/llpolymorph.h b/indra/newview/llpolymorph.h
new file mode 100644
index 0000000000..e5e16eb0ee
--- /dev/null
+++ b/indra/newview/llpolymorph.h
@@ -0,0 +1,163 @@
+/**
+ * @file llpolymorph.h
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPOLYMORPH_H
+#define LL_LLPOLYMORPH_H
+
+#include <string>
+#include <vector>
+
+#include "llviewervisualparam.h"
+#include "llskiplist.h"
+#include "linked_lists.h"
+
+class LLPolyMeshSharedData;
+class LLVOAvatar;
+class LLVector2;
+class LLViewerJointCollisionVolume;
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphData()
+//-----------------------------------------------------------------------------
+class LLPolyMorphData
+{
+public:
+ LLPolyMorphData(char *morph_name);
+ ~LLPolyMorphData();
+
+ BOOL loadBinary(FILE* fp, LLPolyMeshSharedData *mesh);
+ char* loadASCII(char* text, LLPolyMeshSharedData *mesh);
+ char* getName() { return mName; }
+
+public:
+ char* mName;
+
+ // morphology
+ U32 mNumIndices;
+ U32* mVertexIndices;
+ U32 mCurrentIndex;
+ LLVector3* mCoords;
+ LLVector3* mNormals;
+ LLVector3* mBinormals;
+ LLVector2* mTexCoords;
+
+ F32 mTotalDistortion; // vertex distortion summed over entire morph
+ F32 mMaxDistortion; // maximum single vertex distortion in a given morph
+ LLVector3 mAvgDistortion; // average vertex distortion, to infer directionality of the morph
+ LLPolyMeshSharedData* mMesh;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+class LLPolyVertexMask
+{
+public:
+ LLPolyVertexMask(LLPolyMorphData* morph_data);
+ ~LLPolyVertexMask();
+
+ void generateMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert, LLVector4 *clothing_weights);
+ F32* getMorphMaskWeights();
+
+
+protected:
+ F32* mWeights;
+ LLPolyMorphData *mMorphData;
+ BOOL mWeightsGenerated;
+
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget Data structs
+//-----------------------------------------------------------------------------
+struct LLPolyVolumeMorphInfo
+{
+ LLPolyVolumeMorphInfo(LLString &name, LLVector3 &scale, LLVector3 &pos)
+ : mName(name), mScale(scale), mPos(pos) {};
+
+ LLString mName;
+ LLVector3 mScale;
+ LLVector3 mPos;
+};
+
+struct LLPolyVolumeMorph
+{
+ LLPolyVolumeMorph(LLViewerJointCollisionVolume* volume, LLVector3 scale, LLVector3 pos)
+ : mVolume(volume), mScale(scale), mPos(pos) {};
+
+ LLViewerJointCollisionVolume* mVolume;
+ LLVector3 mScale;
+ LLVector3 mPos;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTargetInfo
+// Shared information for LLPolyMorphTargets
+//-----------------------------------------------------------------------------
+class LLPolyMorphTargetInfo : public LLViewerVisualParamInfo
+{
+ friend class LLPolyMorphTarget;
+public:
+ LLPolyMorphTargetInfo();
+ /*virtual*/ ~LLPolyMorphTargetInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mMorphName;
+ BOOL mIsClothingMorph;
+ typedef std::vector<LLPolyVolumeMorphInfo> volume_info_list_t;
+ volume_info_list_t mVolumeInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget
+// A set of vertex data associated with morph target.
+// These morph targets must be topologically consistent with a given Polymesh
+// (share face sets)
+//-----------------------------------------------------------------------------
+class LLPolyMorphTarget : public LLViewerVisualParam
+{
+public:
+ LLPolyMorphTarget(LLPolyMesh *poly_mesh);
+ ~LLPolyMorphTarget();
+
+ // Special: These functions are overridden by child classes
+ LLPolyMorphTargetInfo* getInfo() const { return (LLPolyMorphTargetInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLPolyMorphTargetInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex );
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion();
+ /*virtual*/ const LLVector3& getAvgDistortion();
+ /*virtual*/ F32 getMaxDistortion();
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
+
+ void applyMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert);
+ void addPendingMorphMask() { mNumMorphMasksPending++; }
+
+protected:
+ LLPolyMorphData* mMorphData;
+ LLPolyMesh* mMesh;
+ LLPolyVertexMask * mVertMask;
+ ESex mLastSex;
+ // number of morph masks that haven't been generated, must be 0 before this morph is applied
+ BOOL mNumMorphMasksPending;
+
+ typedef std::vector<LLPolyVolumeMorph> volume_list_t;
+ volume_list_t mVolumeMorphs;
+
+};
+
+#endif // LL_LLPOLYMORPH_H
diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp
new file mode 100644
index 0000000000..5cc2d2e39f
--- /dev/null
+++ b/indra/newview/llpreview.cpp
@@ -0,0 +1,489 @@
+/**
+ * @file llpreview.cpp
+ * @brief LLPreview class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "stdenums.h"
+
+#include "llpreview.h"
+#include "lllineeditor.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llfocusmgr.h"
+#include "lltooldraganddrop.h"
+#include "llradiogroup.h"
+#include "llassetstorage.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "lldbstrings.h"
+#include "llagent.h"
+#include "llvoavatar.h"
+#include "llselectmgr.h"
+#include "llinventoryview.h"
+#include "llviewerinventory.h"
+
+// Constants
+
+// Globals and statics
+LLPreview::preview_multimap_t LLPreview::sPreviewsBySource;
+LLPreview::preview_map_t LLPreview::sInstances;
+
+// Functions
+LLPreview::LLPreview(const std::string& name) :
+ LLFloater(name),
+ mCopyToInvBtn(NULL),
+ mForceClose(FALSE),
+ mCloseAfterSave(FALSE),
+ mAssetStatus(PREVIEW_ASSET_UNLOADED)
+{
+ // don't add to instance list, since ItemID is null
+ mAuxItem = new LLInventoryItem; // (LLPointer is auto-deleted)
+ // don't necessarily steal focus on creation -- sometimes these guys pop up without user action
+ mAutoFocus = FALSE;
+}
+
+LLPreview::LLPreview(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const LLUUID& object_uuid, BOOL allow_resize, S32 min_width, S32 min_height )
+: LLFloater(name, rect, title, allow_resize, min_width, min_height ),
+ mItemUUID(item_uuid),
+ mSourceID(LLUUID::null),
+ mObjectUUID(object_uuid),
+ mCopyToInvBtn( NULL ),
+ mForceClose( FALSE ),
+ mCloseAfterSave(FALSE),
+ mAssetStatus(PREVIEW_ASSET_UNLOADED)
+{
+ mAuxItem = new LLInventoryItem;
+ // don't necessarily steal focus on creation -- sometimes these guys pop up without user action
+ mAutoFocus = FALSE;
+
+ if (mItemUUID.notNull())
+ {
+ sInstances[mItemUUID] = this;
+ }
+
+}
+
+LLPreview::~LLPreview()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+
+ if (mItemUUID.notNull())
+ {
+ sInstances.erase( mItemUUID );
+ }
+
+ if (mSourceID.notNull())
+ {
+ preview_multimap_t::iterator found_it = sPreviewsBySource.find(mSourceID);
+ for (; found_it != sPreviewsBySource.end(); ++found_it)
+ {
+ if (found_it->second == mViewHandle)
+ {
+ sPreviewsBySource.erase(found_it);
+ break;
+ }
+ }
+ }
+}
+
+void LLPreview::setItemID(const LLUUID& item_id)
+{
+ if (mItemUUID.notNull())
+ {
+ sInstances.erase(mItemUUID);
+ }
+
+ mItemUUID = item_id;
+
+ if (mItemUUID.notNull())
+ {
+ sInstances[mItemUUID] = this;
+ }
+}
+
+void LLPreview::setObjectID(const LLUUID& object_id)
+{
+ mObjectUUID = object_id;
+}
+
+void LLPreview::setSourceID(const LLUUID& source_id)
+{
+ if (mSourceID.notNull())
+ {
+ // erase old one
+ preview_multimap_t::iterator found_it = sPreviewsBySource.find(mSourceID);
+ for (; found_it != sPreviewsBySource.end(); ++found_it)
+ {
+ if (found_it->second == mViewHandle)
+ {
+ sPreviewsBySource.erase(found_it);
+ break;
+ }
+ }
+ }
+ mSourceID = source_id;
+ sPreviewsBySource.insert(preview_multimap_t::value_type(mSourceID, mViewHandle));
+}
+
+LLViewerInventoryItem* LLPreview::getItem() const
+{
+ LLViewerInventoryItem* item = NULL;
+ if(mObjectUUID.isNull())
+ {
+ // it's an inventory item, so get the item.
+ item = gInventory.getItem(mItemUUID);
+ }
+ else
+ {
+ // it's an object's inventory item.
+ LLViewerObject* object = gObjectList.findObject(mObjectUUID);
+ if(object)
+ {
+ item = (LLViewerInventoryItem*)object->getInventoryObject(mItemUUID);
+ }
+ }
+ return item;
+}
+
+// Sub-classes should override this function if they allow editing
+void LLPreview::onCommit()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ BOOL has_sale_info = FALSE;
+ LLSaleInfo sale_info;
+ new_item->setDescription(childGetText("desc"));
+ if(mObjectUUID.notNull())
+ {
+ // must be in an object
+ LLViewerObject* object = gObjectList.findObject(mObjectUUID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ else if(item->getPermissions().getOwner() == gAgent.getID())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+
+ // If the item is an attachment that is currently being worn,
+ // update the object itself.
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() );
+ if( obj )
+ {
+ gSelectMgr->deselectAll();
+ gSelectMgr->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
+ gSelectMgr->setObjectDescription( childGetText("desc") );
+
+ if( has_sale_info )
+ {
+ gSelectMgr->setObjectSaleInfo( sale_info );
+ }
+
+ gSelectMgr->deselectAll();
+ }
+ }
+ }
+ }
+ }
+}
+
+// static
+void LLPreview::onText(LLUICtrl*, void* userdata)
+{
+ LLPreview* self = (LLPreview*) userdata;
+ self->onCommit();
+}
+
+// static
+void LLPreview::onRadio(LLUICtrl*, void* userdata)
+{
+ LLPreview* self = (LLPreview*) userdata;
+ self->onCommit();
+}
+
+// static
+LLPreview* LLPreview::find(const LLUUID& item_uuid)
+{
+ LLPreview* instance = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ instance = found_it->second;
+ }
+ return instance;
+}
+
+// static
+LLPreview* LLPreview::show( const LLUUID& item_uuid, BOOL take_focus )
+{
+ LLPreview* instance = LLPreview::find(item_uuid);
+ if(instance)
+ {
+ if (LLFloater::getFloaterHost() && LLFloater::getFloaterHost() != instance->getHost())
+ {
+ // this preview window is being opened in a new context
+ // needs to be rehosted
+ LLFloater::getFloaterHost()->addFloater(instance, TRUE);
+ }
+ instance->open();
+ if (take_focus)
+ {
+ instance->setFocus(TRUE);
+ }
+ }
+
+ return instance;
+}
+
+// static
+bool LLPreview::save( const LLUUID& item_uuid, LLPointer<LLInventoryItem>* itemptr )
+{
+ bool res = false;
+ LLPreview* instance = LLPreview::find(item_uuid);
+ if(instance)
+ {
+ res = instance->saveItem(itemptr);
+ }
+ if (!res)
+ {
+ delete itemptr;
+ }
+ return res;
+}
+
+// static
+void LLPreview::hide(const LLUUID& item_uuid)
+{
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ LLPreview* instance = found_it->second;
+ if( instance->getParent() )
+ {
+ instance->getParent()->removeChild( instance );
+ }
+
+ delete instance;
+ }
+}
+
+// static
+void LLPreview::rename(const LLUUID& item_uuid, const std::string& new_name)
+{
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ LLPreview* instance = found_it->second;
+ instance->setTitle( new_name );
+ }
+}
+
+BOOL LLPreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if(mClientRect.pointInRect(x, y))
+ {
+ // No handler needed for focus lost since this class has no
+ // state that depends on it.
+ bringToFront(x, y);
+ gFocusMgr.setMouseCapture(this, NULL);
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ gToolDragAndDrop->setDragStart(screen_x, screen_y);
+ return TRUE;
+ }
+ return LLFloater::handleMouseDown(x, y, mask);
+}
+
+BOOL LLPreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if(gFocusMgr.getMouseCapture() == this)
+ {
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ return TRUE;
+ }
+ return LLFloater::handleMouseUp(x, y, mask);
+}
+
+BOOL LLPreview::handleHover(S32 x, S32 y, MASK mask)
+{
+ if(gFocusMgr.getMouseCapture() == this)
+ {
+ S32 screen_x;
+ S32 screen_y;
+ LLViewerInventoryItem *item = getItem();
+
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ if(item
+ && item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID())
+ && gToolDragAndDrop->isOverThreshold(screen_x, screen_y))
+ {
+ EDragAndDropType type;
+ type = LLAssetType::lookupDragAndDropType(item->getType());
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ if(!mObjectUUID.isNull())
+ {
+ src = LLToolDragAndDrop::SOURCE_WORLD;
+ }
+ else if(item->getPermissions().getOwner() == gAgent.getID())
+ {
+ src = LLToolDragAndDrop::SOURCE_AGENT;
+ }
+ gToolDragAndDrop->beginDrag(type,
+ item->getUUID(),
+ src,
+ mObjectUUID);
+ return gToolDragAndDrop->handleHover(x, y, mask );
+ }
+ }
+ return LLFloater::handleHover(x,y,mask);
+}
+
+void LLPreview::open()
+{
+ LLMultiFloater* hostp = getHost();
+ if (!sHostp && !hostp && getAssetStatus() == PREVIEW_ASSET_UNLOADED)
+ {
+ loadAsset();
+ }
+ LLFloater::open();
+}
+
+// virtual
+bool LLPreview::saveItem(LLPointer<LLInventoryItem>* itemptr)
+{
+ return false;
+}
+
+
+// static
+void LLPreview::onBtnCopyToInv(void* userdata)
+{
+ LLPreview* self = (LLPreview*) userdata;
+ LLInventoryItem *item = self->mAuxItem;
+
+ if(item && item->getUUID().notNull())
+ {
+ // Copy to inventory
+ if (self->mNotecardInventoryID.notNull())
+ {
+ copy_inventory_from_notecard(self->mObjectID,
+ self->mNotecardInventoryID, item);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ }
+}
+
+// static
+void LLPreview::onKeepBtn(void* data)
+{
+ LLPreview* self = (LLPreview*)data;
+ self->close();
+}
+
+// static
+void LLPreview::onDiscardBtn(void* data)
+{
+ LLPreview* self = (LLPreview*)data;
+
+ LLViewerInventoryItem* item = self->getItem();
+ if (!item) return;
+
+ self->mForceClose = TRUE;
+ self->close();
+
+ // Delete the item entirely
+ /*
+ item->removeFromServer();
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ */
+
+ // Move the item to the trash
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if (item->getParentUUID() != trash_id)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setParent(trash_id);
+ // no need to restamp it though it's a move into trash because
+ // it's a brand new item already.
+ new_item->updateParentOnServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+}
+
+//static
+LLPreview* LLPreview::getFirstPreviewForSource(const LLUUID& source_id)
+{
+ preview_multimap_t::iterator found_it = sPreviewsBySource.find(source_id);
+ if (found_it != sPreviewsBySource.end())
+ {
+ // just return first one
+ return (LLPreview*)LLFloater::getFloaterByHandle(found_it->second);
+ }
+ return NULL;
+}
+
+//
+// LLMultiPreview
+//
+
+LLMultiPreview::LLMultiPreview(const LLRect& rect) : LLMultiFloater("Preview", rect)
+{
+}
+
+void LLMultiPreview::open()
+{
+ LLMultiFloater::open();
+ LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel();
+ if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED)
+ {
+ frontmost_preview->loadAsset();
+ }
+}
+
+void LLMultiPreview::tabOpen(LLFloater* opened_floater, bool from_click)
+{
+ LLPreview* opened_preview = (LLPreview*)opened_floater;
+ if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED)
+ {
+ opened_preview->loadAsset();
+ }
+}
diff --git a/indra/newview/llpreview.h b/indra/newview/llpreview.h
new file mode 100644
index 0000000000..eb82965cd6
--- /dev/null
+++ b/indra/newview/llpreview.h
@@ -0,0 +1,139 @@
+/**
+ * @file llpreview.h
+ * @brief LLPreview class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEW_H
+#define LL_LLPREVIEW_H
+
+#include "llfloater.h"
+#include "llresizehandle.h"
+#include "llmap.h"
+#include "lluuid.h"
+#include "llviewerinventory.h"
+#include "lltabcontainer.h"
+#include "lllineeditor.h"
+
+class LLLineEditor;
+class LLRadioGroup;
+class LLPreview;
+
+class LLMultiPreview : public LLMultiFloater
+{
+public:
+ LLMultiPreview(const LLRect& rect);
+
+ /*virtual*/void open();
+ /*virtual*/void tabOpen(LLFloater* opened_floater, bool from_click);
+};
+
+class LLPreview : public LLFloater
+{
+public:
+ typedef enum e_asset_status
+ {
+ PREVIEW_ASSET_ERROR,
+ PREVIEW_ASSET_UNLOADED,
+ PREVIEW_ASSET_LOADING,
+ PREVIEW_ASSET_LOADED
+ } EAssetStatus;
+public:
+ // Used for XML-based construction.
+ LLPreview(const std::string& name);
+ LLPreview(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const LLUUID& object_uuid, BOOL allow_resize = FALSE, S32 min_width = 0, S32 min_height = 0 );
+ virtual ~LLPreview();
+
+ void setItemID(const LLUUID& item_id);
+ void setObjectID(const LLUUID& object_id);
+ void setSourceID(const LLUUID& source_id);
+ LLViewerInventoryItem* getItem() const;
+
+ static LLPreview* find(const LLUUID& item_uuid);
+ static LLPreview* show(const LLUUID& item_uuid, BOOL take_focus = TRUE );
+ static void hide(const LLUUID& item_uuid);
+ static void rename(const LLUUID& item_uuid, const std::string& new_name);
+ static bool save(const LLUUID& item_uuid, LLPointer<LLInventoryItem>* itemptr);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void open();
+ virtual bool saveItem(LLPointer<LLInventoryItem>* itemptr);
+
+ void setAuxItem( const LLInventoryItem* item )
+ {
+ if ( mAuxItem )
+ mAuxItem->copy(item);
+ }
+
+ static void onBtnCopyToInv(void* userdata);
+
+ void addKeepDiscardButtons();
+ static void onKeepBtn(void* data);
+ static void onDiscardBtn(void* data);
+
+ virtual void loadAsset() { mAssetStatus = PREVIEW_ASSET_LOADED; }
+ virtual EAssetStatus getAssetStatus() { return mAssetStatus;}
+
+ static LLPreview* getFirstPreviewForSource(const LLUUID& source_id);
+ void setNotecardInfo(const LLUUID& notecard_inv_id, const LLUUID& object_id)
+ { mNotecardInventoryID = notecard_inv_id; mObjectID = object_id; }
+
+protected:
+ virtual void onCommit();
+
+ void addDescriptionUI();
+
+ static void onText(LLUICtrl*, void* userdata);
+ static void onRadio(LLUICtrl*, void* userdata);
+
+
+protected:
+ LLUUID mItemUUID;
+ LLUUID mSourceID;
+
+ // mObjectID will have a value if it is associated with a task in
+ // the world, and will be == LLUUID::null if it's in the agent
+ // inventory.
+ LLUUID mObjectUUID;
+
+ LLRect mClientRect;
+
+ LLPointer<LLInventoryItem> mAuxItem; // HACK!
+ LLButton* mCopyToInvBtn;
+
+ // Close without saving changes
+ BOOL mForceClose;
+
+ // When closing springs a "Want to save?" dialog, we want
+ // to keep the preview open until the save completes.
+ BOOL mCloseAfterSave;
+
+ EAssetStatus mAssetStatus;
+
+ typedef std::map<LLUUID, LLPreview*> preview_map_t;
+ typedef std::multimap<LLUUID, LLViewHandle> preview_multimap_t;
+
+ static preview_multimap_t sPreviewsBySource;
+ static preview_map_t sInstances;
+ LLUUID mNotecardInventoryID;
+ LLUUID mObjectID;
+};
+
+
+const S32 PREVIEW_BORDER = 4;
+const S32 PREVIEW_PAD = 5;
+const S32 PREVIEW_BUTTON_WIDTH = 100;
+
+const S32 PREVIEW_LINE_HEIGHT = 19;
+const S32 PREVIEW_CLOSE_BOX_SIZE = 16;
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_VPAD = 2;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREVIEW_HEADER_SIZE = 2*PREVIEW_LINE_HEIGHT + 2 * PREVIEW_VPAD;
+
+#endif // LL_LLPREVIEW_H
diff --git a/indra/newview/llpreviewanim.cpp b/indra/newview/llpreviewanim.cpp
new file mode 100644
index 0000000000..25273d035d
--- /dev/null
+++ b/indra/newview/llpreviewanim.cpp
@@ -0,0 +1,197 @@
+/**
+ * @file llpreviewanim.cpp
+ * @brief LLPreviewAnim class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewanim.h"
+#include "llbutton.h"
+#include "llresmgr.h"
+#include "llinventory.h"
+#include "llinventoryview.h"
+#include "llvoavatar.h"
+#include "llagent.h" // gAgent
+#include "llkeyframemotion.h"
+#include "llfilepicker.h"
+#include "lllineeditor.h"
+#include "lluictrlfactory.h"
+#include "llvieweruictrlfactory.h"
+
+extern LLAgent gAgent;
+
+LLPreviewAnim::LLPreviewAnim(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const S32& activate, const LLUUID& object_uuid ) :
+ LLPreview( name, rect, title, item_uuid, object_uuid)
+{
+ gUICtrlFactory->buildFloater(this,"floater_preview_animation.xml");
+
+ childSetAction("Anim play btn",playAnim,this);
+ childSetAction("Anim audition btn",auditionAnim,this);
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+ // preload the animation
+ if(item)
+ {
+ gAgent.getAvatarObject()->createMotion(item->getAssetUUID());
+ }
+
+ switch ( activate )
+ {
+ case 1:
+ {
+ playAnim( (void *) this );
+ break;
+ }
+ case 2:
+ {
+ auditionAnim( (void *) this );
+ break;
+ }
+ default:
+ {
+ //do nothing
+ }
+ }
+}
+
+// static
+void LLPreviewAnim::endAnimCallback( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+
+ self->childSetValue("Anim play btn", FALSE);
+ self->childSetValue("Anim audition btn", FALSE);
+
+}
+
+// static
+void LLPreviewAnim::playAnim( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item)
+ {
+ LLUUID itemID=item->getAssetUUID();
+
+ LLButton* btn = LLUICtrlFactory::getButtonByName(self, "Anim play btn");
+ if (btn)
+ {
+ btn->toggleState();
+ }
+
+ if (self->childGetValue("Anim play btn").asBoolean() )
+ {
+ self->mPauseRequest = NULL;
+ gAgent.sendAnimationRequest(itemID, ANIM_REQUEST_START);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLMotion* motion = avatar->findMotion(itemID);
+
+ if (motion)
+ motion->setDeactivateCallback(&endAnimCallback, (void *)self);
+ }
+ else
+ {
+ gAgent.getAvatarObject()->stopMotion(itemID);
+ gAgent.sendAnimationRequest(itemID, ANIM_REQUEST_STOP);
+ }
+ }
+}
+
+// static
+void LLPreviewAnim::auditionAnim( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item)
+ {
+ LLUUID itemID=item->getAssetUUID();
+
+ LLButton* btn = LLUICtrlFactory::getButtonByName(self, "Anim audition btn");
+ if (btn)
+ {
+ btn->toggleState();
+ }
+
+ if (self->childGetValue("Anim audition btn").asBoolean() )
+ {
+ self->mPauseRequest = NULL;
+ gAgent.getAvatarObject()->startMotion(item->getAssetUUID());
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLMotion* motion = avatar->findMotion(itemID);
+
+ if (motion)
+ motion->setDeactivateCallback(&endAnimCallback, (void *)self);
+ }
+ else
+ {
+ gAgent.getAvatarObject()->stopMotion(itemID);
+ gAgent.sendAnimationRequest(itemID, ANIM_REQUEST_STOP);
+ }
+ }
+}
+
+void LLPreviewAnim::saveAnim( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item)
+ {
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)gAgent.getAvatarObject()->createMotion( item->getAssetUUID() );
+ if (motionp && motionp->isLoaded())
+ {
+ LLFilePicker& picker = LLFilePicker::instance();
+ LLString proposed_name = item->getName() + LLString(".xaf");
+ if (picker.getSaveFile(LLFilePicker::FFSAVE_ANIM, proposed_name.c_str()))
+ {
+ apr_file_t* fp = ll_apr_file_open(picker.getFirstFile(), LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to open file " << picker.getFirstFile() << llendl;
+ return;
+ }
+ motionp->writeCAL3D(fp);
+ apr_file_close(fp);
+ }
+ }
+ }
+}
+
+void LLPreviewAnim::onClose(bool app_quitting)
+{
+ LLInventoryItem *item = getItem();
+
+ if(item)
+ {
+ gAgent.getAvatarObject()->stopMotion(item->getAssetUUID());
+ gAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_STOP);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLMotion* motion = avatar->findMotion(item->getAssetUUID());
+
+ if (motion)
+ motion->setDeactivateCallback(NULL, (void *)NULL);
+
+ }
+ destroy();
+}
diff --git a/indra/newview/llpreviewanim.h b/indra/newview/llpreviewanim.h
new file mode 100644
index 0000000000..8a85579bba
--- /dev/null
+++ b/indra/newview/llpreviewanim.h
@@ -0,0 +1,39 @@
+/**
+ * @file llpreviewanim.h
+ * @brief LLPreviewAnim class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWANIM_H
+#define LL_LLPREVIEWANIM_H
+
+#include "llpreview.h"
+#include "llcharacter.h"
+
+class LLPreviewAnim : public LLPreview
+{
+public:
+ LLPreviewAnim(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_uuid,
+ const S32& activate,
+ const LLUUID& object_uuid = LLUUID::null);
+
+ static void playAnim( void* userdata );
+ static void auditionAnim( void* userdata );
+ static void saveAnim( void* userdata );
+ static void endAnimCallback( void *userdata );
+
+protected:
+ virtual void onClose(bool app_quitting);
+
+ LLAnimPauseRequest mPauseRequest;
+ LLUUID mItemID;
+ LLString mTitle;
+ LLUUID mObjectID;
+ LLButton* mPlayBtn;
+ LLButton* mAuditionBtn;
+};
+
+#endif // LL_LLPREVIEWSOUND_H
diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp
new file mode 100644
index 0000000000..9496b5df5f
--- /dev/null
+++ b/indra/newview/llpreviewgesture.cpp
@@ -0,0 +1,1698 @@
+/**
+ * @file llpreviewgesture.cpp
+ * @brief Editing UI for inventory-based gestures.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <algorithm>
+
+#include "llpreviewgesture.h"
+
+// libraries
+#include "lldatapacker.h"
+#include "lldarray.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llmultigesture.h"
+#include "llvfile.h"
+
+// newview
+#include "llagent.h" // todo: remove
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfloatergesture.h" // for some label constants
+#include "llgesturemgr.h"
+#include "llinventorymodel.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llradiogroup.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerinventory.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h" // busycount
+#include "viewer.h" // gVFS
+
+#include "llresmgr.h"
+
+const char NONE_LABEL[] = "---";
+const char SHIFT_LABEL[] = "Shift";
+const char CTRL_LABEL[] = "Ctrl";
+
+void dialog_refresh_all();
+
+// used for getting
+
+class LLInventoryGestureAvailable : public LLInventoryCompletionObserver
+{
+public:
+ LLInventoryGestureAvailable() {}
+
+protected:
+ virtual void done();
+};
+
+void LLInventoryGestureAvailable::done()
+{
+ LLPreview* preview = NULL;
+ item_ref_t::iterator it = mComplete.begin();
+ item_ref_t::iterator end = mComplete.end();
+ for(; it < end; ++it)
+ {
+ preview = LLPreview::find((*it));
+ if(preview)
+ {
+ preview->refresh();
+ }
+ }
+ gInventory.removeObserver(this);
+ delete this;
+}
+
+// Used for sorting
+struct SortItemPtrsByName
+{
+ bool operator()(const LLInventoryItem* i1, const LLInventoryItem* i2)
+ {
+ return (LLString::compareDict(i1->getName(), i2->getName()) < 0);
+ }
+};
+
+// static
+LLPreviewGesture* LLPreviewGesture::show(const std::string& title, const LLUUID& item_id, const LLUUID& object_id, BOOL take_focus)
+{
+ LLPreviewGesture* previewp = (LLPreviewGesture*)LLPreview::find(item_id);
+ if (previewp)
+ {
+ previewp->open();
+ if (take_focus)
+ {
+ previewp->setFocus(TRUE);
+ }
+ return previewp;
+ }
+
+ LLPreviewGesture* self = new LLPreviewGesture();
+
+ // Finish internal construction
+ self->init(item_id, object_id);
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(self, "floater_preview_gesture.xml");
+ self->setTitle(title);
+
+ // Move window to top-left of screen
+ LLMultiFloater* hostp = self->getHost();
+ if (hostp == NULL)
+ {
+ LLRect r = self->getRect();
+ LLRect screen = gFloaterView->getRect();
+ r.setLeftTopAndSize(0, screen.getHeight(), r.getWidth(), r.getHeight());
+ self->setRect(r);
+ }
+ else
+ {
+ // re-add to host to update title
+ hostp->addFloater(self, TRUE);
+ }
+
+ // this will call refresh when we have everything.
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
+ if(item && !item->isComplete())
+ {
+ LLInventoryGestureAvailable* observer;
+ observer = new LLInventoryGestureAvailable();
+ observer->watchItem(item_id);
+ gInventory.addObserver(observer);
+ item->fetchFromServer();
+ }
+ else
+ {
+ // not sure this is necessary.
+ self->refresh();
+ }
+
+ if (take_focus)
+ {
+ self->setFocus(TRUE);
+ }
+
+ return self;
+}
+
+
+// virtual
+BOOL LLPreviewGesture::handleKeyHere(KEY key, MASK mask,
+ BOOL called_from_parent)
+{
+ if(getVisible() && getEnabled())
+ {
+ if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL)))
+ {
+ saveIfNeeded();
+ return TRUE;
+ }
+ }
+ return LLPreview::handleKeyHere(key, mask, called_from_parent);
+}
+
+
+// virtual
+BOOL LLPreviewGesture::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = TRUE;
+ switch(cargo_type)
+ {
+ case DAD_ANIMATION:
+ case DAD_SOUND:
+ {
+ // TODO: Don't allow this if you can't transfer the sound/animation
+
+ // make a script step
+ LLInventoryItem* item = (LLInventoryItem*)cargo_data;
+ if (item
+ && gInventory.getItem(item->getUUID()))
+ {
+ LLPermissions perm = item->getPermissions();
+ if (!((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED))
+ {
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ tooltip_msg.assign("Only animations and sounds\n"
+ "with unrestricted permissions\n"
+ "can be added to a gesture.");
+ }
+ break;
+ }
+ else if (drop)
+ {
+ LLScrollListItem* line = NULL;
+ if (cargo_type == DAD_ANIMATION)
+ {
+ line = addStep("Animation");
+ LLGestureStepAnimation* anim = (LLGestureStepAnimation*)line->getUserdata();
+ anim->mAnimAssetID = item->getAssetUUID();
+ anim->mAnimName = item->getName();
+ }
+ else if (cargo_type == DAD_SOUND)
+ {
+ line = addStep("Sound");
+ LLGestureStepSound* sound = (LLGestureStepSound*)line->getUserdata();
+ sound->mSoundAssetID = item->getAssetUUID();
+ sound->mSoundName = item->getName();
+ }
+ updateLabel(line);
+ mDirty = TRUE;
+ refresh();
+ }
+ *accept = ACCEPT_YES_COPY_MULTI;
+ }
+ else
+ {
+ // Not in user's inventory means it was in object inventory
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ default:
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ tooltip_msg.assign("Only animations and sounds\n"
+ "can be added to a gesture.");
+ }
+ break;
+ }
+ return handled;
+}
+
+
+// virtual
+BOOL LLPreviewGesture::canClose()
+{
+ if(!mDirty)
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("SaveChanges",
+ handleSaveChangesDialog,
+ this );
+ return FALSE;
+ }
+}
+
+// virtual
+void LLPreviewGesture::onClose(bool app_quitting)
+{
+ gGestureManager.stopGesture(mPreviewGesture);
+ LLPreview::onClose(app_quitting);
+}
+
+// virtual
+void LLPreviewGesture::setMinimized(BOOL minimize)
+{
+ if (minimize != isMinimized())
+ {
+ LLFloater::setMinimized(minimize);
+
+ // We're being restored
+ if (!minimize)
+ {
+ refresh();
+ }
+ }
+}
+
+
+// static
+void LLPreviewGesture::handleSaveChangesDialog(S32 option, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ switch(option)
+ {
+ case 0: // "Yes"
+ gGestureManager.stopGesture(self->mPreviewGesture);
+ self->mCloseAfterSave = TRUE;
+ onClickSave(data);
+ break;
+
+ case 1: // "No"
+ gGestureManager.stopGesture(self->mPreviewGesture);
+ self->mDirty = FALSE; // Force the dirty flag because user has clicked NO on confirm save dialog...
+ self->close();
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+
+LLPreviewGesture::LLPreviewGesture()
+: LLPreview("Gesture Preview"),
+ mTriggerEditor(NULL),
+ mModifierCombo(NULL),
+ mKeyCombo(NULL),
+ mLibraryList(NULL),
+ mAddBtn(NULL),
+ mUpBtn(NULL),
+ mDownBtn(NULL),
+ mDeleteBtn(NULL),
+ mStepList(NULL),
+ mOptionsText(NULL),
+ mAnimationRadio(NULL),
+ mAnimationCombo(NULL),
+ mSoundCombo(NULL),
+ mChatEditor(NULL),
+ mSaveBtn(NULL),
+ mPreviewBtn(NULL),
+ mPreviewGesture(NULL),
+ mDirty(FALSE)
+{
+}
+
+
+LLPreviewGesture::~LLPreviewGesture()
+{
+ // Userdata for all steps is a LLGestureStep we need to clean up
+ std::vector<LLScrollListItem*> data_list = mStepList->getAllData();
+ std::vector<LLScrollListItem*>::iterator data_itor;
+ for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
+ {
+ LLScrollListItem* item = *data_itor;
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+ delete step;
+ step = NULL;
+ }
+}
+
+
+BOOL LLPreviewGesture::postBuild()
+{
+ LLLineEditor* edit;
+ LLComboBox* combo;
+ LLButton* btn;
+ LLScrollListCtrl* list;
+ LLTextBox* text;
+ LLCheckBoxCtrl* check;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "trigger_editor");
+ edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitCallback(onCommitSetDirty);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mTriggerEditor = edit;
+
+ text = LLViewerUICtrlFactory::getTextBoxByName(this, "replace_text");
+ text->setEnabled(FALSE);
+ mReplaceText = text;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "replace_editor");
+ edit->setEnabled(FALSE);
+ edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitCallback(onCommitSetDirty);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mReplaceEditor = edit;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "modifier_combo");
+ combo->setCommitCallback(onCommitSetDirty);
+ combo->setCallbackUserData(this);
+ mModifierCombo = combo;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "key_combo");
+ combo->setCommitCallback(onCommitSetDirty);
+ combo->setCallbackUserData(this);
+ mKeyCombo = combo;
+
+ list = LLViewerUICtrlFactory::getScrollListByName(this, "library_list");
+ list->setCommitCallback(onCommitLibrary);
+ list->setDoubleClickCallback(onClickAdd);
+ list->setCallbackUserData(this);
+ mLibraryList = list;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "add_btn");
+ btn->setClickedCallback(onClickAdd);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mAddBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "up_btn");
+ btn->setClickedCallback(onClickUp);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mUpBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "down_btn");
+ btn->setClickedCallback(onClickDown);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mDownBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "delete_btn");
+ btn->setClickedCallback(onClickDelete);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mDeleteBtn = btn;
+
+ list = LLViewerUICtrlFactory::getScrollListByName(this, "step_list");
+ list->setCommitCallback(onCommitStep);
+ list->setCallbackUserData(this);
+ mStepList = list;
+
+ // Options
+ text = LLViewerUICtrlFactory::getTextBoxByName(this, "options_text");
+ text->setBorderVisible(TRUE);
+ mOptionsText = text;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "animation_list");
+ combo->setVisible(FALSE);
+ combo->setCommitCallback(onCommitAnimation);
+ combo->setCallbackUserData(this);
+ mAnimationCombo = combo;
+
+ LLRadioGroup* group;
+ group = LLViewerUICtrlFactory::getRadioGroupByName(this, "animation_trigger_type");
+ group->setVisible(FALSE);
+ group->setCommitCallback(onCommitAnimationTrigger);
+ group->setCallbackUserData(this);
+ mAnimationRadio = group;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "sound_list");
+ combo->setVisible(FALSE);
+ combo->setCommitCallback(onCommitSound);
+ combo->setCallbackUserData(this);
+ mSoundCombo = combo;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "chat_editor");
+ edit->setVisible(FALSE);
+ edit->setCommitCallback(onCommitChat);
+ //edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mChatEditor = edit;
+
+ check = LLViewerUICtrlFactory::getCheckBoxByName(this, "wait_anim_check");
+ check->setVisible(FALSE);
+ check->setCommitCallback(onCommitWait);
+ check->setCallbackUserData(this);
+ mWaitAnimCheck = check;
+
+ check = LLViewerUICtrlFactory::getCheckBoxByName(this, "wait_time_check");
+ check->setVisible(FALSE);
+ check->setCommitCallback(onCommitWait);
+ check->setCallbackUserData(this);
+ mWaitTimeCheck = check;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "wait_time_editor");
+ edit->setEnabled(FALSE);
+ edit->setVisible(FALSE);
+ edit->setPrevalidate(LLLineEditor::prevalidateFloat);
+// edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCommitCallback(onCommitWaitTime);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mWaitTimeEditor = edit;
+
+ // Buttons at the bottom
+ check = LLViewerUICtrlFactory::getCheckBoxByName(this, "active_check");
+ check->setCommitCallback(onCommitActive);
+ check->setCallbackUserData(this);
+ mActiveCheck = check;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "save_btn");
+ btn->setClickedCallback(onClickSave);
+ btn->setCallbackUserData(this);
+ mSaveBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "preview_btn");
+ btn->setClickedCallback(onClickPreview);
+ btn->setCallbackUserData(this);
+ mPreviewBtn = btn;
+
+
+ // Populate the comboboxes
+ addModifiers();
+ addKeys();
+ addAnimations();
+ addSounds();
+
+
+ LLInventoryItem* item = getItem();
+
+ if (item)
+ {
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+ }
+
+ return TRUE;
+}
+
+
+void LLPreviewGesture::addModifiers()
+{
+ LLComboBox* combo = mModifierCombo;
+
+ combo->add( NONE_LABEL, ADD_BOTTOM );
+ combo->add( SHIFT_LABEL, ADD_BOTTOM );
+ combo->add( CTRL_LABEL, ADD_BOTTOM );
+ combo->setCurrentByIndex(0);
+}
+
+void LLPreviewGesture::addKeys()
+{
+ LLComboBox* combo = mKeyCombo;
+
+ combo->add( NONE_LABEL );
+ for (KEY key = KEY_F2; key <= KEY_F12; key++)
+ {
+ combo->add( LLKeyboard::stringFromKey(key), ADD_BOTTOM );
+ }
+ combo->setCurrentByIndex(0);
+}
+
+
+// TODO: Sort the legacy and non-legacy together?
+void LLPreviewGesture::addAnimations()
+{
+ LLComboBox* combo = mAnimationCombo;
+
+ combo->removeall();
+
+ combo->add("-- None --", LLUUID::null);
+
+ // Add all the default (legacy) animations
+ S32 i;
+ for (i = 0; i < gUserAnimStatesCount; ++i)
+ {
+ // Use the user-readable name
+ const char* label = gUserAnimStates[i].mLabel;
+ const LLUUID& id = gUserAnimStates[i].mID;
+ combo->add(label, id);
+ }
+
+ // Get all inventory items that are animations
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLIsTypeWithPermissions is_copyable_animation(LLAssetType::AT_ANIMATION,
+ PERM_ITEM_UNRESTRICTED,
+ gAgent.getID(),
+ gAgent.getGroupID());
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_copyable_animation);
+
+ // Copy into something we can sort
+ std::vector<LLInventoryItem*> animations;
+
+ S32 count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ animations.push_back( items.get(i) );
+ }
+
+ // Do the sort
+ std::sort(animations.begin(), animations.end(), SortItemPtrsByName());
+
+ // And load up the combobox
+ std::vector<LLInventoryItem*>::iterator it;
+ for (it = animations.begin(); it != animations.end(); ++it)
+ {
+ LLInventoryItem* item = *it;
+
+ combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM);
+ }
+}
+
+
+void LLPreviewGesture::addSounds()
+{
+ // Get all inventory items that are sounds
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLIsTypeWithPermissions is_copyable_sound(LLAssetType::AT_SOUND,
+ PERM_ITEM_UNRESTRICTED,
+ gAgent.getID(),
+ gAgent.getGroupID());
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_copyable_sound);
+
+ // Copy sounds into something we can sort
+ std::vector<LLInventoryItem*> sounds;
+
+ S32 i;
+ S32 count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ sounds.push_back( items.get(i) );
+ }
+
+ // Do the sort
+ std::sort(sounds.begin(), sounds.end(), SortItemPtrsByName());
+
+ // And load up the combobox
+ LLComboBox* combo = mSoundCombo;
+ combo->removeall();
+ std::vector<LLInventoryItem*>::iterator it;
+ for (it = sounds.begin(); it != sounds.end(); ++it)
+ {
+ LLInventoryItem* item = *it;
+
+ combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM);
+ }
+}
+
+
+void LLPreviewGesture::init(const LLUUID& item_id, const LLUUID& object_id)
+{
+ // Sets ID and adds to instance list
+ setItemID(item_id);
+ setObjectID(object_id);
+}
+
+
+void LLPreviewGesture::refresh()
+{
+ // If previewing or item is incomplete, all controls are disabled
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ bool is_complete = (item && item->isComplete()) ? true : false;
+ if (mPreviewGesture || !is_complete)
+ {
+
+ childSetEnabled("desc", FALSE);
+ //mDescEditor->setEnabled(FALSE);
+ mTriggerEditor->setEnabled(FALSE);
+ mReplaceText->setEnabled(FALSE);
+ mReplaceEditor->setEnabled(FALSE);
+ mModifierCombo->setEnabled(FALSE);
+ mKeyCombo->setEnabled(FALSE);
+ mLibraryList->setEnabled(FALSE);
+ mAddBtn->setEnabled(FALSE);
+ mUpBtn->setEnabled(FALSE);
+ mDownBtn->setEnabled(FALSE);
+ mDeleteBtn->setEnabled(FALSE);
+ mStepList->setEnabled(FALSE);
+ mOptionsText->setEnabled(FALSE);
+ mAnimationCombo->setEnabled(FALSE);
+ mAnimationRadio->setEnabled(FALSE);
+ mSoundCombo->setEnabled(FALSE);
+ mChatEditor->setEnabled(FALSE);
+ mWaitAnimCheck->setEnabled(FALSE);
+ mWaitTimeCheck->setEnabled(FALSE);
+ mWaitTimeEditor->setEnabled(FALSE);
+ mActiveCheck->setEnabled(FALSE);
+ mSaveBtn->setEnabled(FALSE);
+
+ // Make sure preview button is enabled, so we can stop it
+ mPreviewBtn->setEnabled(TRUE);
+ return;
+ }
+
+ BOOL modifiable = item->getPermissions().allowModifyBy(gAgent.getID());
+
+ childSetEnabled("desc", modifiable);
+ mTriggerEditor->setEnabled(TRUE);
+ mLibraryList->setEnabled(modifiable);
+ mStepList->setEnabled(modifiable);
+ mOptionsText->setEnabled(modifiable);
+ mAnimationCombo->setEnabled(modifiable);
+ mAnimationRadio->setEnabled(modifiable);
+ mSoundCombo->setEnabled(modifiable);
+ mChatEditor->setEnabled(modifiable);
+ mWaitAnimCheck->setEnabled(modifiable);
+ mWaitTimeCheck->setEnabled(modifiable);
+ mWaitTimeEditor->setEnabled(modifiable);
+ mActiveCheck->setEnabled(TRUE);
+
+ const std::string& trigger = mTriggerEditor->getText();
+ BOOL have_trigger = !trigger.empty();
+
+ const std::string& replace = mReplaceEditor->getText();
+ BOOL have_replace = !replace.empty();
+
+ LLScrollListItem* library_item = mLibraryList->getFirstSelected();
+ BOOL have_library = (library_item != NULL);
+
+ LLScrollListItem* step_item = mStepList->getFirstSelected();
+ S32 step_index = mStepList->getFirstSelectedIndex();
+ S32 step_count = mStepList->getItemCount();
+ BOOL have_step = (step_item != NULL);
+
+ mReplaceText->setEnabled(have_trigger || have_replace);
+ mReplaceEditor->setEnabled(have_trigger || have_replace);
+
+ mModifierCombo->setEnabled(TRUE);
+ mKeyCombo->setEnabled(TRUE);
+
+ mAddBtn->setEnabled(modifiable && have_library);
+ mUpBtn->setEnabled(modifiable && have_step && step_index > 0);
+ mDownBtn->setEnabled(modifiable && have_step && step_index < step_count-1);
+ mDeleteBtn->setEnabled(modifiable && have_step);
+
+ // Assume all not visible
+ mAnimationCombo->setVisible(FALSE);
+ mAnimationRadio->setVisible(FALSE);
+ mSoundCombo->setVisible(FALSE);
+ mChatEditor->setVisible(FALSE);
+ mWaitAnimCheck->setVisible(FALSE);
+ mWaitTimeCheck->setVisible(FALSE);
+ mWaitTimeEditor->setVisible(FALSE);
+
+ if (have_step)
+ {
+ // figure out the type, show proper options, update text
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ EStepType type = step->getType();
+ switch(type)
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ mOptionsText->setText("Animation to play:");
+ mAnimationCombo->setVisible(TRUE);
+ mAnimationRadio->setVisible(TRUE);
+ mAnimationRadio->setSelectedIndex((anim_step->mFlags & ANIM_FLAG_STOP) ? 1 : 0);
+ mAnimationCombo->setCurrentByID(anim_step->mAnimAssetID);
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ mOptionsText->setText("Sound to play:");
+ mSoundCombo->setVisible(TRUE);
+ mSoundCombo->setCurrentByID(sound_step->mSoundAssetID);
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ mOptionsText->setText("Chat to say:");
+ mChatEditor->setVisible(TRUE);
+ mChatEditor->setText(chat_step->mChatText);
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ mOptionsText->setText("Wait:");
+ mWaitAnimCheck->setVisible(TRUE);
+ mWaitAnimCheck->set(wait_step->mFlags & WAIT_FLAG_ALL_ANIM);
+ mWaitTimeCheck->setVisible(TRUE);
+ mWaitTimeCheck->set(wait_step->mFlags & WAIT_FLAG_TIME);
+ mWaitTimeEditor->setVisible(TRUE);
+ char buffer[16];
+ sprintf(buffer, "%.1f", (double)wait_step->mWaitSeconds);
+ mWaitTimeEditor->setText(buffer);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // no gesture
+ mOptionsText->setText("");
+ }
+
+ BOOL active = gGestureManager.isGestureActive(mItemUUID);
+ mActiveCheck->set(active);
+
+ // Can only preview if there are steps
+ mPreviewBtn->setEnabled(step_count > 0);
+
+ // And can only save if changes have been made
+ mSaveBtn->setEnabled(mDirty);
+ addAnimations();
+ addSounds();
+}
+
+
+void LLPreviewGesture::initDefaultGesture()
+{
+ LLScrollListItem* item;
+ item = addStep("Animation");
+ LLGestureStepAnimation* anim = (LLGestureStepAnimation*)item->getUserdata();
+ anim->mAnimAssetID = ANIM_AGENT_HELLO;
+ anim->mAnimName = "Wave";
+ updateLabel(item);
+
+ item = addStep("Wait");
+ LLGestureStepWait* wait = (LLGestureStepWait*)item->getUserdata();
+ wait->mFlags = WAIT_FLAG_ALL_ANIM;
+ updateLabel(item);
+
+ item = addStep("Chat");
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)item->getUserdata();
+ chat_step->mChatText = "Hello, avatar!";
+ updateLabel(item);
+
+ // Start with item list selected
+ mStepList->selectFirstItem();
+
+ // this is *new* content, so we are dirty
+ mDirty = TRUE;
+}
+
+
+void LLPreviewGesture::loadAsset()
+{
+ LLInventoryItem* item = getItem();
+ if (!item) return;
+
+ LLUUID asset_id = item->getAssetUUID();
+ if (asset_id.isNull())
+ {
+ // Freshly created gesture, don't need to load asset.
+ // Blank gesture will be fine.
+ initDefaultGesture();
+ refresh();
+ return;
+ }
+
+ // TODO: Based on item->getPermissions().allow*
+ // could enable/disable UI.
+
+ // Copy the UUID, because the user might close the preview
+ // window if the download gets stalled.
+ LLUUID* item_idp = new LLUUID(mItemUUID);
+
+ const BOOL high_priority = TRUE;
+ gAssetStorage->getAssetData(asset_id,
+ LLAssetType::AT_GESTURE,
+ onLoadComplete,
+ (void**)item_idp,
+ high_priority);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+}
+
+
+// static
+void LLPreviewGesture::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLUUID* item_idp = (LLUUID*)user_data;
+ LLPreview* preview = LLPreview::find(*item_idp);
+ if (preview)
+ {
+ LLPreviewGesture* self = (LLPreviewGesture*)preview;
+
+ if (0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ char* buffer = new char[size+1];
+ file.read((U8*)buffer, size);
+ buffer[size] = '\0';
+
+ LLMultiGesture* gesture = new LLMultiGesture();
+
+ LLDataPackerAsciiBuffer dp(buffer, size+1);
+ BOOL ok = gesture->deserialize(dp);
+
+ if (ok)
+ {
+ // Everything has been successful. Load up the UI.
+ self->loadUIFromGesture(gesture);
+
+ self->mStepList->selectFirstItem();
+
+ self->mDirty = FALSE;
+ self->refresh();
+ }
+ else
+ {
+ llwarns << "Unable to load gesture" << llendl;
+ }
+
+ delete gesture;
+ gesture = NULL;
+
+ delete [] buffer;
+ buffer = NULL;
+
+ self->mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("GestureMissing");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadGesture");
+ }
+
+ llwarns << "Problem loading gesture: " << status << llendl;
+ self->mAssetStatus = PREVIEW_ASSET_ERROR;
+ }
+ }
+ delete item_idp;
+ item_idp = NULL;
+}
+
+
+void LLPreviewGesture::loadUIFromGesture(LLMultiGesture* gesture)
+{
+ /*LLInventoryItem* item = getItem();
+
+
+
+ if (item)
+ {
+ LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc");
+ descEditor->setText(item->getDescription());
+ }*/
+
+ mTriggerEditor->setText(gesture->mTrigger);
+
+ mReplaceEditor->setText(gesture->mReplaceText);
+
+ switch (gesture->mMask)
+ {
+ default:
+ case MASK_NONE:
+ mModifierCombo->setSimple( NONE_LABEL );
+ break;
+ case MASK_SHIFT:
+ mModifierCombo->setSimple( SHIFT_LABEL );
+ break;
+ case MASK_CONTROL:
+ mModifierCombo->setSimple( CTRL_LABEL );
+ break;
+ }
+
+ mKeyCombo->setCurrentByIndex(0);
+ if (gesture->mKey != KEY_NONE)
+ {
+ mKeyCombo->setSimple(LLKeyboard::stringFromKey(gesture->mKey));
+ }
+
+ // Make UI steps for each gesture step
+ S32 i;
+ S32 count = gesture->mSteps.size();
+ for (i = 0; i < count; ++i)
+ {
+ LLGestureStep* step = gesture->mSteps[i];
+
+ LLGestureStep* new_step = NULL;
+
+ switch(step->getType())
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ LLGestureStepAnimation* new_anim_step =
+ new LLGestureStepAnimation(*anim_step);
+ new_step = new_anim_step;
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ LLGestureStepSound* new_sound_step =
+ new LLGestureStepSound(*sound_step);
+ new_step = new_sound_step;
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ LLGestureStepChat* new_chat_step =
+ new LLGestureStepChat(*chat_step);
+ new_step = new_chat_step;
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ LLGestureStepWait* new_wait_step =
+ new LLGestureStepWait(*wait_step);
+ new_step = new_wait_step;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if (!new_step) continue;
+
+ // Create an enabled item with this step
+ LLScrollListItem* item = new LLScrollListItem(TRUE, new_step);
+ item->addColumn(new_step->getLabel(), LLFontGL::sSansSerifSmall);
+
+ // Add item to bottom of list
+ mStepList->addItem(item, ADD_BOTTOM);
+ }
+}
+
+// Helpful structure so we can look up the inventory item
+// after the save finishes.
+struct LLSaveInfo
+{
+ LLSaveInfo(const LLUUID& item_id, const LLUUID& object_id, const LLString& desc,
+ const LLTransactionID tid)
+ : mItemUUID(item_id), mObjectUUID(object_id), mDesc(desc), mTransactionID(tid)
+ {
+ }
+
+ LLUUID mItemUUID;
+ LLUUID mObjectUUID;
+ LLString mDesc;
+ LLTransactionID mTransactionID;
+};
+
+
+void LLPreviewGesture::saveIfNeeded()
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "Can't save gesture, no asset storage system." << llendl;
+ return;
+ }
+
+ if (!mDirty)
+ {
+ return;
+ }
+
+ // Copy the UI into a gesture
+ LLMultiGesture* gesture = createGesture();
+
+ // Serialize the gesture
+ S32 max_size = gesture->getMaxSerialSize();
+ char* buffer = new char[max_size];
+
+ LLDataPackerAsciiBuffer dp(buffer, max_size);
+
+ BOOL ok = gesture->serialize(dp);
+
+ if (dp.getCurrentSize() > 1000)
+ {
+ gViewerWindow->alertXml("GestureSaveFailedTooManySteps");
+
+ delete gesture;
+ gesture = NULL;
+ }
+ else if (!ok)
+ {
+ gViewerWindow->alertXml("GestureSaveFailedTryAgain");
+ delete gesture;
+ gesture = NULL;
+ }
+ else
+ {
+ // Every save gets a new UUID. Yup.
+ LLTransactionID tid;
+ LLAssetID asset_id;
+ tid.generate();
+ asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLVFile file(gVFS, asset_id, LLAssetType::AT_GESTURE, LLVFile::APPEND);
+
+ S32 size = dp.getCurrentSize();
+ file.setMaxSize(size);
+ file.write((U8*)buffer, size);
+
+ // Upload that asset to the database
+ LLInventoryItem* item = getItem();
+ if (item)
+ {
+ LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc");
+ LLSaveInfo* info = new LLSaveInfo(mItemUUID, mObjectUUID, descEditor->getText(), tid);
+
+ const BOOL temp_file = FALSE;
+
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_GESTURE, onSaveComplete, info, temp_file);
+
+ }
+
+ // If this gesture is active, then we need to update the in-memory
+ // active map with the new pointer.
+ if (gGestureManager.isGestureActive(mItemUUID))
+ {
+ // gesture manager now owns the pointer
+ gGestureManager.replaceGesture(mItemUUID, gesture, asset_id);
+
+ // replaceGesture may deactivate other gestures so let the
+ // inventory know.
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ // we're done with this gesture
+ delete gesture;
+ gesture = NULL;
+ }
+
+ mDirty = FALSE;
+ refresh();
+ }
+
+ delete [] buffer;
+ buffer = NULL;
+}
+
+
+// TODO: This is very similar to LLPreviewNotecard::onSaveComplete.
+// Could merge code.
+// static
+void LLPreviewGesture::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLSaveInfo* info = (LLSaveInfo*)user_data;
+ if (info && (status == 0))
+ {
+ if(info->mObjectUUID.isNull())
+ {
+ // Saving into user inventory
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setDescription(info->mDesc);
+ new_item->setTransactionID(info->mTransactionID);
+ new_item->setAssetUUID(asset_uuid);
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Inventory item for gesture " << info->mItemUUID
+ << " is no longer in agent inventory." << llendl
+ }
+ }
+ else
+ {
+ // Saving into in-world object inventory
+ LLViewerObject* object = gObjectList.findObject(info->mObjectUUID);
+ LLViewerInventoryItem* item = NULL;
+ if(object)
+ {
+ item = (LLViewerInventoryItem*)object->getInventoryObject(info->mItemUUID);
+ }
+ if(object && item)
+ {
+ item->setDescription(info->mDesc);
+ item->setAssetUUID(asset_uuid);
+ item->setTransactionID(info->mTransactionID);
+ object->updateInventory(item, TASK_INVENTORY_ITEM_KEY, false);
+ dialog_refresh_all();
+ }
+ else
+ {
+ gViewerWindow->alertXml("GestureSaveFailedObjectNotFound");
+ }
+ }
+
+ // Find our window and close it if requested.
+ LLPreviewGesture* previewp = (LLPreviewGesture*)LLPreview::find(info->mItemUUID);
+ if (previewp && previewp->mCloseAfterSave)
+ {
+ previewp->close();
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving gesture: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("GestureSaveFailedReason",args);
+ }
+ delete info;
+ info = NULL;
+}
+
+
+LLMultiGesture* LLPreviewGesture::createGesture()
+{
+ LLMultiGesture* gesture = new LLMultiGesture();
+
+ gesture->mTrigger = mTriggerEditor->getText();
+ gesture->mReplaceText = mReplaceEditor->getText();
+
+ const LLString& modifier = mModifierCombo->getSimple();
+ if (modifier == CTRL_LABEL)
+ {
+ gesture->mMask = MASK_CONTROL;
+ }
+ else if (modifier == SHIFT_LABEL)
+ {
+ gesture->mMask = MASK_SHIFT;
+ }
+ else
+ {
+ gesture->mMask = MASK_NONE;
+ }
+
+ if (mKeyCombo->getCurrentIndex() == 0)
+ {
+ gesture->mKey = KEY_NONE;
+ }
+ else
+ {
+ const LLString& key_string = mKeyCombo->getSimple();
+ LLKeyboard::keyFromString(key_string.c_str(), &(gesture->mKey));
+ }
+
+ std::vector<LLScrollListItem*> data_list = mStepList->getAllData();
+ std::vector<LLScrollListItem*>::iterator data_itor;
+ for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
+ {
+ LLScrollListItem* item = *data_itor;
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+
+ switch(step->getType())
+ {
+ case STEP_ANIMATION:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ LLGestureStepAnimation* new_anim_step =
+ new LLGestureStepAnimation(*anim_step);
+ gesture->mSteps.push_back(new_anim_step);
+ break;
+ }
+ case STEP_SOUND:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ LLGestureStepSound* new_sound_step =
+ new LLGestureStepSound(*sound_step);
+ gesture->mSteps.push_back(new_sound_step);
+ break;
+ }
+ case STEP_CHAT:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ LLGestureStepChat* new_chat_step =
+ new LLGestureStepChat(*chat_step);
+ gesture->mSteps.push_back(new_chat_step);
+ break;
+ }
+ case STEP_WAIT:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ LLGestureStepWait* new_wait_step =
+ new LLGestureStepWait(*wait_step);
+ gesture->mSteps.push_back(new_wait_step);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ return gesture;
+}
+
+
+// static
+void LLPreviewGesture::updateLabel(LLScrollListItem* item)
+{
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+
+ LLScrollListCell* cell = item->getColumn(0);
+ LLScrollListText* text_cell = (LLScrollListText*)cell;
+ std::string label = step->getLabel();
+ text_cell->setText(label);
+}
+
+// static
+void LLPreviewGesture::onCommitSetDirty(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onCommitLibrary(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* library_item = self->mLibraryList->getFirstSelected();
+ if (library_item)
+ {
+ self->mStepList->deselectAllItems();
+ self->refresh();
+ }
+}
+
+
+// static
+void LLPreviewGesture::onCommitStep(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ self->mLibraryList->deselectAllItems();
+ self->refresh();
+}
+
+
+// static
+void LLPreviewGesture::onCommitAnimation(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (step_item)
+ {
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() == STEP_ANIMATION)
+ {
+ // Assign the animation name
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ if (self->mAnimationCombo->getCurrentIndex() == 0)
+ {
+ anim_step->mAnimName.clear();
+ anim_step->mAnimAssetID.setNull();
+ }
+ else
+ {
+ anim_step->mAnimName = self->mAnimationCombo->getSimple();
+ anim_step->mAnimAssetID = self->mAnimationCombo->getCurrentID();
+ }
+ //anim_step->mFlags = 0x0;
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitAnimationTrigger(LLUICtrl* ctrl, void *data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (step_item)
+ {
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() == STEP_ANIMATION)
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ if (self->mAnimationRadio->getSelectedIndex() == 0)
+ {
+ // start
+ anim_step->mFlags &= ~ANIM_FLAG_STOP;
+ }
+ else
+ {
+ // stop
+ anim_step->mFlags |= ANIM_FLAG_STOP;
+ }
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitSound(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (step_item)
+ {
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() == STEP_SOUND)
+ {
+ // Assign the sound name
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ sound_step->mSoundName = self->mSoundCombo->getSimple();
+ sound_step->mSoundAssetID = self->mSoundCombo->getCurrentID();
+ sound_step->mFlags = 0x0;
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitChat(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() != STEP_CHAT) return;
+
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ chat_step->mChatText = self->mChatEditor->getText();
+ chat_step->mFlags = 0x0;
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onCommitWait(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() != STEP_WAIT) return;
+
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ U32 flags = 0x0;
+ if (self->mWaitAnimCheck->get()) flags |= WAIT_FLAG_ALL_ANIM;
+ if (self->mWaitTimeCheck->get()) flags |= WAIT_FLAG_TIME;
+ wait_step->mFlags = flags;
+
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ F32 wait_seconds = (F32)atof(self->mWaitTimeEditor->getText().c_str());
+ if (wait_seconds < 0.f) wait_seconds = 0.f;
+ if (wait_seconds > 3600.f) wait_seconds = 3600.f;
+ wait_step->mWaitSeconds = wait_seconds;
+ }
+
+ // Enable the input area if necessary
+ self->mWaitTimeEditor->setEnabled(self->mWaitTimeCheck->get());
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onCommitWaitTime(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() != STEP_WAIT) return;
+
+ self->mWaitTimeCheck->set(TRUE);
+ onCommitWait(ctrl, data);
+}
+
+
+// static
+void LLPreviewGesture::onKeystrokeCommit(LLLineEditor* caller,
+ void* data)
+{
+ // Just commit every keystroke
+ onCommitSetDirty(caller, data);
+}
+
+// static
+void LLPreviewGesture::onClickAdd(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* library_item = self->mLibraryList->getFirstSelected();
+ if (!library_item) return;
+
+ const LLScrollListCell* library_cell = library_item->getColumn(0);
+ const std::string& library_text = library_cell->getText();
+
+ self->addStep(library_text);
+
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+LLScrollListItem* LLPreviewGesture::addStep(const std::string& library_text)
+{
+ LLGestureStep* step = NULL;
+ if (!LLString::compareInsensitive(library_text.c_str(), "Animation"))
+ {
+ step = new LLGestureStepAnimation();
+ }
+ else if (!LLString::compareInsensitive(library_text.c_str(), "Sound"))
+ {
+ step = new LLGestureStepSound();
+ }
+ else if (!LLString::compareInsensitive(library_text.c_str(), "Chat"))
+ {
+ step = new LLGestureStepChat();
+ }
+ else if (!LLString::compareInsensitive(library_text.c_str(), "Wait"))
+ {
+ step = new LLGestureStepWait();
+ }
+
+ // Create an enabled item with this step
+ LLScrollListItem* step_item = new LLScrollListItem(TRUE, step);
+ std::string label = step->getLabel();
+ step_item->addColumn(label, LLFontGL::sSansSerifSmall);
+
+ // Add item to bottom of list
+ mStepList->addItem(step_item, ADD_BOTTOM);
+
+ // And move selection to the list on the right
+ mLibraryList->deselectAllItems();
+ mStepList->deselectAllItems();
+
+ step_item->setSelected(TRUE);
+
+ return step_item;
+}
+
+// static
+void LLPreviewGesture::onClickUp(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ S32 selected_index = self->mStepList->getFirstSelectedIndex();
+ if (selected_index > 0)
+ {
+ self->mStepList->swapWithPrevious(selected_index);
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+}
+
+// static
+void LLPreviewGesture::onClickDown(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ S32 selected_index = self->mStepList->getFirstSelectedIndex();
+ if (selected_index < 0) return;
+
+ S32 count = self->mStepList->getItemCount();
+ if (selected_index < count-1)
+ {
+ self->mStepList->swapWithNext(selected_index);
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+}
+
+// static
+void LLPreviewGesture::onClickDelete(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* item = self->mStepList->getFirstSelected();
+ S32 selected_index = self->mStepList->getFirstSelectedIndex();
+ if (selected_index >= 0)
+ {
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+ delete step;
+ step = NULL;
+
+ self->mStepList->deleteSingleItem(selected_index);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitActive(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ if (!gGestureManager.isGestureActive(self->mItemUUID))
+ {
+ gGestureManager.activateGesture(self->mItemUUID);
+ }
+ else
+ {
+ gGestureManager.deactivateGesture(self->mItemUUID);
+ }
+
+ // Make sure the (active) label in the inventory gets updated.
+ LLViewerInventoryItem* item = gInventory.getItem(self->mItemUUID);
+ if (item)
+ {
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onClickSave(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ self->saveIfNeeded();
+}
+
+// static
+void LLPreviewGesture::onClickPreview(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ if (!self->mPreviewGesture)
+ {
+ // make temporary gesture
+ self->mPreviewGesture = self->createGesture();
+
+ // add a callback
+ self->mPreviewGesture->mDoneCallback = onDonePreview;
+ self->mPreviewGesture->mCallbackData = self;
+
+ // set the button title
+ self->mPreviewBtn->setLabelSelected("Stop");
+ self->mPreviewBtn->setLabelUnselected("Stop");
+
+ // play it, and delete when done
+ gGestureManager.playGesture(self->mPreviewGesture);
+
+ self->refresh();
+ }
+ else
+ {
+ // Will call onDonePreview() below
+ gGestureManager.stopGesture(self->mPreviewGesture);
+
+ self->refresh();
+ }
+}
+
+
+// static
+void LLPreviewGesture::onDonePreview(LLMultiGesture* gesture, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ self->mPreviewBtn->setLabelSelected("Preview");
+ self->mPreviewBtn->setLabelUnselected("Preview");
+
+ delete self->mPreviewGesture;
+ self->mPreviewGesture = NULL;
+
+ self->refresh();
+}
diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h
new file mode 100644
index 0000000000..77d7477d91
--- /dev/null
+++ b/indra/newview/llpreviewgesture.h
@@ -0,0 +1,149 @@
+/**
+ * @file llpreviewgesture.h
+ * @brief Editing UI for inventory-based gestures.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWGESTURE_H
+#define LL_LLPREVIEWGESTURE_H
+
+#include "llpreview.h"
+
+class LLMultiGesture;
+class LLLineEditor;
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLScrollListCtrl;
+class LLScrollListItem;
+class LLButton;
+class LLGestureStep;
+class LLRadioGroup;
+
+class LLPreviewGesture : public LLPreview
+{
+public:
+ // Pass an object_id if this gesture is inside an object in the world,
+ // otherwise use LLUUID::null.
+ static LLPreviewGesture* show(const std::string& title, const LLUUID& item_id, const LLUUID& object_id, BOOL take_focus = TRUE);
+
+ // LLView
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ // LLPanel
+ virtual BOOL postBuild();
+
+ // LLFloater
+ virtual BOOL canClose();
+ virtual void setMinimized(BOOL minimize);
+ virtual void onClose(bool app_quitting);
+
+
+protected:
+ LLPreviewGesture();
+ virtual ~LLPreviewGesture();
+
+ void init(const LLUUID& item_id, const LLUUID& object_id);
+
+ // Populate various comboboxes
+ void addModifiers();
+ void addKeys();
+ void addAnimations();
+ void addSounds();
+
+ void refresh();
+
+ void initDefaultGesture();
+
+ void loadAsset();
+
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ void loadUIFromGesture(LLMultiGesture* gesture);
+
+ void saveIfNeeded();
+
+ static void onSaveComplete(const LLUUID& asset_uuid,
+ void* user_data,
+ S32 status);
+
+ static void handleSaveChangesDialog(S32 option, void* userdata);
+
+ // Write UI back into gesture
+ LLMultiGesture* createGesture();
+
+ // Add a step. Pass the name of the step, like "Animation",
+ // "Sound", "Chat", or "Wait"
+ LLScrollListItem* addStep(const std::string& step_name);
+
+ static void updateLabel(LLScrollListItem* item);
+
+ static void onCommitSetDirty(LLUICtrl* ctrl, void* data);
+ static void onCommitLibrary(LLUICtrl* ctrl, void* data);
+ static void onCommitStep(LLUICtrl* ctrl, void* data);
+ static void onCommitAnimation(LLUICtrl* ctrl, void* data);
+ static void onCommitSound(LLUICtrl* ctrl, void* data);
+ static void onCommitChat(LLUICtrl* ctrl, void* data);
+ static void onCommitWait(LLUICtrl* ctrl, void* data);
+ static void onCommitWaitTime(LLUICtrl* ctrl, void* data);
+
+ static void onCommitAnimationTrigger(LLUICtrl* ctrl, void *data);
+
+ // Handy function to commit each keystroke
+ static void onKeystrokeCommit(LLLineEditor* caller, void* data);
+
+ static void onClickAdd(void* data);
+ static void onClickUp(void* data);
+ static void onClickDown(void* data);
+ static void onClickDelete(void* data);
+
+ static void onCommitActive(LLUICtrl* ctrl, void* data);
+ static void onClickSave(void* data);
+ static void onClickPreview(void* data);
+
+ static void onDonePreview(LLMultiGesture* gesture, void* data);
+
+protected:
+ // LLPreview contains mDescEditor
+ LLLineEditor* mTriggerEditor;
+ LLTextBox* mReplaceText;
+ LLLineEditor* mReplaceEditor;
+ LLComboBox* mModifierCombo;
+ LLComboBox* mKeyCombo;
+
+ LLScrollListCtrl* mLibraryList;
+ LLButton* mAddBtn;
+ LLButton* mUpBtn;
+ LLButton* mDownBtn;
+ LLButton* mDeleteBtn;
+ LLScrollListCtrl* mStepList;
+
+ // Options panels for items in gesture list
+ LLTextBox* mOptionsText;
+ LLRadioGroup* mAnimationRadio;
+ LLComboBox* mAnimationCombo;
+ LLComboBox* mSoundCombo;
+ LLLineEditor* mChatEditor;
+ LLCheckBoxCtrl* mWaitAnimCheck;
+ LLCheckBoxCtrl* mWaitTimeCheck;
+ LLLineEditor* mWaitTimeEditor;
+
+ LLCheckBoxCtrl* mActiveCheck;
+ LLButton* mSaveBtn;
+ LLButton* mPreviewBtn;
+
+ LLMultiGesture* mPreviewGesture;
+ BOOL mDirty;
+};
+
+#endif
diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp
new file mode 100644
index 0000000000..e88c702453
--- /dev/null
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -0,0 +1,579 @@
+/**
+ * @file llpreviewnotecard.cpp
+ * @brief Implementation of the notecard editor
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewnotecard.h"
+
+#include "llinventory.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "roles_constants.h"
+#include "llscrollbar.h"
+#include "llselectmgr.h"
+#include "llviewertexteditor.h"
+#include "llvfile.h"
+#include "llviewerinventory.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "lldir.h"
+//#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "viewer.h" // app_abort_quit()
+#include "lllineeditor.h"
+#include "llvieweruictrlfactory.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const S32 PREVIEW_MIN_WIDTH =
+ 2 * PREVIEW_BORDER +
+ 2 * PREVIEW_BUTTON_WIDTH +
+ PREVIEW_PAD + RESIZE_HANDLE_WIDTH +
+ PREVIEW_PAD;
+const S32 PREVIEW_MIN_HEIGHT =
+ 2 * PREVIEW_BORDER +
+ 3*(20 + PREVIEW_PAD) +
+ 2 * SCROLLBAR_SIZE + 128;
+
+///----------------------------------------------------------------------------
+/// Class LLPreviewNotecard
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLPreviewNotecard::LLPreviewNotecard(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& item_id,
+ const LLUUID& object_id,
+ const LLUUID& asset_id,
+ BOOL show_keep_discard) :
+ LLPreview(name, rect, title, item_id, object_id, TRUE,
+ PREVIEW_MIN_WIDTH,
+ PREVIEW_MIN_HEIGHT),
+ mAssetID( asset_id ),
+ mNotecardItemID(item_id),
+ mObjectID(object_id)
+{
+ LLRect curRect = rect;
+
+ if (show_keep_discard)
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_notecard_keep_discard.xml");
+ childSetAction("Keep",onKeepBtn,this);
+ childSetAction("Discard",onDiscardBtn,this);
+ }
+ else
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_notecard.xml");
+ childSetAction("Save",onClickSave,this);
+
+ if( mAssetID.isNull() )
+ {
+ LLInventoryItem* item = getItem();
+ if( item )
+ {
+ mAssetID = item->getAssetUUID();
+ }
+ }
+ }
+
+ reshape(curRect.getWidth(), curRect.getHeight(), TRUE);
+ setRect(curRect);
+
+ childSetVisible("lock", FALSE);
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ if (item)
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ setTitle(title);
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if (editor)
+ {
+ editor->setWordWrap(TRUE);
+ editor->setSourceID(item_id);
+ editor->setHandleEditKeysDirectly(TRUE);
+ }
+
+ gAgent.changeCameraToDefault();
+}
+
+BOOL LLPreviewNotecard::postBuild()
+{
+ LLViewerTextEditor *ed = (LLViewerTextEditor *)gUICtrlFactory->getTextEditorByName(this, "Notecard Editor");
+ if (ed)
+ {
+ ed->setNotecardInfo(mNotecardItemID, mObjectID);
+ }
+ return TRUE;
+}
+
+bool LLPreviewNotecard::saveItem(LLPointer<LLInventoryItem>* itemptr)
+{
+ LLInventoryItem* item = NULL;
+ if (itemptr && itemptr->notNull())
+ {
+ item = (LLInventoryItem*)(*itemptr);
+ }
+ bool res = saveIfNeeded(item);
+ if (res)
+ {
+ delete itemptr;
+ }
+ return res;
+}
+
+void LLPreviewNotecard::setEnabled( BOOL enabled )
+{
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ childSetEnabled("Notecard Editor", enabled);
+ childSetVisible("lock", !enabled);
+ childSetEnabled("desc", enabled);
+ childSetEnabled("Save", enabled && editor && (!editor->isPristine()));
+
+}
+
+
+void LLPreviewNotecard::draw()
+{
+
+
+ //childSetFocus("Save", FALSE);
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+ BOOL script_changed = !editor->isPristine();
+
+ childSetEnabled("Save", script_changed && getEnabled());
+
+ LLPreview::draw();
+}
+
+// virtual
+BOOL LLPreviewNotecard::handleKeyHere(KEY key, MASK mask,
+ BOOL called_from_parent)
+{
+ if(getVisible() && getEnabled())
+ {
+ if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL)))
+ {
+ saveIfNeeded();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// virtual
+BOOL LLPreviewNotecard::canClose()
+{
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if(mForceClose || editor->isPristine())
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("SaveChanges",
+ &LLPreviewNotecard::handleSaveChangesDialog,
+ this);
+
+ return FALSE;
+ }
+}
+
+const LLInventoryItem* LLPreviewNotecard::getDragItem()
+{
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if(editor)
+ {
+ return editor->getDragItem();
+ }
+ return NULL;
+}
+
+void LLPreviewNotecard::loadAsset()
+{
+ // request the asset.
+ LLInventoryItem* item = getItem();
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if (!editor)
+ return;
+
+
+ if(item)
+ {
+ if (gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE)
+ || gAgent.isGodlike())
+ {
+ mAssetID = item->getAssetUUID();
+ if(mAssetID.isNull())
+ {
+ editor->setText("");
+ editor->makePristine();
+ editor->setEnabled(TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ LLUUID* new_uuid = new LLUUID(mItemUUID);
+ LLHost source_sim = LLHost::invalid;
+ if (mObjectUUID.notNull())
+ {
+ LLViewerObject *objectp = gObjectList.findObject(mObjectUUID);
+ if (objectp && objectp->getRegion())
+ {
+ source_sim = objectp->getRegion()->getHost();
+ }
+ else
+ {
+ // The object that we're trying to look at disappeared, bail.
+ llwarns << "Can't find object " << mObjectUUID << " associated with notecard." << llendl;
+ mAssetID.setNull();
+ editor->setText("Unable to find object containing this note.");
+ editor->makePristine();
+ editor->setEnabled(FALSE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ return;
+ }
+ }
+ gAssetStorage->getInvItemAsset(source_sim,
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ mObjectUUID,
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ &onLoadComplete,
+ (void*)new_uuid,
+ TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+ }
+ }
+ else
+ {
+ mAssetID.setNull();
+ editor->setText("You are not allowed to view this note.");
+ editor->makePristine();
+ editor->setEnabled(FALSE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ if(!gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE))
+ {
+ editor->setEnabled(FALSE);
+ childSetVisible("lock", TRUE);
+ }
+ }
+ else
+ {
+ editor->setText("");
+ editor->makePristine();
+ editor->setEnabled(TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+}
+
+// static
+void LLPreviewNotecard::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "LLPreviewNotecard::onLoadComplete()" << llendl;
+ LLUUID* item_id = (LLUUID*)user_data;
+ LLPreviewNotecard* preview = LLPreviewNotecard::getInstance(*item_id);
+ if( preview )
+ {
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+
+
+ LLViewerTextEditor* previewEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(preview, "Notecard Editor");
+
+ if( (file_length > 19) && !strncmp( buffer, "Linden text version", 19 ) )
+ {
+ if( !previewEditor->importBuffer( buffer ) )
+ {
+ llwarns << "Problem importing notecard" << llendl;
+ }
+ }
+ else
+ {
+ // Version 0 (just text, doesn't include version number)
+ previewEditor->setText(buffer);
+ }
+
+ previewEditor->makePristine();
+
+ LLInventoryItem* item = preview->getItem();
+ BOOL modifiable = item && gAgent.allowOperation(PERM_MODIFY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE);
+ previewEditor->setEnabled(modifiable);
+ delete[] buffer;
+ preview->mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("NotecardMissing");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLNotifyBox::showXml("NotecardNoPermissions");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadNotecard");
+ }
+
+ llwarns << "Problem loading notecard: " << status << llendl;
+ preview->mAssetStatus = PREVIEW_ASSET_ERROR;
+ }
+ }
+ delete item_id;
+}
+
+// static
+LLPreviewNotecard* LLPreviewNotecard::getInstance(const LLUUID& item_id)
+{
+ LLPreview* instance = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_id);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ instance = found_it->second;
+ }
+ return (LLPreviewNotecard*)instance;
+}
+
+// static
+void LLPreviewNotecard::onClickSave(void* user_data)
+{
+ //llinfos << "LLPreviewNotecard::onBtnSave()" << llendl;
+ LLPreviewNotecard* preview = (LLPreviewNotecard*)user_data;
+ if(preview)
+ {
+ preview->saveIfNeeded();
+ }
+}
+
+struct LLSaveNotecardInfo
+{
+ LLPreviewNotecard* mSelf;
+ LLUUID mItemUUID;
+ LLUUID mObjectUUID;
+ LLTransactionID mTransactionID;
+ LLPointer<LLInventoryItem> mCopyItem;
+ LLSaveNotecardInfo(LLPreviewNotecard* self, const LLUUID& item_id, const LLUUID& object_id,
+ const LLTransactionID& transaction_id, LLInventoryItem* copyitem) :
+ mSelf(self), mItemUUID(item_id), mObjectUUID(object_id), mTransactionID(transaction_id), mCopyItem(copyitem)
+ {
+ }
+};
+
+bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem)
+{
+ if(!gAssetStorage)
+ {
+ llwarns << "Not connected to an asset storage system." << llendl;
+ return false;
+ }
+
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if(!editor->isPristine())
+ {
+ // We need to update the asset information
+ LLTransactionID tid;
+ LLAssetID asset_id;
+ tid.generate();
+ asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLVFile file(gVFS, asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND);
+
+ LLString buffer;
+ if (!editor->exportBuffer(buffer))
+ {
+ return false;
+ }
+
+ editor->makePristine();
+
+ S32 size = buffer.length() + 1;
+ file.setMaxSize(size);
+ file.write((U8*)buffer.c_str(), size);
+
+ LLInventoryItem* item = getItem();
+ // save it out to database
+ if (item)
+ {
+
+ LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID, mObjectUUID,
+ tid, copyitem);
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_NOTECARD,
+ &onSaveComplete,
+ (void*)info,
+ FALSE);
+ }
+ }
+ return true;
+}
+
+// static
+void LLPreviewNotecard::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLSaveNotecardInfo* info = (LLSaveNotecardInfo*)user_data;
+ if(info && (0 == status))
+ {
+ if(info->mObjectUUID.isNull())
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setAssetUUID(asset_uuid);
+ new_item->setTransactionID(info->mTransactionID);
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Inventory item for script " << info->mItemUUID
+ << " is no longer in agent inventory." << llendl;
+ }
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(info->mObjectUUID);
+ LLViewerInventoryItem* item = NULL;
+ if(object)
+ {
+ item = (LLViewerInventoryItem*)object->getInventoryObject(info->mItemUUID);
+ }
+ if(object && item)
+ {
+ item->setAssetUUID(asset_uuid);
+ item->setTransactionID(info->mTransactionID);
+ object->updateInventory(item, TASK_INVENTORY_ITEM_KEY, false);
+ dialog_refresh_all();
+ }
+ else
+ {
+ gViewerWindow->alertXml("SaveNotecardFailObjectNotFound");
+ }
+ }
+ // Perform item copy to inventory
+ if (info->mCopyItem.notNull())
+ {
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(info->mSelf, "Notecard Editor");
+ if (editor)
+ {
+ editor->copyInventory(info->mCopyItem);
+ }
+ }
+
+ // Find our window and close it if requested.
+ LLPreviewNotecard* previewp = (LLPreviewNotecard*)LLPreview::find(info->mItemUUID);
+ if (previewp && previewp->mCloseAfterSave)
+ {
+ previewp->close();
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving notecard: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("SaveNotecardFailReason",args);
+ }
+
+ char uuid_string[UUID_STR_LENGTH];
+ asset_uuid.toString(uuid_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.tmp", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLFile::remove(filename);
+ delete info;
+}
+
+// static
+void LLPreviewNotecard::handleSaveChangesDialog(S32 option, void* userdata)
+{
+ LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
+ switch(option)
+ {
+ case 0: // "Yes"
+ self->mCloseAfterSave = TRUE;
+ LLPreviewNotecard::onClickSave((void*)self);
+ break;
+
+ case 1: // "No"
+ self->mForceClose = TRUE;
+ self->close();
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+void LLPreviewNotecard::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPreview::reshape( width, height, called_from_parent );
+
+ if( !isMinimized() )
+ {
+ // So that next time you open a script it will have the same height and width
+ // (although not the same position).
+ gSavedSettings.setRect("NotecardEditorRect", mRect);
+ }
+}
+
+// EOF
diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h
new file mode 100644
index 0000000000..3bb3da5f54
--- /dev/null
+++ b/indra/newview/llpreviewnotecard.h
@@ -0,0 +1,84 @@
+/**
+ * @file llpreviewnotecard.h
+ * @brief LLPreviewNotecard class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWNOTECARD_H
+#define LL_LLPREVIEWNOTECARD_H
+
+#include "llpreview.h"
+#include "llassetstorage.h"
+#include "lliconctrl.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPreviewNotecard
+//
+// This class allows us to edit notecards
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLViewerTextEditor;
+class LLButton;
+
+class LLPreviewNotecard : public LLPreview
+{
+public:
+ LLPreviewNotecard(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_id,
+ const LLUUID& object_id = LLUUID::null,
+ const LLUUID& asset_id = LLUUID::null,
+ BOOL show_keep_discard = FALSE);
+
+ // llpreview
+ virtual bool saveItem(LLPointer<LLInventoryItem>* itemptr);
+
+ // llview
+ virtual void draw();
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual void setEnabled( BOOL enabled );
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ // llfloater
+ virtual BOOL canClose();
+
+ // llpanel
+ virtual BOOL postBuild();
+
+ // reach into the text editor, and grab the drag item
+ const LLInventoryItem* getDragItem();
+
+
+protected:
+
+ virtual void loadAsset();
+ bool saveIfNeeded(LLInventoryItem* copyitem = NULL);
+
+ static LLPreviewNotecard* getInstance(const LLUUID& uuid);
+
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ static void onClickSave(void* data);
+
+ static void onSaveComplete(const LLUUID& asset_uuid,
+ void* user_data,
+ S32 status);
+
+ static void handleSaveChangesDialog(S32 option, void* userdata);
+
+protected:
+ LLViewerTextEditor* mEditor;
+ LLButton* mSaveBtn;
+
+ LLUUID mAssetID;
+
+ LLUUID mNotecardItemID;
+ LLUUID mObjectID;
+};
+
+
+#endif // LL_LLPREVIEWNOTECARD_H
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
new file mode 100644
index 0000000000..175824d866
--- /dev/null
+++ b/indra/newview/llpreviewscript.cpp
@@ -0,0 +1,1984 @@
+/**
+ * @file llpreviewscript.cpp
+ * @brief LLPreviewScript class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewscript.h"
+
+#include "llassetstorage.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldir.h"
+#include "llinventorymodel.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+
+#include "llresmgr.h"
+#include "llscrollbar.h"
+#include "llscrollcontainer.h"
+#include "llscrolllistctrl.h"
+#include "llslider.h"
+#include "lscript_rt_interface.h"
+#include "lscript_export.h"
+#include "lltextbox.h"
+#include "lltooldraganddrop.h"
+#include "llvfile.h"
+
+#include "llagent.h"
+#include "llnotify.h"
+#include "llmenugl.h"
+#include "roles_constants.h"
+#include "llselectmgr.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llkeyboard.h"
+#include "llscrollcontainer.h"
+#include "llcheckboxctrl.h"
+#include "llselectmgr.h"
+#include "lltooldraganddrop.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llslider.h"
+#include "viewer.h"
+#include "lldir.h"
+#include "llcombobox.h"
+//#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llvieweruictrlfactory.h"
+#include "lluictrlfactory.h"
+
+#include "viewer.h"
+
+#include "llpanelinventory.h"
+
+
+const char HELLO_LSL[] =
+ "default\n"
+ "{\n"
+ " state_entry()\n"
+ " {\n"
+ " llSay(0, \"Hello, Avatar!\");\n"
+ " }\n"
+ "\n"
+ " touch_start(integer total_number)\n"
+ " {\n"
+ " llSay(0, \"Touched.\");\n"
+ " }\n"
+ "}\n";
+const char HELP_LSL[] = "lsl_guide.html";
+
+const char DEFAULT_SCRIPT_NAME[] = "New Script";
+const char DEFAULT_SCRIPT_DESC[] = "(No Description)";
+
+const char ENABLED_RUNNING_CHECKBOX_LABEL[] = "Running";
+const char DISABLED_RUNNING_CHECKBOX_LABEL[] = "Public Objects cannot run scripts";
+
+// Description and header information
+
+const S32 SCRIPT_BORDER = 4;
+const S32 SCRIPT_PAD = 5;
+const S32 SCRIPT_BUTTON_WIDTH = 128;
+const S32 SCRIPT_BUTTON_HEIGHT = 24; // HACK: Use BTN_HEIGHT where possible.
+const S32 LINE_COLUMN_HEIGHT = 14;
+const S32 BTN_PAD = 8;
+
+const S32 SCRIPT_EDITOR_MIN_HEIGHT = 2 * SCROLLBAR_SIZE + 2 * LLPANEL_BORDER_WIDTH + 128;
+
+const S32 SCRIPT_MIN_WIDTH =
+ 2 * SCRIPT_BORDER +
+ 2 * SCRIPT_BUTTON_WIDTH +
+ SCRIPT_PAD + RESIZE_HANDLE_WIDTH +
+ SCRIPT_PAD;
+
+const S32 SCRIPT_MIN_HEIGHT =
+ 2 * SCRIPT_BORDER +
+ 3*(SCRIPT_BUTTON_HEIGHT + SCRIPT_PAD) +
+ LINE_COLUMN_HEIGHT +
+ SCRIPT_EDITOR_MIN_HEIGHT;
+
+const S32 MAX_EXPORT_SIZE = 1000;
+
+const S32 SCRIPT_SEARCH_WIDTH = 300;
+const S32 SCRIPT_SEARCH_HEIGHT = 120;
+const S32 SCRIPT_SEARCH_LABEL_WIDTH = 50;
+const S32 SCRIPT_SEARCH_BUTTON_WIDTH = 80;
+const S32 TEXT_EDIT_COLUMN_HEIGHT = 16;
+/// ---------------------------------------------------------------------------
+/// LLFloaterScriptSearch
+/// ---------------------------------------------------------------------------
+class LLFloaterScriptSearch : public LLFloater
+{
+public:
+ LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core);
+ ~LLFloaterScriptSearch();
+
+ static void show(LLScriptEdCore* editor_core);
+ static void onBtnSearch(void* userdata);
+ void handleBtnSearch();
+
+ static void onBtnReplace(void* userdata);
+ void handleBtnReplace();
+
+ static void onBtnReplaceAll(void* userdata);
+ void handleBtnReplaceAll();
+
+ LLScriptEdCore* getEditorCore() { return mEditorCore; }
+ static LLFloaterScriptSearch* getInstance() { return sInstance; }
+
+ void open();
+
+private:
+
+ LLScriptEdCore* mEditorCore;
+
+ static LLFloaterScriptSearch* sInstance;
+};
+
+LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL;
+
+LLFloaterScriptSearch::LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core)
+ : LLFloater("script search",rect,title), mEditorCore(editor_core)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_script_search.xml");
+
+ childSetAction("search_btn", onBtnSearch,this);
+ childSetAction("replace_btn", onBtnReplace,this);
+ childSetAction("replace_all_btn", onBtnReplaceAll,this);
+
+ setDefaultBtn("search_btn");
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+ sInstance = this;
+
+ childSetFocus("search_text", TRUE);
+}
+
+//static
+void LLFloaterScriptSearch::show(LLScriptEdCore* editor_core)
+{
+ if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core)
+ {
+ sInstance->close();
+ delete sInstance;
+ }
+
+ if (!sInstance)
+ {
+ S32 left = 0;
+ S32 top = 0;
+ gFloaterView->getNewFloaterPosition(&left,&top);
+
+ // sInstance will be assigned in the constructor.
+ new LLFloaterScriptSearch("Script Search",LLRect(left,top,left + SCRIPT_SEARCH_WIDTH,top - SCRIPT_SEARCH_HEIGHT),editor_core);
+ }
+
+ sInstance->open();
+}
+
+LLFloaterScriptSearch::~LLFloaterScriptSearch()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterScriptSearch::onBtnSearch(void *userdata)
+{
+ LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
+ self->handleBtnSearch();
+}
+
+void LLFloaterScriptSearch::handleBtnSearch()
+{
+ LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text");
+ mEditorCore->mEditor->selectNext(childGetText("search_text"), caseChk->get());
+}
+
+// static
+void LLFloaterScriptSearch::onBtnReplace(void *userdata)
+{
+ LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
+ self->handleBtnReplace();
+}
+
+void LLFloaterScriptSearch::handleBtnReplace()
+{
+ LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text");
+ mEditorCore->mEditor->replaceText(childGetText("search_text"), childGetText("replace_text"), caseChk->get());
+}
+
+// static
+void LLFloaterScriptSearch::onBtnReplaceAll(void *userdata)
+{
+ LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
+ self->handleBtnReplaceAll();
+}
+
+void LLFloaterScriptSearch::handleBtnReplaceAll()
+{
+ LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text");
+ mEditorCore->mEditor->replaceTextAll(childGetText("search_text"), childGetText("replace_text"), caseChk->get());
+}
+
+void LLFloaterScriptSearch::open()
+{
+ LLFloater::open();
+ childSetFocus("search_text", TRUE);
+}
+/// ---------------------------------------------------------------------------
+/// LLScriptEdCore
+/// ---------------------------------------------------------------------------
+
+LLScriptEdCore::LLScriptEdCore(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& sample,
+ const std::string& help,
+ const LLViewHandle& floater_handle,
+ void (*load_callback)(void*),
+ void (*save_callback)(void*, BOOL),
+ void* userdata,
+ S32 bottom_pad)
+ :
+ LLPanel( "name", rect ),
+ mSampleText(sample),
+ mHelpFile ( help ),
+ mEditor( NULL ),
+ mLoadCallback( load_callback ),
+ mSaveCallback( save_callback ),
+ mUserdata( userdata ),
+ mForceClose( FALSE )
+{
+ setFollowsAll();
+ setBorderVisible(FALSE);
+
+
+ gUICtrlFactory->buildPanel(this, "floater_script_ed_panel.xml");
+
+ mErrorList = LLUICtrlFactory::getScrollListByName(this, "lsl errors");
+
+ mFunctions = LLUICtrlFactory::getComboBoxByName(this, "Insert...");
+
+ childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this);
+ mFunctions->setLabel("Insert...");
+
+ mEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Script Editor");
+ mEditor->setReadOnlyBgColor(gColors.getColor( "ScriptBgReadOnlyColor" ) );
+ mEditor->setFollowsAll();
+ mEditor->setHandleEditKeysDirectly(TRUE);
+ mEditor->setEnabled(TRUE);
+ mEditor->setWordWrap(TRUE);
+
+ LLDynamicArray<const char*> funcs;
+ LLDynamicArray<const char*> tooltips;
+ for (S32 i = 0; i < gScriptLibrary.mNextNumber; i++)
+ {
+ // Make sure this isn't a god only function, or the agent is a god.
+ if (!gScriptLibrary.mFunctions[i]->mGodOnly || gAgent.isGodlike())
+ {
+ funcs.put(gScriptLibrary.mFunctions[i]->mName);
+ tooltips.put(gScriptLibrary.mFunctions[i]->mDesc);
+ }
+ }
+ LLColor3 color(0.5f, 0.0f, 0.15f);
+
+ mEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color);
+
+
+ LLKeywordToken *token;
+ LLKeywords::word_token_map_t::iterator token_it;
+ for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it)
+ {
+ token = token_it->second;
+ if (token->mColor == color)
+ mFunctions->add(wstring_to_utf8str(token->mToken));
+ }
+
+ for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it)
+ {
+ token = token_it->second;
+ if (token->mColor != color)
+ mFunctions->add(wstring_to_utf8str(token->mToken));
+ }
+
+
+ childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this);
+ childSetAction("Save_btn", onBtnSave,this);
+
+ initMenu();
+
+
+ // Do the work that addTabPanel() normally does.
+ //LLRect tab_panel_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ //tab_panel_rect.stretch( -LLPANEL_BORDER_WIDTH );
+ //mCodePanel->setFollowsAll();
+ //mCodePanel->translate( tab_panel_rect.mLeft - mCodePanel->getRect().mLeft, tab_panel_rect.mBottom - mCodePanel->getRect().mBottom);
+ //mCodePanel->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
+
+}
+
+LLScriptEdCore::~LLScriptEdCore()
+{
+ deleteBridges();
+
+ // If the search window is up for this editor, close it.
+ LLFloaterScriptSearch* script_search = LLFloaterScriptSearch::getInstance();
+ if (script_search && script_search->getEditorCore() == this)
+ {
+ script_search->close();
+ delete script_search;
+ }
+}
+
+void LLScriptEdCore::initMenu()
+{
+
+ LLMenuItemCallGL* menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Save");
+ menuItem->setMenuCallback(onBtnSave, this);
+ menuItem->setEnabledCallback(hasChanged);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Revert All Changes");
+ menuItem->setMenuCallback(onBtnUndoChanges, this);
+ menuItem->setEnabledCallback(hasChanged);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Undo");
+ menuItem->setMenuCallback(onUndoMenu, this);
+ menuItem->setEnabledCallback(enableUndoMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Redo");
+ menuItem->setMenuCallback(onRedoMenu, this);
+ menuItem->setEnabledCallback(enableRedoMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Cut");
+ menuItem->setMenuCallback(onCutMenu, this);
+ menuItem->setEnabledCallback(enableCutMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Copy");
+ menuItem->setMenuCallback(onCopyMenu, this);
+ menuItem->setEnabledCallback(enableCopyMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Paste");
+ menuItem->setMenuCallback(onPasteMenu, this);
+ menuItem->setEnabledCallback(enablePasteMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Select All");
+ menuItem->setMenuCallback(onSelectAllMenu, this);
+ menuItem->setEnabledCallback(enableSelectAllMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Search / Replace...");
+ menuItem->setMenuCallback(onSearchMenu, this);
+ menuItem->setEnabledCallback(NULL);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Help...");
+ menuItem->setMenuCallback(onBtnHelp, this);
+ menuItem->setEnabledCallback(NULL);
+
+}
+
+BOOL LLScriptEdCore::hasChanged(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+
+ return !self->mEditor->isPristine();
+}
+
+void LLScriptEdCore::draw()
+{
+ BOOL script_changed = !mEditor->isPristine();
+ childSetEnabled("Save_btn", script_changed);
+
+ if( mEditor->hasFocus() )
+ {
+ S32 line = 0;
+ S32 column = 0;
+ mEditor->getCurrentLineAndColumn( &line, &column, FALSE ); // don't include wordwrap
+ char cursor_pos[STD_STRING_BUF_SIZE];
+ sprintf( cursor_pos, "Line %d, Column %d", line, column );
+ childSetText("line_col", cursor_pos);
+ }
+ else
+ {
+ childSetText("line_col", "");
+ }
+
+ LLPanel::draw();
+}
+
+BOOL LLScriptEdCore::canClose()
+{
+ if(mForceClose || mEditor->isPristine())
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("SaveChanges", LLScriptEdCore::handleSaveChangesDialog, this);
+ return FALSE;
+ }
+}
+
+// static
+void LLScriptEdCore::handleSaveChangesDialog( S32 option, void* userdata )
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+ switch( option )
+ {
+ case 0: // "Yes"
+ // close after saving
+ LLScriptEdCore::doSave( self, TRUE );
+ break;
+
+ case 1: // "No"
+ self->mForceClose = TRUE;
+ // This will close immediately because mForceClose is true, so we won't
+ // infinite loop with these dialogs. JC
+ ((LLFloater*) self->getParent())->close();
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+// static
+void LLScriptEdCore::onHelpWebDialog(S32 option, void* userdata)
+{
+ LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
+
+ switch(option)
+ {
+ case 0:
+ load_url_local_file(corep->mHelpFile.c_str());
+ break;
+ default:
+ break;
+ }
+}
+
+// static
+void LLScriptEdCore::onBtnHelp(void* userdata)
+{
+ gViewerWindow->alertXml("WebLaunchLSLGuide",
+ onHelpWebDialog,
+ userdata);
+}
+
+// static
+void LLScriptEdCore::onBtnInsertSample(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+
+ // Insert sample code
+ self->mEditor->selectAll();
+ self->mEditor->cut();
+ self->mEditor->insertText(self->mSampleText);
+}
+
+// static
+void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+
+ // Insert sample code
+ if(self->mEditor->getEnabled())
+ {
+ self->mEditor->insertText(self->mFunctions->getSimple());
+ }
+ self->mEditor->setFocus(TRUE);
+}
+
+// static
+void LLScriptEdCore::doSave( void* userdata, BOOL close_after_save )
+{
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_LSL_SAVE_COUNT );
+ }
+
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+
+ if( self->mSaveCallback )
+ {
+ self->mSaveCallback( self->mUserdata, close_after_save );
+ }
+}
+
+// static
+void LLScriptEdCore::onBtnSave(void* data)
+{
+ // do the save, but don't close afterwards
+ doSave(data, FALSE);
+}
+
+// static
+void LLScriptEdCore::onBtnUndoChanges( void* userdata )
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+ if( !self->mEditor->tryToRevertToPristineState() )
+ {
+ gViewerWindow->alertXml("ScriptCannotUndo",
+ LLScriptEdCore::handleReloadFromServerDialog, self);
+ }
+}
+
+void LLScriptEdCore::onSearchMenu(void* userdata)
+{
+ LLScriptEdCore* sec = (LLScriptEdCore*)userdata;
+ LLFloaterScriptSearch::show(sec);
+}
+
+// static
+void LLScriptEdCore::onUndoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->undo();
+}
+
+// static
+void LLScriptEdCore::onRedoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->redo();
+}
+
+// static
+void LLScriptEdCore::onCutMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->cut();
+}
+
+// static
+void LLScriptEdCore::onCopyMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->copy();
+}
+
+// static
+void LLScriptEdCore::onPasteMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->paste();
+}
+
+// static
+void LLScriptEdCore::onSelectAllMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->selectAll();
+}
+
+// static
+void LLScriptEdCore::onDeselectMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->deselect();
+}
+
+// static
+BOOL LLScriptEdCore::enableUndoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canUndo();
+}
+
+// static
+BOOL LLScriptEdCore::enableRedoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canRedo();
+}
+
+// static
+BOOL LLScriptEdCore::enableCutMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canCut();
+}
+
+// static
+BOOL LLScriptEdCore::enableCopyMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canCopy();
+}
+
+// static
+BOOL LLScriptEdCore::enablePasteMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canPaste();
+}
+
+// static
+BOOL LLScriptEdCore::enableSelectAllMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canSelectAll();
+}
+
+// static
+BOOL LLScriptEdCore::enableDeselectMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canDeselect();
+}
+
+// static
+void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)user_data;
+ LLScrollListItem* item = self->mErrorList->getFirstSelected();
+ if(item)
+ {
+ // *FIX: This fucked up little hack is here because we don't
+ // have a grep library. This is very brittle code.
+ S32 row = 0;
+ S32 column = 0;
+ const LLScrollListCell* cell = item->getColumn(0);
+ LLString line(cell->getText());
+ line.erase(0, 1);
+ LLString::replaceChar(line, ',',' ');
+ LLString::replaceChar(line, ')',' ');
+ sscanf(line.c_str(), "%d %d", &row, &column);
+ //llinfos << "LLScriptEdCore::onErrorList() - " << row << ", "
+ //<< column << llendl;
+ self->mEditor->setCursor(row, column);
+ self->mEditor->setFocus(TRUE);
+ }
+}
+
+// static
+void LLScriptEdCore::handleReloadFromServerDialog( S32 option, void* userdata )
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+ switch( option )
+ {
+ case 0: // "Yes"
+ if( self->mLoadCallback )
+ {
+ self->mEditor->setText( "Loading..." );
+ self->mLoadCallback( self->mUserdata );
+ }
+ break;
+
+ case 1: // "No"
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+void LLScriptEdCore::selectFirstError()
+{
+ // Select the first item;
+ mErrorList->selectFirstItem();
+ onErrorList(mErrorList, this);
+}
+
+
+struct LLEntryAndEdCore
+{
+ LLScriptEdCore* mCore;
+ LLEntryAndEdCore(LLScriptEdCore* core) :
+ mCore(core)
+ {}
+};
+
+void LLScriptEdCore::deleteBridges()
+{
+ S32 count = mBridges.count();
+ LLEntryAndEdCore* eandc;
+ for(S32 i = 0; i < count; i++)
+ {
+ eandc = mBridges.get(i);
+ delete eandc;
+ mBridges[i] = NULL;
+ }
+ mBridges.reset();
+}
+
+// virtual
+BOOL LLScriptEdCore::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if(getVisible() && getEnabled())
+ {
+ if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL)))
+ {
+ if(mSaveCallback)
+ {
+ // don't close after saving
+ mSaveCallback(mUserdata, FALSE);
+ }
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/// ---------------------------------------------------------------------------
+/// LLPreviewLSL
+/// ---------------------------------------------------------------------------
+
+struct LLScriptSaveInfo
+{
+ LLUUID mItemUUID;
+ LLString mDescription;
+ LLTransactionID mTransactionID;
+
+ LLScriptSaveInfo(const LLUUID& uuid, const LLString& desc, LLTransactionID tid) :
+ mItemUUID(uuid), mDescription(desc), mTransactionID(tid) {}
+};
+
+
+
+//static
+void* LLPreviewLSL::createScriptEdPanel(void* userdata)
+{
+
+ LLPreviewLSL *self = (LLPreviewLSL*)userdata;
+
+ self->mScriptEd = new LLScriptEdCore("script panel",
+ LLRect(),
+ HELLO_LSL,
+ HELP_LSL,
+ self->mViewHandle,
+ LLPreviewLSL::onLoad,
+ LLPreviewLSL::onSave,
+ self,
+ 0);
+
+ return self->mScriptEd;
+}
+
+
+LLPreviewLSL::LLPreviewLSL(const std::string& name, const LLRect& rect,
+ const std::string& title, const LLUUID& item_id )
+: LLPreview( name, rect, title, item_id, LLUUID::null, TRUE,
+ SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT ),
+ mPendingUploads(0)
+{
+
+ LLRect curRect = rect;
+
+
+ LLCallbackMap::map_t factory_map;
+ factory_map["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this);
+
+
+ gUICtrlFactory->buildFloater(this,"floater_script_preview.xml", &factory_map);
+
+ moveResizeHandleToFront();
+
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ LLMultiFloater* hostp = getHost();
+
+ if (!sHostp && !hostp && getAssetStatus() == PREVIEW_ASSET_UNLOADED)
+ {
+ loadAsset();
+ }
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ reshape(curRect.getWidth(), curRect.getHeight(), TRUE);
+ setRect(curRect);
+ }
+}
+
+
+void LLPreviewLSL::loadAsset()
+{
+ // *HACK: we poke into inventory to see if it's there, and if so,
+ // then it might be part of the inventory library. If it's in the
+ // library, then you can see the script, but not modify it.
+ LLInventoryItem* item = gInventory.getItem(mItemUUID);
+ BOOL is_library = item
+ && !gInventory.isObjectDescendentOf(mItemUUID,
+ gAgent.getInventoryRootID());
+ if(!item)
+ {
+ // do the more generic search.
+ getItem();
+ }
+ if(item && !(item->getAssetUUID().isNull()))
+ {
+ BOOL is_copyable = gAgent.allowOperation(PERM_COPY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE);
+ BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE);
+ if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library)))
+ {
+ LLUUID* new_uuid = new LLUUID(mItemUUID);
+ gAssetStorage->getInvItemAsset(LLHost::invalid,
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ LLUUID::null,
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ &LLPreviewLSL::onLoadComplete,
+ (void*)new_uuid,
+ TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+ }
+ else
+ {
+ mScriptEd->mEditor->setText("You are not allowed to view this script.");
+ mScriptEd->mEditor->makePristine();
+ mScriptEd->mEditor->setEnabled(FALSE);
+ mScriptEd->mFunctions->setEnabled(FALSE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ childSetVisible("lock", !is_modifiable);
+ mScriptEd->childSetEnabled("Insert...", is_modifiable);
+ }
+ else
+ {
+ mScriptEd->mEditor->setText(HELLO_LSL);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+}
+
+
+BOOL LLPreviewLSL::canClose()
+{
+ return mScriptEd->canClose();
+}
+
+//override the llpreview open which attempts to load asset, load after xml ui made
+void LLPreviewLSL::open()
+{
+ LLFloater::open();
+}
+
+// static
+void LLPreviewLSL::onLoad(void* userdata)
+{
+ LLPreviewLSL* self = (LLPreviewLSL*)userdata;
+ self->loadAsset();
+}
+
+// static
+void LLPreviewLSL::onSave(void* userdata, BOOL close_after_save)
+{
+ LLPreviewLSL* self = (LLPreviewLSL*)userdata;
+ self->mCloseAfterSave = close_after_save;
+ self->saveIfNeeded();
+}
+
+
+// Save needs to compile the text in the buffer. If the compile
+// succeeds, then save both assets out to the database. If the compile
+// fails, go ahead and save the text anyway so that the user doesn't
+// get too fucked.
+void LLPreviewLSL::saveIfNeeded()
+{
+ // llinfos << "LLPreviewLSL::save()" << llendl;
+ if(!mScriptEd->mEditor->isPristine())
+ {
+ mPendingUploads = 0;
+ mScriptEd->mErrorList->deleteAllItems();
+ mScriptEd->mEditor->makePristine();
+
+ // We need to update the asset information
+ LLTransactionID tid;
+ LLAssetID uuid;
+ tid.generate();
+ uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+ char uuid_string[UUID_STR_LENGTH];
+ uuid.toString(uuid_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ FILE* fp = LLFile::fopen(filename, "wb");
+ if(!fp)
+ {
+ llwarns << "Unable to write to " << filename << llendl;
+ LLScrollListItem* item = new LLScrollListItem();
+ item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall);
+ mScriptEd->mErrorList->addItem(item);
+ return;
+ }
+
+ LLString utf8text = mScriptEd->mEditor->getText();
+ //fprintf(fp, "%s|%s\n", LLAssetType::lookup(LLAssetType::AT_LSL_TEXT),
+ //uuid_string);
+ //fprintf(fp,"{\n%s}\n", text.c_str());
+ fputs(utf8text.c_str(), fp);
+ fclose(fp);
+ fp = NULL;
+
+ // also write it out to the vfs for upload
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND);
+ S32 size = utf8text.length() + 1;
+
+ file.setMaxSize(size);
+ file.write((U8*)utf8text.c_str(), size);
+
+ LLInventoryItem *inv_item = getItem();
+
+ // save it out to database
+ if(gAssetStorage && inv_item)
+ {
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLScriptSaveInfo* info = NULL;
+
+ LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc");
+
+ info = new LLScriptSaveInfo(mItemUUID,
+ descEditor->getText(),
+ tid);
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &LLPreviewLSL::onSaveComplete, info);
+ }
+
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ char err_filename[LL_MAX_PATH];
+ sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLScrollListItem* item = NULL;
+ const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA);
+ if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike()))
+ {
+ llinfos << "Compile failed!" << llendl;
+ //char command[256];
+ //sprintf(command, "type %s\n", err_filename);
+ //system(command);
+
+ // load the error file into the error scrolllist
+ if(NULL != (fp = LLFile::fopen(err_filename, "r")))
+ {
+ char buffer[MAX_STRING];
+ LLString line;
+ while(!feof(fp))
+ {
+
+ fgets(buffer, MAX_STRING, fp);
+ if(feof(fp))
+ {
+ break;
+ }
+ else if(!buffer)
+ {
+ continue;
+ }
+ else
+ {
+ line.assign(buffer);
+ LLString::stripNonprintable(line);
+ item = new LLScrollListItem();
+ item->addColumn(line, err_font);
+ mScriptEd->mErrorList->addItem(item);
+ }
+ }
+ fclose(fp);
+ mScriptEd->selectFirstError();
+ }
+ }
+ else
+ {
+ llinfos << "Compile worked!" << llendl;
+ if(gAssetStorage)
+ {
+ // move the compiled file into the vfs for transport
+ FILE* fp = LLFile::fopen(dst_filename, "rb");
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+ fp = NULL;
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLUUID* this_uuid = new LLUUID(mItemUUID);
+ gAssetStorage->storeAssetData(tid,
+ LLAssetType::AT_LSL_BYTECODE,
+ &LLPreviewLSL::onSaveBytecodeComplete,
+ (void**)this_uuid);
+ }
+ }
+
+ // get rid of any temp files left lying around
+ LLFile::remove(filename);
+ LLFile::remove(err_filename);
+ LLFile::remove(dst_filename);
+ }
+}
+
+
+// static
+void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLScriptSaveInfo* info = reinterpret_cast<LLScriptSaveInfo*>(user_data);
+ if(0 == status)
+ {
+ if (info)
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setAssetUUID(asset_uuid);
+ new_item->setTransactionID(info->mTransactionID);
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Inventory item for script " << info->mItemUUID
+ << " is no longer in agent inventory." << llendl
+ }
+
+ // Find our window and close it if requested.
+ LLPreviewLSL* self = (LLPreviewLSL*)LLPreview::find(info->mItemUUID);
+ if (self)
+ {
+ getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving script: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("SaveScriptFailReason", args);
+ }
+ delete info;
+}
+
+// static
+void LLPreviewLSL::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLUUID* instance_uuid = (LLUUID*)user_data;
+ LLPreviewLSL* self = NULL;
+ if(instance_uuid)
+ {
+ self = LLPreviewLSL::getInstance(*instance_uuid);
+ }
+ if (0 == status)
+ {
+ if (self)
+ {
+ LLScrollListItem* item = new LLScrollListItem();
+ item->addColumn("Compile successful!", LLFontGL::sSansSerifSmall);
+ self->mScriptEd->mErrorList->addItem(item);
+
+ // Find our window and close it if requested.
+ self->getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving LSL Bytecode (Preview)" << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("SaveBytecodeFailReason", args);
+ }
+ delete instance_uuid;
+}
+
+// static
+void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLUUID* item_uuid = (LLUUID*)user_data;
+ LLPreviewLSL* preview = LLPreviewLSL::getInstance(*item_uuid);
+ if( preview )
+ {
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type);
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+ preview->mScriptEd->mEditor->setText(buffer);
+ preview->mScriptEd->mEditor->makePristine();
+ delete [] buffer;
+ LLInventoryItem* item = gInventory.getItem(*item_uuid);
+ BOOL is_modifiable = FALSE;
+ if(item
+ && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE))
+ {
+ is_modifiable = TRUE;
+ }
+ preview->mScriptEd->mEditor->setEnabled(is_modifiable);
+ preview->mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("ScriptMissing");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLNotifyBox::showXml("ScriptNoPermissions");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadScript");
+ }
+
+ preview->mAssetStatus = PREVIEW_ASSET_ERROR;
+ llwarns << "Problem loading script: " << status << llendl;
+ }
+ }
+ delete item_uuid;
+}
+
+// static
+LLPreviewLSL* LLPreviewLSL::getInstance( const LLUUID& item_uuid )
+{
+ LLPreview* instance = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ instance = found_it->second;
+ }
+ return (LLPreviewLSL*)instance;
+}
+
+void LLPreviewLSL::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPreview::reshape( width, height, called_from_parent );
+
+ if( !isMinimized() )
+ {
+ // So that next time you open a script it will have the same height and width
+ // (although not the same position).
+ gSavedSettings.setRect("PreviewScriptRect", mRect);
+ }
+}
+
+/// ---------------------------------------------------------------------------
+/// LLLiveLSLEditor
+/// ---------------------------------------------------------------------------
+
+LLMap<LLUUID, LLLiveLSLEditor*> LLLiveLSLEditor::sInstances;
+
+
+
+//static
+void* LLLiveLSLEditor::createScriptEdPanel(void* userdata)
+{
+
+ LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata;
+
+ self->mScriptEd = new LLScriptEdCore("script ed panel",
+ LLRect(),
+ HELLO_LSL,
+ HELP_LSL,
+ self->mViewHandle,
+ &LLLiveLSLEditor::onLoad,
+ &LLLiveLSLEditor::onSave,
+ self,
+ 0);
+
+ return self->mScriptEd;
+}
+
+
+LLLiveLSLEditor::LLLiveLSLEditor(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& object_id,
+ const LLUUID& item_id) :
+ LLFloater(name, rect, title, TRUE, SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT),
+ mObjectID(object_id),
+ mItemID(item_id),
+ mScriptEd(NULL),
+ mAskedForRunningInfo(FALSE),
+ mHaveRunningInfo(FALSE),
+ mCloseAfterSave(FALSE),
+ mPendingUploads(0)
+{
+
+
+ BOOL is_new = FALSE;
+ if(mItemID.isNull())
+ {
+ mItemID.generate();
+ is_new = TRUE;
+ }
+
+
+ LLLiveLSLEditor::sInstances.addData(mItemID ^ mObjectID, this);
+
+
+
+ LLCallbackMap::map_t factory_map;
+ factory_map["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this);
+
+ moveResizeHandleToFront();
+
+ gUICtrlFactory->buildFloater(this,"floater_live_lsleditor.xml", &factory_map);
+
+
+ childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this);
+ childSetEnabled("running", FALSE);
+
+ childSetAction("Reset",&LLLiveLSLEditor::onReset,this);
+ childSetEnabled("Reset", TRUE);
+
+
+ mScriptEd->mEditor->makePristine();
+ loadAsset(is_new);
+ mScriptEd->mEditor->setFocus(TRUE);
+
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+
+ setTitle(title);
+
+}
+
+LLLiveLSLEditor::~LLLiveLSLEditor()
+{
+ LLLiveLSLEditor::sInstances.removeData(mItemID ^ mObjectID);
+}
+
+
+void LLLiveLSLEditor::loadAsset(BOOL is_new)
+{
+ //llinfos << "LLLiveLSLEditor::loadAsset()" << llendl;
+ if(!is_new)
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ // HACK! we "know" that mItemID refers to a LLViewerInventoryItem...
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)object->getInventoryObject(mItemID);
+ if(item
+ && (gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE)
+ || gAgent.isGodlike()))
+ {
+ mItem = new LLViewerInventoryItem(item);
+ //llinfos << "asset id " << mItem->getAssetUUID() << llendl;
+ }
+
+ if(!gAgent.isGodlike()
+ && (item
+ && (!gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE)
+ || !gAgent.allowOperation(PERM_MODIFY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE))))
+
+ {
+ mScriptEd->mEditor->setText("You are not allowed to view this script.");
+ mScriptEd->mEditor->makePristine();
+ mScriptEd->mEditor->setEnabled(FALSE);
+ }
+ else if(mItem.notNull() && mItem->getAssetUUID().notNull())
+ {
+ // request the text from the object
+ LLUUID* user_data = new LLUUID(mItemID ^ mObjectID);
+ gAssetStorage->getInvItemAsset(object->getRegion()->getHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ object->getID(),
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ &LLLiveLSLEditor::onLoadComplete,
+ (void*)user_data,
+ TRUE);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_GetScriptRunning);
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+ msg->addUUIDFast(_PREHASH_ItemID, mItemID);
+ msg->sendReliable(object->getRegion()->getHost());
+ mAskedForRunningInfo = TRUE;
+ }
+ else
+ {
+ mScriptEd->mEditor->setText("");
+ mScriptEd->mEditor->makePristine();
+ }
+
+ if(item
+ && !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE))
+ {
+ mScriptEd->mEditor->setEnabled(FALSE);
+ }
+ // This is commented out, because we don't completely
+ // handle script exports yet.
+ /*
+ // request the exports from the object
+ gMessageSystem->newMessage("GetScriptExports");
+ gMessageSystem->nextBlock("ScriptBlock");
+ gMessageSystem->addUUID("AgentID", gAgent.getID());
+ U32 local_id = object->getLocalID();
+ gMessageSystem->addData("LocalID", &local_id);
+ gMessageSystem->addUUID("ItemID", mItemID);
+ LLHost host(object->getRegion()->getIP(),
+ object->getRegion()->getPort());
+ gMessageSystem->sendReliable(host);
+ */
+ }
+ }
+ else
+ {
+ mScriptEd->mEditor->setText(HELLO_LSL);
+ //mScriptEd->mEditor->setText("");
+ //mScriptEd->mEditor->makePristine();
+ LLPermissions perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, gAgent.getGroupID());
+ perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
+ mItem = new LLViewerInventoryItem(mItemID,
+ mObjectID,
+ perm,
+ LLUUID::null,
+ LLAssetType::AT_LSL_TEXT,
+ LLInventoryType::IT_LSL,
+ DEFAULT_SCRIPT_NAME,
+ DEFAULT_SCRIPT_DESC,
+ LLSaleInfo::DEFAULT,
+ LLInventoryItem::II_FLAGS_NONE,
+ time_corrected());
+ }
+}
+
+// static
+void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLLiveLSLEditor* instance = NULL;
+ LLUUID* xored_id = (LLUUID*)user_data;
+
+ if( LLLiveLSLEditor::sInstances.checkData(*xored_id) )
+ {
+ if( LL_ERR_NOERR == status )
+ {
+ instance = LLLiveLSLEditor::sInstances[*xored_id];
+ instance->loadScriptText(vfs, asset_id, type);
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("ScriptMissing");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLNotifyBox::showXml("ScriptNoPermissions");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadScript");
+ }
+ }
+ }
+
+ delete xored_id;
+}
+
+void LLLiveLSLEditor::loadScriptText(const char* filename)
+{
+ FILE* file = LLFile::fopen(filename, "rb");
+ if(file)
+ {
+ // read in the whole file
+ fseek(file, 0L, SEEK_END);
+ S32 file_length = ftell(file);
+ fseek(file, 0L, SEEK_SET);
+ char* buffer = new char[file_length+1];
+ fread(buffer, file_length, 1, file);
+ fclose(file);
+ buffer[file_length] = 0;
+ mScriptEd->mEditor->setText(buffer);
+ mScriptEd->mEditor->makePristine();
+ delete[] buffer;
+ }
+ else
+ {
+ llwarns << "Error opening " << filename << llendl;
+ }
+}
+
+void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
+{
+ LLVFile file(vfs, uuid, type);
+ S32 file_length = file.getSize();
+ char *buffer = new char[file_length + 1];
+ file.read((U8*)buffer, file_length);
+
+ if (file.getLastBytesRead() != file_length ||
+ file_length <= 0)
+ {
+ llwarns << "Error reading " << uuid << ":" << type << llendl;
+ }
+
+ buffer[file_length] = '\0';
+
+ mScriptEd->mEditor->setText(buffer);
+ mScriptEd->mEditor->makePristine();
+ delete[] buffer;
+
+}
+
+
+void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata )
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
+ LLViewerObject* object = gObjectList.findObject( self->mObjectID );
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(self, "running");
+ BOOL running = runningCheckbox->get();
+ //self->mRunningCheckbox->get();
+ if( object )
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetScriptRunning);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID);
+ msg->addUUIDFast(_PREHASH_ItemID, self->mItemID);
+ msg->addBOOLFast(_PREHASH_Running, running);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ else
+ {
+ runningCheckbox->set(!running);
+ gViewerWindow->alertXml("CouldNotStartStopScript");
+ }
+}
+
+void LLLiveLSLEditor::onReset(void *userdata)
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
+
+ LLViewerObject* object = gObjectList.findObject( self->mObjectID );
+ if(object)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ScriptReset);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID);
+ msg->addUUIDFast(_PREHASH_ItemID, self->mItemID);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ else
+ {
+ gViewerWindow->alertXml("CouldNotStartStopScript");
+ }
+}
+
+void LLLiveLSLEditor::draw()
+{
+ if(getVisible())
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running");
+ if(object && mAskedForRunningInfo && mHaveRunningInfo)
+ {
+ if(object->permAnyOwner())
+ {
+ runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL);
+ runningCheckbox->setEnabled(TRUE);
+ }
+ else
+ {
+ runningCheckbox->setLabel(DISABLED_RUNNING_CHECKBOX_LABEL);
+ runningCheckbox->setEnabled(FALSE);
+ // *FIX: Set it to false so that the ui is correct for
+ // a box that is released to public. It could be
+ // incorrect after a release/claim cycle, but will be
+ // correct after clicking on it.
+ runningCheckbox->set(FALSE);
+ }
+ }
+ else if(!object)
+ {
+ // HACK: Display this information in the title bar.
+ // Really ought to put in main window.
+ setTitle("Script (object out of range)");
+ runningCheckbox->setEnabled(FALSE);
+ // object may have fallen out of range.
+ mHaveRunningInfo = FALSE;
+ }
+ LLFloater::draw();
+ }
+}
+
+struct LLLiveLSLSaveData
+{
+ LLLiveLSLSaveData(const LLUUID& id, const LLViewerInventoryItem* item, BOOL active);
+ LLUUID mObjectID;
+ LLPointer<LLViewerInventoryItem> mItem;
+ BOOL mActive;
+};
+
+LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id,
+ const LLViewerInventoryItem* item,
+ BOOL active) :
+ mObjectID(id),
+ mActive(active)
+{
+ llassert(item);
+ mItem = new LLViewerInventoryItem(item);
+}
+
+void LLLiveLSLEditor::saveIfNeeded()
+{
+ llinfos << "LLLiveLSLEditor::saveIfNeeded()" << llendl;
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(!object)
+ {
+ gViewerWindow->alertXml("SaveScriptFailObjectNotFound");
+ return;
+ }
+
+ // get the latest info about it. We used to be losing the script
+ // name on save, because the viewer object version of the item,
+ // and the editor version would get out of synch. Here's a good
+ // place to synch them back up.
+ // HACK! we "know" that mItemID refers to a LLInventoryItem...
+ LLInventoryItem* inv_item = (LLInventoryItem*)object->getInventoryObject(mItemID);
+ if(inv_item)
+ {
+ mItem->copy(inv_item);
+ }
+
+ // Don't need to save if we're pristine
+ if(mScriptEd->mEditor->isPristine())
+ {
+ return;
+ }
+
+ mPendingUploads = 0;
+
+ // save the script
+ mScriptEd->mEditor->makePristine();
+ mScriptEd->mErrorList->deleteAllItems();
+
+ // set up the save on the local machine.
+ mScriptEd->mEditor->makePristine();
+ LLTransactionID tid;
+ LLAssetID uuid;
+ tid.generate();
+ uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+ mItem->setAssetUUID(uuid);
+ mItem->setTransactionID(tid);
+
+ // write out the data, and store it in the asset database
+ char uuid_string[UUID_STR_LENGTH];
+ uuid.toString(uuid_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ FILE* fp = LLFile::fopen(filename, "wb");
+ if(!fp)
+ {
+ llwarns << "Unable to write to " << filename << llendl;
+ LLScrollListItem* item = new LLScrollListItem();
+ item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall);
+ mScriptEd->mErrorList->addItem(item);
+ return;
+ }
+ LLString utf8text = mScriptEd->mEditor->getText();
+ fputs(utf8text.c_str(), fp);
+ fclose(fp);
+
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running");
+
+ // save it out to database
+ if(gAssetStorage)
+ {
+ // write it out to the vfs for upload
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND);
+ S32 size = utf8text.length() + 1;
+
+ file.setMaxSize(size);
+ file.write((U8*)utf8text.c_str(), size);
+
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLLiveLSLSaveData* data = new LLLiveLSLSaveData(mObjectID,
+ mItem,
+ runningCheckbox->get());
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &onSaveTextComplete, (void*)data, FALSE);
+ }
+
+#if LL_WINDOWS
+ // This major hack was inserted because sometimes compilation
+ // would fail because it couldn't open this file... I decided
+ // to make a loop until open was successful. This seems to be
+ // a problem specific to ntfs.
+ fp = NULL;
+ const U32 MAX_TRIES = 20;
+ U32 tries = MAX_TRIES;
+ while((!fp) && --tries)
+ {
+ ms_sleep(17);
+ fp = LLFile::fopen(filename, "r");
+ if(!fp)
+ {
+ llwarns << "Trying to open the source file " << filename
+ << " again" << llendl;
+ }
+ else
+ {
+ fclose(fp);
+ }
+ }
+ fp = NULL;
+#endif
+
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ char err_filename[LL_MAX_PATH];
+ sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLScrollListItem* item = NULL;
+ const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA);
+ if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike()))
+ {
+ // load the error file into the error scrolllist
+ llinfos << "Compile failed!" << llendl;
+ if(NULL != (fp = LLFile::fopen(err_filename, "r")))
+ {
+ char buffer[MAX_STRING];
+ LLString line;
+ while(!feof(fp))
+ {
+
+ fgets(buffer, MAX_STRING, fp);
+ if(feof(fp))
+ {
+ break;
+ }
+ else if(!buffer)
+ {
+ continue;
+ }
+ else
+ {
+ line.assign(buffer);
+ LLString::stripNonprintable(line);
+ item = new LLScrollListItem();
+ item->addColumn(line, err_font);
+ mScriptEd->mErrorList->addItem(item);
+ }
+ }
+ fclose(fp);
+ mScriptEd->selectFirstError();
+ // don't set the asset id, because we want to save the
+ // script, even though the compile failed.
+ //mItem->setAssetUUID(LLUUID::null);
+ object->saveScript(mItem, FALSE, false);
+ dialog_refresh_all();
+ }
+ }
+ else
+ {
+ llinfos << "Compile worked!" << llendl;
+ mScriptEd->mErrorList->addSimpleItem("Compile successful, saving...");
+ if(gAssetStorage)
+ {
+ llinfos << "LLLiveLSLEditor::saveAsset "
+ << mItem->getAssetUUID() << llendl;
+
+ // move the compiled file into the vfs for transport
+ FILE* fp = LLFile::fopen(dst_filename, "rb");
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+ fp = NULL;
+
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLLiveLSLSaveData* data = NULL;
+ data = new LLLiveLSLSaveData(mObjectID,
+ mItem,
+ runningCheckbox->get());
+ gAssetStorage->storeAssetData(tid,
+ LLAssetType::AT_LSL_BYTECODE,
+ &LLLiveLSLEditor::onSaveBytecodeComplete,
+ (void*)data);
+ dialog_refresh_all();
+ }
+ }
+
+ // get rid of any temp files left lying around
+ LLFile::remove(filename);
+ LLFile::remove(err_filename);
+ LLFile::remove(dst_filename);
+
+ // If we successfully saved it, then we should be able to check/uncheck the running box!
+ runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL);
+ runningCheckbox->setEnabled(TRUE);
+}
+
+void LLLiveLSLEditor::onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
+
+ if (status)
+ {
+ llwarns << "Unable to save text for a script." << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveText", args);
+ }
+ else
+ {
+ LLLiveLSLEditor* self = sInstances.getIfThere(data->mItem->getUUID() ^ data->mObjectID);
+ if (self)
+ {
+ self->getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ }
+ delete data;
+ data = NULL;
+}
+
+
+void LLLiveLSLEditor::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
+ if(!data)
+ return;
+ if(0 ==status)
+ {
+ llinfos << "LSL Bytecode saved" << llendl;
+ LLUUID xor_id = data->mItem->getUUID() ^ data->mObjectID;
+ LLLiveLSLEditor* self = sInstances.getIfThere(xor_id);
+ if(self)
+ {
+ // Tell the user that the compile worked.
+ self->mScriptEd->mErrorList->addSimpleItem("Save complete.");
+ // close the window if this completes both uploads
+ self->getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ LLViewerObject* object = gObjectList.findObject(data->mObjectID);
+ if(object)
+ {
+ object->saveScript(data->mItem, data->mActive, false);
+ dialog_refresh_all();
+ //LLToolDragAndDrop::dropScript(object, ids->first,
+ // LLAssetType::AT_LSL_TEXT, FALSE);
+ }
+ }
+ else
+ {
+ llinfos << "Problem saving LSL Bytecode (Live Editor)" << llendl;
+ llwarns << "Unable to save a compiled script." << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveBytecode", args);
+ }
+ char uuid_string[UUID_STR_LENGTH];
+ data->mItem->getAssetUUID().toString(uuid_string);
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLFile::remove(dst_filename);
+ sprintf(dst_filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLFile::remove(dst_filename);
+ delete data;
+}
+
+BOOL LLLiveLSLEditor::canClose()
+{
+ return (mScriptEd->canClose());
+}
+
+// static
+void LLLiveLSLEditor::onLoad(void* userdata)
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
+ self->loadAsset();
+}
+
+// static
+void LLLiveLSLEditor::onSave(void* userdata, BOOL close_after_save)
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
+ self->mCloseAfterSave = close_after_save;
+ self->saveIfNeeded();
+}
+
+// static
+LLLiveLSLEditor* LLLiveLSLEditor::show(const LLUUID& script_id, const LLUUID& object_id)
+{
+ LLLiveLSLEditor* instance = NULL;
+ LLUUID xored_id = script_id ^ object_id;
+ if(LLLiveLSLEditor::sInstances.checkData(xored_id))
+ {
+ // Move the existing view to the front
+ instance = LLLiveLSLEditor::sInstances[xored_id];
+ instance->open();
+ }
+ return instance;
+}
+
+// static
+void LLLiveLSLEditor::hide(const LLUUID& script_id, const LLUUID& object_id)
+{
+ LLUUID xored_id = script_id ^ object_id;
+ if( LLLiveLSLEditor::sInstances.checkData( xored_id ) )
+ {
+ LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id];
+ if(instance->getParent())
+ {
+ instance->getParent()->removeChild(instance);
+ }
+ delete instance;
+ }
+}
+
+// static
+void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**)
+{
+ LLUUID item_id;
+ LLUUID object_id;
+ msg->getUUIDFast(_PREHASH_Script, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_Script, _PREHASH_ItemID, item_id);
+ LLUUID xored_id = item_id ^ object_id;
+ if(LLLiveLSLEditor::sInstances.checkData(xored_id))
+ {
+ LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id];
+ instance->mHaveRunningInfo = TRUE;
+ BOOL running;
+ msg->getBOOLFast(_PREHASH_Script, _PREHASH_Running, running);
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(instance, "running");
+ runningCheckbox->set(running);
+ }
+}
+
+void LLLiveLSLEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLFloater::reshape( width, height, called_from_parent );
+
+ if( !isMinimized() )
+ {
+ // So that next time you open a script it will have the same height and width
+ // (although not the same position).
+ gSavedSettings.setRect("PreviewScriptRect", mRect);
+ }
+}
diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h
new file mode 100644
index 0000000000..228ca8c637
--- /dev/null
+++ b/indra/newview/llpreviewscript.h
@@ -0,0 +1,212 @@
+/**
+ * @file llpreviewscript.h
+ * @brief LLPreviewScript class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWSCRIPT_H
+#define LL_LLPREVIEWSCRIPT_H
+
+#include "lldarray.h"
+#include "llpreview.h"
+#include "lltabcontainer.h"
+#include "llinventory.h"
+#include "llcombobox.h"
+#include "lliconctrl.h"
+
+
+class LLMessageSystem;
+class LLTextEditor;
+class LLButton;
+class LLCheckBoxCtrl;
+class LLScrollListCtrl;
+class LLViewerObject;
+struct LLEntryAndEdCore;
+class LLMenuBarGL;
+class LLFloaterScriptSearch;
+
+// Inner, implementation class. LLPreviewScript and LLLiveScriptEditor each own one of these.
+class LLScriptEdCore : public LLPanel
+{
+ friend class LLPreviewScript;
+ friend class LLPreviewLSL;
+ friend class LLLiveScriptEditor;
+ friend class LLLiveLSLEditor;
+ friend class LLFloaterScriptSearch;
+
+public:
+ LLScriptEdCore(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& sample,
+ const std::string& help,
+ const LLViewHandle& floater_handle,
+ void (*load_callback)(void* userdata),
+ void (*save_callback)(void* userdata, BOOL close_after_save),
+ void* userdata,
+ S32 bottom_pad = 0); // pad below bottom row of buttons
+ ~LLScriptEdCore();
+
+ void initMenu();
+
+ virtual void draw();
+
+ BOOL canClose();
+
+ static void handleSaveChangesDialog(S32 option, void* userdata);
+ static void handleReloadFromServerDialog(S32 option, void* userdata);
+
+ static void onHelpWebDialog(S32 option, void* userdata);
+ static void onBtnHelp(void* userdata);
+ static void onBtnInsertSample(void*);
+ static void onBtnInsertFunction(LLUICtrl*, void*);
+ static void doSave( void* userdata, BOOL close_after_save );
+ static void onBtnSave(void*);
+ static void onBtnUndoChanges(void*);
+ static void onSearchMenu(void* userdata);
+
+ static void onUndoMenu(void* userdata);
+ static void onRedoMenu(void* userdata);
+ static void onCutMenu(void* userdata);
+ static void onCopyMenu(void* userdata);
+ static void onPasteMenu(void* userdata);
+ static void onSelectAllMenu(void* userdata);
+ static void onDeselectMenu(void* userdata);
+
+ static BOOL enableUndoMenu(void* userdata);
+ static BOOL enableRedoMenu(void* userdata);
+ static BOOL enableCutMenu(void* userdata);
+ static BOOL enableCopyMenu(void* userdata);
+ static BOOL enablePasteMenu(void* userdata);
+ static BOOL enableSelectAllMenu(void* userdata);
+ static BOOL enableDeselectMenu(void* userdata);
+
+ static BOOL hasChanged(void* userdata);
+
+ void selectFirstError();
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+protected:
+ void deleteBridges();
+
+ static void onErrorList(LLUICtrl*, void* user_data);
+
+private:
+ LLString mSampleText;
+ std::string mHelpFile;
+ LLTextEditor* mEditor;
+ void (*mLoadCallback)(void* userdata);
+ void (*mSaveCallback)(void* userdata, BOOL close_after_save);
+ void* mUserdata;
+ LLComboBox *mFunctions;
+ BOOL mForceClose;
+ //LLPanel* mGuiPanel;
+ LLPanel* mCodePanel;
+ LLScrollListCtrl* mErrorList;
+ LLDynamicArray<LLEntryAndEdCore*> mBridges;
+};
+
+
+// Used to view and edit a LSL from your inventory.
+class LLPreviewLSL : public LLPreview
+{
+public:
+ LLPreviewLSL(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_uuid );
+
+ /*virtual*/ void open();
+
+protected:
+ virtual BOOL canClose();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ virtual void loadAsset();
+ void saveIfNeeded();
+
+ static void onLoad(void* userdata);
+ static void onSave(void* userdata, BOOL close_after_save);
+
+ static void onLoadComplete(LLVFS *vfs, const LLUUID& uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+ static void onSaveComplete(const LLUUID& uuid, void* user_data, S32 status);
+ static void onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status);
+ static LLPreviewLSL* getInstance(const LLUUID& uuid);
+
+ static void* createScriptEdPanel(void* userdata);
+
+
+protected:
+ LLScriptEdCore* mScriptEd;
+ // Can safely close only after both text and bytecode are uploaded
+ S32 mPendingUploads;
+};
+
+
+// Used to view and edit an LSL that is attached to an object.
+class LLLiveLSLEditor : public LLFloater
+{
+public:
+ LLLiveLSLEditor(const std::string& name, const LLRect& rect,
+ const std::string& title,
+ const LLUUID& object_id, const LLUUID& item_id);
+ ~LLLiveLSLEditor();
+
+
+ static LLLiveLSLEditor* show(const LLUUID& item_id, const LLUUID& object_id);
+ static void hide(const LLUUID& item_id, const LLUUID& object_id);
+
+ static void processScriptRunningReply(LLMessageSystem* msg, void**);
+
+protected:
+ virtual BOOL canClose();
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ void loadAsset(BOOL is_new = FALSE);
+ void saveIfNeeded();
+
+ static void onLoad(void* userdata);
+ static void onSave(void* userdata, BOOL close_after_save);
+
+ static void onLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+ static void onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status);
+ static void onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status);
+ static void onRunningCheckboxClicked(LLUICtrl*, void* userdata);
+ static void onReset(void* userdata);
+
+ void loadScriptText(const char* filename);
+ void loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type);
+
+ static void onErrorList(LLUICtrl*, void* user_data);
+
+ static void* createScriptEdPanel(void* userdata);
+
+
+protected:
+ LLUUID mObjectID;
+ LLUUID mItemID; // The inventory item this script is associated with
+ BOOL mIsNew;
+ LLScriptEdCore* mScriptEd;
+ //LLUUID mTransmitID;
+ LLCheckBoxCtrl *mRunningCheckbox;
+ BOOL mAskedForRunningInfo;
+ BOOL mHaveRunningInfo;
+ LLButton *mResetButton;
+ LLPointer<LLViewerInventoryItem> mItem;
+ BOOL mCloseAfterSave;
+ // need to save both text and script, so need to decide when done
+ S32 mPendingUploads;
+
+ static LLMap<LLUUID, LLLiveLSLEditor*> sInstances;
+};
+
+// name of help file for lsl
+extern const char HELP_LSL[];
+
+#endif // LL_LLPREVIEWSCRIPT_H
diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp
new file mode 100644
index 0000000000..8b0f55aff9
--- /dev/null
+++ b/indra/newview/llpreviewsound.cpp
@@ -0,0 +1,87 @@
+/**
+ * @file llpreviewsound.cpp
+ * @brief LLPreviewSound class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewsound.h"
+#include "llbutton.h"
+#include "llresmgr.h"
+#include "llinventory.h"
+#include "llinventoryview.h"
+#include "audioengine.h"
+#include "llviewermessage.h" // send_guid_sound_trigger
+#include "llagent.h" // gAgent
+#include "llvieweruictrlfactory.h"
+
+extern LLAudioEngine* gAudiop;
+extern LLAgent gAgent;
+
+const F32 SOUND_GAIN = 1.0f;
+
+LLPreviewSound::LLPreviewSound(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const LLUUID& object_uuid) :
+ LLPreview( name, rect, title, item_uuid, object_uuid)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_preview_sound.xml");
+
+ childSetAction("Sound play btn",&LLPreviewSound::playSound,this);
+ childSetAction("Sound audition btn",&LLPreviewSound::auditionSound,this);
+
+ LLButton* button = LLUICtrlFactory::getButtonByName(this, "Sound play btn");
+ button->setSoundFlags(LLView::SILENT);
+
+ button = LLUICtrlFactory::getButtonByName(this, "Sound audition btn");
+ button->setSoundFlags(LLView::SILENT);
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ // preload the sound
+ if(item && gAudiop)
+ {
+ gAudiop->preloadSound(item->getAssetUUID());
+ }
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+}
+
+// static
+void LLPreviewSound::playSound( void *userdata )
+{
+ LLPreviewSound* self = (LLPreviewSound*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item && gAudiop)
+ {
+ send_sound_trigger(item->getAssetUUID(), SOUND_GAIN);
+ }
+}
+
+// static
+void LLPreviewSound::auditionSound( void *userdata )
+{
+ LLPreviewSound* self = (LLPreviewSound*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item && gAudiop)
+ {
+ LLVector3d lpos_global = gAgent.getPositionGlobal();
+
+ gAudiop->triggerSound(item->getAssetUUID(), gAgent.getID(), SOUND_GAIN, lpos_global);
+ }
+}
diff --git a/indra/newview/llpreviewsound.h b/indra/newview/llpreviewsound.h
new file mode 100644
index 0000000000..dc19c37bb1
--- /dev/null
+++ b/indra/newview/llpreviewsound.h
@@ -0,0 +1,26 @@
+/**
+ * @file llpreviewsound.h
+ * @brief LLPreviewSound class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWSOUND_H
+#define LL_LLPREVIEWSOUND_H
+
+#include "llpreview.h"
+
+class LLPreviewSound : public LLPreview
+{
+public:
+ LLPreviewSound(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_uuid,
+ const LLUUID& object_uuid = LLUUID::null);
+
+ static void playSound( void* userdata );
+ static void auditionSound( void* userdata );
+
+};
+
+#endif // LL_LLPREVIEWSOUND_H
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
new file mode 100644
index 0000000000..a50ded3b53
--- /dev/null
+++ b/indra/newview/llpreviewtexture.cpp
@@ -0,0 +1,365 @@
+/**
+ * @file llpreviewtexture.cpp
+ * @brief LLPreviewTexture class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewtexture.h"
+
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llresmgr.h"
+#include "llagent.h"
+#include "llbutton.h"
+#include "llui.h"
+#include "llinventoryview.h"
+#include "llinventory.h"
+#include "llviewerwindow.h"
+#include "lltextbox.h"
+#include "llimagetga.h"
+#include "llfilepicker.h"
+#include "llvieweruictrlfactory.h"
+
+const S32 PREVIEW_TEXTURE_MIN_WIDTH = 300;
+const S32 PREVIEW_TEXTURE_MIN_HEIGHT = 120;
+
+const S32 CLIENT_RECT_VPAD = 4;
+
+const F32 SECONDS_TO_SHOW_FILE_SAVED_MSG = 8.f;
+
+LLPreviewTexture::LLPreviewTexture(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& item_uuid,
+ const LLUUID& object_id,
+ BOOL show_keep_discard)
+: LLPreview(name, rect, title, item_uuid, object_id, TRUE, PREVIEW_TEXTURE_MIN_WIDTH, PREVIEW_TEXTURE_MIN_HEIGHT ),
+ mLoadingFullImage( FALSE ),
+ mShowKeepDiscard(show_keep_discard),
+ mCopyToInv(FALSE),
+ mIsCopyable(FALSE),
+ mLastHeight(0),
+ mLastWidth(0)
+{
+ LLInventoryItem *item = getItem();
+ if(item)
+ {
+ mImageID = item->getAssetUUID();
+ const LLPermissions& perm = item->getPermissions();
+ U32 mask = PERM_NONE;
+ if(perm.getOwner() == gAgent.getID())
+ {
+ mask = perm.getMaskBase();
+ }
+ else if(gAgent.isInGroup(perm.getGroup()))
+ {
+ mask = perm.getMaskGroup();
+ }
+ else
+ {
+ mask = perm.getMaskEveryone();
+ }
+ if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ mIsCopyable = TRUE;
+ }
+ }
+
+ init();
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+}
+
+
+// Note: uses asset_id as a dummy item id.
+LLPreviewTexture::LLPreviewTexture(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& asset_id,
+ BOOL copy_to_inv)
+ :
+ LLPreview(
+ name,
+ rect,
+ title,
+ asset_id,
+ LLUUID::null,
+ TRUE,
+ PREVIEW_TEXTURE_MIN_WIDTH,
+ PREVIEW_TEXTURE_MIN_HEIGHT ),
+ mImageID(asset_id),
+ mLoadingFullImage( FALSE ),
+ mShowKeepDiscard(FALSE),
+ mCopyToInv(copy_to_inv),
+ mIsCopyable(TRUE),
+ mLastHeight(0),
+ mLastWidth(0)
+{
+
+ init();
+
+ setTitle(title);
+
+ LLRect curRect = getRect();
+ translate(curRect.mLeft - rect.mLeft, curRect.mTop - rect.mTop);
+
+}
+
+
+LLPreviewTexture::~LLPreviewTexture()
+{
+ if( mLoadingFullImage )
+ {
+ getWindow()->decBusyCount();
+ }
+
+ mImage = NULL;
+}
+
+
+void LLPreviewTexture::init()
+{
+
+
+ if (mCopyToInv)
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_embedded_texture.xml");
+
+ childSetAction("Copy To Inventory",LLPreview::onBtnCopyToInv,this);
+ }
+
+ else if (mShowKeepDiscard)
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_texture_keep_discard.xml");
+
+ childSetAction("Keep",onKeepBtn,this);
+ childSetAction("Discard",onDiscardBtn,this);
+ }
+
+ else
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_texture.xml");
+ }
+
+
+ if (!mCopyToInv)
+ {
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+ }
+}
+
+
+// virtual
+BOOL LLPreviewTexture::canSaveAs()
+{
+ return mIsCopyable && !mLoadingFullImage && mImage.notNull() && !mImage->isMissingAsset();
+}
+
+
+// virtual
+void LLPreviewTexture::saveAs()
+{
+ if( !mLoadingFullImage )
+ {
+ LLFilePicker& file_picker = LLFilePicker::instance();
+ if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_TGA ) )
+ {
+ // User canceled save.
+ return;
+ }
+ mSaveFileName = file_picker.getFirstFile();
+ mLoadingFullImage = TRUE;
+ getWindow()->incBusyCount();
+ mImage->setLoadedCallback( LLPreviewTexture::onFileLoadedForSave,
+ 0,
+ TRUE,
+ new LLUUID( mItemUUID ) );
+ }
+}
+
+
+// static
+void LLPreviewTexture::onFileLoadedForSave(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata)
+{
+ LLUUID* item_uuid = (LLUUID*) userdata;
+ LLPreviewTexture* self = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(*item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ self = (LLPreviewTexture*) found_it->second;
+ }
+
+ if( final || !success )
+ {
+ delete item_uuid;
+
+ if( self )
+ {
+ self->getWindow()->decBusyCount();
+ self->mLoadingFullImage = FALSE;
+ }
+ }
+
+ if( self && final && success )
+ {
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA;
+ if( !image_tga->encode( src ) )
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = self->mSaveFileName;
+ gViewerWindow->alertXml("CannotEncodeFile", args);
+ }
+ else if( !image_tga->save( self->mSaveFileName ) )
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = self->mSaveFileName;
+ gViewerWindow->alertXml("CannotWriteFile", args);
+ }
+ else
+ {
+ self->mSavedFileTimer.reset();
+ self->mSavedFileTimer.setTimerExpirySec( SECONDS_TO_SHOW_FILE_SAVED_MSG );
+ }
+
+ self->mSaveFileName.clear();
+ }
+
+ if( self && !success )
+ {
+ gViewerWindow->alertXml("CannotDownloadFile");
+ }
+}
+
+
+// It takes a while until we get height and width information.
+// When we receive it, reshape the window accordingly.
+void LLPreviewTexture::updateAspectRatio()
+{
+ if (!mImage) return;
+
+ S32 image_height = llmax(1, mImage->getHeight(0));
+ S32 image_width = llmax(1, mImage->getWidth(0));
+ // Attempt to make the image 1:1 on screen.
+ // If that fails, cut width by half.
+ S32 client_width = image_width;
+ S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE;
+ S32 screen_width = gViewerWindow->getWindowWidth();
+ S32 max_client_width = screen_width - horiz_pad;
+
+ while (client_width > max_client_width)
+ {
+ client_width /= 2;
+ }
+
+ // Demand width at least 128
+ if (client_width < 128)
+ {
+ client_width = 128;
+ }
+
+ S32 view_width = client_width + horiz_pad;
+
+ // Adjust the height based on the width computed above.
+ F32 inv_aspect_ratio = (F32) image_height / (F32) image_width;
+ S32 client_height = llround(client_width * inv_aspect_ratio);
+ S32 view_height =
+ PREVIEW_HEADER_SIZE + // header (includes top border)
+ client_height + 2 * CLIENT_RECT_VPAD + // texture plus uniform spacing (which leaves room for resize handle)
+ LLPANEL_BORDER_WIDTH; // bottom border
+
+ // set text on dimensions display (should be moved out of here and into a callback of some sort)
+ childSetTextArg("dimensions", "[WIDTH]", llformat("%d", mImage->mFullWidth));
+ childSetTextArg("dimensions", "[HEIGHT]", llformat("%d", mImage->mFullHeight));
+
+ // add space for dimensions
+ S32 info_height = 0;
+ LLRect dim_rect;
+ childGetRect("dimensions", dim_rect);
+ S32 dim_height = dim_rect.getHeight();
+ info_height += dim_height + CLIENT_RECT_VPAD;
+ view_height += info_height;
+
+ S32 button_height = 0;
+ if (mShowKeepDiscard || mCopyToInv) { //mCopyToInvBtn
+
+ // add space for buttons
+ view_height += BTN_HEIGHT + CLIENT_RECT_VPAD;
+ button_height = BTN_HEIGHT + PREVIEW_PAD;
+ }
+
+ LLRect window_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ window_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD);
+ window_rect.mBottom += PREVIEW_BORDER + button_height + CLIENT_RECT_VPAD + info_height + CLIENT_RECT_VPAD;
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ // try to keep aspect ratio when hosted, as hosting view can resize without user input
+ mClientRect.setLeftTopAndSize(window_rect.getCenterX() - (client_width / 2), window_rect.mTop, client_width, client_height);
+ }
+ else
+ {
+ mClientRect.setLeftTopAndSize(LLPANEL_BORDER_WIDTH + PREVIEW_PAD + 6,
+ mRect.getHeight() - (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD),
+ mRect.getWidth() - horiz_pad,
+ mRect.getHeight() - (view_height - client_height) - 8);
+ }
+
+ if (mImage->mFullHeight > mLastHeight && mImage->mFullWidth > mLastWidth)
+ {
+ mLastWidth = image_width;
+ mLastHeight = image_height;
+
+ S32 old_top = mRect.mTop;
+ S32 old_left = mRect.mLeft;
+ if (hostp)
+ {
+ hostp->growToFit(this, view_width, view_height);
+ }
+ else
+ {
+ reshape( view_width, view_height );
+ S32 new_bottom = old_top - mRect.getHeight();
+ setOrigin( old_left, new_bottom );
+ }
+
+ // Try to keep whole view onscreen, don't allow partial offscreen.
+ gFloaterView->adjustToFitScreen(this, FALSE);
+ }
+}
+
+void LLPreviewTexture::loadAsset()
+{
+ mImage = gImageList.getImage(mImageID, MIPMAP_FALSE, FALSE);
+ mImage->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+}
+
+LLPreview::EAssetStatus LLPreviewTexture::getAssetStatus()
+{
+ if (mImage.notNull() && (mImage->mFullWidth * mImage->mFullHeight > 0))
+ {
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ return mAssetStatus;
+}
diff --git a/indra/newview/llpreviewtexture.h b/indra/newview/llpreviewtexture.h
new file mode 100644
index 0000000000..765a40c639
--- /dev/null
+++ b/indra/newview/llpreviewtexture.h
@@ -0,0 +1,78 @@
+/**
+ * @file llpreviewtexture.h
+ * @brief LLPreviewTexture class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWTEXTURE_H
+#define LL_LLPREVIEWTEXTURE_H
+
+#include "llpreview.h"
+#include "llbutton.h"
+#include "llframetimer.h"
+#include "llviewerimage.h"
+
+class LLImageRaw;
+
+class LLPreviewTexture : public LLPreview
+{
+public:
+ LLPreviewTexture(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& item_uuid,
+ const LLUUID& object_id,
+ BOOL show_keep_discard = FALSE);
+ LLPreviewTexture(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& asset_id,
+ BOOL copy_to_inv = FALSE);
+ ~LLPreviewTexture();
+
+ virtual void draw();
+
+ virtual BOOL canSaveAs();
+ virtual void saveAs();
+
+ virtual void loadAsset();
+ virtual EAssetStatus getAssetStatus();
+
+ static void saveToFile(void* userdata);
+ static void onFileLoadedForSave(
+ BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata );
+
+
+protected:
+ void init();
+ void updateAspectRatio();
+
+protected:
+ LLUUID mImageID;
+ LLPointer<LLViewerImage> mImage;
+ BOOL mLoadingFullImage;
+ LLString mSaveFileName;
+ LLFrameTimer mSavedFileTimer;
+ BOOL mShowKeepDiscard;
+ BOOL mCopyToInv;
+
+ // This is stored off in a member variable, because the save-as
+ // button and drag and drop functionality need to know.
+ BOOL mIsCopyable;
+
+ S32 mLastHeight;
+ S32 mLastWidth;
+};
+
+
+#endif // LL_LLPREVIEWTEXTURE_H
diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp
new file mode 100644
index 0000000000..2faa9ba351
--- /dev/null
+++ b/indra/newview/llprogressview.cpp
@@ -0,0 +1,326 @@
+/**
+ * @file llprogressview.cpp
+ * @brief LLProgressView class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llprogressview.h"
+
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llgl.h"
+#include "llui.h"
+#include "llfontgl.h"
+#include "llimagegl.h"
+#include "lltimer.h"
+#include "llglheaders.h"
+
+#include "llagent.h"
+#include "llfocusmgr.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+LLProgressView* LLProgressView::sInstance = NULL;
+
+LLPointer<LLImageGL> gStartImageGL = NULL;
+S32 gStartImageWidth = 1;
+S32 gStartImageHeight = 1;
+const F32 FADE_IN_TIME = 1.f;
+
+const LLString ANIMATION_FILENAME = "Login Sequence ";
+const LLString ANIMATION_SUFFIX = ".jpg";
+const F32 TOTAL_LOGIN_TIME = 10.f; // seconds, wild guess at time from GL context to actual world view
+S32 gLastStartAnimationFrame = 0; // human-style indexing, first image = 1
+const S32 ANIMATION_FRAMES = 1; //13;
+
+// XUI:translate
+LLProgressView::LLProgressView(const std::string& name, const LLRect &rect)
+: LLView(name, rect, TRUE)
+{
+ mPercentDone = 0.f;
+ mDrawBackground = TRUE;
+
+ const S32 CANCEL_BTN_WIDTH = 70;
+ const S32 CANCEL_BTN_OFFSET = 16;
+ LLRect r;
+ r.setOriginAndSize(
+ mRect.getWidth() - CANCEL_BTN_OFFSET - CANCEL_BTN_WIDTH, CANCEL_BTN_OFFSET,
+ CANCEL_BTN_WIDTH, BTN_HEIGHT );
+
+ mCancelBtn = new LLButton(
+ "Quit",
+ r,
+ "",
+ LLProgressView::onCancelButtonClicked,
+ NULL );
+ mCancelBtn->setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCancelBtn );
+ mFadeTimer.stop();
+ setVisible(FALSE);
+
+ sInstance = this;
+}
+
+
+LLProgressView::~LLProgressView()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ sInstance = NULL;
+}
+
+EWidgetType LLProgressView::getWidgetType() const
+{
+ return WIDGET_TYPE_PROGRESS_VIEW;
+}
+
+LLString LLProgressView::getWidgetTag() const
+{
+ return LL_PROGRESS_VIEW_TAG;
+}
+
+BOOL LLProgressView::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( childrenHandleHover( x, y, mask ) == NULL )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLProgressView" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_WAIT);
+ }
+ return TRUE;
+}
+
+
+BOOL LLProgressView::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if( getVisible() )
+ {
+ // Suck up all keystokes except CTRL-Q.
+ if( ('Q' == key) && (MASK_CONTROL == mask) )
+ {
+ app_request_quit();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLProgressView::setVisible(BOOL visible)
+{
+ if (getVisible() && !visible)
+ {
+ mFadeTimer.start();
+ }
+ else if (!getVisible() && visible)
+ {
+ gFocusMgr.setTopView(this, NULL);
+ mFadeTimer.stop();
+ mProgressTimer.start();
+ LLView::setVisible(visible);
+ }
+}
+
+
+void LLProgressView::draw()
+{
+ static LLTimer timer;
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ // Make sure the progress view always fills the entire window.
+ S32 width = gViewerWindow->getWindowWidth();
+ S32 height = gViewerWindow->getWindowHeight();
+ if( (width != mRect.getWidth()) || (height != mRect.getHeight()) )
+ {
+ reshape( width, height );
+ }
+
+ // Paint bitmap if we've got one
+ if (mDrawBackground)
+ {
+ glPushMatrix();
+ if (gStartImageGL)
+ {
+ LLGLSUIDefault gls_ui;
+ LLViewerImage::bindTexture(gStartImageGL);
+ glColor4f(1.f, 1.f, 1.f, mFadeTimer.getStarted() ? clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, FADE_IN_TIME, 1.f, 0.f) : 1.f);
+ F32 image_aspect = (F32)gStartImageWidth / (F32)gStartImageHeight;
+ F32 view_aspect = (F32)width / (F32)height;
+ // stretch image to maintain aspect ratio
+ if (image_aspect > view_aspect)
+ {
+ glTranslatef(-0.5f * (image_aspect / view_aspect - 1.f) * width, 0.f, 0.f);
+ glScalef(image_aspect / view_aspect, 1.f, 1.f);
+ }
+ else
+ {
+ glTranslatef(0.f, -0.5f * (view_aspect / image_aspect - 1.f) * height, 0.f);
+ glScalef(1.f, view_aspect / image_aspect, 1.f);
+ }
+ gl_rect_2d_simple_tex( mRect.getWidth(), mRect.getHeight() );
+ gStartImageGL->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4f(0.f, 0.f, 0.f, 1.f);
+ gl_rect_2d(mRect);
+ }
+ glPopMatrix();
+ }
+
+ if (mFadeTimer.getStarted())
+ {
+ LLView::draw();
+ if (mFadeTimer.getElapsedTimeF32() > FADE_IN_TIME)
+ {
+ gFocusMgr.removeTopViewWithoutCallback(this);
+ LLView::setVisible(FALSE);
+ gStartImageGL = NULL;
+ }
+ return;
+ }
+
+ S32 line_x = mRect.getWidth() / 2;
+ S32 line_one_y = mRect.getHeight() / 2 + 64;
+ const S32 LINE_SPACING = 25;
+ S32 line_two_y = line_one_y - LINE_SPACING;
+ const LLFontGL* font = LLFontGL::sSansSerif;
+
+ LLViewerImage* shadow_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square_soft.tga")), MIPMAP_FALSE, TRUE);
+ LLViewerImage* bar_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
+
+ //LLColor4 background_color = gColors.getColor("DefaultShadowLight");
+ LLColor4 background_color = LLColor4(0.3254f, 0.4f, 0.5058f, 1.0f);
+
+ F32 alpha = 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32()));
+ // background_color.mV[3] = background_color.mV[3]*alpha;
+
+ LLString top_line = gSecondLife;
+
+ font->renderUTF8(top_line, 0,
+ line_x, line_one_y,
+ LLColor4::white,
+ LLFontGL::HCENTER, LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+ font->renderUTF8(mText, 0,
+ line_x, line_two_y,
+ LLColor4::white,
+ LLFontGL::HCENTER, LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+
+ S32 bar_bottom = line_two_y - 30;
+ S32 bar_height = 18;
+ S32 bar_width = mRect.getWidth() * 2 / 3;
+ S32 bar_left = (mRect.getWidth() / 2) - (bar_width / 2);
+
+ gl_draw_scaled_image_with_border(
+ bar_left + 2,
+ bar_bottom - 2,
+ 16,
+ 16,
+ bar_width,
+ bar_height,
+ shadow_imagep,
+ gColors.getColor("ColorDropShadow"));
+
+ gl_draw_scaled_image_with_border(
+ bar_left,
+ bar_bottom,
+ 16,
+ 16,
+ bar_width,
+ bar_height,
+ bar_imagep,
+ LLColor4(0.7f, 0.7f, 0.8f, 1.0f));
+
+ gl_draw_scaled_image_with_border(bar_left + 2, bar_bottom + 2, 16, 16,
+ bar_width - 4, bar_height - 4,
+ bar_imagep,
+ background_color);
+
+ LLColor4 bar_color = LLColor4(0.5764f, 0.6627f, 0.8352f, 1.0f);
+ bar_color.mV[3] = alpha;
+ gl_draw_scaled_image_with_border(bar_left + 2, bar_bottom + 2, 16, 16,
+ llround((bar_width - 4) * (mPercentDone / 100.f)), bar_height - 4,
+ bar_imagep,
+ bar_color);
+
+ S32 line_three_y = line_two_y - LINE_SPACING * 3;
+
+ // draw the message if there is one
+ if(!mMessage.empty())
+ {
+ LLWString wmessage = utf8str_to_wstring(mMessage);
+ const F32 MAX_PIXELS = 640.0f;
+ S32 chars_left = wmessage.length();
+ S32 chars_this_time = 0;
+ S32 msgidx = 0;
+ while(chars_left > 0)
+ {
+ chars_this_time = font->maxDrawableChars(wmessage.substr(msgidx).c_str(),
+ MAX_PIXELS,
+ MAX_STRING - 1,
+ TRUE);
+ LLWString wbuffer = wmessage.substr(msgidx, chars_this_time);
+ font->render(wbuffer, 0,
+ (F32)line_x, (F32)line_three_y,
+ LLColor4::white,
+ LLFontGL::HCENTER, LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+ msgidx += chars_this_time;
+ chars_left -= chars_this_time;
+ line_three_y -= LINE_SPACING;
+ }
+ }
+
+ // draw children
+ LLView::draw();
+}
+
+void LLProgressView::setText(const LLString& text)
+{
+ mText = text;
+}
+
+void LLProgressView::setPercent(const F32 percent)
+{
+ mPercentDone = llclamp(percent, 0.f, 100.f);
+}
+
+void LLProgressView::setMessage(const LLString& msg)
+{
+ mMessage = msg;
+}
+
+void LLProgressView::setCancelButtonVisible(BOOL b, const LLString& label)
+{
+ mCancelBtn->setVisible( b );
+ mCancelBtn->setEnabled( b );
+ mCancelBtn->setLabelSelected(label);
+ mCancelBtn->setLabelUnselected(label);
+}
+
+// static
+void LLProgressView::onCancelButtonClicked(void*)
+{
+ if (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE)
+ {
+ app_request_quit();
+ }
+ else
+ {
+ gAgent.teleportCancel();
+ sInstance->mCancelBtn->setEnabled(FALSE);
+ sInstance->setVisible(FALSE);
+ }
+}
diff --git a/indra/newview/llprogressview.h b/indra/newview/llprogressview.h
new file mode 100644
index 0000000000..474a431b09
--- /dev/null
+++ b/indra/newview/llprogressview.h
@@ -0,0 +1,54 @@
+/**
+ * @file llprogressview.h
+ * @brief LLProgressView class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPROGRESSVIEW_H
+#define LL_LLPROGRESSVIEW_H
+
+#include "llview.h"
+#include "llframetimer.h"
+
+class LLImageRaw;
+class LLButton;
+
+class LLProgressView : public LLView
+{
+public:
+ LLProgressView(const std::string& name, const LLRect& rect);
+ virtual ~LLProgressView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ void setVisible(BOOL visible);
+
+ void setText(const LLString& text);
+ void setPercent(const F32 percent);
+
+ // Set it to NULL when you want to eliminate the message.
+ void setMessage(const LLString& msg);
+
+ void setCancelButtonVisible(BOOL b, const LLString& label);
+
+ static void onCancelButtonClicked( void* );
+
+protected:
+ BOOL mDrawBackground;
+ F32 mPercentDone;
+ LLString mText;
+ LLString mMessage;
+ LLButton* mCancelBtn;
+ LLFrameTimer mFadeTimer;
+ LLFrameTimer mProgressTimer;
+
+ static LLProgressView* sInstance;
+};
+
+#endif // LL_LLPROGRESSVIEW_H
diff --git a/indra/newview/llregionposition.cpp b/indra/newview/llregionposition.cpp
new file mode 100644
index 0000000000..e2a2dc7140
--- /dev/null
+++ b/indra/newview/llregionposition.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llregionposition.cpp
+ * @brief Region position storing class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llregionposition.h"
+
+#include "llagent.h"
+#include "llworld.h"
+#include "llviewerregion.h"
+
+LLRegionPosition::LLRegionPosition()
+{
+ mRegionp = NULL;
+}
+
+LLRegionPosition::LLRegionPosition(LLViewerRegion *regionp, const LLVector3 &position)
+{
+ mRegionp = regionp;
+ mPositionRegion = position;
+}
+
+LLRegionPosition::LLRegionPosition(const LLVector3d &global_position)
+{
+ setPositionGlobal(global_position);
+}
+
+LLViewerRegion *LLRegionPosition::getRegion() const
+{
+ return mRegionp;
+}
+
+const LLVector3 &LLRegionPosition::getPositionRegion() const
+{
+ return mPositionRegion;
+}
+
+const LLVector3 LLRegionPosition::getPositionAgent() const
+{
+ return mRegionp->getPosAgentFromRegion( mPositionRegion );
+}
+
+LLVector3d LLRegionPosition::getPositionGlobal() const
+{
+ if (mRegionp)
+ {
+ return mRegionp->getPosGlobalFromRegion(mPositionRegion);
+ }
+ else
+ {
+ LLVector3d pos_global;
+ pos_global.setVec(mPositionRegion);
+ return pos_global;
+ }
+}
+
+
+void LLRegionPosition::setPositionGlobal(const LLVector3d& position_global )
+{
+ mRegionp = gWorldPointer->getRegionFromPosGlobal(position_global);
+ if (mRegionp)
+ {
+ mPositionRegion = mRegionp->getPosRegionFromGlobal(position_global);
+ }
+ else
+ {
+ mRegionp = gAgent.getRegion();
+ llassert(mRegionp);
+ mPositionRegion = mRegionp->getPosRegionFromGlobal(position_global);
+ }
+}
diff --git a/indra/newview/llregionposition.h b/indra/newview/llregionposition.h
new file mode 100644
index 0000000000..a505da2833
--- /dev/null
+++ b/indra/newview/llregionposition.h
@@ -0,0 +1,43 @@
+/**
+ * @file llregionposition.h
+ * @brief Region position storing class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLREGIONPOSITION_H
+#define LL_LLREGIONPOSITION_H
+
+/**
+ * This class maintains a region, offset pair to store position, so when our "global"
+ * coordinate frame shifts, all calculations are still correct.
+ */
+
+#include "v3math.h"
+#include "v3dmath.h"
+
+class LLViewerRegion;
+
+class LLRegionPosition
+{
+private:
+ LLViewerRegion *mRegionp;
+public:
+ LLVector3 mPositionRegion;
+ LLRegionPosition();
+ LLRegionPosition(LLViewerRegion *regionp, const LLVector3 &position_local);
+ LLRegionPosition(const LLVector3d &global_position); // From global coords ONLY!
+
+ LLViewerRegion* getRegion() const;
+ void setPositionGlobal(const LLVector3d& global_pos);
+ LLVector3d getPositionGlobal() const;
+ const LLVector3& getPositionRegion() const;
+ const LLVector3 getPositionAgent() const;
+
+
+ void clear() { mRegionp = NULL; mPositionRegion.clearVec(); }
+// LLRegionPosition operator+(const LLRegionPosition &pos) const;
+};
+
+#endif // LL_REGION_POSITION_H
diff --git a/indra/newview/llsavedsettingsglue.cpp b/indra/newview/llsavedsettingsglue.cpp
new file mode 100644
index 0000000000..e98940f170
--- /dev/null
+++ b/indra/newview/llsavedsettingsglue.cpp
@@ -0,0 +1,51 @@
+/**
+ * @file llsavedsettingsglue.cpp
+ * @author James Cook
+ * @brief LLSavedSettingsGlue class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsavedsettingsglue.h"
+
+#include "lluictrl.h"
+
+#include "llviewercontrol.h"
+
+void LLSavedSettingsGlue::setBOOL(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setBOOL(name, value.asBoolean());
+}
+
+void LLSavedSettingsGlue::setS32(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setS32(name, value.asInteger());
+}
+
+void LLSavedSettingsGlue::setF32(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setF32(name, (F32)value.asReal());
+}
+
+void LLSavedSettingsGlue::setU32(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setU32(name, (U32)value.asInteger());
+}
+
+void LLSavedSettingsGlue::setString(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setString(name, value.asString());
+}
diff --git a/indra/newview/llsavedsettingsglue.h b/indra/newview/llsavedsettingsglue.h
new file mode 100644
index 0000000000..5f5d5ab787
--- /dev/null
+++ b/indra/newview/llsavedsettingsglue.h
@@ -0,0 +1,28 @@
+/**
+ * @file llsavedsettingsglue.h
+ * @author James Cook
+ * @brief LLSavedSettingsGlue class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSAVEDSETTINGSGLUE_H
+#define LL_LLSAVEDSETTINGSGLUE_H
+
+class LLUICtrl;
+
+// Helper to change gSavedSettings from UI widget commit callbacks.
+// Set the widget callback to be one of the setFoo() calls below,
+// and assign the control name as a const char* to the userdata.
+class LLSavedSettingsGlue
+{
+public:
+ static void setBOOL(LLUICtrl* ctrl, void* name);
+ static void setS32(LLUICtrl* ctrl, void* name);
+ static void setF32(LLUICtrl* ctrl, void* name);
+ static void setU32(LLUICtrl* ctrl, void* name);
+ static void setString(LLUICtrl* ctrl, void* name);
+};
+
+#endif
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
new file mode 100644
index 0000000000..8eeddeaa18
--- /dev/null
+++ b/indra/newview/llselectmgr.cpp
@@ -0,0 +1,6732 @@
+/**
+ * @file llselectmgr.cpp
+ * @brief A manager for selected objects and faces.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llselectmgr.h"
+
+// library includes
+#include "llcachename.h"
+#include "lldbstrings.h"
+#include "lleconomy.h"
+#include "llgl.h"
+#include "llpermissions.h"
+#include "llpermissionsflags.h"
+#include "llptrskiplist.h"
+#include "llundo.h"
+#include "lluuid.h"
+#include "llvolume.h"
+#include "message.h"
+#include "object_flags.h"
+#include "llquaternion.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "lldrawable.h"
+#include "llfloaterproperties.h"
+#include "llfloaterrate.h"
+#include "llfloaterreporter.h"
+#include "llfloatertools.h"
+#include "llframetimer.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llinventorymodel.h"
+#include "llmenugl.h"
+#include "llstatusbar.h"
+#include "llsurface.h"
+#include "lltool.h"
+#include "lltooldraganddrop.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvoavatar.h"
+#include "llvovolume.h"
+#include "pipeline.h"
+
+#include "llglheaders.h"
+
+//
+// Consts
+//
+
+const S32 NUM_SELECTION_UNDO_ENTRIES = 200;
+const F32 SILHOUETTE_UPDATE_THRESHOLD_SQUARED = 0.02f;
+const S32 OWNERSHIP_COST_PER_OBJECT = 10; // Must be the same as economy_constants.price_object_claim in the database.
+const S32 MAX_ACTION_QUEUE_SIZE = 20;
+const S32 MAX_SILS_PER_FRAME = 50;
+const S32 MAX_OBJECTS_PER_PACKET = 254;
+
+extern LLGlobalEconomy *gGlobalEconomy;
+extern LLUUID gLastHitObjectID;
+extern LLVector3d gLastHitObjectOffset;
+
+//
+// Globals
+//
+LLSelectMgr* gSelectMgr = NULL;
+
+BOOL gDebugSelectMgr = FALSE;
+
+BOOL gHideSelectedObjects = FALSE;
+BOOL gAllowSelectAvatar = FALSE;
+
+BOOL LLSelectMgr::sRectSelectInclusive = TRUE;
+BOOL LLSelectMgr::sRenderHiddenSelections = TRUE;
+BOOL LLSelectMgr::sRenderLightRadius = FALSE;
+F32 LLSelectMgr::sHighlightThickness = 0.f;
+F32 LLSelectMgr::sHighlightUScale = 0.f;
+F32 LLSelectMgr::sHighlightVScale = 0.f;
+F32 LLSelectMgr::sHighlightAlpha = 0.f;
+F32 LLSelectMgr::sHighlightAlphaTest = 0.f;
+F32 LLSelectMgr::sHighlightUAnim = 0.f;
+F32 LLSelectMgr::sHighlightVAnim = 0.f;
+LLColor4 LLSelectMgr::sSilhouetteParentColor;
+LLColor4 LLSelectMgr::sSilhouetteChildColor;
+LLColor4 LLSelectMgr::sHighlightParentColor;
+LLColor4 LLSelectMgr::sHighlightChildColor;
+LLColor4 LLSelectMgr::sContextSilhouetteColor;
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// struct LLDeRezInfo
+//
+// Used to keep track of important derez info.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+struct LLDeRezInfo
+{
+ EDeRezDestination mDestination;
+ LLUUID mDestinationID;
+ LLDeRezInfo(EDeRezDestination dest, const LLUUID& dest_id) :
+ mDestination(dest), mDestinationID(dest_id) {}
+};
+
+//
+// Imports
+//
+
+
+//
+// Functions
+//
+
+//-----------------------------------------------------------------------------
+// LLSelectMgr()
+//-----------------------------------------------------------------------------
+LLSelectMgr::LLSelectMgr()
+{
+ mTEMode = FALSE;
+ mLastCameraPos.clearVec();
+
+ sHighlightThickness = gSavedSettings.getF32("SelectionHighlightThickness");
+ sHighlightUScale = gSavedSettings.getF32("SelectionHighlightUScale");
+ sHighlightVScale = gSavedSettings.getF32("SelectionHighlightVScale");
+ sHighlightAlpha = gSavedSettings.getF32("SelectionHighlightAlpha");
+ sHighlightAlphaTest = gSavedSettings.getF32("SelectionHighlightAlphaTest");
+ sHighlightUAnim = gSavedSettings.getF32("SelectionHighlightUAnim");
+ sHighlightVAnim = gSavedSettings.getF32("SelectionHighlightVAnim");
+
+ sSilhouetteParentColor = gColors.getColor("SilhouetteParentColor");
+ sSilhouetteChildColor = gColors.getColor("SilhouetteChildColor");
+ sHighlightParentColor = gColors.getColor("HighlightParentColor");
+ sHighlightChildColor = gColors.getColor("HighlightChildColor");
+ sContextSilhouetteColor = gColors.getColor("ContextSilhouetteColor")*0.5f;
+
+ sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius");
+
+ mRenderSilhouettes = TRUE;
+
+ mGridMode = GRID_MODE_WORLD;
+ gSavedSettings.setS32("GridMode", (S32)GRID_MODE_WORLD);
+ mGridValid = FALSE;
+
+ mSelectType = SELECT_TYPE_WORLD;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLSelectMgr()
+//-----------------------------------------------------------------------------
+LLSelectMgr::~LLSelectMgr()
+{
+ mHoverObjects.deleteAllNodes();
+ mSelectedObjects.deleteAllNodes();
+ mHighlightedObjects.deleteAllNodes();
+ mRectSelectedObjects.clear();
+ mGridObjects.deleteAllNodes();
+ mUndoQueue.clear();
+ mRedoQueue.clear();
+}
+
+bool LLSelectMgr::applyToObjects(LLSelectedObjectFunctor* func)
+{
+ bool result = true;
+ LLViewerObject* object;
+ for (object = getFirstObject(); object != NULL; object = getNextObject())
+ {
+ result = result && func->apply(object);
+ }
+ return result;
+}
+
+bool LLSelectMgr::applyToRootObjects(LLSelectedObjectFunctor* func)
+{
+ bool result = true;
+ LLViewerObject* object;
+ for (object = getFirstRootObject();
+ object != NULL;
+ object = getNextRootObject())
+ {
+ result = result && func->apply(object);
+ }
+ return result;
+}
+
+bool LLSelectMgr::applyToNodes(LLSelectedNodeFunctor *func)
+{
+ bool result = true;
+ LLSelectNode* node;
+ for (node = getFirstNode(); node != NULL; node = getNextNode())
+ {
+ result = result && func->apply(node);
+ }
+ return result;
+}
+
+void LLSelectMgr::updateEffects()
+{
+ if (mEffectsTimer.getElapsedTimeF32() > 1.f)
+ {
+ mSelectedObjects.updateEffects();
+ mEffectsTimer.reset();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Select just the object, not any other group members.
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectObjectOnly(LLViewerObject* object, S32 face)
+{
+ llassert( object );
+
+ // Don't add an object that is already in the list
+ if (object->isSelected() ) {
+ // make sure point at position is updated
+ updatePointAt();
+ gEditMenuHandler = this;
+ return;
+ }
+
+ if (!canSelectObject(object))
+ {
+ //make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ // llinfos << "Adding object to selected object list" << llendl;
+
+ // Place it in the list and tag it.
+ // This will refresh dialogs.
+ addAsIndividual(object, face);
+
+ // Stop the object from moving (this anticipates changes on the
+ // simulator in LLTask::userSelect)
+ //FIXME: shouldn't zero out these either
+ object->setVelocity(LLVector3::zero);
+ object->setAcceleration(LLVector3::zero);
+ //object->setAngularVelocity(LLVector3::zero);
+ object->resetRot();
+
+ // Always send to simulator, so you get a copy of the
+ // permissions structure back.
+ gMessageSystem->newMessageFast(_PREHASH_ObjectSelect);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() );
+ LLViewerRegion* regionp = object->getRegion();
+ gMessageSystem->sendReliable( regionp->getHost());
+
+ updatePointAt();
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+
+ // have selection manager handle edit menu immediately after
+ // user selects an object
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Select the object, parents and children.
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, BOOL add_to_end)
+{
+ llassert( obj );
+
+ // This may be incorrect if things weren't family selected before... - djs 07/08/02
+ // Don't add an object that is already in the list
+ if (obj->isSelected() )
+ {
+ // make sure pointat position is updated
+ updatePointAt();
+ gEditMenuHandler = this;
+ return;
+ }
+
+ if (!canSelectObject(obj))
+ {
+ //make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ // Since we're selecting a family, start at the root, but
+ // don't include an avatar.
+ LLViewerObject* root = obj;
+
+ while(!root->isAvatar() && root->getParent() && !root->isJointChild())
+ {
+ LLViewerObject* parent = (LLViewerObject*)root->getParent();
+ if (parent->isAvatar())
+ {
+ break;
+ }
+ root = parent;
+ }
+
+ // Collect all of the objects
+ LLDynamicArray<LLViewerObject*> objects;
+
+ root->addThisAndNonJointChildren(objects);
+ addAsFamily(objects, add_to_end);
+
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updatePointAt();
+
+ dialog_refresh_all();
+
+ // Always send to simulator, so you get a copy of the permissions
+ // structure back.
+ sendSelect();
+
+ // Stop the object from moving (this anticipates changes on the
+ // simulator in LLTask::userSelect)
+ root->setVelocity(LLVector3::zero);
+ root->setAcceleration(LLVector3::zero);
+ //root->setAngularVelocity(LLVector3::zero);
+ root->resetRot();
+
+ // leave component mode
+ if (!gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ gSavedSettings.setBOOL("SelectLinkedSet", TRUE);
+ promoteSelectionToRoot();
+ }
+
+ // have selection manager handle edit menu immediately after
+ // user selects an object
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Select the object, parents and children.
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectObjectAndFamily(const LLDynamicArray<LLViewerObject*>& object_list,
+ BOOL send_to_sim)
+{
+ // Collect all of the objects, children included
+ LLDynamicArray<LLViewerObject*> objects;
+ LLViewerObject *object;
+ S32 i;
+
+ if (object_list.count() < 1) return;
+
+ // NOTE -- we add the objects in REVERSE ORDER
+ // to preserve the order in the mSelectedObjects list
+ for (i = object_list.count() - 1; i >= 0; i--)
+ {
+ object = object_list.get(i);
+
+ llassert( object );
+
+ if (!canSelectObject(object)) continue;
+
+ object->addThisAndNonJointChildren(objects);
+ addAsFamily(objects);
+
+ // Stop the object from moving (this anticipates changes on the
+ // simulator in LLTask::userSelect)
+ object->setVelocity(LLVector3::zero);
+ object->setAcceleration(LLVector3::zero);
+ //object->setAngularVelocity(LLVector3::zero);
+ object->resetRot();
+ }
+
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updatePointAt();
+ dialog_refresh_all();
+
+ // Almost always send to simulator, so you get a copy of the permissions
+ // structure back.
+ // JC: The one case where you don't want to do this is if you're selecting
+ // all the objects on a sim.
+ if (send_to_sim)
+ {
+ sendSelect();
+ }
+
+ // leave component mode
+ if (!gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ gSavedSettings.setBOOL("SelectLinkedSet", TRUE);
+ promoteSelectionToRoot();
+ }
+
+ // have selection manager handle edit menu immediately after
+ // user selects an object
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+// Use for when the simulator kills an object. This version also
+// handles informing the current tool of the object's deletion.
+//
+// Caller needs to call dialog_refresh_all if necessary.
+BOOL LLSelectMgr::selectionRemoveObject(const LLUUID &id)
+{
+
+
+ BOOL object_found = FALSE;
+ LLTool *tool = NULL;
+ if (!gNoRender)
+ {
+ tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+
+ // It's possible that the tool is editing an object that is not selected
+ LLViewerObject* tool_editing_object = tool->getEditingObject();
+ if( tool_editing_object && tool_editing_object->mID == id)
+ {
+ tool->stopEditing();
+ object_found = TRUE;
+ }
+ }
+
+ // Iterate through selected objects list and kill the object
+ if( !object_found )
+ {
+ LLViewerObject* prevobjp = NULL;
+ for( LLViewerObject* tobjp = getFirstObject(); tobjp != NULL; tobjp = getNextObject() )
+ {
+ if (tobjp == prevobjp)
+ {
+ // Somehow we got stuck in an infinite loop... (DaveP)
+ // this logic is kind of twisted, not sure how this is happening, so...
+ llwarns << "Detected infinite loop #1 in LLSelectMgr::selectionRemoveObject:|" << llendl;
+ //MikeS. adding warning and comment...
+ //These infinite loops happen because the LLSelectMgr iteration routines are non-reentrant.
+ //deselectObjectAndFamily uses getFirstObject and getNextObject to mess with the array,
+ //resetting the arrays internal iterator state. This needs fixing BAD.
+ continue;
+ }
+ // It's possible the item being removed has an avatar sitting on it
+ // So remove the avatar that is sitting on the object.
+ if (tobjp->mID == id || tobjp->isAvatar())
+ {
+ if (!gNoRender)
+ {
+ tool->stopEditing();
+ }
+
+ // lose the selection, don't tell simulator, it knows
+ deselectObjectAndFamily(tobjp, FALSE);
+
+ if (tobjp->mID == id)
+ {
+ if(object_found == TRUE){
+ //MikeS. adding warning... This happens when removing a linked attachment while sitting on an object..
+ //I think the selection manager needs to be rewritten. BAD.
+ llwarns << "Detected infinite loop #2 in LLSelectMgr::selectionRemoveObject:|" << llendl;
+ break;
+ }
+ object_found = TRUE;
+ }
+ }
+ prevobjp = tobjp;
+ }
+ }
+
+ return object_found;
+}
+
+void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim)
+{
+ // bail if nothing selected or if object wasn't selected in the first place
+ if(!object) return;
+ if(!object->isSelected()) return;
+
+ // Collect all of the objects, and remove them
+ LLDynamicArray<LLViewerObject*> objects;
+ object = (LLViewerObject*)object->getRoot();
+ object->addThisAndAllChildren(objects);
+ remove(objects);
+
+ if (!send_to_sim) return;
+
+ //-----------------------------------------------------------
+ // Inform simulator of deselection
+ //-----------------------------------------------------------
+ LLViewerRegion* regionp = object->getRegion();
+
+ BOOL start_new_message = TRUE;
+ S32 select_count = 0;
+
+ LLMessageSystem* msg = gMessageSystem;
+ for (S32 i = 0; i < objects.count(); i++)
+ {
+ if (start_new_message)
+ {
+ msg->newMessageFast(_PREHASH_ObjectDeselect);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ select_count++;
+ start_new_message = FALSE;
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, (objects[i])->getLocalID());
+ select_count++;
+
+ if(msg->mCurrentSendTotal >= MTUBYTES || select_count >= MAX_OBJECTS_PER_PACKET)
+ {
+ msg->sendReliable(regionp->getHost() );
+ select_count = 0;
+ start_new_message = TRUE;
+ }
+ }
+
+ if (!start_new_message)
+ {
+ msg->sendReliable(regionp->getHost() );
+ }
+
+ updatePointAt();
+ updateSelectionCenter();
+}
+
+void LLSelectMgr::deselectObjectOnly(LLViewerObject* object, BOOL send_to_sim)
+{
+ // bail if nothing selected or if object wasn't selected in the first place
+ if (!object) return;
+ if (!object->isSelected() ) return;
+
+ if (send_to_sim)
+ {
+ LLViewerRegion* region = object->getRegion();
+ gMessageSystem->newMessageFast(_PREHASH_ObjectDeselect);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() );
+ gMessageSystem->sendReliable(region->getHost());
+ }
+
+ // This will refresh dialogs.
+ remove( object );
+
+ updatePointAt();
+ updateSelectionCenter();
+}
+
+
+//-----------------------------------------------------------------------------
+// addAsFamily
+//-----------------------------------------------------------------------------
+
+void LLSelectMgr::addAsFamily(LLDynamicArray<LLViewerObject*>& objects, BOOL add_to_end)
+{
+ S32 count = objects.count();
+ LLViewerObject *objectp = NULL;
+
+ LLSelectNode *nodep = NULL;
+ for (S32 i = 0; i < count; i++)
+ {
+ objectp = objects.get(i);
+
+ // Can't select yourself
+ if (objectp->mID == gAgentID
+ && !gAllowSelectAvatar)
+ {
+ continue;
+ }
+
+ if (!objectp->isSelected())
+ {
+ nodep = new LLSelectNode(objectp, TRUE);
+ if (add_to_end)
+ {
+ mSelectedObjects.addNodeAtEnd(nodep);
+ }
+ else
+ {
+ mSelectedObjects.addNode(nodep);
+ }
+ objectp->setSelected(TRUE);
+
+ if (objectp->getNumTEs() > 0)
+ {
+ nodep->selectAllTEs(TRUE);
+ }
+ else
+ {
+ // object has no faces, so don't mess with faces
+ }
+ }
+ else
+ {
+ // we want this object to be selected for real
+ // so clear transient flag
+ LLSelectNode* select_node = findSelectNode(objectp);
+ if (select_node)
+ {
+ select_node->setTransient(FALSE);
+ }
+ }
+ }
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+}
+
+//-----------------------------------------------------------------------------
+// addAsIndividual() - a single object, face, etc
+//-----------------------------------------------------------------------------
+void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoable)
+{
+ // check to see if object is already in list
+ LLSelectNode *nodep = findSelectNode(objectp);
+
+ // if not in list, add it
+ if (!nodep)
+ {
+ nodep = new LLSelectNode(objectp, TRUE);
+ mSelectedObjects.addNode(nodep);
+ }
+ else
+ {
+ // make this a full-fledged selection
+ nodep->setTransient(FALSE);
+ // Move it to the front of the list
+ mSelectedObjects.removeNode(nodep);
+ mSelectedObjects.addNode(nodep);
+ }
+
+ // Make sure the object is tagged as selected
+ objectp->setSelected( TRUE );
+
+ // And make sure we don't consider it as part of a family
+ nodep->mIndividualSelection = TRUE;
+
+ // Handle face selection
+ if (objectp->getNumTEs() <= 0)
+ {
+ // object has no faces, so don't do anything
+ }
+ else if (face == SELECT_ALL_TES)
+ {
+ nodep->selectAllTEs(TRUE);
+ }
+ else if (0 <= face && face < SELECT_MAX_TES)
+ {
+ nodep->selectTE(face, TRUE);
+ }
+ else
+ {
+ llerrs << "LLSelectMgr::add face " << face << " out-of-range" << llendl;
+ return;
+ }
+
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+
+void LLSelectMgr::setHoverObject(LLViewerObject *objectp)
+{
+ // Always blitz hover list when setting
+ mHoverObjects.deleteAllNodes();
+
+ if (!objectp)
+ {
+ return;
+ }
+
+ // Can't select yourself
+ if (objectp->mID == gAgentID)
+ {
+ return;
+ }
+
+ // Can't select land
+ if (objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+ {
+ return;
+ }
+
+ // Collect all of the objects
+ LLDynamicArray<LLViewerObject*> objects;
+ objectp = objectp->getRootEdit();
+ objectp->addThisAndNonJointChildren(objects);
+
+
+ S32 count = objects.count();
+ LLViewerObject* cur_objectp = NULL;
+ LLSelectNode* nodep = NULL;
+ for(S32 i = 0; i < count; i++)
+ {
+ cur_objectp = objects[i];
+ nodep = new LLSelectNode(cur_objectp, FALSE);
+ mHoverObjects.addNodeAtEnd(nodep);
+ }
+
+ requestObjectPropertiesFamily(objectp);
+}
+
+LLSelectNode *LLSelectMgr::getHoverNode()
+{
+ return getHoverObjects().getFirstRootNode();
+}
+
+void LLSelectMgr::highlightObjectOnly(LLViewerObject* objectp)
+{
+ if (!objectp)
+ {
+ return;
+ }
+
+ if (objectp->getPCode() != LL_PCODE_VOLUME)
+ {
+ return;
+ }
+
+ if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !objectp->permYouOwner()) ||
+ (gSavedSettings.getBOOL("SelectMovableOnly") && !objectp->permMove()))
+ {
+ // only select my own objects
+ return;
+ }
+
+ mRectSelectedObjects.insert(objectp);
+}
+
+void LLSelectMgr::highlightObjectAndFamily(LLViewerObject* objectp)
+{
+ if (!objectp)
+ {
+ return;
+ }
+
+ LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot();
+
+ highlightObjectOnly(root_obj);
+
+ for(U32 i = 0; i < root_obj->mChildList.size(); i++)
+ {
+ highlightObjectOnly(root_obj->mChildList[i]);
+ }
+}
+
+// Note that this ignores the "select owned only" flag
+// It's also more efficient than calling the single-object version over and
+// over.
+void LLSelectMgr::highlightObjectAndFamily(const LLDynamicArray<LLViewerObject*>& list)
+{
+ S32 i;
+ S32 count = list.count();
+
+ for (i = 0; i < count; i++)
+ {
+ LLViewerObject* object = list.get(i);
+
+ if (!object) continue;
+ if (object->getPCode() != LL_PCODE_VOLUME) continue;
+
+ LLViewerObject* root = (LLViewerObject*)object->getRoot();
+ mRectSelectedObjects.insert(root);
+
+ S32 j;
+ S32 child_count = root->mChildList.size();
+ for (j = 0; j < child_count; j++)
+ {
+ LLViewerObject* child = root->mChildList[j];
+ mRectSelectedObjects.insert(child);
+ }
+ }
+}
+
+void LLSelectMgr::unhighlightObjectOnly(LLViewerObject* objectp)
+{
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+
+ mRectSelectedObjects.erase(objectp);
+}
+
+void LLSelectMgr::unhighlightObjectAndFamily(LLViewerObject* objectp)
+{
+ if (!objectp)
+ {
+ return;
+ }
+
+ LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot();
+
+ unhighlightObjectOnly(root_obj);
+
+ for(U32 i = 0; i < root_obj->mChildList.size(); i++)
+ {
+ unhighlightObjectOnly(root_obj->mChildList[i]);
+ }
+}
+
+
+void LLSelectMgr::unhighlightAll()
+{
+ mRectSelectedObjects.clear();
+ mHighlightedObjects.deleteAllNodes();
+}
+
+void LLSelectMgr::selectHighlightedObjects()
+{
+ if (!mHighlightedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ LLSelectNode *nodep;
+ for (nodep = mHighlightedObjects.getFirstNode();
+ nodep;
+ nodep = mHighlightedObjects.getNextNode())
+ {
+ LLViewerObject* objectp = nodep->getObject();
+
+ if (!canSelectObject(objectp))
+ {
+ continue;
+ }
+
+ // already selected
+ if (objectp->isSelected())
+ {
+ continue;
+ }
+
+ LLSelectNode* new_nodep = new LLSelectNode(*nodep);
+ mSelectedObjects.addNode(new_nodep);
+
+ // flag this object as selected
+ objectp->setSelected(TRUE);
+
+ mSelectType = getSelectTypeForObject(objectp);
+
+ // request properties on root objects
+ if (objectp->isRootEdit())
+ {
+ requestObjectPropertiesFamily(objectp);
+ }
+ }
+
+ // pack up messages to let sim know these objects are selected
+ sendSelect();
+ unhighlightAll();
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updatePointAt();
+
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+void LLSelectMgr::deselectHighlightedObjects()
+{
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mRectSelectedObjects.begin();
+ iter != mRectSelectedObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (!select_linked_set)
+ {
+ deselectObjectOnly(objectp);
+ }
+ else
+ {
+ LLViewerObject* root_object = (LLViewerObject*)objectp->getRoot();
+ if (root_object->isSelected())
+ {
+ deselectObjectAndFamily(root_object);
+ }
+ }
+ }
+
+ unhighlightAll();
+}
+
+void LLSelectMgr::addGridObject(LLViewerObject* objectp)
+{
+ LLSelectNode* nodep = new LLSelectNode(objectp, FALSE);
+ mGridObjects.addNodeAtEnd(nodep);
+ for (U32 i = 0; i < objectp->mChildList.size(); i++)
+ {
+ nodep = new LLSelectNode(objectp->mChildList[i], FALSE);
+ mGridObjects.addNodeAtEnd(nodep);
+ }
+}
+
+void LLSelectMgr::clearGridObjects()
+{
+ mGridObjects.deleteAllNodes();
+}
+
+void LLSelectMgr::setGridMode(EGridMode mode)
+{
+ mGridMode = mode;
+ gSavedSettings.setS32("GridMode", mode);
+ updateSelectionCenter();
+ mGridValid = FALSE;
+}
+
+void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &scale)
+{
+ LLSelectNode* grid_node = mGridObjects.getFirstNode();
+ LLViewerObject* grid_object = mGridObjects.getFirstObject();
+ //FIXME: get to work with multiple grid objects
+ if (grid_node && grid_node->getObject()->isDead())
+ {
+ mGridObjects.removeNode(grid_node);
+ grid_object = NULL;
+ }
+
+ if (mGridMode == GRID_MODE_LOCAL && gSelectMgr->getObjectCount())
+ {
+ LLBBox bbox = mSavedSelectionBBox;
+ mGridOrigin = mSavedSelectionBBox.getCenterAgent();
+ mGridRotation = mSavedSelectionBBox.getRotation();
+ mGridScale = mSavedSelectionBBox.getExtentLocal() * 0.5f;
+ }
+ else if (mGridMode == GRID_MODE_REF_OBJECT && grid_object && grid_object->mDrawable.notNull())
+ {
+ mGridRotation = grid_object->getRenderRotation();
+ LLVector3 first_grid_obj_pos = grid_object->getRenderPosition();
+
+ LLVector3 min_extents(F32_MAX, F32_MAX, F32_MAX);
+ LLVector3 max_extents(F32_MIN, F32_MIN, F32_MIN);
+ BOOL grid_changed = FALSE;
+ LLSelectNode* grid_nodep;
+ for (grid_nodep = mGridObjects.getFirstNode();
+ grid_nodep;
+ grid_nodep = mGridObjects.getNextNode())
+ {
+ grid_object = grid_nodep->getObject();
+
+ LLVector3 local_min_extents(F32_MAX, F32_MAX, F32_MAX);
+ LLVector3 local_max_extents(F32_MIN, F32_MIN, F32_MIN);
+
+ //FIXME: silhouette flag is insufficient as it gets cleared by view update
+ if (!mGridValid ||
+ grid_object->isChanged(LLXform::SILHOUETTE)
+ || (grid_object->getParent() && grid_object->getParent()->isChanged(LLXform::SILHOUETTE)))
+ {
+ getSilhouetteExtents(grid_nodep, mGridRotation, local_min_extents, local_max_extents);
+ grid_changed = TRUE;
+ LLVector3 object_offset = (grid_object->getRenderPosition() - first_grid_obj_pos) * ~mGridRotation;
+ local_min_extents += object_offset;
+ local_max_extents += object_offset;
+ }
+ min_extents.mV[VX] = llmin(min_extents.mV[VX], local_min_extents.mV[VX]);
+ min_extents.mV[VY] = llmin(min_extents.mV[VY], local_min_extents.mV[VY]);
+ min_extents.mV[VZ] = llmin(min_extents.mV[VZ], local_min_extents.mV[VZ]);
+ max_extents.mV[VX] = llmax(max_extents.mV[VX], local_max_extents.mV[VX]);
+ max_extents.mV[VY] = llmax(max_extents.mV[VY], local_max_extents.mV[VY]);
+ max_extents.mV[VZ] = llmax(max_extents.mV[VZ], local_max_extents.mV[VZ]);
+ }
+ if (grid_changed)
+ {
+ mGridOrigin = lerp(min_extents, max_extents, 0.5f);
+ mGridOrigin = mGridOrigin * ~mGridRotation;
+ mGridOrigin += first_grid_obj_pos;
+ mGridScale = (max_extents - min_extents) * 0.5f;
+ }
+ }
+ else // GRID_MODE_WORLD or just plain default
+ {
+ LLViewerObject* first_object = gSelectMgr->getFirstRootObject();
+ if (!first_object)
+ {
+ first_object = gSelectMgr->getFirstObject();
+ }
+
+ mGridOrigin.clearVec();
+ mGridRotation.loadIdentity();
+
+ mSelectType = getSelectTypeForObject( first_object );
+
+ switch (mSelectType)
+ {
+ case SELECT_TYPE_ATTACHMENT:
+ if (first_object)
+ {
+ // this means this object *has* to be an attachment
+ LLXform* attachment_point_xform = first_object->getRootEdit()->mDrawable->mXform.getParent();
+ mGridOrigin = attachment_point_xform->getWorldPosition();
+ mGridRotation = attachment_point_xform->getWorldRotation();
+ mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution");
+ }
+ break;
+ case SELECT_TYPE_HUD:
+ // use HUD-scaled grid
+ mGridScale = LLVector3(0.25f, 0.25f, 0.25f);
+ break;
+ case SELECT_TYPE_WORLD:
+ mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution");
+ break;
+ }
+ }
+ llassert(mGridOrigin.isFinite());
+
+ origin = mGridOrigin;
+ rotation = mGridRotation;
+ scale = mGridScale;
+ mGridValid = TRUE;
+}
+
+
+
+LLSelectNode* LLSelectMgr::findSelectNode(LLViewerObject *object)
+{
+ return mSelectedObjects.findNode(object);
+}
+
+//-----------------------------------------------------------------------------
+// contains()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::contains(LLViewerObject* object)
+{
+ return mSelectedObjects.findNode(object) != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// contains()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::contains(LLViewerObject* object, S32 te)
+{
+ LLSelectNode *nodep;
+ if (te == SELECT_ALL_TES)
+ {
+ // ...all faces
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->getObject() == object)
+ {
+ BOOL all_selected = TRUE;
+ for (S32 i = 0; i < SELECT_MAX_TES; i++)
+ {
+ all_selected = all_selected && nodep->isTESelected(i);
+ }
+ return all_selected;
+ }
+ }
+ return FALSE;
+ }
+ else
+ {
+ // ...one face
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->getObject() == object && nodep->isTESelected(te))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// remove() - an array of objects
+//-----------------------------------------------------------------------------
+
+void LLSelectMgr::remove(LLDynamicArray<LLViewerObject*>& objects)
+{
+ S32 count = objects.count();
+ LLViewerObject *objectp = NULL;
+ LLSelectNode *nodep = NULL;
+ for(S32 i = 0; i < count; i++)
+ {
+ objectp = objects.get(i);
+ for(nodep = mSelectedObjects.getFirstNode();
+ nodep != NULL;
+ nodep = mSelectedObjects.getNextNode())
+ {
+ if(nodep->getObject() == objectp)
+ {
+ objectp->setSelected(FALSE);
+ mSelectedObjects.removeNode(nodep);
+ break;
+ }
+ }
+ }
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+
+//-----------------------------------------------------------------------------
+// remove() - a single object
+//-----------------------------------------------------------------------------
+void LLSelectMgr::remove(LLViewerObject *objectp, S32 te, BOOL undoable)
+{
+ // check if object already in list
+ // FIXME: can we just check isSelected()?
+ LLSelectNode *nodep = findSelectNode(objectp);
+
+ if (!nodep)
+ {
+ return;
+ }
+
+
+ // if face = all, remove object from list
+ if (objectp->getNumTEs() <= 0)
+ {
+ // object doesn't have faces, so blow it away
+ mSelectedObjects.removeNode(nodep);
+ objectp->setSelected( FALSE );
+ }
+ else if (te == SELECT_ALL_TES)
+ {
+ mSelectedObjects.removeNode(nodep);
+ objectp->setSelected( FALSE );
+ }
+ else if (0 <= te && te < SELECT_MAX_TES)
+ {
+ // ...valid face, check to see if it was on
+ if (nodep->isTESelected(te))
+ {
+ nodep->selectTE(te, FALSE);
+ }
+ else
+ {
+ llerrs << "LLSelectMgr::remove - tried to remove TE " << te << " that wasn't selected" << llendl;
+ return;
+ }
+
+ // ...check to see if this operation turned off all faces
+ BOOL found = FALSE;
+ for (S32 i = 0; i < nodep->getObject()->getNumTEs(); i++)
+ {
+ found = found || nodep->isTESelected(i);
+ }
+
+ // ...all faces now turned off, so remove
+ if (!found)
+ {
+ mSelectedObjects.removeNode(nodep);
+ objectp->setSelected( FALSE );
+
+ // BUG: Doesn't update simulator that object is gone.
+ }
+ }
+ else
+ {
+ // ...out of range face
+ llerrs << "LLSelectMgr::remove - TE " << te << " out of range" << llendl;
+ }
+
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+
+//-----------------------------------------------------------------------------
+// removeAll()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::removeAll()
+{
+ LLViewerObject *objectp;
+ for (objectp = mSelectedObjects.getFirstObject(); objectp; objectp = mSelectedObjects.getNextObject() )
+ {
+ objectp->setSelected( FALSE );
+ }
+
+ mSelectedObjects.deleteAllNodes();
+
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+//-----------------------------------------------------------------------------
+// promoteSelectionToRoot()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::promoteSelectionToRoot()
+{
+ std::set<LLViewerObject*> selection_set;
+
+ BOOL selection_changed = FALSE;
+
+ LLSelectNode* nodep;
+ LLViewerObject *objectp;
+ for (nodep = mSelectedObjects.getFirstNode();
+ nodep;
+ nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->mIndividualSelection)
+ {
+ selection_changed = TRUE;
+ }
+
+ objectp = nodep->getObject();
+ LLViewerObject* parentp = objectp;
+ while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild()))
+ {
+ parentp = (LLViewerObject*)parentp->getParent();
+ }
+
+ selection_set.insert(parentp);
+ }
+
+ if (selection_changed)
+ {
+ deselectAll();
+
+ std::set<LLViewerObject*>::iterator set_iter;
+ for (set_iter = selection_set.begin(); set_iter != selection_set.end(); ++set_iter)
+ {
+ selectObjectAndFamily(*set_iter);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// demoteSelectionToIndividuals()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::demoteSelectionToIndividuals()
+{
+ LLDynamicArray<LLViewerObject*> objects;
+
+ for (LLViewerObject* root_objectp = mSelectedObjects.getFirstRootObject();
+ root_objectp;
+ root_objectp = mSelectedObjects.getNextRootObject())
+ {
+ root_objectp->addThisAndNonJointChildren(objects);
+ }
+
+ if (objects.getLength())
+ {
+ deselectAll();
+ for(S32 i = 0; i < objects.count(); i++)
+ {
+ selectObjectOnly(objects[i]);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getObjectCount()
+//-----------------------------------------------------------------------------
+S32 LLSelectMgr::getObjectCount()
+{
+ return mSelectedObjects.getNumNodes();
+}
+
+
+//-----------------------------------------------------------------------------
+// getTECount()
+//-----------------------------------------------------------------------------
+S32 LLSelectMgr::getTECount()
+{
+ S32 count = 0;
+
+ LLSelectNode* nodep;
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->getObject())
+ {
+ S32 num_tes = nodep->getObject()->getNumTEs();
+ for (S32 te = 0; te < num_tes; te++)
+ {
+ if (nodep->isTESelected(te))
+ {
+ count++;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+//-----------------------------------------------------------------------------
+// getRootObjectCount()
+//-----------------------------------------------------------------------------
+S32 LLSelectMgr::getRootObjectCount()
+{
+ LLSelectNode *nodep;
+
+ S32 count = 0;
+ for(nodep = mSelectedObjects.getFirstRootNode(); nodep; nodep = mSelectedObjects.getNextRootNode())
+ {
+ ++count;
+ }
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// dump()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::dump()
+{
+ llinfos << "Selection Manager: " << mSelectedObjects.getNumNodes() << " items" << llendl;
+
+ llinfos << "TE mode " << mTEMode << llendl;
+
+ S32 i = 0;
+
+ LLViewerObject *objectp;
+ for (objectp = mSelectedObjects.getFirstObject();
+ objectp;
+ objectp = mSelectedObjects.getNextObject())
+ {
+ llinfos << "Object " << i << " type " << LLPrimitive::pCodeToString(objectp->getPCode()) << llendl;
+ llinfos << " hasLSL " << objectp->flagScripted() << llendl;
+ llinfos << " hasTouch " << objectp->flagHandleTouch() << llendl;
+ llinfos << " hasMoney " << objectp->flagTakesMoney() << llendl;
+ llinfos << " getposition " << objectp->getPosition() << llendl;
+ llinfos << " getpositionAgent " << objectp->getPositionAgent() << llendl;
+ llinfos << " getpositionRegion " << objectp->getPositionRegion() << llendl;
+ llinfos << " getpositionGlobal " << objectp->getPositionGlobal() << llendl;
+ LLDrawable* drawablep = objectp->mDrawable;
+ llinfos << " " << (drawablep&& drawablep->isVisible() ? "visible" : "invisible") << llendl;
+ llinfos << " " << (drawablep&& drawablep->isState(LLDrawable::FORCE_INVISIBLE) ? "force_invisible" : "") << llendl;
+ i++;
+ }
+
+ // Face iterator
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&objectp, &te);
+ objectp;
+ mSelectedObjects.getNextTE(&objectp, &te))
+ {
+ llinfos << "Object " << objectp << " te " << te << llendl;
+ }
+
+ llinfos << mHighlightedObjects.getNumNodes() << " objects currently highlighted." << llendl;
+
+ llinfos << "Center global " << mSelectionCenterGlobal << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// cleanup()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::cleanup()
+{
+ mSilhouetteImagep = NULL;
+}
+
+
+//---------------------------------------------------------------------------
+// Manipulate properties of selected objects
+//---------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// selectionSetImage()
+//-----------------------------------------------------------------------------
+//FIXME: re-arch texture applying out of lltooldraganddrop
+void LLSelectMgr::selectionSetImage(const LLUUID& imageid)
+{
+ // First for (no copy) textures and multiple object selection
+ LLViewerInventoryItem* item = gInventory.getItem(imageid);
+
+ if(item
+ && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())
+ && (mSelectedObjects.getNumNodes() > 1) )
+ {
+ llwarns << "Attempted to apply no-copy texture to multiple objects"
+ << llendl;
+ return;
+ }
+
+ LLViewerObject* objectp;
+ S32 te;
+
+ // Apply the texture to each side
+ for (mSelectedObjects.getFirstTE(&objectp, &te); objectp; mSelectedObjects.getNextTE(&objectp, &te))
+ {
+
+ if (item)
+ {
+ LLToolDragAndDrop::dropTextureOneFace(objectp,te,item,LLToolDragAndDrop::SOURCE_AGENT,LLUUID::null);
+
+ // HACK! HACK! ARG!
+ // FIXME: Replace mSelectedObjects with a REAL container class!
+ LLViewerObject* tmp_object;
+ S32 tmp_te;
+ mSelectedObjects.getCurrentTE(&tmp_object,&tmp_te);
+ if ((tmp_object != objectp) || (tmp_te != te) )
+ {
+ //AAARG someone has moved our list around!
+ mSelectedObjects.getFirstTE(&tmp_object, &tmp_te);
+ while ((tmp_object != objectp) || (tmp_te != te))
+ {
+ mSelectedObjects.getNextTE(&tmp_object, &tmp_te);
+ }
+ }
+ }
+ else
+ {
+ // Texture picker defaults aren't inventory items
+ // * Don't need to worry about permissions for them
+ // * Can just apply the texture and be done with it.
+ objectp->setTEImage(te, gImageList.getImage(imageid));
+ objectp->sendTEUpdate();
+ }
+ }
+
+ // 1 particle effect per object
+ if (mSelectType != SELECT_TYPE_HUD)
+ {
+ for (objectp = mSelectedObjects.getFirstObject(); objectp; objectp = mSelectedObjects.getNextObject())
+ {
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(objectp);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// selectionSetColor()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetColor(const LLColor4 &color)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, color);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// selectionSetColorOnly()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetColorOnly(const LLColor4 &color)
+{
+ LLViewerObject* object;
+ LLColor4 new_color = color;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLColor4 prev_color = object->getTE(te)->getColor();
+ new_color.mV[VALPHA] = prev_color.mV[VALPHA];
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, new_color);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// selectionSetAlphaOnly()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetAlphaOnly(const F32 alpha)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLColor4 prev_color = object->getTE(te)->getColor();
+ prev_color.mV[VALPHA] = alpha;
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, prev_color);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+void LLSelectMgr::selectionRevertColors()
+{
+ LLViewerObject* object;
+ S32 te;
+
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLSelectNode* nodep = mSelectedObjects.findNode(object);
+ if (nodep && te < (S32)nodep->mSavedColors.size())
+ {
+ LLColor4 color = nodep->mSavedColors[te];
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, color);
+ }
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+BOOL LLSelectMgr::selectionRevertTextures()
+{
+ LLViewerObject* object;
+ S32 te;
+
+ BOOL revert_successful = TRUE;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLSelectNode* nodep = mSelectedObjects.findNode(object);
+ if (nodep && te < (S32)nodep->mSavedTextures.size())
+ {
+ LLUUID id = nodep->mSavedTextures[te];
+ // update textures on viewer side
+ if (id.isNull())
+ {
+ // this was probably a no-copy texture, leave image as-is
+ revert_successful = FALSE;
+ }
+ else
+ {
+ object->setTEImage(te, gImageList.getImage(id));
+ }
+ }
+ }
+ }
+
+ // propagate texture changes to server
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+
+ return revert_successful;
+}
+
+void LLSelectMgr::selectionSetBumpmap(U8 bumpmap)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEBumpmap(te, bumpmap);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+void LLSelectMgr::selectionSetTexGen(U8 texgen)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTETexGen(te, texgen);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+void LLSelectMgr::selectionSetShiny(U8 shiny)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEShiny(te, shiny);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+void LLSelectMgr::selectionSetFullbright(U8 fullbright)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEFullbright(te, fullbright);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ if (fullbright)
+ {
+ U8 material = object->getMaterial();
+ U8 mcode = material & LL_MCODE_MASK;
+ if (mcode == LL_MCODE_LIGHT)
+ {
+ mcode = LL_MCODE_GLASS;
+ material = (material & ~LL_MCODE_MASK) | mcode;
+ object->setMaterial(material);
+ object->sendMaterialUpdate();
+ }
+ }
+ }
+ }
+}
+
+void LLSelectMgr::selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url)
+{
+ LLViewerObject* object;
+ S32 te;
+ U8 media_flags = LLTextureEntry::MF_NONE;
+ if (media_type == LLViewerObject::MEDIA_TYPE_WEB_PAGE)
+ {
+ media_flags = LLTextureEntry::MF_WEB_PAGE;
+ }
+
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEMediaFlags(te, media_flags);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ // JAMESDEBUG TODO set object media type
+ object->setMediaType(media_type);
+ object->setMediaURL(media_url);
+
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// findObjectPermissions()
+//-----------------------------------------------------------------------------
+LLPermissions* LLSelectMgr::findObjectPermissions(const LLViewerObject* object)
+{
+ LLSelectNode* nodep;
+
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if((nodep->getObject() == object) && nodep->mValid)
+ {
+ return nodep->mPermissions;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionGetTexUUID()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetTexUUID(LLUUID& id)
+{
+ LLViewerObject* first_objectp;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_objectp, &first_te);
+
+ // nothing selected
+ if (!first_objectp)
+ {
+ return FALSE;
+ }
+
+ LLViewerImage* first_imagep = first_objectp->getTEImage(first_te);
+
+ if (!first_imagep)
+ {
+ return FALSE;
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject *objectp;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&objectp, &te); objectp; mSelectedObjects.getNextTE(&objectp, &te) )
+ {
+ if (objectp->getTEImage(te) != first_imagep)
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ id = first_imagep->getID();
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetColor()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetColor(LLColor4 &color)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ LLColor4 first_color;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_color = first_object->getTE(first_te)->getColor();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getColor() != first_color))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ color = first_color;
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionGetBumpmap()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetBumpmap(U8 *bumpmap)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getBumpmap();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getBumpmap() != first_value))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *bumpmap = first_value;
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetShiny()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetShiny(U8 *shiny)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getShiny();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getShiny() != first_value))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *shiny = first_value;
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetFullbright()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetFullbright(U8 *fullbright)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getFullbright();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getFullbright() != first_value))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *fullbright = first_value;
+ return identical;
+}
+
+// JAMESDEBUG TODO make this return mediatype off viewer object
+bool LLSelectMgr::selectionGetMediaType(U8 *media_type)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return false;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return false;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getMediaFlags();
+ }
+
+ bool identical = true;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getMediaFlags() != first_value))
+ {
+ identical = false;
+ break;
+ }
+ }
+
+ *media_type = first_value;
+ return identical;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// selectionSetMaterial()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetMaterial(U8 material)
+{
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstObject(); object != NULL; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ U8 cur_material = object->getMaterial();
+ material |= (cur_material & ~LL_MCODE_MASK);
+ object->setMaterial(material);
+ object->sendMaterialUpdate();
+ }
+ }
+}
+
+// True if all selected objects have this PCode
+BOOL LLSelectMgr::selectionAllPCode(LLPCode code)
+{
+ LLViewerObject *object;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->getPCode() != code)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetMaterial()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetMaterial(U8 *out_material)
+{
+ LLViewerObject *object = mSelectedObjects.getFirstObject();
+ if (!object) return FALSE;
+
+ U8 material = object->getMaterial();
+
+ BOOL identical = TRUE;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if ( material != object->getMaterial())
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *out_material = material;
+ return identical;
+}
+
+BOOL LLSelectMgr::selectionGetClickAction(U8 *out_action)
+{
+ LLViewerObject *object = mSelectedObjects.getFirstObject();
+ if (!object) return FALSE;
+
+ U8 action = object->getClickAction();
+
+ BOOL identical = TRUE;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if ( action != object->getClickAction())
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *out_action = action;
+ return identical;
+}
+
+void LLSelectMgr::selectionSetClickAction(U8 action)
+{
+ LLViewerObject* object = NULL;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ object->setClickAction(action);
+ }
+ sendListToRegions(
+ "ObjectClickAction",
+ packAgentAndSessionID,
+ packObjectClickAction,
+ &action,
+ SEND_INDIVIDUALS);
+}
+
+
+//-----------------------------------------------------------------------------
+// godlike requests
+//-----------------------------------------------------------------------------
+
+typedef std::pair<const LLString, const LLString> godlike_request_t;
+void LLSelectMgr::sendGodlikeRequest(const LLString& request, const LLString& param)
+{
+ // If the agent is neither godlike nor an estate owner, the server
+ // will reject the request.
+ LLString message_type;
+ if (gAgent.isGodlike())
+ {
+ message_type = "GodlikeMessage";
+ }
+ else
+ {
+ message_type = "EstateOwnerMessage";
+ }
+
+ godlike_request_t data(request, param);
+ if(!getRootObjectCount())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage(message_type.c_str());
+ LLSelectMgr::packGodlikeHead(&data);
+ gAgent.sendReliableMessage();
+ }
+ else
+ {
+ sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS);
+ }
+}
+
+void LLSelectMgr::packGodlikeHead(void* user_data)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ godlike_request_t* data = (godlike_request_t*)user_data;
+ msg->nextBlock("MethodData");
+ msg->addString("Method", data->first.c_str());
+ msg->addUUID("Invoice", LLUUID::null);
+
+ // The parameters used to be restricted to either string or
+ // integer. This mimics that behavior under the new 'string-only'
+ // parameter list by not packing a string if there wasn't one
+ // specified. The object ids will be packed in the
+ // packObjectIDAsParam() method.
+ if(data->second.size() > 0)
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", data->second);
+ }
+}
+
+// static
+void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *)
+{
+ char buf [MAX_STRING];
+ sprintf(buf, "%u", node->getObject()->getLocalID());
+ gMessageSystem->nextBlock("ParamList");
+ gMessageSystem->addString("Parameter", buf);
+}
+
+//-----------------------------------------------------------------------------
+// Rotation options
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionResetRotation()
+{
+ LLQuaternion identity(0.f, 0.f, 0.f, 1.f);
+
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstRootObject(); object; object = mSelectedObjects.getNextRootObject() )
+ {
+ object->setRotation(identity);
+ if (object->mDrawable.notNull())
+ {
+ gPipeline.markMoved(object->mDrawable, TRUE);
+ }
+ object->sendRotationUpdate();
+ }
+}
+
+void LLSelectMgr::selectionRotateAroundZ(F32 degrees)
+{
+ LLQuaternion rot( degrees * DEG_TO_RAD, LLVector3(0,0,1) );
+
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstRootObject(); object; object = mSelectedObjects.getNextRootObject() )
+ {
+ object->setRotation( object->getRotationEdit() * rot );
+ if (object->mDrawable.notNull())
+ {
+ gPipeline.markMoved(object->mDrawable, TRUE);
+ }
+ object->sendRotationUpdate();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionTexScaleAutofit()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionTexScaleAutofit(F32 repeats_per_meter)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->permModify())
+ {
+ continue;
+ }
+
+ if (object->getNumTEs() == 0)
+ {
+ continue;
+ }
+
+ // Compute S,T to axis mapping
+ U32 s_axis, t_axis;
+ if (!getTESTAxes(object, te, &s_axis, &t_axis))
+ {
+ continue;
+ }
+
+ F32 new_s = object->getScale().mV[s_axis] * repeats_per_meter;
+ F32 new_t = object->getScale().mV[t_axis] * repeats_per_meter;
+
+ object->setTEScale(te, new_s, new_t);
+ }
+
+ for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject())
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+// BUG: Only works for boxes.
+// Face numbering for flex boxes as of 1.14.2002
+//-----------------------------------------------------------------------------
+// getFaceSTAxes()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::getTESTAxes(const LLViewerObject* object, const U8 face, U32* s_axis, U32* t_axis)
+{
+ if (face == 0)
+ {
+ *s_axis = VX; *t_axis = VY;
+ return TRUE;
+ }
+ else if (face == 1)
+ {
+ *s_axis = VX; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 2)
+ {
+ *s_axis = VY; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 3)
+ {
+ *s_axis = VX; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 4)
+ {
+ *s_axis = VY; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 5)
+ {
+ *s_axis = VX; *t_axis = VY;
+ return TRUE;
+ }
+ else
+ {
+ // unknown face
+ return FALSE;
+ }
+}
+
+// Called at the end of a scale operation, this adjusts the textures to attempt to
+// maintain a constant repeats per meter.
+// BUG: Only works for flex boxes.
+//-----------------------------------------------------------------------------
+// adjustTexturesByScale()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::adjustTexturesByScale(BOOL send_to_sim, BOOL stretch)
+{
+ LLViewerObject* object;
+ LLSelectNode* selectNode;
+
+ BOOL send = FALSE;
+
+ for (selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode())
+ {
+ object = selectNode->getObject();
+ if (!object->permModify())
+ {
+ continue;
+ }
+
+ if (object->getNumTEs() == 0)
+ {
+ continue;
+ }
+
+ for (U8 te_num = 0; te_num < object->getNumTEs(); te_num++)
+ {
+ const LLTextureEntry* tep = object->getTE(te_num);
+
+ BOOL planar = tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR;
+ if (planar == stretch)
+ {
+ // Figure out how S,T changed with scale operation
+ U32 s_axis, t_axis;
+ if (!getTESTAxes(object, te_num, &s_axis, &t_axis)) continue;
+
+ LLVector3 scale_ratio = selectNode->mTextureScaleRatios[te_num];
+ LLVector3 object_scale = object->getScale();
+
+ // Apply new scale to face
+ if (planar)
+ {
+ object->setTEScale(te_num, 1.f/object_scale.mV[s_axis]*scale_ratio.mV[s_axis],
+ 1.f/object_scale.mV[t_axis]*scale_ratio.mV[t_axis]);
+ }
+ else
+ {
+ object->setTEScale(te_num, scale_ratio.mV[s_axis]*object_scale.mV[s_axis],
+ scale_ratio.mV[t_axis]*object_scale.mV[t_axis]);
+ }
+ send = send_to_sim;
+ }
+ }
+
+ if (send)
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionResetTexInfo()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionResetTexInfo(S32 selected_face)
+{
+ S32 start_face, end_face;
+
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject())
+ {
+ if (!object->permModify())
+ {
+ continue;
+ }
+ if (object->getNumTEs() == 0)
+ {
+ continue;
+ }
+
+ if (selected_face == -1)
+ {
+ start_face = 0;
+ end_face = object->getNumTEs() - 1;
+ }
+ else
+ {
+ start_face = selected_face;
+ end_face = selected_face;
+ }
+
+ for (S32 face = start_face; face <= end_face; face++)
+ {
+ // Actually, each object should reset to its appropriate value.
+ object->setTEScale(face, 1.f, 1.f);
+ object->setTEOffset(face, 0.f, 0.f);
+ object->setTERotation(face, 0.f);
+ }
+
+ object->sendTEUpdate();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getFirstEditableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstEditableObject(BOOL get_root)
+{
+ LLViewerObject* object = NULL;
+ for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject())
+ {
+ if( cur->permModify() )
+ {
+ object = cur;
+ break;
+ }
+ }
+
+ if (get_root && object)
+ {
+ LLViewerObject *parent;
+ while ((parent = (LLViewerObject*)object->getParent()))
+ {
+ if (parent->isSelected())
+ {
+ object = parent;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstMoveableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstMoveableObject(BOOL get_root)
+{
+ LLViewerObject* object = NULL;
+ for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject())
+ {
+ if( cur->permMove() )
+ {
+ object = cur;
+ break;
+ }
+ }
+
+ if (get_root && object && !object->isJointChild())
+ {
+ LLViewerObject *parent;
+ while ((parent = (LLViewerObject*)object->getParent()))
+ {
+ if (parent->isSelected())
+ {
+ object = parent;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstEditableNode()
+//-----------------------------------------------------------------------------
+LLSelectNode* LLSelectMgr::getFirstEditableNode(BOOL get_root)
+{
+ LLSelectNode* selectNode = NULL;
+
+ if (get_root)
+ {
+ for(selectNode = mSelectedObjects.getFirstRootNode(); selectNode; selectNode = mSelectedObjects.getNextRootNode())
+ {
+ if( selectNode->getObject()->permModify() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+ }
+ for(selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode())
+ {
+ if( selectNode->getObject()->permModify() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstMoveableNode()
+//-----------------------------------------------------------------------------
+LLSelectNode* LLSelectMgr::getFirstMoveableNode(BOOL get_root)
+{
+ LLSelectNode* selectNode = NULL;
+
+ if (get_root)
+ {
+ for(selectNode = mSelectedObjects.getFirstRootNode(); selectNode; selectNode = mSelectedObjects.getNextRootNode())
+ {
+ if( selectNode->getObject()->permMove() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+ }
+ for(selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode())
+ {
+ if( selectNode->getObject()->permMove() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstDeleteableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstDeleteableObject(BOOL get_root)
+{
+ //RN: don't currently support deletion of child objects, as that requires separating them first
+ // then derezzing to trash
+ get_root = TRUE;
+
+ LLViewerObject* object = NULL;
+ if (get_root)
+ {
+ for(LLViewerObject* current = getFirstRootObject();
+ current != NULL;
+ current = getNextRootObject())
+ {
+ // you can delete an object if permissions allow it, you are
+ // the owner, you are an officer in the group that owns the
+ // object, or you are not the owner but it is on land you own
+ // or land owned by your group. (whew!)
+ if( (current->permModify())
+ || (current->permYouOwner())
+ || (!current->permAnyOwner()) // public
+ || (current->isOverAgentOwnedLand())
+ || (current->isOverGroupOwnedLand())
+ )
+ {
+
+ if( !current->isAttachment() )
+ {
+ object = current;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for(LLViewerObject* current = getFirstObject();
+ current != NULL;
+ current = getNextObject())
+ {
+ // you can delete an object if permissions allow it, you are
+ // the owner, you are an officer in the group that owns the
+ // object, or you are not the owner but it is on land you own
+ // or land owned by your group. (whew!)
+ if( (current->permModify())
+ || (current->permYouOwner())
+ || (!current->permAnyOwner()) // public
+ || (current->isOverAgentOwnedLand())
+ || (current->isOverGroupOwnedLand())
+ )
+ {
+ if( !current->isAttachment() )
+ {
+ object = current;
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstCopyableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstCopyableObject(BOOL get_root)
+{
+ LLViewerObject* object = NULL;
+ for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject())
+ {
+ if( cur->permCopy() && !cur->isAttachment())
+ {
+ object = cur;
+ break;
+ }
+ }
+
+ if (get_root && object)
+ {
+ LLViewerObject *parent;
+ while ((parent = (LLViewerObject*)object->getParent()))
+ {
+ if (parent->isSelected())
+ {
+ object = parent;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// areMultpleEditableObjectsSelected()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::areMultpleEditableObjectsSelected()
+{
+ S32 count = 0;
+ for( LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject() )
+ {
+ if( cur->permModify() )
+ {
+ count++;
+ if( count > 1 )
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetAllRootsValid()
+// Returns true if the viewer has information on all selected objects
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetAllRootsValid()
+{
+ for( LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+
+ if( !node->getObject() )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetAllValid()
+// Returns true if the viewer has information on all selected objects
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetAllValid()
+{
+ for( LLSelectNode* node = getFirstNode(); node; node = getNextNode() )
+ {
+
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+
+ if( !node->getObject() )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetModify() - return true if current agent can modify all
+// selected objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetModify()
+{
+ for( LLSelectNode* node = getFirstNode(); node; node = getNextNode() )
+ {
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if( !object || !object->permModify() )
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetRootsModify() - return true if current agent can modify all
+// selected root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetRootsModify()
+{
+ for( LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if( !object || !object->permModify() )
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetRootsTransfer() - return true if current agent can transfer all
+// selected root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetRootsTransfer()
+{
+ for(LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if(!object || !object->permTransfer())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetRootsCopy() - return true if current agent can copy all
+// selected root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetRootsCopy()
+{
+ for(LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if(!object || !object->permCopy())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetCreator()
+// Creator information only applies to root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetCreator(LLUUID& id, LLString& name)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ id = node->mPermissions->getCreator();
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ if ( !(id == node->mPermissions->getCreator() ) )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ if (identical)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(id, firstname, lastname);
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+ }
+ else
+ {
+ name.assign( "(multiple)" );
+ }
+
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetOwner()
+// Owner information only applies to roots.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetOwner(LLUUID& id, LLString& name)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ BOOL group_owner = FALSE;
+ id.setNull();
+ node->mPermissions->getOwnership(id, group_owner);
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ LLUUID owner_id;
+ BOOL is_group_owned = FALSE;
+ if (!(node->mPermissions->getOwnership(owner_id, is_group_owned))
+ || owner_id != id )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ BOOL public_owner = (id.isNull() && !group_owner);
+
+ if (identical)
+ {
+ if (group_owner)
+ {
+ name.assign( "(Group Owned)");
+ }
+ else if(!public_owner)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(id, firstname, lastname);
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+ }
+ else
+ {
+ name.assign("Public");
+ }
+ }
+ else
+ {
+ name.assign( "(multiple)" );
+ }
+
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetLastOwner()
+// Owner information only applies to roots.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetLastOwner(LLUUID& id, LLString& name)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ id = node->mPermissions->getLastOwner();
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ if ( !(id == node->mPermissions->getLastOwner() ) )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ BOOL public_owner = (id.isNull());
+
+ if (identical)
+ {
+ if(!public_owner)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(id, firstname, lastname);
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+ }
+ else
+ {
+ name.assign("Public or Group");
+ }
+ }
+ else
+ {
+ name.assign( "" );
+ }
+
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetGroup()
+// Group information only applies to roots.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetGroup(LLUUID& id)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ id = node->mPermissions->getGroup();
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ if ( !(id == node->mPermissions->getGroup() ) )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectIsGroupOwned()
+// Only operates on root nodes.
+// Returns TRUE if all have valid data and they are all group owned.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectIsGroupOwned()
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ BOOL is_group_owned = node->mPermissions->isGroupOwned();
+
+ if(is_group_owned)
+ {
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ is_group_owned = FALSE;
+ break;
+ }
+
+ if ( !( node->mPermissions->isGroupOwned() ) )
+ {
+ is_group_owned = FALSE;
+ break;
+ }
+ }
+ }
+ return is_group_owned;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetPerm()
+// Only operates on root nodes.
+// Returns TRUE if all have valid data.
+// mask_on has bits set to true where all permissions are true
+// mask_off has bits set to true where all permissions are false
+// if a bit is off both in mask_on and mask_off, the values differ within
+// the selection.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetPerm(U8 which_perm, U32* mask_on, U32* mask_off)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+
+ U32 mask;
+ U32 mask_and = 0xffffffff;
+ U32 mask_or = 0x00000000;
+ BOOL all_valid = TRUE;
+
+ for ( node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ all_valid = FALSE;
+ break;
+ }
+
+ switch( which_perm )
+ {
+ case PERM_BASE:
+ mask = node->mPermissions->getMaskBase();
+ break;
+ case PERM_OWNER:
+ mask = node->mPermissions->getMaskOwner();
+ break;
+ case PERM_GROUP:
+ mask = node->mPermissions->getMaskGroup();
+ break;
+ case PERM_EVERYONE:
+ mask = node->mPermissions->getMaskEveryone();
+ break;
+ case PERM_NEXT_OWNER:
+ mask = node->mPermissions->getMaskNextOwner();
+ break;
+ default:
+ mask = 0x0;
+ break;
+ }
+ mask_and &= mask;
+ mask_or |= mask;
+ }
+
+ if (all_valid)
+ {
+ // ...true through all ANDs means all true
+ *mask_on = mask_and;
+
+ // ...false through all ORs means all false
+ *mask_off = ~mask_or;
+ return TRUE;
+ }
+ else
+ {
+ *mask_on = 0;
+ *mask_off = 0;
+ return FALSE;
+ }
+}
+
+
+
+BOOL LLSelectMgr::selectGetOwnershipCost(S32* out_cost)
+{
+ return mSelectedObjects.getOwnershipCost(*out_cost);
+}
+
+BOOL LLSelectMgr::selectGetPermissions(LLPermissions& perm)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ perm = *(node->mPermissions);
+ for(node = getNextRootNode(); node != NULL; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ perm.accumulate(*(node->mPermissions));
+ }
+ return valid;
+}
+
+
+void LLSelectMgr::selectDelete()
+{
+ S32 deleteable_count = 0;
+
+ BOOL locked_but_deleteable_object = FALSE;
+ BOOL no_copy_but_deleteable_object = FALSE;
+ BOOL all_owned_by_you = TRUE;
+ for(LLViewerObject* obj = getFirstObject();
+ obj != NULL;
+ obj = getNextObject())
+ {
+ if( obj->isAttachment() )
+ {
+ continue;
+ }
+
+ deleteable_count++;
+
+ // Check to see if you can delete objects which are locked.
+ if(!obj->permMove())
+ {
+ locked_but_deleteable_object = TRUE;
+ }
+ if(!obj->permCopy())
+ {
+ no_copy_but_deleteable_object = TRUE;
+ }
+ if(!obj->permYouOwner())
+ {
+ all_owned_by_you = FALSE;
+ }
+ }
+
+ if( 0 == deleteable_count )
+ {
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ if(locked_but_deleteable_object ||
+ no_copy_but_deleteable_object ||
+ !all_owned_by_you)
+ {
+ // convert any transient pie-menu selections to full selection so this operation
+ // has some context
+ // NOTE: if user cancels delete operation, this will potentially leave objects selected outside of build mode
+ // but this is ok, if not ideal
+ convertTransient();
+
+ //This is messy, but needed to get all english our of the UI.
+ if(locked_but_deleteable_object && !no_copy_but_deleteable_object && all_owned_by_you)
+ {
+ //Locked only
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLock",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you)
+ {
+ //No Copy only
+ gViewerWindow->alertXml( "ConfirmObjectDeleteNoCopy",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(!locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you)
+ {
+ //not owned only
+ gViewerWindow->alertXml( "ConfirmObjectDeleteNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you)
+ {
+ //locked and no copy
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoCopy",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you)
+ {
+ //locked and not owned
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && !all_owned_by_you)
+ {
+ //no copy and not owned
+ gViewerWindow->alertXml( "ConfirmObjectDeleteNoCopyNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else
+ {
+ //locked, no copy and not owned
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoCopyNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+
+
+
+ }
+ else
+ {
+ confirmDelete(0, (void*)this);
+ }
+}
+
+// static
+void LLSelectMgr::confirmDelete(S32 option, void* data)
+{
+ LLSelectMgr* self = (LLSelectMgr*)data;
+ if(!self) return;
+ switch(option)
+ {
+ case 0:
+ {
+ // TODO: Make sure you have delete permissions on all of them.
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ // attempt to derez into the trash.
+ LLDeRezInfo* info = new LLDeRezInfo(DRD_TRASH, trash_id);
+ self->sendListToRegions("DeRezObject",
+ packDeRezHeader,
+ packObjectLocalID,
+ (void*)info,
+ SEND_ONLY_ROOTS);
+ // VEFFECT: Delete Object - one effect for all deletes
+ if (self->mSelectType != SELECT_TYPE_HUD)
+ {
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal( self->getSelectionCenterGlobal() );
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ F32 duration = 0.5f;
+ duration += self->getObjectCount() / 64.f;
+ effectp->setDuration(duration);
+ }
+
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+
+ // Keep track of how many objects have been deleted.
+ F64 obj_delete_count = gViewerStats->getStat(LLViewerStats::ST_OBJECT_DELETE_COUNT);
+ obj_delete_count += self->getObjectCount();
+ gViewerStats->setStat(LLViewerStats::ST_OBJECT_DELETE_COUNT, obj_delete_count );
+ }
+ break;
+ case 1:
+ default:
+ break;
+ }
+}
+
+
+void LLSelectMgr::selectForceDelete()
+{
+ sendListToRegions(
+ "ObjectDelete",
+ packDeleteHeader,
+ packObjectLocalID,
+ (void*)TRUE,
+ SEND_ONLY_ROOTS);
+}
+
+
+// returns TRUE if anything is for sale. calculates the total price
+// and stores that value in price.
+BOOL LLSelectMgr::selectIsForSale(S32& price)
+{
+ BOOL any_for_sale = FALSE;
+ price = 0;
+
+ LLSelectNode *node;
+ for (node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+ if (node->mSaleInfo.isForSale())
+ {
+ price += node->mSaleInfo.getSalePrice();
+ any_for_sale = TRUE;
+ }
+ }
+
+ return any_for_sale;
+
+}
+
+// returns TRUE if all nodes are valid. method also stores an
+// accumulated sale info.
+BOOL LLSelectMgr::selectGetSaleInfo(LLSaleInfo& sale_info)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ sale_info = node->mSaleInfo;
+ for(node = getNextRootNode(); node != NULL; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ sale_info.accumulate(node->mSaleInfo);
+ }
+ return valid;
+}
+
+BOOL LLSelectMgr::selectGetAggregatePermissions(LLAggregatePermissions& ag_perm)
+{
+ LLSelectNode* node = getFirstNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ ag_perm = node->mAggregatePerm;
+ for(node = getNextNode(); node != NULL; node = getNextNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ ag_perm.aggregate(node->mAggregatePerm);
+ }
+ return valid;
+}
+
+BOOL LLSelectMgr::selectGetAggregateTexturePermissions(LLAggregatePermissions& ag_perm)
+{
+ LLSelectNode* node = getFirstNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ ag_perm = node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm;
+ for(node = getNextNode(); node != NULL; node = getNextNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ ag_perm.aggregate(node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm);
+ }
+ return valid;
+}
+
+
+// returns TRUE is any node is currenly worn as an attachment
+BOOL LLSelectMgr::selectionIsAttachment()
+{
+ return (mSelectType == SELECT_TYPE_ATTACHMENT || mSelectType == SELECT_TYPE_HUD);
+}
+
+//--------------------------------------------------------------------
+// Duplicate objects
+//--------------------------------------------------------------------
+
+// JC - If this doesn't work right, duplicate the selection list
+// before doing anything, do a deselect, then send the duplicate
+// messages.
+struct LLDuplicateData
+{
+ LLVector3 offset;
+ U32 flags;
+};
+
+void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy)
+{
+ if (selectionIsAttachment())
+ {
+ //RN: do not duplicate attachments
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+ LLDuplicateData data;
+
+ data.offset = offset;
+ data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0);
+
+ sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS);
+
+ if (select_copy)
+ {
+ // the new copy will be coming in selected
+ deselectAll();
+ }
+ else
+ {
+ for (LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ node->mDuplicated = TRUE;
+ node->mDuplicatePos = node->getObject()->getPositionGlobal();
+ node->mDuplicateRot = node->getObject()->getRotation();
+ }
+ }
+}
+
+void LLSelectMgr::repeatDuplicate()
+{
+ if (selectionIsAttachment())
+ {
+ //RN: do not duplicate attachments
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ LLSelectNode* node;
+ LLDynamicArray<LLViewerObject*> non_duplicated_objects;
+
+ for (node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if (!node->mDuplicated)
+ {
+ non_duplicated_objects.put(node->getObject());
+ }
+ }
+
+ // make sure only previously duplicated objects are selected
+ for (S32 i = 0; i < non_duplicated_objects.count(); i++)
+ {
+ deselectObjectAndFamily(non_duplicated_objects[i]);
+ }
+
+ // duplicate objects in place
+ LLDuplicateData data;
+
+ data.offset = LLVector3::zero;
+ data.flags = 0x0;
+
+ sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS);
+
+ // move current selection based on delta from duplication position and update duplication position
+ for (node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if (node->mDuplicated)
+ {
+ LLQuaternion cur_rot = node->getObject()->getRotation();
+ LLQuaternion rot_delta = (~node->mDuplicateRot * cur_rot);
+ LLQuaternion new_rot = cur_rot * rot_delta;
+ LLVector3d cur_pos = node->getObject()->getPositionGlobal();
+ LLVector3d new_pos = cur_pos + ((cur_pos - node->mDuplicatePos) * rot_delta);
+
+ node->mDuplicatePos = node->getObject()->getPositionGlobal();
+ node->mDuplicateRot = node->getObject()->getRotation();
+ node->getObject()->setPositionGlobal(new_pos);
+ node->getObject()->setRotation(new_rot);
+ }
+ }
+
+ sendMultipleUpdate(UPD_ROTATION | UPD_POSITION);
+}
+
+// static
+void LLSelectMgr::packDuplicate( LLSelectNode* node, void *duplicate_data )
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID());
+}
+
+
+//--------------------------------------------------------------------
+// Duplicate On Ray
+//--------------------------------------------------------------------
+
+// Duplicates the selected objects, but places the copy along a cast
+// ray.
+struct LLDuplicateOnRayData
+{
+ LLVector3 mRayStartRegion;
+ LLVector3 mRayEndRegion;
+ BOOL mBypassRaycast;
+ BOOL mRayEndIsIntersection;
+ LLUUID mRayTargetID;
+ BOOL mCopyCenters;
+ BOOL mCopyRotates;
+ U32 mFlags;
+};
+
+void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region,
+ const LLVector3 &ray_end_region,
+ BOOL bypass_raycast,
+ BOOL ray_end_is_intersection,
+ const LLUUID &ray_target_id,
+ BOOL copy_centers,
+ BOOL copy_rotates,
+ BOOL select_copy)
+{
+ if (selectionIsAttachment())
+ {
+ // do not duplicate attachments
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ LLDuplicateOnRayData data;
+
+ data.mRayStartRegion = ray_start_region;
+ data.mRayEndRegion = ray_end_region;
+ data.mBypassRaycast = bypass_raycast;
+ data.mRayEndIsIntersection = ray_end_is_intersection;
+ data.mRayTargetID = ray_target_id;
+ data.mCopyCenters = copy_centers;
+ data.mCopyRotates = copy_rotates;
+ data.mFlags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0);
+
+ sendListToRegions("ObjectDuplicateOnRay",
+ packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS);
+
+ if (select_copy)
+ {
+ // the new copy will be coming in selected
+ deselectAll();
+ }
+}
+
+// static
+void LLSelectMgr::packDuplicateOnRayHead(void *user_data)
+{
+ LLMessageSystem *msg = gMessageSystem;
+ LLDuplicateOnRayData *data = (LLDuplicateOnRayData *)user_data;
+
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID() );
+ msg->addVector3Fast(_PREHASH_RayStart, data->mRayStartRegion );
+ msg->addVector3Fast(_PREHASH_RayEnd, data->mRayEndRegion );
+ msg->addBOOLFast(_PREHASH_BypassRaycast, data->mBypassRaycast );
+ msg->addBOOLFast(_PREHASH_RayEndIsIntersection, data->mRayEndIsIntersection );
+ msg->addBOOLFast(_PREHASH_CopyCenters, data->mCopyCenters );
+ msg->addBOOLFast(_PREHASH_CopyRotates, data->mCopyRotates );
+ msg->addUUIDFast(_PREHASH_RayTargetID, data->mRayTargetID );
+ msg->addU32Fast(_PREHASH_DuplicateFlags, data->mFlags );
+}
+
+
+
+//------------------------------------------------------------------------
+// Object position, scale, rotation update, all-in-one
+//------------------------------------------------------------------------
+
+void LLSelectMgr::sendMultipleUpdate(U32 type)
+{
+ if (type == UPD_NONE) return;
+ // send individual updates when selecting textures or individual objects
+ ESendType send_type = (gSavedSettings.getBOOL("SelectLinkedSet") && !getTEMode()) ? SEND_ONLY_ROOTS : SEND_ROOTS_FIRST;
+ if (send_type == SEND_ONLY_ROOTS)
+ {
+ // tell simulator to apply to whole linked sets
+ type |= UPD_LINKED_SETS;
+ }
+
+ sendListToRegions(
+ "MultipleObjectUpdate",
+ packAgentAndSessionID,
+ packMultipleUpdate,
+ &type,
+ send_type);
+}
+
+// static
+void LLSelectMgr::packMultipleUpdate(LLSelectNode* node, void *user_data)
+{
+ LLViewerObject* object = node->getObject();
+ U32 *type32 = (U32 *)user_data;
+ U8 type = (U8)*type32;
+ U8 data[256];
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() );
+ gMessageSystem->addU8Fast(_PREHASH_Type, type );
+
+ S32 offset = 0;
+
+ // JC: You MUST pack the data in this order. The receiving
+ // routine process_multiple_update_message on simulator will
+ // extract them in this order.
+
+ if (type & UPD_POSITION)
+ {
+ htonmemcpy(&data[offset], &(object->getPosition().mV), MVT_LLVector3, 12);
+ offset += 12;
+ }
+ if (type & UPD_ROTATION)
+ {
+ LLQuaternion quat = object->getRotation();
+ LLVector3 vec = quat.packToVector3();
+ htonmemcpy(&data[offset], &(vec.mV), MVT_LLQuaternion, 12);
+ offset += 12;
+ }
+ if (type & UPD_SCALE)
+ {
+ //llinfos << "Sending object scale " << object->getScale() << llendl;
+ htonmemcpy(&data[offset], &(object->getScale().mV), MVT_LLVector3, 12);
+ offset += 12;
+ }
+ gMessageSystem->addBinaryDataFast(_PREHASH_Data, data, offset);
+}
+
+//------------------------------------------------------------------------
+// Ownership
+//------------------------------------------------------------------------
+struct LLOwnerData
+{
+ LLUUID owner_id;
+ LLUUID group_id;
+ BOOL override;
+};
+
+void LLSelectMgr::sendOwner(const LLUUID& owner_id,
+ const LLUUID& group_id,
+ BOOL override)
+{
+ LLOwnerData data;
+
+ data.owner_id = owner_id;
+ data.group_id = group_id;
+ data.override = override;
+
+ sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS);
+}
+
+// static
+void LLSelectMgr::packOwnerHead(void *user_data)
+{
+ LLOwnerData *data = (LLOwnerData *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ gMessageSystem->nextBlockFast(_PREHASH_HeaderData);
+ gMessageSystem->addBOOLFast(_PREHASH_Override, data->override);
+ gMessageSystem->addUUIDFast(_PREHASH_OwnerID, data->owner_id);
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id);
+}
+
+//------------------------------------------------------------------------
+// Group
+//------------------------------------------------------------------------
+
+void LLSelectMgr::sendGroup(const LLUUID& group_id)
+{
+ LLUUID local_group_id(group_id);
+ sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS);
+}
+
+
+//------------------------------------------------------------------------
+// Buy
+//------------------------------------------------------------------------
+
+struct LLBuyData
+{
+ LLDynamicArray<LLViewerObject*> mObjectsSent;
+ LLUUID mCategoryID;
+ LLSaleInfo mSaleInfo;
+};
+
+// FIXME: doesn't work for multiple object buy, which UI does not currently support
+// sale info is used for verification only, if it doesn't match region info then sale is canceled
+// Need to get sale info -as displayed in the UI- for every item.
+void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info)
+{
+ LLBuyData buy;
+ buy.mCategoryID = category_id;
+ buy.mSaleInfo = sale_info;
+ sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS);
+}
+
+// static
+void LLSelectMgr::packBuyObjectIDs(LLSelectNode* node, void* data)
+{
+ LLBuyData* buy = (LLBuyData*)data;
+
+ LLViewerObject* object = node->getObject();
+ if(buy->mObjectsSent.find(object) == LLDynamicArray<LLViewerObject*>::FAIL)
+ {
+ buy->mObjectsSent.put(object);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() );
+ gMessageSystem->addU8Fast(_PREHASH_SaleType, buy->mSaleInfo.getSaleType());
+ gMessageSystem->addS32Fast(_PREHASH_SalePrice, buy->mSaleInfo.getSalePrice());
+ }
+}
+
+//------------------------------------------------------------------------
+// Permissions
+//------------------------------------------------------------------------
+
+struct LLPermData
+{
+ U8 mField;
+ BOOL mSet;
+ U32 mMask;
+ BOOL mOverride;
+};
+
+// TODO: Make this able to fail elegantly.
+void LLSelectMgr::setObjectPermissions(U8 field,
+ BOOL set,
+ U32 mask,
+ BOOL override)
+{
+ LLPermData data;
+
+ data.mField = field;
+ data.mSet = set;
+ data.mMask = mask;
+ data.mOverride = override;
+
+ sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::packPermissionsHead(void* user_data)
+{
+ LLPermData* data = (LLPermData*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_HeaderData);
+ gMessageSystem->addBOOLFast(_PREHASH_Override, data->mOverride);
+}
+
+
+// Now that you've added a bunch of objects, send a select message
+// on the entire list for efficiency.
+/*
+void LLSelectMgr::sendSelect()
+{
+ llerrs << "Not implemented" << llendl;
+}
+*/
+
+void LLSelectMgr::deselectAll()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDeselect",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_INDIVIDUALS);
+
+ removeAll();
+
+ mLastSentSelectionCenterGlobal.clearVec();
+
+ updatePointAt();
+ gHUDManager->clearJoints();
+ updateSelectionCenter();
+}
+
+void LLSelectMgr::deselectTransient()
+{
+ std::set<LLViewerObject*> objects_to_deselect;
+ LLSelectNode *nodep;
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode())
+ {
+ if (nodep->isTransient())
+ {
+ objects_to_deselect.insert(nodep->getObject());
+ }
+ }
+
+ std::set<LLViewerObject*>::iterator iter;
+ for (iter = objects_to_deselect.begin();
+ iter != objects_to_deselect.end();
+ ++iter)
+ {
+ deselectObjectOnly(*iter);
+ }
+
+ gHUDManager->clearJoints();
+ updateSelectionCenter();
+}
+
+void LLSelectMgr::convertTransient()
+{
+ LLSelectNode *nodep;
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode())
+ {
+ nodep->setTransient(FALSE);
+ }
+}
+
+void LLSelectMgr::deselectAllIfTooFar()
+{
+ if (isEmpty() || mSelectType == SELECT_TYPE_HUD)
+ {
+ return;
+ }
+
+ // HACK: Don't deselect when we're navigating to rate an object's
+ // owner or creator. JC
+ if (gPieObject->getVisible() || gPieRate->getVisible() )
+ {
+ return;
+ }
+
+ LLVector3d selectionCenter = getSelectionCenterGlobal();
+ if (gSavedSettings.getBOOL("LimitSelectDistance")
+ && !selectionCenter.isExactlyZero())
+ {
+ F32 deselect_dist = gSavedSettings.getF32("MaxSelectDistance");
+ F32 deselect_dist_sq = deselect_dist * deselect_dist;
+
+ LLVector3d select_delta = gAgent.getPositionGlobal() - selectionCenter;
+ F32 select_dist_sq = (F32) select_delta.magVecSquared();
+
+ if (select_dist_sq > deselect_dist_sq)
+ {
+ if (gDebugSelectMgr)
+ {
+ llinfos << "Selection manager: auto-deselecting, select_dist = " << fsqrtf(select_dist_sq) << llendl;
+ llinfos << "agent pos global = " << gAgent.getPositionGlobal() << llendl;
+ llinfos << "selection pos global = " << selectionCenter << llendl;
+ }
+
+ deselectAll();
+ }
+ }
+}
+
+
+void LLSelectMgr::setObjectName(const LLString& name)
+{
+ // we only work correctly if 1 object is selected.
+ if(getRootObjectCount() == 1)
+ {
+ sendListToRegions("ObjectName",
+ packAgentAndSessionID,
+ packObjectName,
+ (void*)name.c_str(),
+ SEND_ONLY_ROOTS);
+ }
+ else if(getObjectCount() == 1)
+ {
+ sendListToRegions("ObjectName",
+ packAgentAndSessionID,
+ packObjectName,
+ (void*)name.c_str(),
+ SEND_INDIVIDUALS);
+ }
+}
+
+void LLSelectMgr::setObjectDescription(const LLString& desc)
+{
+ // we only work correctly if 1 object is selected.
+ if(getRootObjectCount() == 1)
+ {
+ sendListToRegions("ObjectDescription",
+ packAgentAndSessionID,
+ packObjectDescription,
+ (void*)desc.c_str(),
+ SEND_ONLY_ROOTS);
+ }
+ else if(getObjectCount() == 1)
+ {
+ sendListToRegions("ObjectDescription",
+ packAgentAndSessionID,
+ packObjectDescription,
+ (void*)desc.c_str(),
+ SEND_INDIVIDUALS);
+ }
+}
+
+void LLSelectMgr::setObjectCategory(const LLCategory& category)
+{
+ // for now, we only want to be able to set one root category at
+ // a time.
+ if(getRootObjectCount() != 1) return;
+ sendListToRegions("ObjectCategory",
+ packAgentAndSessionID,
+ packObjectCategory,
+ (void*)(&category),
+ SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::setObjectSaleInfo(const LLSaleInfo& sale_info)
+{
+ // Only one sale info at a time for now
+ if(getRootObjectCount() != 1) return;
+ sendListToRegions("ObjectSaleInfo",
+ packAgentAndSessionID,
+ packObjectSaleInfo,
+ (void*)(&sale_info),
+ SEND_ONLY_ROOTS);
+}
+
+//----------------------------------------------------------------------
+// Attachments
+//----------------------------------------------------------------------
+
+void LLSelectMgr::sendAttach(U8 attachment_point)
+{
+ LLViewerObject* attach_object = mSelectedObjects.getFirstRootObject();
+
+ if (!attach_object || !gAgent.getAvatarObject() || mSelectType != SELECT_TYPE_WORLD)
+ {
+ return;
+ }
+
+ // Special case: Attach to default location for this object.
+ if (0 == attachment_point)
+ {
+ sendListToRegions(
+ "ObjectAttach",
+ packAgentIDAndSessionAndAttachment,
+ packObjectIDAndRotation,
+ &attachment_point,
+ SEND_ONLY_ROOTS );
+ }
+ else
+ {
+ LLViewerJointAttachment* attachment = gAgent.getAvatarObject()->mAttachmentPoints.getIfThere(attachment_point);
+ if (attachment)
+ {
+ LLQuaternion object_world_rot = attach_object->getRenderRotation();
+ LLQuaternion attachment_pt__world_rot = attachment->getWorldRotation();
+ LLQuaternion local_rot = object_world_rot * ~attachment_pt__world_rot;
+
+ F32 x,y,z;
+ local_rot.getEulerAngles(&x, &y, &z);
+ // snap to nearest 90 degree rotation
+ // make sure all euler angles are positive
+ if (x < F_PI_BY_TWO) x += F_TWO_PI;
+ if (y < F_PI_BY_TWO) y += F_TWO_PI;
+ if (z < F_PI_BY_TWO) z += F_TWO_PI;
+
+ // add 45 degrees so that rounding down becomes rounding off
+ x += F_PI_BY_TWO / 2.f;
+ y += F_PI_BY_TWO / 2.f;
+ z += F_PI_BY_TWO / 2.f;
+ // round down to nearest multiple of 90 degrees
+ x -= fmodf(x, F_PI_BY_TWO);
+ y -= fmodf(y, F_PI_BY_TWO);
+ z -= fmodf(z, F_PI_BY_TWO);
+
+ // pass the requested rotation on to the simulator
+ local_rot.setQuat(x, y, z);
+ attach_object->setRotation(local_rot);
+
+ sendListToRegions(
+ "ObjectAttach",
+ packAgentIDAndSessionAndAttachment,
+ packObjectIDAndRotation,
+ &attachment_point,
+ SEND_ONLY_ROOTS );
+ }
+ }
+}
+
+void LLSelectMgr::sendDetach()
+{
+ if (!mSelectedObjects.getNumNodes() || mSelectType == SELECT_TYPE_WORLD)
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDetach",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS );
+}
+
+
+void LLSelectMgr::sendDropAttachment()
+{
+ if (!mSelectedObjects.getNumNodes() || mSelectType == SELECT_TYPE_WORLD)
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDrop",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS);
+}
+
+//----------------------------------------------------------------------
+// Links
+//----------------------------------------------------------------------
+
+void LLSelectMgr::sendLink()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectLink",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::sendDelink()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ // Delink needs to send individuals so you can unlink a single object from
+ // a linked set.
+ sendListToRegions(
+ "ObjectDelink",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_INDIVIDUALS);
+}
+
+
+//----------------------------------------------------------------------
+// Hinges
+//----------------------------------------------------------------------
+
+void LLSelectMgr::sendHinge(U8 type)
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectHinge",
+ packHingeHead,
+ packObjectLocalID,
+ &type,
+ SEND_ONLY_ROOTS);
+}
+
+
+void LLSelectMgr::sendDehinge()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDehinge",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::sendSelect()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectSelect",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_INDIVIDUALS);
+}
+
+// static
+void LLSelectMgr::packHingeHead(void *user_data)
+{
+ U8 *type = (U8 *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ gMessageSystem->nextBlockFast(_PREHASH_JointType);
+ gMessageSystem->addU8Fast(_PREHASH_Type, *type );
+}
+
+
+void LLSelectMgr::selectionDump()
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ object->dump();
+ }
+}
+
+void LLSelectMgr::saveSelectedObjectColors()
+{
+ LLSelectNode* selectNode;
+ for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() )
+ {
+ selectNode->saveColors();
+ }
+}
+
+void LLSelectMgr::saveSelectedObjectTextures()
+{
+ LLSelectNode* selectNode;
+
+ // invalidate current selection so we update saved textures
+ for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() )
+ {
+ selectNode->mValid = FALSE;
+ }
+
+ // request object properties message to get updated permissions data
+ sendSelect();
+}
+
+
+// This routine should be called whenever a drag is initiated.
+// also need to know to which simulator to send update message
+void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type)
+{
+ LLSelectNode* selectNode;
+
+ if (isEmpty())
+ {
+ // nothing selected, so nothing to save
+ return;
+ }
+
+ for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() )
+ {
+ LLViewerObject* object;
+ object = selectNode->getObject();
+ selectNode->mSavedPositionLocal = object->getPosition();
+ if (object->isAttachment())
+ {
+ if (object->isRootEdit())
+ {
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition());
+ }
+ else
+ {
+ LLViewerObject* attachment_root = (LLViewerObject*)object->getParent();
+ LLXform* parent_xform = attachment_root->mDrawable->getXform()->getParent();
+ LLVector3 root_pos = (attachment_root->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ LLQuaternion root_rot = (attachment_root->getRotation() * parent_xform->getWorldRotation());
+ selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * root_rot) + root_pos);
+ }
+ selectNode->mSavedRotation = object->getRenderRotation();
+ }
+ else
+ {
+ selectNode->mSavedPositionGlobal = object->getPositionGlobal();
+ selectNode->mSavedRotation = object->getRotationRegion();
+ }
+
+ selectNode->mSavedScale = object->getScale();
+ selectNode->saveTextureScaleRatios();
+
+ if (object->isAttachment() &&
+ action_type != SELECT_ACTION_TYPE_PICK)
+ {
+ LLSelectAction* selectAction = new LLSelectAction();
+ selectAction->mActionType = action_type;
+ selectAction->mPosition = object->getPosition();
+ selectAction->mRotation = object->getRotation();
+ selectAction->mScale = object->getScale();
+ selectAction->mObjectID = object->getID();
+ selectAction->mIndividualSelection = selectNode->mIndividualSelection;
+
+ mUndoQueue.push_back(selectAction);
+
+ while ((mUndoQueue.size() > (U32)MAX_ACTION_QUEUE_SIZE))
+ {
+ LLSelectAction* action = mUndoQueue.front();
+ mUndoQueue.pop_front();
+ delete action;
+ }
+
+ // remove this object from the redo queue
+ std::deque<LLSelectAction*>::iterator it;
+ for (it = mRedoQueue.begin(); it != mRedoQueue.end();)
+ {
+ if ((*it)->mObjectID == object->getID())
+ {
+ LLSelectAction* actionp = *it;
+ it = mRedoQueue.erase(it);
+ delete actionp;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ }
+ }
+ mSavedSelectionBBox = getBBoxOfSelection();
+}
+
+void LLSelectMgr::selectionUpdatePhysics(BOOL physics)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || !(object->isRoot() // don't send for child objects
+ || object->isJointChild()))
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_USE_PHYSICS, physics);
+ }
+}
+
+void LLSelectMgr::selectionUpdateTemporary(BOOL is_temporary)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || !(object->isRoot() // don't send for child objects
+ || object->isJointChild()))
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_TEMPORARY_ON_REZ, is_temporary);
+ }
+}
+
+void LLSelectMgr::selectionUpdatePhantom(BOOL is_phantom)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || !(object->isRoot() // don't send for child objects
+ || object->isJointChild()))
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_PHANTOM, is_phantom);
+ }
+}
+
+void LLSelectMgr::selectionUpdateCastShadows(BOOL cast_shadows)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || object->isJointChild())
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_CAST_SHADOWS, cast_shadows);
+ }
+}
+
+
+//----------------------------------------------------------------------
+// Helpful packing functions for sendObjectMessage()
+//----------------------------------------------------------------------
+
+// static
+void LLSelectMgr::packAgentIDAndSessionAndAttachment( void *user_data)
+{
+ U8 *attachment_point = (U8*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addU8Fast(_PREHASH_AttachmentPoint, *attachment_point);
+}
+
+// static
+void LLSelectMgr::packAgentID( void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+}
+
+// static
+void LLSelectMgr::packAgentAndSessionID(void* user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+}
+
+// static
+void LLSelectMgr::packAgentAndGroupID(void* user_data)
+{
+ LLOwnerData *data = (LLOwnerData *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, data->owner_id );
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id );
+}
+
+// static
+void LLSelectMgr::packAgentAndSessionAndGroupID(void* user_data)
+{
+ LLUUID* group_idp = (LLUUID*) user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, *group_idp);
+}
+
+// static
+void LLSelectMgr::packDuplicateHeader(void* data)
+{
+ LLUUID group_id(gAgent.getGroupID());
+ packAgentAndSessionAndGroupID(&group_id);
+
+ LLDuplicateData* dup_data = (LLDuplicateData*) data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_SharedData);
+ gMessageSystem->addVector3Fast(_PREHASH_Offset, dup_data->offset);
+ gMessageSystem->addU32Fast(_PREHASH_DuplicateFlags, dup_data->flags);
+}
+
+// static
+void LLSelectMgr::packDeleteHeader(void* userdata)
+{
+ BOOL force = (BOOL)(intptr_t)userdata;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addBOOLFast(_PREHASH_Force, force);
+}
+
+// static
+void LLSelectMgr::packAgentGroupAndCatID(void* user_data)
+{
+ LLBuyData* buy = (LLBuyData*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ gMessageSystem->addUUIDFast(_PREHASH_CategoryID, buy->mCategoryID);
+}
+
+//static
+void LLSelectMgr::packDeRezHeader(void* user_data)
+{
+ LLDeRezInfo* info = (LLDeRezInfo*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ gMessageSystem->addU8Fast(_PREHASH_Destination, (U8)info->mDestination);
+ gMessageSystem->addUUIDFast(_PREHASH_DestinationID, info->mDestinationID);
+ LLUUID tid;
+ tid.generate();
+ gMessageSystem->addUUIDFast(_PREHASH_TransactionID, tid);
+ const U8 PACKET = 1;
+ gMessageSystem->addU8Fast(_PREHASH_PacketCount, PACKET);
+ gMessageSystem->addU8Fast(_PREHASH_PacketNumber, PACKET);
+}
+
+// static
+void LLSelectMgr::packObjectID(LLSelectNode* node, void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addUUIDFast(_PREHASH_ObjectID, node->getObject()->mID );
+}
+
+void LLSelectMgr::packObjectIDAndRotation(LLSelectNode* node, void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() );
+ gMessageSystem->addQuatFast(_PREHASH_Rotation, node->getObject()->getRotation());
+}
+
+void LLSelectMgr::packObjectClickAction(LLSelectNode* node, void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() );
+ gMessageSystem->addU8("ClickAction", node->getObject()->getClickAction());
+}
+
+// static
+void LLSelectMgr::packObjectLocalID(LLSelectNode* node, void *)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID());
+}
+
+// static
+void LLSelectMgr::packObjectName(LLSelectNode* node, void* user_data)
+{
+ char* name = (char*)user_data;
+ if(!name) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ gMessageSystem->addStringFast(_PREHASH_Name, name);
+}
+
+// static
+void LLSelectMgr::packObjectDescription(LLSelectNode* node,
+ void* user_data)
+{
+ char* desc = (char*)user_data;
+ if(!desc) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ gMessageSystem->addStringFast(_PREHASH_Description, desc);
+}
+
+// static
+void LLSelectMgr::packObjectCategory(LLSelectNode* node, void* user_data)
+{
+ LLCategory* category = (LLCategory*)user_data;
+ if(!category) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ category->packMessage(gMessageSystem);
+}
+
+// static
+void LLSelectMgr::packObjectSaleInfo(LLSelectNode* node, void* user_data)
+{
+ LLSaleInfo* sale_info = (LLSaleInfo*)user_data;
+ if(!sale_info) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ sale_info->packMessage(gMessageSystem);
+}
+
+// static
+void LLSelectMgr::packPhysics(LLSelectNode* node, void *user_data)
+{
+}
+
+// static
+void LLSelectMgr::packShape(LLSelectNode* node, void *user_data)
+{
+}
+
+// static
+void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data)
+{
+ LLPermData *data = (LLPermData *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID());
+
+ gMessageSystem->addU8Fast(_PREHASH_Field, data->mField);
+ gMessageSystem->addBOOLFast(_PREHASH_Set, data->mSet);
+ gMessageSystem->addU32Fast(_PREHASH_Mask, data->mMask);
+}
+
+// Utility function to send some information to every region containing
+// an object on the selection list. We want to do this to reduce the total
+// number of packets sent by the viewer.
+void LLSelectMgr::sendListToRegions(const LLString& message_name,
+ void (*pack_header)(void *user_data),
+ void (*pack_body)(LLSelectNode* node, void *user_data),
+ void *user_data,
+ ESendType send_type)
+{
+ LLSelectNode* node;
+ LLViewerRegion* last_region;
+ LLViewerRegion* current_region;
+
+ S32 objects_sent = 0;
+ S32 packets_sent = 0;
+ S32 objects_in_this_packet = 0;
+
+ std::queue<LLSelectNode*> nodes_to_send;
+
+ switch(send_type)
+ {
+ case SEND_ONLY_ROOTS:
+ node = mSelectedObjects.getFirstRootNode();
+ while(node)
+ {
+ nodes_to_send.push(node);
+ node = mSelectedObjects.getNextRootNode();
+ }
+ break;
+ case SEND_INDIVIDUALS:
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ nodes_to_send.push(node);
+ node = mSelectedObjects.getNextNode();
+ }
+ break;
+ case SEND_ROOTS_FIRST:
+ // first roots...
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+
+ // then children...
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (!node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+ break;
+ case SEND_CHILDREN_FIRST:
+ // first children...
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (!node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+
+ // ...then roots
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+ break;
+
+ default:
+ llerrs << "Bad send type " << send_type << " passed to SendListToRegions()" << llendl;
+ }
+
+ // bail if nothing selected
+ if (nodes_to_send.empty()) return;
+
+ node = nodes_to_send.front();
+ nodes_to_send.pop();
+
+ // cache last region information
+ current_region = node->getObject()->getRegion();
+
+ // Start duplicate message
+ // CRO: this isn't
+ gMessageSystem->newMessage(message_name.c_str());
+ (*pack_header)(user_data);
+
+ // For each object
+ while (node != NULL)
+ {
+ // remember the last region, look up the current one
+ last_region = current_region;
+ current_region = node->getObject()->getRegion();
+
+ // if to same simulator and message not too big
+ if ((current_region == last_region)
+ && (gMessageSystem->mCurrentSendTotal < MTUBYTES)
+ && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET))
+ {
+ // add another instance of the body of the data
+ (*pack_body)(node, user_data);
+ ++objects_sent;
+ ++objects_in_this_packet;
+
+ // and on to the next object
+ if(nodes_to_send.empty())
+ {
+ node = NULL;
+ }
+ else
+ {
+ node = nodes_to_send.front();
+ nodes_to_send.pop();
+ }
+ }
+ else
+ {
+ // otherwise send current message and start new one
+ gMessageSystem->sendReliable( last_region->getHost());
+ packets_sent++;
+ objects_in_this_packet = 0;
+
+ gMessageSystem->newMessage(message_name.c_str());
+ (*pack_header)(user_data);
+
+ // don't move to the next object, we still need to add the
+ // body data.
+ }
+ }
+
+ // flush messages
+ if (gMessageSystem->mCurrentSendTotal > 0)
+ {
+ gMessageSystem->sendReliable( current_region->getHost());
+ packets_sent++;
+ }
+ else
+ {
+ gMessageSystem->clearMessage();
+ }
+
+ // llinfos << "sendListToRegions " << message_name << " obj " << objects_sent << " pkt " << packets_sent << llendl;
+}
+
+
+//
+// Network communications
+//
+
+void LLSelectMgr::requestObjectPropertiesFamily(LLViewerObject* object)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_RequestFlags, 0x0 );
+ msg->addUUIDFast(_PREHASH_ObjectID, object->mID );
+
+ LLViewerRegion* regionp = object->getRegion();
+ msg->sendReliable( regionp->getHost() );
+}
+
+
+// static
+void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data)
+{
+ S32 i;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_ObjectData);
+ for (i = 0; i < count; i++)
+ {
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id, i);
+
+ LLUUID creator_id;
+ LLUUID owner_id;
+ LLUUID group_id;
+ LLUUID last_owner_id;
+ LLUUID extra_id;
+ U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask;
+ LLSaleInfo sale_info;
+ LLCategory category;
+ LLAggregatePermissions ag_perms;
+ LLAggregatePermissions ag_texture_perms;
+ LLAggregatePermissions ag_texture_perms_owner;
+
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_CreatorID, creator_id, i);
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, i);
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_GroupMask, group_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask, i);
+ sale_info.unpackMultiMessage(msg, _PREHASH_ObjectData, i);
+
+ ag_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePerms, i);
+ ag_texture_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTextures, i);
+ ag_texture_perms_owner.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTexturesOwner, i);
+ category.unpackMultiMessage(msg, _PREHASH_ObjectData, i);
+
+ S16 inv_serial = 0;
+ msg->getS16Fast(_PREHASH_ObjectData, _PREHASH_InventorySerial, inv_serial, i);
+
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ItemID, item_id, i);
+ LLUUID folder_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FolderID, folder_id, i);
+ LLUUID from_task_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FromTaskID, from_task_id, i);
+
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id, i);
+
+ char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name, i);
+ char desc[DB_INV_ITEM_DESC_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc, i);
+
+ char touch_name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_TouchName, DB_INV_ITEM_NAME_BUF_SIZE, touch_name, i);
+ char sit_name[DB_INV_ITEM_DESC_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_SitName, DB_INV_ITEM_DESC_BUF_SIZE, sit_name, i);
+
+ //unpack TE IDs
+ std::vector<LLUUID> texture_ids;
+ S32 size = msg->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_TextureID);
+ if (size > 0)
+ {
+ S8 packed_buffer[SELECT_MAX_TES * UUID_BYTES];
+ msg->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureID, packed_buffer, 0, i, SELECT_MAX_TES * UUID_BYTES);
+
+ for (S32 buf_offset = 0; buf_offset < size; buf_offset += UUID_BYTES)
+ {
+ LLUUID id;
+ memcpy(id.mData, packed_buffer + buf_offset, UUID_BYTES);
+ texture_ids.push_back(id);
+ }
+ }
+
+
+ // Iterate through nodes at end, since it can be on both the regular AND hover list
+ BOOL found = FALSE;
+ LLSelectNode* node;
+ for (node = gSelectMgr->mSelectedObjects.getFirstNode();
+ node;
+ node = gSelectMgr->mSelectedObjects.getNextNode())
+ {
+ if (node->getObject()->mID == id)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+
+ if (node)
+ {
+ if (node->mInventorySerial != inv_serial)
+ {
+ node->getObject()->dirtyInventory();
+ }
+
+ // save texture data as soon as we get texture perms first time
+ if (!node->mValid)
+ {
+ BOOL can_copy = FALSE;
+ BOOL can_transfer = FALSE;
+
+ LLAggregatePermissions::EValue value = LLAggregatePermissions::AP_NONE;
+ if(node->getObject()->permYouOwner())
+ {
+ value = ag_texture_perms_owner.getValue(PERM_COPY);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_copy = TRUE;
+ }
+ value = ag_texture_perms_owner.getValue(PERM_TRANSFER);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_transfer = TRUE;
+ }
+ }
+ else
+ {
+ value = ag_texture_perms.getValue(PERM_COPY);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_copy = TRUE;
+ }
+ value = ag_texture_perms.getValue(PERM_TRANSFER);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_transfer = TRUE;
+ }
+ }
+
+ if (can_copy && can_transfer)
+ {
+ // this should be the only place that saved textures is called
+ node->saveTextures(texture_ids);
+ }
+ }
+
+ node->mValid = TRUE;
+ node->mPermissions->init(creator_id, owner_id,
+ last_owner_id, group_id);
+ node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask);
+ node->mItemID = item_id;
+ node->mFolderID = folder_id;
+ node->mFromTaskID = from_task_id;
+ node->mName.assign(name);
+ node->mDescription.assign(desc);
+ node->mSaleInfo = sale_info;
+ node->mAggregatePerm = ag_perms;
+ node->mAggregateTexturePerm = ag_texture_perms;
+ node->mAggregateTexturePermOwner = ag_texture_perms_owner;
+ node->mCategory = category;
+ node->mInventorySerial = inv_serial;
+ node->mSitName.assign(sit_name);
+ node->mTouchName.assign(touch_name);
+ }
+ }
+
+ dialog_refresh_all();
+
+ // silly hack to allow 'save into inventory'
+ if(gPopupMenuView->getVisible())
+ {
+ gPopupMenuView->setItemEnabled(SAVE_INTO_INVENTORY,
+ enable_save_into_inventory(NULL));
+ }
+
+ // hack for left-click buy object
+ LLToolPie::selectionPropertiesReceived();
+}
+
+// static
+void LLSelectMgr::processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data)
+{
+ LLUUID id;
+
+ U32 request_flags;
+ LLUUID creator_id;
+ LLUUID owner_id;
+ LLUUID group_id;
+ LLUUID extra_id;
+ U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask;
+ LLSaleInfo sale_info;
+ LLCategory category;
+
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_RequestFlags, request_flags );
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id );
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id );
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask );
+ msg->getU32Fast(_PREHASH_ObjectData,_PREHASH_GroupMask, group_mask );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask);
+ sale_info.unpackMessage(msg, _PREHASH_ObjectData);
+ category.unpackMessage(msg, _PREHASH_ObjectData);
+
+ LLUUID last_owner_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id );
+
+ // unpack name & desc
+ char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name);
+
+ char desc[DB_INV_ITEM_DESC_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc);
+
+ // the reporter widget askes the server for info about picked objects
+ if (request_flags & (COMPLAINT_REPORT_REQUEST | BUG_REPORT_REQUEST))
+ {
+ EReportType report_type = (COMPLAINT_REPORT_REQUEST & request_flags) ? COMPLAINT_REPORT : BUG_REPORT;
+ LLFloaterReporter *reporterp = LLFloaterReporter::getReporter(report_type);
+ if (reporterp)
+ {
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first_name, last_name);
+ LLString fullname(first_name);
+ fullname.append(" ");
+ fullname.append(last_name);
+ reporterp->setPickedObjectProperties(name, fullname.c_str());
+ }
+ }
+
+ // Now look through all of the hovered nodes
+ BOOL found = FALSE;
+ LLSelectNode* node;
+ for (node = gSelectMgr->mHoverObjects.getFirstNode();
+ node;
+ node = gSelectMgr->mHoverObjects.getNextNode())
+ {
+ if (node->getObject()->mID == id)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (node)
+ {
+ node->mValid = TRUE;
+ node->mPermissions->init(LLUUID::null, owner_id,
+ last_owner_id, group_id);
+ node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask);
+ node->mSaleInfo = sale_info;
+ node->mCategory = category;
+ node->mName.assign(name);
+ node->mDescription.assign(desc);
+ }
+
+ dialog_refresh_all();
+}
+
+
+// static
+void LLSelectMgr::processForceObjectSelect(LLMessageSystem* msg, void**)
+{
+ BOOL reset_list;
+ msg->getBOOL("Header", "ResetList", reset_list);
+
+ if (reset_list)
+ {
+ gSelectMgr->deselectAll();
+ }
+
+ LLUUID full_id;
+ S32 local_id;
+ LLViewerObject* object;
+ LLDynamicArray<LLViewerObject*> objects;
+ S32 i;
+ S32 block_count = msg->getNumberOfBlocks("Data");
+
+ for (i = 0; i < block_count; i++)
+ {
+ msg->getS32("Data", "LocalID", local_id, i);
+
+ gObjectList.getUUIDFromLocal(full_id,
+ local_id,
+ msg->getSenderIP(),
+ msg->getSenderPort());
+ object = gObjectList.findObject(full_id);
+ if (object)
+ {
+ objects.put(object);
+ }
+ }
+
+ // Don't select, just highlight
+ gSelectMgr->highlightObjectAndFamily(objects);
+}
+
+
+extern LLGLdouble gGLModelView[16];
+
+void LLSelectMgr::updateSilhouettes()
+{
+ LLSelectNode *node;
+ S32 num_sils_genned = 0;
+
+ LLVector3d cameraPos = gAgent.getCameraPositionGlobal();
+ F32 currentCameraZoom = gAgent.getCurrentCameraBuildOffset();
+
+ if (!mSilhouetteImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("silhouette.tga") );
+ mSilhouetteImagep = gImageList.getImage(id, TRUE, TRUE);
+ }
+
+
+ if((cameraPos - mLastCameraPos).magVecSquared() > SILHOUETTE_UPDATE_THRESHOLD_SQUARED * currentCameraZoom * currentCameraZoom)
+ {
+ for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() )
+ {
+ if (node->getObject())
+ {
+ node->getObject()->setChanged(LLXform::SILHOUETTE);
+ }
+ }
+
+ mLastCameraPos = gAgent.getCameraPositionGlobal();
+ }
+
+ LLDynamicArray<LLViewerObject*> changed_objects;
+
+ if (mSelectedObjects.getNumNodes())
+ {
+ //gGLSPipelineSelection.set();
+
+ //mSilhouetteImagep->bindTexture();
+ //glAlphaFunc(GL_GREATER, sHighlightAlphaTest);
+
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+
+ // do roots first, then children so that root flags are cleared ASAP
+ BOOL roots_only = (pass == 0);
+ BOOL is_root = (objectp->isRootEdit());
+ if (roots_only != is_root || objectp->mDrawable.isNull())
+ {
+ continue;
+ }
+
+ if (!node->mSilhouetteExists
+ || objectp->isChanged(LLXform::SILHOUETTE)
+ || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE)))
+ {
+ if (num_sils_genned++ < MAX_SILS_PER_FRAME && objectp->mDrawable->isVisible())
+ {
+ generateSilhouette(node, gCamera->getOrigin());
+ changed_objects.put(objectp);
+ }
+ else if (objectp->isAttachment())
+ {
+ //RN: hack for orthogonal projection of HUD attachments
+ LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent();
+ if (attachment_pt && attachment_pt->getIsHUDAttachment())
+ {
+ LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f);
+ generateSilhouette(node, camera_pos);
+ }
+ }
+ }
+
+ //if (!gFloaterTools || !gFloaterTools->getVisible())
+ //{
+ // node->renderOneSilhouette(sContextSilhouetteColor);
+ //}
+ //else if (objectp->isRootEdit())
+ //{
+ // node->renderOneSilhouette(sSilhouetteParentColor);
+ //}
+ //else
+ //{
+ // node->renderOneSilhouette(sSilhouetteChildColor);
+ //}
+ }
+ }
+ //mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D);
+ }
+
+ if (mRectSelectedObjects.size() > 0)
+ {
+ //gGLSPipelineSelection.set();
+
+ //mSilhouetteImagep->bindTexture();
+ //glAlphaFunc(GL_GREATER, sHighlightAlphaTest);
+
+ std::set<LLViewerObject*> roots;
+
+ // sync mHighlightedObjects with mRectSelectedObjects since the latter is rebuilt every frame and former
+ // persists from frame to frame to avoid regenerating object silhouettes
+ // mHighlightedObjects includes all siblings of rect selected objects
+
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+
+ // generate list of roots from current object selection
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mRectSelectedObjects.begin();
+ iter != mRectSelectedObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (select_linked_set)
+ {
+ LLViewerObject *rootp = (LLViewerObject*)objectp->getRoot();
+ roots.insert(rootp);
+ }
+ else
+ {
+ roots.insert(objectp);
+ }
+ }
+
+ // remove highlight nodes not in roots list
+ LLDynamicArray<LLSelectNode*> remove_these_nodes;
+ LLDynamicArray<LLViewerObject*> remove_these_roots;
+ for (LLSelectNode* nodep = mHighlightedObjects.getFirstNode(); nodep; nodep = mHighlightedObjects.getNextNode())
+ {
+ LLViewerObject* objectp = nodep->getObject();
+ if (objectp->isRoot() || !select_linked_set)
+ {
+ if (roots.count(objectp) == 0)
+ {
+ remove_these_nodes.put(nodep);
+ }
+ else
+ {
+ remove_these_roots.put(objectp);
+ }
+ }
+ else
+ {
+ LLViewerObject* rootp = (LLViewerObject*)objectp->getRoot();
+
+ if (roots.count(rootp) == 0)
+ {
+ remove_these_nodes.put(nodep);
+ }
+ }
+ }
+
+ // remove all highlight nodes no longer in rectangle selection
+ S32 i;
+ for (i = 0; i < remove_these_nodes.count(); i++)
+ {
+ mHighlightedObjects.removeNode(remove_these_nodes[i]);
+ }
+
+ // remove all root objects already being highlighted
+ for (i = 0; i < remove_these_roots.count(); i++)
+ {
+ roots.erase(remove_these_roots[i]);
+ }
+
+ // add all new objects in rectangle selection
+ for (std::set<LLViewerObject*>::iterator iter = roots.begin();
+ iter != roots.end(); iter++)
+ {
+ LLViewerObject* objectp = *iter;
+ LLSelectNode* rect_select_node = new LLSelectNode(objectp, TRUE);
+ rect_select_node->selectAllTEs(TRUE);
+
+ if (!canSelectObject(objectp))
+ {
+ continue;
+ }
+
+ mHighlightedObjects.addNode(rect_select_node);
+
+ if (!select_linked_set)
+ {
+ rect_select_node->mIndividualSelection = TRUE;
+ }
+ else
+ {
+ for (U32 i = 0; i < objectp->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = objectp->mChildList[i];
+
+ if (!canSelectObject(child_objectp))
+ {
+ continue;
+ }
+
+ rect_select_node = new LLSelectNode(objectp->mChildList[i], TRUE);
+ rect_select_node->selectAllTEs(TRUE);
+ mHighlightedObjects.addNode(rect_select_node);
+ }
+ }
+ }
+
+ num_sils_genned = 0;
+
+ // render silhouettes for highlighted objects
+ //BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL);
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mHighlightedObjects.getFirstNode(); node; node = mHighlightedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+
+ // do roots first, then children so that root flags are cleared ASAP
+ BOOL roots_only = (pass == 0);
+ BOOL is_root = objectp->isRootEdit();
+ if (roots_only != is_root)
+ {
+ continue;
+ }
+
+ if (!node->mSilhouetteExists
+ || objectp->isChanged(LLXform::SILHOUETTE)
+ || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE)))
+ {
+ if (num_sils_genned++ < MAX_SILS_PER_FRAME)
+ {
+ generateSilhouette(node, gCamera->getOrigin());
+ changed_objects.put(objectp);
+ }
+ else if (objectp->isAttachment() && objectp->getRootEdit()->mDrawable.notNull())
+ {
+ //RN: hack for orthogonal projection of HUD attachments
+ LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent();
+ if (attachment_pt && attachment_pt->getIsHUDAttachment())
+ {
+ LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f);
+ generateSilhouette(node, camera_pos);
+ }
+ }
+ }
+ //LLColor4 highlight_color;
+ //
+ //if (subtracting_from_selection)
+ //{
+ // node->renderOneSilhouette(LLColor4::red);
+ //}
+ //else if (!objectp->isSelected())
+ //{
+ // highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor;
+ // node->renderOneSilhouette(highlight_color);
+ //}
+ }
+ }
+ //mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ mHighlightedObjects.deleteAllNodes();
+ }
+
+ for (S32 i = 0; i < changed_objects.count(); i++)
+ {
+ // clear flags after traversing node list (as child objects need to refer to parent flags, etc)
+ changed_objects[i]->clearChanged(LLXform::MOVED | LLXform::SILHOUETTE);
+ }
+
+ //glAlphaFunc(GL_GREATER, 0.01f);
+}
+
+void LLSelectMgr::renderSilhouettes(BOOL for_hud)
+{
+ if (!mRenderSilhouettes)
+ {
+ return;
+ }
+
+ LLSelectNode *node;
+ LLViewerImage::bindTexture(gSelectMgr->mSilhouetteImagep);
+ LLGLSPipelineSelection gls_select;
+ glAlphaFunc(GL_GREATER, 0.0f);
+ LLGLEnable blend(GL_BLEND);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (for_hud && avatar)
+ {
+ LLBBox hud_bbox = avatar->getHUDBBox();
+
+ F32 cur_zoom = avatar->mHUDCurZoom;
+
+ // set up transform to encompass bounding box of HUD
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
+ glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, depth);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+ glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f);
+ glScalef(cur_zoom, cur_zoom, cur_zoom);
+ }
+ if (mSelectedObjects.getNumNodes())
+ {
+ glPushAttrib(GL_FOG_BIT);
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+ if (objectp->isHUDAttachment() != for_hud)
+ {
+ continue;
+ }
+ if (node->isTransient())
+ {
+ BOOL oldHidden = LLSelectMgr::sRenderHiddenSelections;
+ LLSelectMgr::sRenderHiddenSelections = FALSE;
+ node->renderOneSilhouette(sContextSilhouetteColor);
+ LLSelectMgr::sRenderHiddenSelections = oldHidden;
+ }
+ else if (objectp->isRootEdit())
+ {
+ node->renderOneSilhouette(sSilhouetteParentColor);
+ }
+ else
+ {
+ node->renderOneSilhouette(sSilhouetteChildColor);
+ }
+ }
+ }
+ glPopAttrib();
+ }
+
+ if (mHighlightedObjects.getNumNodes())
+ {
+ // render silhouettes for highlighted objects
+ BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL);
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mHighlightedObjects.getFirstNode(); node; node = mHighlightedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+ if (objectp->isHUDAttachment() != for_hud)
+ {
+ continue;
+ }
+
+ if (subtracting_from_selection)
+ {
+ node->renderOneSilhouette(LLColor4::red);
+ }
+ else if (!objectp->isSelected())
+ {
+ LLColor4 highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor;
+ node->renderOneSilhouette(highlight_color);
+ }
+ }
+ }
+ }
+
+ if (for_hud && avatar)
+ {
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ stop_glerror();
+ }
+
+ gSelectMgr->mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D);
+ glAlphaFunc(GL_GREATER, 0.01f);
+}
+
+void LLSelectMgr::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point)
+{
+ LLViewerObject* objectp = nodep->getObject();
+
+ if (objectp && objectp->getPCode() == LL_PCODE_VOLUME)
+ {
+ ((LLVOVolume*)objectp)->generateSilhouette(nodep, view_point);
+ }
+}
+
+void LLSelectMgr::getSilhouetteExtents(LLSelectNode* nodep, const LLQuaternion& orientation, LLVector3& min_extents, LLVector3& max_extents)
+{
+ LLViewerObject* objectp = nodep->getObject();
+
+ if (objectp->mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLQuaternion test_rot = orientation * ~objectp->getRenderRotation();
+ LLVector3 x_axis_rot = LLVector3::x_axis * test_rot;
+ LLVector3 y_axis_rot = LLVector3::y_axis * test_rot;
+ LLVector3 z_axis_rot = LLVector3::z_axis * test_rot;
+
+ x_axis_rot.scaleVec(objectp->mDrawable->getScale());
+ y_axis_rot.scaleVec(objectp->mDrawable->getScale());
+ z_axis_rot.scaleVec(objectp->mDrawable->getScale());
+
+ generateSilhouette(nodep, objectp->mDrawable->getPositionAgent() + x_axis_rot * 100.f);
+
+ S32 num_vertices = nodep->mSilhouetteVertices.size();
+ if (num_vertices)
+ {
+ min_extents.mV[VY] = llmin(min_extents.mV[VY], nodep->mSilhouetteVertices[0] * y_axis_rot);
+ max_extents.mV[VY] = llmax(max_extents.mV[VY], nodep->mSilhouetteVertices[0] * y_axis_rot);
+
+ min_extents.mV[VZ] = llmin(min_extents.mV[VZ], nodep->mSilhouetteVertices[0] * z_axis_rot);
+ max_extents.mV[VZ] = llmax(min_extents.mV[VZ], nodep->mSilhouetteVertices[0] * z_axis_rot);
+
+ for (S32 vert = 1; vert < num_vertices; vert++)
+ {
+ F32 y_pos = nodep->mSilhouetteVertices[vert] * y_axis_rot;
+ F32 z_pos = nodep->mSilhouetteVertices[vert] * z_axis_rot;
+ min_extents.mV[VY] = llmin(y_pos, min_extents.mV[VY]);
+ max_extents.mV[VY] = llmax(y_pos, max_extents.mV[VY]);
+ min_extents.mV[VZ] = llmin(z_pos, min_extents.mV[VZ]);
+ max_extents.mV[VZ] = llmax(z_pos, max_extents.mV[VZ]);
+ }
+ }
+
+ generateSilhouette(nodep, objectp->mDrawable->getPositionAgent() + y_axis_rot * 100.f);
+
+ num_vertices = nodep->mSilhouetteVertices.size();
+ if (num_vertices)
+ {
+ min_extents.mV[VX] = llmin(min_extents.mV[VX], nodep->mSilhouetteVertices[0] * x_axis_rot);
+ max_extents.mV[VX] = llmax(max_extents.mV[VX], nodep->mSilhouetteVertices[0] * x_axis_rot);
+
+ for (S32 vert = 1; vert < num_vertices; vert++)
+ {
+ F32 x_pos = nodep->mSilhouetteVertices[vert] * x_axis_rot;
+ min_extents.mV[VX] = llmin(x_pos, min_extents.mV[VX]);
+ max_extents.mV[VX] = llmax(x_pos, max_extents.mV[VX]);
+ }
+ }
+
+ generateSilhouette(nodep, gCamera->getOrigin());
+}
+
+
+//
+// Utility classes
+//
+LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow)
+{
+ mObject = object;
+ selectAllTEs(FALSE);
+ mIndividualSelection = FALSE;
+ mTransient = FALSE;
+ mValid = FALSE;
+ mPermissions = new LLPermissions();
+ mInventorySerial = 0;
+ mName = LLString::null;
+ mDescription = LLString::null;
+ mTouchName = LLString::null;
+ mSitName = LLString::null;
+ mSilhouetteExists = FALSE;
+ mDuplicated = FALSE;
+
+ saveColors();
+}
+
+LLSelectNode::LLSelectNode(const LLSelectNode& nodep)
+{
+ S32 i;
+ for (i = 0; i < SELECT_MAX_TES; i++)
+ {
+ mTESelected[i] = nodep.mTESelected[i];
+ }
+ mLastTESelected = nodep.mLastTESelected;
+
+ mIndividualSelection = nodep.mIndividualSelection;
+
+ mValid = nodep.mValid;
+ mTransient = nodep.mTransient;
+ mPermissions = new LLPermissions(*nodep.mPermissions);
+ mSaleInfo = nodep.mSaleInfo;;
+ mAggregatePerm = nodep.mAggregatePerm;
+ mAggregateTexturePerm = nodep.mAggregateTexturePerm;
+ mAggregateTexturePermOwner = nodep.mAggregateTexturePermOwner;
+ mName = nodep.mName;
+ mDescription = nodep.mDescription;
+ mCategory = nodep.mCategory;
+ mSavedPositionLocal = nodep.mSavedPositionLocal;
+ mSavedPositionGlobal = nodep.mSavedPositionGlobal;
+ mSavedScale = nodep.mSavedScale;
+ mSavedRotation = nodep.mSavedRotation;
+ mDuplicated = nodep.mDuplicated;
+ mDuplicatePos = nodep.mDuplicatePos;
+ mDuplicateRot = nodep.mDuplicateRot;
+ mItemID = nodep.mItemID;
+ mFolderID = nodep.mFolderID;
+ mFromTaskID = nodep.mFromTaskID;
+ mTouchName = nodep.mTouchName;
+ mSitName = nodep.mSitName;
+
+ mSilhouetteVertices = nodep.mSilhouetteVertices;
+ mSilhouetteNormals = nodep.mSilhouetteNormals;
+ mSilhouetteSegments = nodep.mSilhouetteSegments;
+ mSilhouetteExists = nodep.mSilhouetteExists;
+ mObject = nodep.mObject;
+
+ std::vector<LLColor4>::const_iterator color_iter;
+ mSavedColors.clear();
+ for (color_iter = nodep.mSavedColors.begin(); color_iter != nodep.mSavedColors.end(); ++color_iter)
+ {
+ mSavedColors.push_back(*color_iter);
+ }
+
+ saveTextures(nodep.mSavedTextures);
+}
+
+LLSelectNode::~LLSelectNode()
+{
+ delete mPermissions;
+ mPermissions = NULL;
+}
+
+void LLSelectNode::selectAllTEs(BOOL b)
+{
+ for (S32 i = 0; i < SELECT_MAX_TES; i++)
+ {
+ mTESelected[i] = b;
+ }
+ mLastTESelected = 0;
+}
+
+void LLSelectNode::selectTE(S32 te_index, BOOL selected)
+{
+ if (te_index < 0 || te_index >= SELECT_MAX_TES)
+ {
+ return;
+ }
+ mTESelected[te_index] = selected;
+ mLastTESelected = te_index;
+}
+
+BOOL LLSelectNode::isTESelected(S32 te_index)
+{
+ if (te_index < 0 || te_index >= mObject->getNumTEs())
+ {
+ return FALSE;
+ }
+ return mTESelected[te_index];
+}
+
+S32 LLSelectNode::getLastSelectedTE()
+{
+ if (!isTESelected(mLastTESelected))
+ {
+ return -1;
+ }
+ return mLastTESelected;
+}
+
+LLViewerObject *LLSelectNode::getObject()
+{
+ if (!mObject)
+ {
+ return NULL;
+ }
+ else if (mObject->isDead())
+ {
+ mObject = NULL;
+ }
+ return mObject;
+}
+
+void LLSelectNode::saveColors()
+{
+ if (mObject.notNull())
+ {
+ mSavedColors.clear();
+ for (S32 i = 0; i < mObject->getNumTEs(); i++)
+ {
+ const LLTextureEntry* tep = mObject->getTE(i);
+ mSavedColors.push_back(tep->getColor());
+ }
+ }
+}
+
+void LLSelectNode::saveTextures(const std::vector<LLUUID>& textures)
+{
+ if (mObject.notNull())
+ {
+ mSavedTextures.clear();
+
+ std::vector<LLUUID>::const_iterator texture_it;
+ for (texture_it = textures.begin(); texture_it != textures.end(); ++texture_it)
+ {
+ mSavedTextures.push_back(*texture_it);
+ }
+ }
+}
+
+void LLSelectNode::saveTextureScaleRatios()
+{
+ mTextureScaleRatios.clear();
+ if (mObject.notNull())
+ {
+ for (U8 i = 0; i < mObject->getNumTEs(); i++)
+ {
+ F32 s,t;
+ const LLTextureEntry* tep = mObject->getTE(i);
+ tep->getScale(&s,&t);
+ U32 s_axis, t_axis;
+
+ gSelectMgr->getTESTAxes(mObject, i, &s_axis, &t_axis);
+
+ LLVector3 v;
+ LLVector3 scale = mObject->getScale();
+
+ if (tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR)
+ {
+ v.mV[s_axis] = s*scale.mV[s_axis];
+ v.mV[t_axis] = t*scale.mV[t_axis];
+ }
+ else
+ {
+ v.mV[s_axis] = s/scale.mV[s_axis];
+ v.mV[t_axis] = t/scale.mV[t_axis];
+ }
+
+ mTextureScaleRatios.push_back(v);
+ }
+ }
+}
+
+
+// This implementation should be similar to LLTask::allowOperationOnTask
+BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power) const
+{
+ // Extract ownership.
+ BOOL object_is_group_owned = FALSE;
+ LLUUID object_owner_id;
+ mPermissions->getOwnership(object_owner_id, object_is_group_owned);
+
+ // Operations on invalid or public objects is not allowed.
+ if (!mObject || (mObject->isDead()) || !mPermissions->isOwned())
+ {
+ return FALSE;
+ }
+
+ // The transfer permissions can never be given through proxy.
+ if (PERM_TRANSFER == op)
+ {
+ // The owner of an agent-owned object can transfer to themselves.
+ if ( !object_is_group_owned
+ && (gAgent.getID() == object_owner_id) )
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Otherwise check aggregate permissions.
+ return mObject->permTransfer();
+ }
+ }
+
+ if (PERM_MOVE == op
+ || PERM_MODIFY == op)
+ {
+ // only owners can move or modify their attachments
+ // no proxy allowed.
+ if (mObject->isAttachment() && object_owner_id != gAgent.getID())
+ {
+ return FALSE;
+ }
+ }
+
+ // Calculate proxy_agent_id and group_id to use for permissions checks.
+ // proxy_agent_id may be set to the object owner through group powers.
+ // group_id can only be set to the object's group, if the agent is in that group.
+ LLUUID group_id = LLUUID::null;
+ LLUUID proxy_agent_id = gAgent.getID();
+
+ // Gods can always operate.
+ if (gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+
+ // Check if the agent is in the same group as the object.
+ LLUUID object_group_id = mPermissions->getGroup();
+ if (object_group_id.notNull() &&
+ gAgent.isInGroup(object_group_id))
+ {
+ // Assume the object's group during this operation.
+ group_id = object_group_id;
+ }
+
+ // Only allow proxy powers for PERM_COPY if the actual agent can
+ // receive the item (ie has PERM_TRANSFER permissions).
+ // NOTE: op == PERM_TRANSFER has already been handled, but if
+ // that ever changes we need to BLOCK proxy powers for PERM_TRANSFER. DK 03/28/06
+ if (PERM_COPY != op || mPermissions->allowTransferTo(gAgent.getID()))
+ {
+ // Check if the agent can assume ownership through group proxy or agent-granted proxy.
+ if ( ( object_is_group_owned
+ && gAgent.hasPowerInGroup(object_owner_id, group_proxy_power))
+ // Only allow proxy for move, modify, and copy.
+ || ( (PERM_MOVE == op || PERM_MODIFY == op || PERM_COPY == op)
+ && (!object_is_group_owned
+ && gAgent.isGrantedProxy(*mPermissions))))
+ {
+ // This agent is able to assume the ownership role for this operation.
+ proxy_agent_id = object_owner_id;
+ }
+ }
+
+ // We now have max ownership information.
+ if (PERM_OWNER == op)
+ {
+ // This this was just a check for ownership, we can now return the answer.
+ return (proxy_agent_id == object_owner_id ? TRUE : FALSE);
+ }
+
+ // check permissions to see if the agent can operate
+ return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id));
+}
+
+//-----------------------------------------------------------------------------
+// renderOneSilhouette()
+//-----------------------------------------------------------------------------
+void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
+{
+ LLViewerObject* objectp = getObject();
+ if (!objectp)
+ {
+ return;
+ }
+
+ LLDrawable* drawable = objectp->mDrawable;
+ if(!drawable)
+ {
+ return;
+ }
+
+ if (!mSilhouetteExists)
+ {
+ return;
+ }
+
+ BOOL is_hud_object = objectp->isHUDAttachment();
+
+ if (!drawable->isVisible() && !is_hud_object)
+ {
+ return;
+ }
+
+ if (mSilhouetteVertices.size() == 0 || mSilhouetteNormals.size() != mSilhouetteVertices.size())
+ {
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (!is_hud_object)
+ {
+ glLoadIdentity();
+ glMultMatrixd(gGLModelView);
+ }
+
+
+ if (drawable->isActive())
+ {
+ glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix);
+ }
+
+ LLVolume *volume = objectp->getVolume();
+ if (volume)
+ {
+ F32 silhouette_thickness;
+ if (is_hud_object && gAgent.getAvatarObject())
+ {
+ silhouette_thickness = LLSelectMgr::sHighlightThickness / gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ LLVector3 view_vector = gCamera->getOrigin() - objectp->getRenderPosition();
+ silhouette_thickness = drawable->mDistanceWRTCamera * LLSelectMgr::sHighlightThickness * (gCamera->getView() / gCamera->getDefaultFOV());
+ }
+ F32 animationTime = (F32)LLFrameTimer::getElapsedSeconds();
+
+ F32 u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f);
+ F32 v_coord = 1.f - fmod(animationTime * LLSelectMgr::sHighlightVAnim, 1.f);
+ F32 u_divisor = 1.f / ((F32)(mSilhouetteVertices.size() - 1));
+
+ if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible())
+ {
+ glBlendFunc(GL_SRC_COLOR, GL_ONE);
+ LLGLEnable fog(GL_FOG);
+ glFogi(GL_FOG_MODE, GL_LINEAR);
+ float d = (gCamera->getPointOfInterest()-gCamera->getOrigin()).magVec();
+ LLColor4 fogCol = color * (F32)llclamp((gSelectMgr->getSelectionCenterGlobal()-gAgent.getCameraPositionGlobal()).magVec()/(gSelectMgr->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0);
+ glFogf(GL_FOG_START, d);
+ glFogf(GL_FOG_END, d*(1 + (gCamera->getView() / gCamera->getDefaultFOV())));
+ glFogfv(GL_FOG_COLOR, fogCol.mV);
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL);
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBegin(GL_LINES);
+ {
+ S32 i = 0;
+ for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++)
+ {
+// S32 first_i = i;
+ for(; i < mSilhouetteSegments[seg_num]; i++)
+ {
+ u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[i].mV );
+ }
+
+ /*u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[first_i].mV );*/
+ }
+ }
+ glEnd();
+ u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f);
+ }
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ //glAlphaFunc(GL_GREATER, LLSelectMgr::sHighlightAlphaTest);
+ glBegin(GL_TRIANGLES);
+ {
+ S32 i = 0;
+ for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++)
+ {
+ S32 first_i = i;
+ LLVector3 v;
+ LLVector2 t;
+
+ for(; i < mSilhouetteSegments[seg_num]; i++)
+ {
+
+ if (i == first_i) {
+ LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness;
+ vert += mSilhouetteVertices[i];
+
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
+ glTexCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale );
+ glVertex3fv( vert.mV );
+
+ u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+
+ glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[i].mV );
+
+ v = mSilhouetteVertices[i];
+ t = LLVector2(u_coord, v_coord);
+ }
+ else {
+ LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness;
+ vert += mSilhouetteVertices[i];
+
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
+ glTexCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale );
+ glVertex3fv( vert.mV );
+ glVertex3fv( vert.mV );
+
+ glTexCoord2fv(t.mV);
+ u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+ glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
+ glVertex3fv(v.mV);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[i].mV );
+
+ }
+ }
+ }
+ }
+ glEnd();
+
+ }
+ glPopMatrix();
+}
+
+//
+// Utility Functions
+//
+
+// Update everyone who cares about the selection list
+void dialog_refresh_all()
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ //could refresh selected object info in toolbar here
+
+ gFloaterTools->dirty();
+
+ if( gPieObject->getVisible() )
+ {
+ gPieObject->arrange();
+ }
+
+ LLFloaterProperties::dirtyAll();
+}
+
+S32 get_family_count(LLViewerObject *parent)
+{
+ if (!parent)
+ {
+ llwarns << "Trying to get_family_count on null parent!" << llendl;
+ }
+ S32 count = 1; // for this object
+ for (U32 i = 0; i < parent->mChildList.size(); i++)
+ {
+ LLViewerObject* child = parent->mChildList[i];
+
+ if (!child)
+ {
+ llwarns << "Family object has NULL child! Show Doug." << llendl;
+ }
+ else if (child->isDead())
+ {
+ llwarns << "Family object has dead child object. Show Doug." << llendl;
+ }
+ else
+ {
+ if (gSelectMgr->canSelectObject(child))
+ {
+ count += get_family_count( child );
+ }
+ }
+ }
+ return count;
+}
+
+//-----------------------------------------------------------------------------
+// updateSelectionCenter
+//-----------------------------------------------------------------------------
+void LLSelectMgr::updateSelectionCenter()
+{
+ const F32 MOVE_SELECTION_THRESHOLD = 1.f; // Movement threshold in meters for updating selection
+ // center (tractor beam)
+
+ LLViewerObject* object = mSelectedObjects.getFirstObject();
+ if (!object)
+ {
+ // nothing selected, probably grabbing
+ // Ignore by setting to avatar origin.
+ mSelectionCenterGlobal.clearVec();
+ mShowSelection = FALSE;
+ mSelectionBBox = LLBBox();
+ mPauseRequest = NULL;
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->mHUDTargetZoom = 1.f;
+ gAgent.getAvatarObject()->mHUDCurZoom = 1.f;
+ }
+ }
+ else
+ {
+ mSelectType = getSelectTypeForObject(object);
+
+ if (mSelectType == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ mPauseRequest = gAgent.getAvatarObject()->requestPause();
+ }
+ else
+ {
+ mPauseRequest = NULL;
+ }
+
+ if (mSelectType != SELECT_TYPE_HUD && gAgent.getAvatarObject())
+ {
+ // reset hud ZOOM
+ gAgent.getAvatarObject()->mHUDTargetZoom = 1.f;
+ gAgent.getAvatarObject()->mHUDCurZoom = 1.f;
+ }
+
+ mShowSelection = FALSE;
+ LLBBox bbox;
+
+ // have stuff selected
+ LLVector3d select_center;
+ // keep a list of jointed objects for showing the joint HUDEffects
+ gHUDManager->clearJoints();
+ LLDynamicArray < LLViewerObject *> jointed_objects;
+
+ for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ LLViewerObject *myAvatar = gAgent.getAvatarObject();
+ LLViewerObject *root = object->getRootEdit();
+ if (mSelectType == SELECT_TYPE_WORLD && // not an attachment
+ !root->isChild(myAvatar) && // not the object you're sitting on
+ !object->isAvatar()) // not another avatar
+ {
+ mShowSelection = TRUE;
+ }
+
+ bbox.addBBoxAgent( object->getBoundingBoxAgent() );
+
+ if (object->isJointChild())
+ {
+ jointed_objects.put(object);
+ }
+ } // end for
+
+ LLVector3 bbox_center_agent = bbox.getCenterAgent();
+ mSelectionCenterGlobal = gAgent.getPosGlobalFromAgent(bbox_center_agent);
+ mSelectionBBox = bbox;
+
+ if (jointed_objects.count())
+ {
+ gHUDManager->showJoints(&jointed_objects);
+ }
+ }
+
+ if ( !(gAgentID == LLUUID::null) )
+ {
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (mShowSelection)
+ {
+ LLVector3d select_center_global;
+
+ if( tool->isEditing() )
+ {
+ select_center_global = tool->getEditingPointGlobal();
+ }
+ else
+ {
+ select_center_global = mSelectionCenterGlobal;
+ }
+
+ // Send selection center if moved beyond threshold (used to animate tractor beam)
+ LLVector3d diff;
+ diff = select_center_global - mLastSentSelectionCenterGlobal;
+
+ if ( diff.magVecSquared() > MOVE_SELECTION_THRESHOLD*MOVE_SELECTION_THRESHOLD )
+ {
+ // Transmit updated selection center
+ mLastSentSelectionCenterGlobal = select_center_global;
+ }
+ }
+ }
+
+ // give up edit menu if no objects selected
+ if (gEditMenuHandler == this && getObjectCount() == 0)
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+void LLSelectMgr::updatePointAt()
+{
+ if (mShowSelection)
+ {
+ if (getObjectCount())
+ {
+ LLVector3 select_offset;
+ LLViewerObject *click_object = gObjectList.findObject(gLastHitObjectID);
+ if (click_object && click_object->isSelected())
+ {
+ // clicked on another object in our selection group, use that as target
+ select_offset.setVec(gLastHitObjectOffset);
+ select_offset.rotVec(~click_object->getRenderRotation());
+
+ gAgent.setPointAt(POINTAT_TARGET_SELECT, click_object, select_offset);
+ gAgent.setLookAt(LOOKAT_TARGET_SELECT, click_object, select_offset);
+ }
+ else
+ {
+ // didn't click on an object this time, revert to pointing at center of first object
+ gAgent.setPointAt(POINTAT_TARGET_SELECT, getFirstObject());
+ gAgent.setLookAt(LOOKAT_TARGET_SELECT, getFirstObject());
+ }
+ }
+ else
+ {
+ gAgent.setPointAt(POINTAT_TARGET_CLEAR);
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+ }
+ else
+ {
+ gAgent.setPointAt(POINTAT_TARGET_CLEAR);
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getBBoxOfSelection()
+//-----------------------------------------------------------------------------
+LLBBox LLSelectMgr::getBBoxOfSelection() const
+{
+ return mSelectionBBox;
+}
+
+
+//-----------------------------------------------------------------------------
+// canUndo()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canUndo()
+{
+ return getFirstEditableObject() != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// undo()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::undo()
+{
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ LLUUID group_id(gAgent.getGroupID());
+ sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
+}
+
+//-----------------------------------------------------------------------------
+// canRedo()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canRedo()
+{
+ return getFirstEditableObject() != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// redo()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::redo()
+{
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ LLUUID group_id(gAgent.getGroupID());
+ sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
+}
+
+//-----------------------------------------------------------------------------
+// canDoDelete()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canDoDelete()
+{
+ return getFirstDeleteableObject() != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// doDelete()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::doDelete()
+{
+ selectDelete();
+}
+
+//-----------------------------------------------------------------------------
+// canDeselect()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canDeselect()
+{
+ return !isEmpty();
+}
+
+//-----------------------------------------------------------------------------
+// deselect()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::deselect()
+{
+ deselectAll();
+}
+//-----------------------------------------------------------------------------
+// canDuplicate()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canDuplicate()
+{
+ return getFirstCopyableObject() != NULL;
+}
+//-----------------------------------------------------------------------------
+// duplicate()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::duplicate()
+{
+ LLVector3 offset(0.5f, 0.5f, 0.f);
+ selectDuplicate(offset, TRUE);
+}
+//-----------------------------------------------------------------------------
+// undoRedo()
+//-----------------------------------------------------------------------------
+U32 LLSelectMgr::undoRedo(std::deque<LLSelectAction*> &queue_src, std::deque<LLSelectAction*> &queue_dst, const LLUUID &object_id)
+{
+ if (queue_src.size() == 0)
+ {
+ return 0;
+ }
+
+ U32 update_type = 0;
+ std::deque<LLSelectAction*> temp_queue;
+ LLSelectAction* src_actionp = queue_src.back();
+
+ while (src_actionp->mObjectID != object_id)
+ {
+ temp_queue.push_back(src_actionp);
+ queue_src.pop_back();
+ if (!queue_src.size())
+ {
+ // put everything back
+ LLSelectAction* actionp;
+ while (temp_queue.size())
+ {
+ actionp = temp_queue.back();
+ temp_queue.pop_back();
+ queue_src.push_back(actionp);
+ }
+ return 0;
+ }
+ src_actionp = queue_src.back();
+ }
+
+ if(src_actionp)
+ {
+ LLSelectAction* dst_actionp = new LLSelectAction();
+ dst_actionp->mActionType = src_actionp->mActionType;
+ dst_actionp->mObjectID = src_actionp->mObjectID;
+ dst_actionp->mIndividualSelection = src_actionp->mIndividualSelection;
+
+ LLViewerObject* object = gObjectList.findObject(src_actionp->mObjectID);
+ if (object && object->mDrawable.notNull())
+ {
+ LLVector3 old_position_local = object->getPosition();
+
+ switch(src_actionp->mActionType)
+ {
+ case SELECT_ACTION_TYPE_MOVE:
+ dst_actionp->mPosition = object->mDrawable->getPosition();
+ object->setPosition(src_actionp->mPosition, TRUE);
+ if (object->isRootEdit() && src_actionp->mIndividualSelection)
+ {
+ // counter-translate children
+ LLVector3 parent_offset = (src_actionp->mPosition - old_position_local) * ~object->getRotation();
+
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+ childp->setPosition(childp->getPosition() - parent_offset);
+ }
+ }
+ update_type |= UPD_POSITION;
+ break;
+ case SELECT_ACTION_TYPE_ROTATE:
+ dst_actionp->mPosition = object->mDrawable->getPosition();
+ dst_actionp->mRotation = object->mDrawable->getRotation();
+ object->setRotation(src_actionp->mRotation, TRUE);
+ object->setPosition(src_actionp->mPosition, TRUE);
+ if (object->isRootEdit() && src_actionp->mIndividualSelection)
+ {
+ // counter-translate and rotate children
+ LLVector3 parent_offset = (src_actionp->mPosition - old_position_local) * ~object->getRotation();
+
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+ LLQuaternion delta_rot_inv = dst_actionp->mRotation * ~src_actionp->mRotation;
+ childp->setPosition((childp->getPosition() * delta_rot_inv) - parent_offset);
+ childp->setRotation(childp->getRotation() * delta_rot_inv );
+ }
+ }
+ update_type |= UPD_ROTATION | UPD_POSITION;
+ break;
+ case SELECT_ACTION_TYPE_SCALE:
+ dst_actionp->mPosition = object->mDrawable->getPosition();
+ dst_actionp->mScale = object->getScale();
+ object->setScale(src_actionp->mScale, TRUE);
+ object->setPosition(src_actionp->mPosition, TRUE);
+ update_type |= UPD_SCALE | UPD_POSITION;
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ }
+ queue_src.pop_back();
+ delete src_actionp;
+ queue_dst.push_back(dst_actionp);
+ while (queue_dst.size() > (U32)MAX_ACTION_QUEUE_SIZE)
+ {
+ LLSelectAction* action = queue_dst.front();
+ queue_dst.pop_front();
+ delete action;
+ }
+
+ }
+
+ // put everything back
+ LLSelectAction* actionp;
+ while (temp_queue.size())
+ {
+ actionp = temp_queue.back();
+ temp_queue.pop_back();
+ queue_src.push_back(actionp);
+ }
+
+ return update_type;
+}
+
+ESelectType LLSelectMgr::getSelectTypeForObject(LLViewerObject* object)
+{
+ if (!object)
+ {
+ return SELECT_TYPE_WORLD;
+ }
+ if (object->isHUDAttachment())
+ {
+ return SELECT_TYPE_HUD;
+ }
+ else if (object->isAttachment())
+ {
+ return SELECT_TYPE_ATTACHMENT;
+ }
+ else
+ {
+ return SELECT_TYPE_WORLD;
+ }
+}
+
+void LLSelectMgr::validateSelection()
+{
+ LLViewerObject* objectp;
+ for (objectp = getFirstObject(); objectp; objectp = getNextObject())
+ {
+ if (!canSelectObject(objectp))
+ {
+ deselectObjectOnly(objectp);
+ }
+ }
+}
+
+BOOL LLSelectMgr::canSelectObject(LLViewerObject* object)
+{
+ if (mForceSelection)
+ {
+ return TRUE;
+ }
+
+ if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !object->permYouOwner()) ||
+ (gSavedSettings.getBOOL("SelectMovableOnly") && !object->permMove()))
+ {
+ // only select my own objects
+ return FALSE;
+ }
+
+ // Can't select dead objects
+ if (object->isDead()) return FALSE;
+
+ // Can't select orphans
+ if (object->isOrphaned()) return FALSE;
+
+ // Can't select avatars
+ if (object->isAvatar()) return FALSE;
+
+ // Can't select land
+ if (object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) return FALSE;
+
+ ESelectType selection_type = getSelectTypeForObject(object);
+ if (getObjectCount() > 0 && mSelectType != selection_type) return FALSE;
+
+ return TRUE;
+}
+
+LLSelectNodeList::LLSelectNodeList() : std::list<LLSelectNode*>()
+{
+ mCurrentTE = -1;
+ mCurrentNode = end();
+}
+
+LLSelectNodeList::~LLSelectNodeList()
+{
+ std::for_each(begin(), end(), DeletePointer());
+}
+
+void LLSelectNodeList::updateEffects()
+{
+}
+
+S32 LLSelectNodeList::getNumNodes()
+{
+ return size();
+}
+
+void LLSelectNodeList::addNode(LLSelectNode *nodep)
+{
+ push_front(nodep);
+ mSelectNodeMap[nodep->getObject()] = nodep;
+}
+
+void LLSelectNodeList::addNodeAtEnd(LLSelectNode *nodep)
+{
+ push_back(nodep);
+ mSelectNodeMap[nodep->getObject()] = nodep;
+}
+
+void LLSelectNodeList::removeNode(LLSelectNode *nodep)
+{
+ std::list<LLSelectNode*>::iterator iter;
+ for (iter = begin(); iter != end(); ++iter)
+ {
+ if ((*iter) == nodep)
+ {
+ mSelectNodeMap.erase(nodep->getObject());
+ erase(iter++);
+ }
+ }
+}
+
+void LLSelectNodeList::deleteAllNodes()
+{
+ std::for_each(begin(), end(), DeletePointer());
+ clear();
+ mSelectNodeMap.clear();
+}
+
+LLSelectNode* LLSelectNodeList::findNode(LLViewerObject* objectp)
+{
+ std::map<LLViewerObject*, LLSelectNode*>::iterator found_it = mSelectNodeMap.find(objectp);
+ if (found_it != mSelectNodeMap.end())
+ {
+ return found_it->second;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getFirstNode()
+{
+ mCurrentNode = begin();//getFirstData();
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return *mCurrentNode;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getCurrentNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getCurrentNode()
+{
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return *mCurrentNode;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getNextNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getNextNode()
+{
+ ++mCurrentNode;
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return *mCurrentNode;
+ }
+ return NULL;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// getFirstObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectNodeList::getFirstObject()
+{
+ mCurrentNode = begin();
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return (*mCurrentNode)->getObject();
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectNodeList::getNextObject()
+{
+ ++mCurrentNode;// = getNextData();
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return (*mCurrentNode)->getObject();
+ }
+
+ return NULL;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// getPrimaryTE()
+//-----------------------------------------------------------------------------
+void LLSelectNodeList::getPrimaryTE(LLViewerObject* *object, S32 *te)
+{
+ // initialize object and te
+ *te = 0;
+ *object = NULL;
+
+ BOOL searching_roots = TRUE;
+
+ // try for root node first, then first child node
+ LLSelectNode *primary_node = getFirstNode(); //getFirstRootNode();
+ if (!primary_node)
+ {
+ primary_node = getFirstNode();
+ searching_roots = FALSE;
+ }
+
+ while (primary_node)
+ {
+ S32 last_selected_te = primary_node->getLastSelectedTE();
+ if (last_selected_te >= 0)
+ {
+ *object = primary_node->getObject();
+ *te = last_selected_te;
+ return;
+ }
+ for(S32 cur_te = 0; cur_te < primary_node->getObject()->getNumTEs(); cur_te++)
+ {
+ // if face selected
+ if (primary_node->isTESelected(cur_te))
+ {
+ // return this object and face
+ *object = primary_node->getObject();
+ *te = cur_te;
+ return;
+ }
+ }
+ if (searching_roots)
+ {
+ primary_node = getNextRootNode();
+ if (!primary_node)
+ {
+ primary_node = getFirstNode();
+ searching_roots = FALSE;
+ }
+ }
+ else
+ {
+ primary_node = getNextNode();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getFirstTE()
+//-----------------------------------------------------------------------------
+void LLSelectNodeList::getFirstTE(LLViewerObject* *object, S32 *te)
+{
+ // start with first face
+ mCurrentTE = 0;
+
+ LLSelectNode *cur_node = getFirstNode();
+
+ // repeat over all selection nodes
+ while (cur_node)
+ {
+ // skip objects with no faces
+ if (cur_node->getObject()->getNumTEs() == 0)
+ {
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ continue;
+ }
+
+ // repeat over all faces for this object
+ while (mCurrentTE < cur_node->getObject()->getNumTEs())
+ {
+ // if face selected
+ if (cur_node->isTESelected(mCurrentTE))
+ {
+ // return this object and face
+ *object = cur_node->getObject();
+ *te = mCurrentTE;
+ return;
+ }
+
+ mCurrentTE++;
+ }
+
+ // Couldn't find a selected face.
+ // This can happen if an object's volume parameters are changed in such a way
+ // that texture entries are eliminated.
+ //
+ // TODO: Consider selecting all faces in this case? Subscribe the selection
+ // list to the volume changing code?
+
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ }
+
+ // The list doesn't contain any nodes. Return NULL.
+ *object = NULL;
+ *te = -1;
+ return;
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextFace()
+//-----------------------------------------------------------------------------
+void LLSelectNodeList::getNextTE(LLViewerObject* *object, S32 *te)
+{
+ // try next face
+ mCurrentTE++;
+
+ LLSelectNode *cur_node = getCurrentNode();
+ // repeat over remaining selection nodes
+ while ( cur_node )
+ {
+ // skip objects with no faces
+ if (cur_node->getObject()->getNumTEs() == 0)
+ {
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ continue;
+ }
+
+ // repeat over all faces for this object
+ // CRO: getNumTEs() no longer equals mFaces.count(), so use mFaces.count() instead
+ while ( mCurrentTE < cur_node->getObject()->getNumTEs() )
+ {
+ // if face selected
+ if (cur_node->isTESelected(mCurrentTE))
+ {
+ // return this object and face
+ *object = cur_node->getObject();
+ *te = mCurrentTE;
+ return;
+ }
+
+ mCurrentTE++;
+ }
+
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ }
+
+ // The list doesn't contain any nodes. Return NULL.
+ *object = NULL;
+ *te = -1;
+ return;
+}
+
+void LLSelectNodeList::getCurrentTE(LLViewerObject* *object, S32 *te)
+{
+ if (mCurrentNode != end())
+ {
+ *object = (*mCurrentNode)->getObject();
+ *te = mCurrentTE;
+ }
+ else
+ {
+ *object = NULL;
+ *te = -1;
+ }
+}
+//-----------------------------------------------------------------------------
+// getFirstRootNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getFirstRootNode()
+{
+ LLSelectNode *cur_node = getFirstNode();
+
+ // scan through child objects and roots set to ignore
+ while (cur_node &&
+ (!(cur_node->getObject()->isRootEdit() || cur_node->getObject()->isJointChild()) ||
+ cur_node->mIndividualSelection))
+ {
+ cur_node = getNextNode();
+ }
+
+ return cur_node;
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextRootNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getNextRootNode()
+{
+ LLSelectNode *cur_node = getNextNode();
+
+ while (cur_node &&
+ (!(cur_node->getObject()->isRootEdit() || cur_node->getObject()->isJointChild()) ||
+ cur_node->mIndividualSelection))
+ {
+ cur_node = getNextNode();
+ }
+
+ return cur_node;
+}
+
+
+//-----------------------------------------------------------------------------
+// getFirstRootObject()
+//-----------------------------------------------------------------------------
+LLViewerObject *LLSelectNodeList::getFirstRootObject()
+{
+ LLSelectNode *node = getFirstRootNode();
+
+ if (node)
+ {
+ return node->getObject();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextRootObject()
+//-----------------------------------------------------------------------------
+LLViewerObject *LLSelectNodeList::getNextRootObject()
+{
+ LLSelectNode *node = getNextRootNode();
+
+ if (node)
+ {
+ return node->getObject();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// isEmpty()
+//-----------------------------------------------------------------------------
+BOOL LLSelectNodeList::isEmpty()
+{
+ return (size() == 0);
+}
+
+//-----------------------------------------------------------------------------
+// getOwnershipCost()
+//-----------------------------------------------------------------------------
+BOOL LLSelectNodeList::getOwnershipCost(S32 &cost)
+{
+ S32 count = 0;
+ for( LLSelectNode* nodep = getFirstNode(); nodep; nodep = getNextNode() )
+ {
+ count++;
+ }
+
+ cost = count * OWNERSHIP_COST_PER_OBJECT;
+
+ return (count > 0);
+}
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
new file mode 100644
index 0000000000..df07e5518f
--- /dev/null
+++ b/indra/newview/llselectmgr.h
@@ -0,0 +1,625 @@
+/**
+ * @file llselectmgr.h
+ * @brief A manager for selected objects and TEs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSELECTMGR_H
+#define LL_LLSELECTMGR_H
+
+#include "llcharacter.h"
+#include "lldarray.h"
+#include "lleditmenuhandler.h"
+#include "llstring.h"
+#include "llundo.h"
+#include "lluuid.h"
+#include "llmemory.h"
+#include "llsaleinfo.h"
+#include "llcategory.h"
+#include "v3dmath.h"
+#include "llquaternion.h"
+#include "llcoord.h"
+#include "llframetimer.h"
+#include "llbbox.h"
+#include "llpermissions.h"
+
+#include <deque>
+
+class LLMessageSystem;
+class LLViewerImage;
+class LLViewerObject;
+class LLColor4;
+class LLVector3;
+class LLSelectNode;
+
+const S32 SELECT_ALL_TES = -1;
+const S32 SELECT_MAX_TES = 32;
+
+// Do something to all objects in the selection manager.
+// The bool return value can be used to indicate if all
+// objects are identical (gathering information) or if
+// the operation was successful.
+class LLSelectedObjectFunctor
+{
+public:
+ virtual ~LLSelectedObjectFunctor() {};
+ virtual bool apply(LLViewerObject* object) = 0;
+};
+
+// Do something to all select nodes in the selection manager.
+// The bool return value can be used to indicate if all
+// objects are identical (gathering information) or if
+// the operation was successful.
+class LLSelectedNodeFunctor
+{
+public:
+ virtual ~LLSelectedNodeFunctor() {};
+ virtual bool apply(LLSelectNode* node) = 0;
+};
+
+typedef enum e_send_type
+{
+ SEND_ONLY_ROOTS,
+ SEND_INDIVIDUALS,
+ SEND_ROOTS_FIRST, // useful for serial undos on linked sets
+ SEND_CHILDREN_FIRST // useful for serial transforms of linked sets
+} ESendType;
+
+typedef enum e_grid_mode
+{
+ GRID_MODE_WORLD,
+ GRID_MODE_LOCAL,
+ GRID_MODE_REF_OBJECT
+} EGridMode;
+
+typedef enum e_action_type
+{
+ SELECT_ACTION_TYPE_BEGIN,
+ SELECT_ACTION_TYPE_PICK,
+ SELECT_ACTION_TYPE_MOVE,
+ SELECT_ACTION_TYPE_ROTATE,
+ SELECT_ACTION_TYPE_SCALE,
+ NUM_ACTION_TYPES
+}EActionType;
+
+typedef enum e_selection_type
+{
+ SELECT_TYPE_WORLD,
+ SELECT_TYPE_ATTACHMENT,
+ SELECT_TYPE_HUD
+}ESelectType;
+
+class LLSelectNodeList : public std::list<LLSelectNode*>
+{
+public:
+ LLSelectNodeList();
+ virtual ~LLSelectNodeList();
+
+ void updateEffects();
+
+ BOOL isEmpty();
+
+ S32 getOwnershipCost(S32 &cost);
+
+ LLSelectNode* getFirstNode();
+ LLSelectNode* getCurrentNode(); // Warning! This is NOT the same as the linked_list getCurrentNode
+ LLSelectNode* getNextNode();
+
+ LLSelectNode *getFirstRootNode();
+ LLSelectNode *getNextRootNode();
+
+ // iterate through objects
+ LLViewerObject* getFirstObject();
+ LLViewerObject* getNextObject();
+
+ // iterate through root objects
+ LLViewerObject *getFirstRootObject();
+ LLViewerObject *getNextRootObject();
+
+ // iterate through texture entries
+ void getPrimaryTE(LLViewerObject* *object, S32 *te);
+ void getFirstTE(LLViewerObject* *object, S32 *te);
+ void getNextTE(LLViewerObject* *object, S32 *te);
+ void getCurrentTE(LLViewerObject* *object, S32 *te);
+
+ void addNode(LLSelectNode *nodep);
+ void addNodeAtEnd(LLSelectNode *nodep);
+ void removeNode(LLSelectNode *nodep);
+ void deleteAllNodes(); // Delete all nodes
+ S32 getNumNodes();
+ LLSelectNode* findNode(LLViewerObject* objectp);
+private:
+ const LLSelectNodeList &operator=(const LLSelectNodeList &);
+
+ std::list<LLSelectNode*>::iterator mCurrentNode;
+ S32 mCurrentTE;
+ std::map<LLViewerObject*, LLSelectNode*> mSelectNodeMap;
+};
+
+struct LLSelectAction
+{
+public:
+ EActionType mActionType;
+ LLVector3 mPosition;
+ LLVector3 mScale;
+ LLQuaternion mRotation;
+ LLUUID mObjectID;
+ BOOL mIndividualSelection;
+};
+
+class LLSelectMgr : public LLEditMenuHandler
+{
+public:
+ static BOOL sRectSelectInclusive; // do we need to surround an object to pick it?
+ static BOOL sRenderHiddenSelections; // do we show selection silhouettes that are occluded?
+ static BOOL sRenderLightRadius; // do we show the radius of selected lights?
+ static F32 sHighlightThickness;
+ static F32 sHighlightUScale;
+ static F32 sHighlightVScale;
+ static F32 sHighlightAlpha;
+ static F32 sHighlightAlphaTest;
+ static F32 sHighlightUAnim;
+ static F32 sHighlightVAnim;
+ static LLColor4 sSilhouetteParentColor;
+ static LLColor4 sSilhouetteChildColor;
+ static LLColor4 sHighlightParentColor;
+ static LLColor4 sHighlightChildColor;
+ static LLColor4 sContextSilhouetteColor;
+public:
+ LLSelectMgr();
+ ~LLSelectMgr();
+
+ // LLEditMenuHandler interface
+ virtual BOOL canUndo();
+ virtual void undo();
+
+ virtual BOOL canRedo();
+ virtual void redo();
+
+ virtual BOOL canDoDelete();
+ virtual void doDelete();
+
+ virtual void deselect();
+ virtual BOOL canDeselect();
+
+ virtual void duplicate();
+ virtual BOOL canDuplicate();
+
+ // Apply functors to various subsets of the selected objects
+ // Returns the AND of all apply() calls.
+ bool applyToRootObjects(LLSelectedObjectFunctor* func);
+ bool applyToObjects(LLSelectedObjectFunctor* func);
+ bool applyToNodes(LLSelectedNodeFunctor* func);
+
+ void updateEffects(); // Update HUD effects
+
+ void setForceSelection(BOOL force) { mForceSelection = force; }
+
+ // For when you want just a child object.
+ void selectObjectOnly(LLViewerObject* object, S32 face = SELECT_ALL_TES);
+
+ // This method is meant to select an object, and then select all
+ // of the ancestors and descendents. This should be the normal behavior.
+ void selectObjectAndFamily(LLViewerObject* object, BOOL add_to_end = FALSE);
+
+ // Same as above, but takes a list of objects. Used by rectangle select.
+ void selectObjectAndFamily(const LLDynamicArray<LLViewerObject*>& object_list, BOOL send_to_sim = TRUE);
+
+ void deselectObjectOnly(LLViewerObject* object, BOOL send_to_sim = TRUE);
+
+ void deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim = TRUE);
+
+ void deselectTransient(); // deselect "temporarily" selected objects (via pie menu)
+ void convertTransient(); // converts temporarily selected objects to full-fledged selections
+
+ // Send deselect messages to simulator, then clear the list
+ void deselectAll();
+
+ // Deselect if the selection center is too far away from the agent.
+ void deselectAllIfTooFar();
+
+ BOOL selectionRemoveObject(const LLUUID &id);
+
+ BOOL contains(LLViewerObject* object);
+ BOOL contains(LLViewerObject* object, S32 te);
+
+ ESelectType getSelectType() { return mSelectType; }
+
+ // count members
+ S32 getObjectCount();
+ S32 getTECount();
+ S32 getRootObjectCount();
+
+ BOOL isEmpty() { return !mSelectedObjects.getNumNodes(); }
+
+ BOOL shouldShowSelection() { return mShowSelection; }
+
+ LLSelectNodeList &getHoverObjects() { return mHoverObjects; }
+ LLSelectNodeList &getSelectedObjects() { return mSelectedObjects; }
+
+ // iterate through objects
+ LLViewerObject* getFirstObject() { return mSelectedObjects.getFirstObject(); }
+ LLViewerObject* getNextObject() { return mSelectedObjects.getNextObject(); }
+
+ // iterate through root objects
+ LLViewerObject *getFirstRootObject() { return mSelectedObjects.getFirstRootObject(); }
+ LLViewerObject *getNextRootObject() { return mSelectedObjects.getNextRootObject(); }
+
+ LLViewerObject* getFirstHighlightedObject() { return mHighlightedObjects.getFirstObject(); }
+ LLViewerObject* getNextHighlightedObject() { return mHighlightedObjects.getNextObject(); }
+
+ // iterate through tes
+ void getPrimaryTE(LLViewerObject* *object, S32 *te) { mSelectedObjects.getPrimaryTE(object, te); }
+ void getFirstTE(LLViewerObject* *object, S32 *te) { mSelectedObjects.getFirstTE(object, te); }
+ void getNextTE(LLViewerObject* *object, S32 *te) { mSelectedObjects.getNextTE(object, te); };
+
+ void setHoverObject(LLViewerObject *objectp);
+
+ void addGridObject(LLViewerObject* objectp);
+ void clearGridObjects();
+ void setGridMode(EGridMode mode);
+ EGridMode getGridMode() { return mGridMode; }
+ void getGrid(LLVector3& origin, LLQuaternion& rotation, LLVector3 &scale);
+
+ void highlightObjectOnly(LLViewerObject *objectp);
+ void highlightObjectAndFamily(LLViewerObject *objectp);
+ void highlightObjectAndFamily(const LLDynamicArray<LLViewerObject*>& list);
+ void unhighlightObjectOnly(LLViewerObject *objectp);
+ void unhighlightObjectAndFamily(LLViewerObject *objectp);
+ void unhighlightAll();
+ void selectHighlightedObjects();
+ void deselectHighlightedObjects();
+
+ LLSelectNode *findSelectNode(LLViewerObject *objectp);
+ LLSelectNode *getFirstRootNode() { return mSelectedObjects.getFirstRootNode(); }
+ LLSelectNode *getNextRootNode() { return mSelectedObjects.getNextRootNode(); }
+ LLSelectNode* getFirstNode() { return mSelectedObjects.getFirstNode(); }
+ LLSelectNode* getNextNode() { return mSelectedObjects.getNextNode(); }
+
+ LLSelectNode *getHoverNode();
+
+ BOOL getTEMode() { return mTEMode; }
+ void setTEMode(BOOL b) { mTEMode = b; }
+
+ LLViewerObject* getFirstCopyableObject(BOOL get_root = FALSE);
+ LLViewerObject* getFirstEditableObject(BOOL get_root = FALSE);
+ LLViewerObject* getFirstMoveableObject(BOOL get_root = FALSE);
+ LLViewerObject* getFirstDeleteableObject(BOOL get_root = FALSE);
+
+ LLSelectNode* getFirstEditableNode(BOOL get_root = FALSE);
+ LLSelectNode* getFirstMoveableNode(BOOL get_root = FALSE);
+
+ LLBBox getBBoxOfSelection() const;
+ LLBBox getSavedBBoxOfSelection() const { return mSavedSelectionBBox; }
+
+ BOOL areMultpleEditableObjectsSelected();
+
+ void dump();
+ void cleanup();
+
+ void updateSilhouettes();
+ void renderSilhouettes(BOOL for_hud);
+ void enableSilhouette(BOOL enable) { mRenderSilhouettes = enable; }
+
+ // Utility functions to operate on the list
+
+ void saveSelectedObjectTransform(EActionType action_type);
+ void saveSelectedObjectColors();
+ void saveSelectedObjectTextures();
+
+ void selectionUpdatePhysics(BOOL use_physics);
+ void selectionUpdateTemporary(BOOL is_temporary);
+ void selectionUpdatePhantom(BOOL is_ghost);
+ void selectionUpdateCastShadows(BOOL cast_shadows);
+ void selectionDump();
+
+ BOOL selectionAllPCode(LLPCode code); // all objects have this PCode
+ BOOL selectionGetMaterial(U8 *material); // all objects have same material
+ BOOL selectionGetTexUUID(LLUUID& id); // true if all selected tes have same texture
+ BOOL selectionGetColor(LLColor4 &color); // all tes have same color
+ BOOL selectionGetTexScale(F32 *u, F32 *v); // true if all selected tes have same scale
+ BOOL selectionGetTexOffset(F32 *u, F32 *v); // true if all selected tes have same offset
+ BOOL selectionGetTexRotation(F32 *rad); // true if all selected tes have same rotation
+ BOOL selectionGetBumpmap(U8 *bumpmap); // true if all selected tes have same
+ BOOL selectionGetShiny(U8 *shiny); // true if all selected tes have same
+ BOOL selectionGetFullbright(U8 *fullbright);// true if all selected tes have same
+ bool selectionGetMediaType(U8 *media_type); // true if all selected tes have same
+ BOOL selectionGetClickAction(U8* action);
+
+ void selectionSetMaterial(U8 material);
+ void selectionSetImage(const LLUUID& imageid); // could be item or asset id
+ void selectionSetColor(const LLColor4 &color);
+ void selectionSetColorOnly(const LLColor4 &color); // Set only the RGB channels
+ void selectionSetAlphaOnly(const F32 alpha); // Set only the alpha channel
+ void selectionRevertColors();
+ BOOL selectionRevertTextures();
+ void selectionSetBumpmap( U8 bumpmap );
+ void selectionSetTexGen( U8 texgen );
+ void selectionSetShiny( U8 shiny );
+ void selectionSetFullbright( U8 fullbright );
+ void selectionSetMediaTypeAndURL( U8 media_type, const std::string& media_url );
+ void selectionSetClickAction(U8 action);
+
+ void setObjectPermissions(U8 perm_field, BOOL set, U32 perm_mask, BOOL override = FALSE);
+ void setObjectName(const LLString& name);
+ void setObjectDescription(const LLString& desc);
+ void setObjectCategory(const LLCategory& category);
+ void setObjectSaleInfo(const LLSaleInfo& sale_info);
+
+ void selectionTexScaleAutofit(F32 repeats_per_meter);
+ void selectionResetTexInfo(S32 te); // sets S,T to 1
+ void adjustTexturesByScale(BOOL send_to_sim, BOOL stretch);
+ BOOL getTESTAxes(const LLViewerObject* object, const U8 te, U32* s_axis, U32* t_axis); // Only for flex boxes
+
+ void selectionResetRotation(); // sets rotation quat to identity
+ void selectionRotateAroundZ(F32 degrees);
+ void sendGodlikeRequest(const LLString& request, const LLString& parameter);
+
+
+ // will make sure all selected object meet current criteria, or deselect them otherwise
+ void validateSelection();
+
+ // returns TRUE if it is possible to select this object
+ BOOL canSelectObject(LLViewerObject* object);
+
+ // Returns true if the viewer has information on all selected objects
+ BOOL selectGetAllRootsValid();
+ BOOL selectGetAllValid();
+
+ // returns TRUE if you can modify all selected objects.
+ BOOL selectGetRootsModify();
+ BOOL selectGetModify();
+
+ // returns TRUE if selected objects can be transferred.
+ BOOL selectGetRootsTransfer();
+
+ // returns TRUE if selected objects can be copied.
+ BOOL selectGetRootsCopy();
+
+ BOOL selectGetCreator(LLUUID& id, LLString& name); // true if all have same creator, returns id
+ BOOL selectGetOwner(LLUUID& id, LLString& name); // true if all objects have same owner, returns id
+ BOOL selectGetLastOwner(LLUUID& id, LLString& name); // true if all objects have same owner, returns id
+
+ // returns TRUE if all are the same. id is stuffed with
+ // the value found if available.
+ BOOL selectGetGroup(LLUUID& id);
+ BOOL selectGetPerm( U8 which_perm, U32* mask_on, U32* mask_off); // true if all have data, returns two masks, each indicating which bits are all on and all off
+ BOOL selectGetOwnershipCost(S32* cost); // sum of all ownership costs
+
+ BOOL selectIsGroupOwned(); // true if all root objects have valid data and are group owned.
+
+ // returns TRUE if all the nodes are valid. Accumulates
+ // permissions in the parameter.
+ BOOL selectGetPermissions(LLPermissions& perm);
+
+ // returns TRUE if anything is for sale. calculates the total
+ // price and stores that value in price.
+ BOOL selectIsForSale(S32& price);
+
+ // returns TRUE if all nodes are valid.
+ BOOL selectGetCategory(LLCategory& category);
+
+ // returns TRUE if all nodes are valid. method also stores an
+ // accumulated sale info.
+ BOOL selectGetSaleInfo(LLSaleInfo& sale_info);
+
+ // returns TRUE if all nodes are valid. fills passed in object
+ // with the aggregate permissions of the selection.
+ BOOL selectGetAggregatePermissions(LLAggregatePermissions& ag_perm);
+
+ // returns TRUE if all nodes are valid. fills passed in object
+ // with the aggregate permissions for texture inventory items of the selection.
+ BOOL selectGetAggregateTexturePermissions(LLAggregatePermissions& ag_perm);
+
+ // returns TRUE is any node is currenly worn as an attachment
+ BOOL selectionIsAttachment();
+
+ LLPermissions* findObjectPermissions(const LLViewerObject* object);
+
+ void selectDelete(); // Delete on simulator
+ void selectForceDelete(); // just delete, no into trash
+ void selectDuplicate(const LLVector3& offset, BOOL select_copy); // Duplicate on simulator
+ void repeatDuplicate();
+ void selectDuplicateOnRay(const LLVector3 &ray_start_region,
+ const LLVector3 &ray_end_region,
+ BOOL bypass_raycast,
+ BOOL ray_end_is_intersection,
+ const LLUUID &ray_target_id,
+ BOOL copy_centers,
+ BOOL copy_rotates,
+ BOOL select_copy);
+
+ void sendMultipleUpdate(U32 type); // Position, rotation, scale all in one
+ void sendOwner(const LLUUID& owner_id, const LLUUID& group_id, BOOL override = FALSE);
+ void sendGroup(const LLUUID& group_id);
+
+ // Category ID is the UUID of the folder you want to contain the purchase
+ // FIXME: sale_info check doesn't work for multiple object buy, which UI does not currently support
+ // sale info is used for verification only, if it doesn't match region info then sale is canceled
+ void sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info);
+ void sendAttach(U8 attachment_point);
+ void sendDetach();
+ void sendDropAttachment();
+ void sendLink();
+ void sendDelink();
+ void sendHinge(U8 type);
+ void sendDehinge();
+ void sendSelect();
+
+ void requestObjectPropertiesFamily(LLViewerObject* object); // asks sim for creator, permissions, resources, etc.
+ static void processObjectProperties(LLMessageSystem *mesgsys, void **user_data);
+ static void processObjectPropertiesFamily(LLMessageSystem *mesgsys, void **user_data);
+ static void processForceObjectSelect(LLMessageSystem* msg, void**);
+
+ void requestGodInfo();
+
+ LLVector3d getSelectionCenterGlobal() const { return mSelectionCenterGlobal; }
+ void updateSelectionCenter();
+ void updatePointAt();
+
+ // Internal list maintenance functions. TODO: Make these private!
+ void remove(LLDynamicArray<LLViewerObject*>& objects);
+ void remove(LLViewerObject* object, S32 te = SELECT_ALL_TES, BOOL undoable = TRUE);
+ void removeAll();
+ void addAsIndividual(LLViewerObject* object, S32 te = SELECT_ALL_TES, BOOL undoable = TRUE);
+ void promoteSelectionToRoot();
+ void demoteSelectionToIndividuals();
+
+private:
+
+ ESelectType getSelectTypeForObject(LLViewerObject* object);
+ void addAsFamily(LLDynamicArray<LLViewerObject*>& objects, BOOL add_to_end = FALSE);
+ void generateSilhouette(LLSelectNode *nodep, const LLVector3& view_point);
+ void getSilhouetteExtents(LLSelectNode* nodep, const LLQuaternion& orientation, LLVector3& min_extents, LLVector3& max_extents);
+ // Send one message to each region containing an object on selection list.
+ void sendListToRegions( const LLString& message_name,
+ void (*pack_header)(void *user_data),
+ void (*pack_body)(LLSelectNode* node, void *user_data),
+ void *user_data,
+ ESendType send_type);
+ U32 undoRedo(std::deque<LLSelectAction*> &queue_src, std::deque<LLSelectAction*> &queue_dst, const LLUUID &object_id);
+
+ static void packAgentID( void *);
+ static void packAgentAndSessionID(void* user_data);
+ static void packAgentAndGroupID(void* user_data);
+ static void packAgentAndSessionAndGroupID(void* user_data);
+ static void packAgentIDAndSessionAndAttachment(void*);
+ static void packAgentGroupAndCatID(void*);
+ static void packDeleteHeader(void* userdata);
+ static void packDeRezHeader(void* user_data);
+ static void packObjectID( LLSelectNode* node, void *);
+ static void packObjectIDAsParam(LLSelectNode* node, void *);
+ static void packObjectIDAndRotation( LLSelectNode* node, void *);
+ static void packObjectLocalID(LLSelectNode* node, void *);
+ static void packObjectClickAction(LLSelectNode* node, void* data);
+ static void packObjectName(LLSelectNode* node, void* user_data);
+ static void packObjectDescription(LLSelectNode* node, void* user_data);
+ static void packObjectCategory(LLSelectNode* node, void* user_data);
+ static void packObjectSaleInfo(LLSelectNode* node, void* user_data);
+ static void packBuyObjectIDs(LLSelectNode* node, void* user_data);
+ static void packDuplicate( LLSelectNode* node, void *duplicate_data);
+ static void packDuplicateHeader(void*);
+ static void packDuplicateOnRayHead(void *user_data);
+ static void packPermissions(LLSelectNode* node, void *user_data);
+ static void packDeselect( LLSelectNode* node, void *user_data);
+ static void packMultipleUpdate(LLSelectNode* node, void *user_data);
+ static void packPhysics(LLSelectNode* node, void *user_data);
+ static void packShape(LLSelectNode* node, void *user_data);
+ static void packOwnerHead(void *user_data);
+ static void packHingeHead(void *user_data);
+ static void packPermissionsHead(void* user_data);
+ static void packGodlikeHead(void* user_data);
+ static void confirmDelete(S32 option, void* data);
+
+private:
+ LLPointer<LLViewerImage> mSilhouetteImagep;
+
+ LLSelectNodeList mSelectedObjects;
+
+ LLSelectNodeList mHoverObjects;
+
+ std::set<LLPointer<LLViewerObject> > mRectSelectedObjects;
+
+ LLSelectNodeList mGridObjects;
+ LLQuaternion mGridRotation;
+ LLVector3 mGridOrigin;
+ LLVector3 mGridScale;
+ EGridMode mGridMode;
+ BOOL mGridValid;
+
+ LLSelectNodeList mHighlightedObjects;
+
+ BOOL mTEMode; // render te
+ LLVector3d mSelectionCenterGlobal;
+ LLBBox mSelectionBBox;
+
+ LLVector3d mLastSentSelectionCenterGlobal;
+ BOOL mShowSelection; // do we send the selection center name value and do we animate this selection?
+ LLVector3d mLastCameraPos; // camera position from last generation of selection silhouette
+ BOOL mRenderSilhouettes; // do we render the silhouette
+ LLBBox mSavedSelectionBBox;
+
+ ESelectType mSelectType;
+
+ LLFrameTimer mEffectsTimer;
+ BOOL mForceSelection;
+
+ std::deque<LLSelectAction*> mUndoQueue;
+ std::deque<LLSelectAction*> mRedoQueue;
+
+ LLAnimPauseRequest mPauseRequest;
+};
+
+
+// Contains information about a selected object, particularly which
+// tes are selected.
+class LLSelectNode
+{
+public:
+ LLSelectNode(LLViewerObject* object, BOOL do_glow);
+ LLSelectNode(const LLSelectNode& nodep);
+ ~LLSelectNode();
+
+ void selectAllTEs(BOOL b);
+ void selectTE(S32 te_index, BOOL selected);
+ BOOL isTESelected(S32 te_index);
+ S32 getLastSelectedTE();
+ void renderOneSilhouette(const LLColor4 &color);
+ void setTransient(BOOL transient) { mTransient = transient; }
+ BOOL isTransient() { return mTransient; }
+ LLViewerObject *getObject();
+ //FIXME: invalidate stored textures and colors when # faces change
+ void saveColors();
+ void saveTextures(const std::vector<LLUUID>& textures);
+ void saveTextureScaleRatios();
+
+ BOOL allowOperationOnNode(PermissionBit op, U64 group_proxy_power) const;
+
+public:
+ BOOL mIndividualSelection; // For root objects and objects individually selected
+
+ BOOL mTransient;
+ BOOL mValid; // is extra information valid?
+ LLPermissions* mPermissions;
+ LLSaleInfo mSaleInfo;
+ LLAggregatePermissions mAggregatePerm;
+ LLAggregatePermissions mAggregateTexturePerm;
+ LLAggregatePermissions mAggregateTexturePermOwner;
+ LLString mName;
+ LLString mDescription;
+ LLCategory mCategory;
+ S16 mInventorySerial;
+ LLVector3 mSavedPositionLocal; // for interactively modifying object position
+ LLVector3d mSavedPositionGlobal; // for interactively modifying object position
+ LLVector3 mSavedScale; // for interactively modifying object scale
+ LLQuaternion mSavedRotation; // for interactively modifying object rotation
+ BOOL mDuplicated;
+ LLVector3d mDuplicatePos;
+ LLQuaternion mDuplicateRot;
+ LLUUID mItemID;
+ LLUUID mFolderID;
+ LLUUID mFromTaskID;
+ LLString mTouchName;
+ LLString mSitName;
+ std::vector<LLColor4> mSavedColors;
+ std::vector<LLUUID> mSavedTextures;
+ std::vector<LLVector3> mTextureScaleRatios;
+ std::vector<LLVector3> mSilhouetteVertices; // array of vertices to render silhouette of object
+ std::vector<LLVector3> mSilhouetteNormals; // array of normals to render silhouette of object
+ std::vector<S32> mSilhouetteSegments; // array of normals to render silhouette of object
+ BOOL mSilhouetteExists; // need to generate silhouette?
+
+protected:
+ LLPointer<LLViewerObject> mObject;
+ BOOL mTESelected[SELECT_MAX_TES];
+ S32 mLastTESelected;
+};
+
+extern LLSelectMgr* gSelectMgr;
+
+// Utilities
+void dialog_refresh_all(); // Update subscribers to the selection list
+
+#endif
diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp
new file mode 100644
index 0000000000..022f02c57e
--- /dev/null
+++ b/indra/newview/llsky.cpp
@@ -0,0 +1,427 @@
+/**
+ * @file llsky.cpp
+ * @brief IndraWorld sky class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Ideas:
+// -haze should be controlled by global query from sims
+// -need secondary optical effects on sun (flare)
+// -stars should be brought down from sims
+// -star intensity should be driven by global ambient level from sims,
+// so that eclipses, etc can be easily done.
+//
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsky.h"
+
+// linden library includes
+#include "llerror.h"
+#include "llmath.h"
+#include "math.h"
+#include "v4color.h"
+
+#include "llviewerobjectlist.h"
+#include "llviewerobject.h"
+#include "llviewercamera.h"
+#include "pipeline.h"
+#include "llagent.h"
+#include "lldrawpool.h"
+
+#include "llvosky.h"
+#include "llvostars.h"
+#include "llcubemap.h"
+#include "llviewercontrol.h"
+
+extern LLPipeline gPipeline;
+
+F32 azimuth_from_vector(const LLVector3 &v);
+F32 elevation_from_vector(const LLVector3 &v);
+
+// ---------------- LLSky ----------------
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+LLSky::LLSky()
+{
+ // Set initial clear color to black
+ // Set fog color
+ mFogColor.mV[VRED] = mFogColor.mV[VGREEN] = mFogColor.mV[VBLUE] = 0.5f;
+ mFogColor.mV[VALPHA] = 0.0f;
+
+ mLightingGeneration = 0;
+ mUpdatedThisFrame = TRUE;
+ mOverrideSimSunPosition = FALSE;
+ mSunPhase = 0.f;
+}
+
+
+LLSky::~LLSky()
+{
+}
+
+void LLSky::cleanup()
+{
+ mVOSkyp = NULL;
+ mVOStarsp = NULL;
+ mVOGroundp = NULL;
+}
+
+void LLSky::destroyGL()
+{
+ if (!mVOSkyp.isNull() && mVOSkyp->getCubeMap())
+ {
+ mVOSkyp->cleanupGL();
+ }
+}
+
+void LLSky::restoreGL()
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->restoreGL();
+ }
+}
+
+void LLSky::setOverrideSun(BOOL override)
+{
+ if (!mOverrideSimSunPosition && override)
+ {
+ mLastSunDirection = getSunDirection();
+ }
+ else if (mOverrideSimSunPosition && !override)
+ {
+ setSunDirection(mLastSunDirection, LLVector3::zero);
+ }
+ mOverrideSimSunPosition = override;
+}
+
+void LLSky::setSunDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity)
+{
+ mVOSkyp->setSunDirection(sun_direction, sun_ang_velocity);
+}
+
+
+void LLSky::setSunTargetDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity)
+{
+ mSunTargDir = sun_direction;
+}
+
+
+LLVector3 LLSky::getSunDirection() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getToSun();
+ }
+ else
+ {
+ return LLVector3::z_axis;
+ }
+}
+
+
+LLVector3 LLSky::getMoonDirection() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getToMoon();
+ }
+ else
+ {
+ return LLVector3::z_axis;
+ }
+}
+
+
+LLColor4 LLSky::getSunDiffuseColor() const
+{
+ if (mVOSkyp)
+ {
+ return LLColor4(mVOSkyp->getSunDiffuseColor());
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+LLColor4 LLSky::getMoonDiffuseColor() const
+{
+ if (mVOSkyp)
+ {
+ return LLColor4(mVOSkyp->getMoonDiffuseColor());
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+LLColor4 LLSky::getTotalAmbientColor() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getTotalAmbientColor();
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+BOOL LLSky::sunUp() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->isSunUp();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+
+LLColor4 LLSky::calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exag) const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->calcInScatter(transp, point, exag);
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+LLColor4U LLSky::getFadeColor() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getFadeColor();
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Public Methods
+//////////////////////////////////////////////////////////////////////
+
+
+void LLSky::init(const LLVector3 &sun_direction)
+{
+ mVOSkyp = (LLVOSky *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_SKY, gAgent.getRegion());
+ mVOSkyp->initSunDirection(sun_direction, LLVector3());
+ gPipeline.addObject((LLViewerObject *)mVOSkyp);
+
+ mVOStarsp = (LLVOStars *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_STARS, gAgent.getRegion());
+ gPipeline.addObject((LLViewerObject *)mVOStarsp);
+
+ mVOGroundp = (LLVOGround*)gObjectList.createObjectViewer(LLViewerObject::LL_VO_GROUND, gAgent.getRegion());
+ LLVOGround *groundp = mVOGroundp;
+ gPipeline.addObject((LLViewerObject *)groundp);
+
+ gSky.setFogRatio(gSavedSettings.getF32("RenderFogRatio"));
+
+ ////////////////////////////
+ //
+ // Legacy code, ignore
+ //
+ //
+
+ // Get the parameters.
+ mSunDefaultPosition = gSavedSettings.getVector3("SkySunDefaultPosition");
+
+
+ if (gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || mOverrideSimSunPosition)
+ {
+ setSunDirection(mSunDefaultPosition, LLVector3(0.f, 0.f, 0.f));
+ }
+ else
+ {
+ setSunDirection(sun_direction, LLVector3(0.f, 0.f, 0.f));
+ }
+
+ mUpdatedThisFrame = TRUE;
+}
+
+
+void LLSky::setCloudDensityAtAgent(F32 cloud_density)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->setCloudDensity(cloud_density);
+ }
+}
+
+
+void LLSky::setWind(const LLVector3& average_wind)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->setWind(average_wind);
+ }
+}
+
+
+void LLSky::propagateHeavenlyBodies(F32 dt)
+{
+ if (!mOverrideSimSunPosition)
+ {
+ LLVector3 curr_dir = getSunDirection();
+ LLVector3 diff = mSunTargDir - curr_dir;
+ const F32 dist = diff.normVec();
+ if (dist > 0)
+ {
+ const F32 step = llmin (dist, 0.00005f);
+ //const F32 step = min (dist, 0.0001);
+ diff *= step;
+ curr_dir += diff;
+ curr_dir.normVec();
+ if (mVOSkyp)
+ {
+ mVOSkyp->setSunDirection(curr_dir, LLVector3());
+ }
+ }
+ }
+}
+
+F32 LLSky::getSunPhase() const
+{
+ return mSunPhase;
+}
+
+void LLSky::setSunPhase(const F32 phase)
+{
+ mSunPhase = phase;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Private Methods
+//////////////////////////////////////////////////////////////////////
+
+
+LLColor4 LLSky::getFogColor() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getFogColor();
+ }
+
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+}
+
+
+void LLSky::updateFog(const F32 distance)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->updateFog(distance);
+ }
+}
+
+void LLSky::updateCull()
+{
+ /*if (mVOSkyp.notNull() && mVOSkyp->mDrawable.notNull())
+ {
+ gPipeline.markVisible(mVOSkyp->mDrawable);
+ }
+ else
+ {
+ llinfos << "No sky drawable!" << llendl;
+ }*/
+
+ if (mVOStarsp.notNull() && mVOStarsp->mDrawable.notNull())
+ {
+ gPipeline.markVisible(mVOStarsp->mDrawable);
+ }
+ else
+ {
+ llinfos << "No stars drawable!" << llendl;
+ }
+
+ /*if (mVOGroundp.notNull() && mVOGroundp->mDrawable.notNull())
+ {
+ gPipeline.markVisible(mVOGroundp->mDrawable);
+ }*/
+}
+
+void LLSky::updateSky()
+{
+ if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY))
+ {
+ return;
+ }
+ if (mVOSkyp)
+ {
+ mVOSkyp->updateSky();
+ }
+ if (mVOStarsp)
+ {
+ if (mVOStarsp->mDrawable)
+ {
+ gPipeline.markRebuild(mVOStarsp->mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ }
+}
+
+
+void LLSky::setFogRatio(const F32 fog_ratio)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->setFogRatio(fog_ratio);
+ }
+}
+
+
+F32 LLSky::getFogRatio() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getFogRatio();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+
+// Returns angle (DEGREES) between the horizontal plane and "v",
+// where the angle is negative when v.mV[VZ] < 0.0f
+F32 elevation_from_vector(const LLVector3 &v)
+{
+ F32 elevation = 0.0f;
+ F32 xy_component = (F32) sqrt(v.mV[VX] * v.mV[VX] + v.mV[VY] * v.mV[VY]);
+ if (xy_component != 0.0f)
+ {
+ elevation = RAD_TO_DEG * (F32) atan(v.mV[VZ]/xy_component);
+ }
+ else
+ {
+ if (v.mV[VZ] > 0.f)
+ {
+ elevation = 90.f;
+ }
+ else
+ {
+ elevation = -90.f;
+ }
+ }
+ return elevation;
+}
diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h
new file mode 100644
index 0000000000..2413615085
--- /dev/null
+++ b/indra/newview/llsky.h
@@ -0,0 +1,101 @@
+/**
+ * @file llsky.h
+ * @brief It's, uh, the sky!
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSKY_H
+#define LL_LLSKY_H
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "llvosky.h"
+#include "llvostars.h"
+#include "llvoground.h"
+
+const F32 NIGHTTIME_ELEVATION = -8.0f; // degrees
+const F32 NIGHTTIME_ELEVATION_COS = (F32)sin(NIGHTTIME_ELEVATION*DEG_TO_RAD);
+
+class LLViewerCamera;
+
+
+class LLSky
+{
+public:
+ LLSky();
+ ~LLSky();
+
+ void init(const LLVector3 &sun_direction);
+ void free();
+
+ void cleanup();
+
+ void setOverrideSun(BOOL override);
+ BOOL getOverrideSun() { return mOverrideSimSunPosition; }
+ void setSunDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity);
+ void setSunTargetDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity);
+
+
+ LLColor4 getFogColor() const;
+
+
+ void setCloudDensityAtAgent(F32 cloud_density);
+ void setWind(const LLVector3& wind);
+
+ void updateFog(const F32 distance);
+ void updateCull();
+ void updateSky();
+
+ void propagateHeavenlyBodies(F32 dt); // dt = seconds
+
+ S32 mLightingGeneration;
+ BOOL mUpdatedThisFrame;
+
+ void setFogRatio(const F32 fog_ratio); // Fog distance as fraction of cull distance.
+ F32 getFogRatio() const;
+ LLColor4U getFadeColor() const;
+
+ LLVector3 getSunDirection() const;
+ LLVector3 getMoonDirection() const;
+ LLColor4 getSunDiffuseColor() const;
+ LLColor4 getMoonDiffuseColor() const;
+ LLColor4 getTotalAmbientColor() const;
+ BOOL sunUp() const;
+ LLColor4 calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exag) const;
+
+ F32 getSunPhase() const;
+ void setSunPhase(const F32 phase);
+
+ void destroyGL();
+ void restoreGL();
+
+public:
+ LLPointer<LLVOSky> mVOSkyp; // Pointer to the LLVOSky object (only one, ever!)
+ LLPointer<LLVOStars> mVOStarsp; // Pointer to the LLVOStars object (only one, ever!)
+ LLPointer<LLVOGround> mVOGroundp;
+
+ LLVector3 mSunTargDir;
+
+ // Legacy stuff
+ LLVector3 mSunDefaultPosition;
+
+protected:
+ BOOL mOverrideSimSunPosition;
+
+ F32 mSunPhase;
+ LLColor4 mFogColor; // Color to use for fog and haze
+
+ F32 mHaze; // a multiplier to scale the lighting
+ F32 mDomeRadius; // sky dome size
+
+ LLVector3 mLastSunDirection;
+};
+
+extern LLSky gSky;
+#endif
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
new file mode 100644
index 0000000000..8b5db02c85
--- /dev/null
+++ b/indra/newview/llspatialpartition.cpp
@@ -0,0 +1,2255 @@
+/**
+ * @file llspatialpartition.cpp
+ * @brief LLSpatialGroup class implementation and supporting functions
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llspatialpartition.h"
+
+#include "llglheaders.h"
+
+#include "llviewerobjectlist.h"
+#include "llvovolume.h"
+#include "llviewercamera.h"
+#include "llface.h"
+#include "viewer.h"
+
+#include "llcamera.h"
+#include "pipeline.h"
+
+static BOOL sIgnoreOcclusion = TRUE;
+static GLuint sBoxList = 0;
+
+const S32 SG_LOD_SWITCH_STAGGER = 4;
+const F32 SG_MAX_OBJ_RAD = 1.f;
+const F32 SG_OCCLUSION_FUDGE = 1.1f;
+const S32 SG_MOVE_PERIOD = 32;
+const S32 SG_LOD_PERIOD = 16;
+
+#define SG_DISCARD_TOLERANCE 0.25f
+
+#if LL_OCTREE_PARANOIA_CHECK
+#define assert_octree_valid(x) x->validate()
+#else
+#define assert_octree_valid(x)
+#endif
+
+static F32 sLastMaxTexPriority = 1.f;
+static F32 sCurMaxTexPriority = 1.f;
+
+//static counter for frame to switch LOD on
+S32 LLSpatialGroup::sLODSeed = 0;
+
+void sg_assert(BOOL expr)
+{
+#if LL_OCTREE_PARANOIA_CHECK
+ if (!expr)
+ {
+ llerrs << "Octree invalid!" << llendl;
+ }
+#endif
+}
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+void validate_drawable(LLDrawable* drawablep)
+{
+ F64 rad = drawablep->getBinRadius();
+ const LLVector3* ext = drawablep->getSpatialExtents();
+
+ if (rad < 0 || rad > 4096 ||
+ (ext[1]-ext[0]).magVec() > 4096)
+ {
+ llwarns << "Invalid drawable found in octree." << llendl;
+ }
+}
+#else
+#define validate_drawable(x)
+#endif
+
+BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
+
+BOOL LLLineSegmentAABB(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
+{
+ float fAWdU[3];
+ LLVector3 dir;
+ LLVector3 diff;
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ dir.mV[i] = 0.5f * (end.mV[i] - start.mV[i]);
+ diff.mV[i] = (0.5f * (end.mV[i] + start.mV[i])) - center.mV[i];
+ fAWdU[i] = fabsf(dir.mV[i]);
+ if(fabsf(diff.mV[i])>size.mV[i] + fAWdU[i]) return false;
+ }
+
+ float f;
+ f = dir.mV[1] * diff.mV[2] - dir.mV[2] * diff.mV[1]; if(fabsf(f)>size.mV[1]*fAWdU[2] + size.mV[2]*fAWdU[1]) return false;
+ f = dir.mV[2] * diff.mV[0] - dir.mV[0] * diff.mV[2]; if(fabsf(f)>size.mV[0]*fAWdU[2] + size.mV[2]*fAWdU[0]) return false;
+ f = dir.mV[0] * diff.mV[1] - dir.mV[1] * diff.mV[0]; if(fabsf(f)>size.mV[0]*fAWdU[1] + size.mV[1]*fAWdU[0]) return false;
+
+ return true;
+}
+
+//returns:
+// 0 if sphere and AABB are not intersecting
+// 1 if they are
+// 2 if AABB is entirely inside sphere
+
+S32 LLSphereAABB(const LLVector3& center, const LLVector3& size, const LLVector3& pos, const F32 &rad)
+{
+ S32 ret = 2;
+
+ LLVector3 min = center - size;
+ LLVector3 max = center + size;
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (min.mV[i] > pos.mV[i] + rad ||
+ max.mV[i] < pos.mV[i] - rad)
+ { //totally outside
+ return 0;
+ }
+
+ if (min.mV[i] < pos.mV[i] - rad ||
+ max.mV[i] > pos.mV[i] + rad)
+ { //intersecting
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+LLSpatialGroup::~LLSpatialGroup()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (!safeToDelete())
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Spatial Group deleted while being tracked " << ((void*) mState) << llendl;
+#else
+ llerrs << "Spatial Group deleted while being tracked " << ((void*) mState) << llendl;
+#endif
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 i = 0; i < mSpatialPartition->mOccludedList.size(); i++)
+ {
+ if (mSpatialPartition->mOccludedList[i] == this)
+ {
+ llerrs << "Spatial Group deleted while being tracked STATE ERROR " << ((void*) mState) << llendl;
+ }
+ }
+#endif
+}
+
+BOOL LLSpatialGroup::safeToDelete()
+{
+ return gQuit || !isState(IN_QUEUE | ACTIVE_OCCLUSION | RESHADOW_QUEUE);
+}
+
+class LLRelightPainter : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLVector3 mOrigin, mDir;
+ F32 mRadius;
+
+ LLRelightPainter(LLVector3 origin, LLVector3 dir, F32 radius)
+ : mOrigin(origin), mDir(dir), mRadius(radius)
+ { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup::OctreeNode* node = (LLSpatialGroup::OctreeNode*) n;
+
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+ group->setState(LLSpatialGroup::RESHADOW);
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ const LLSpatialGroup::OctreeNode* child = node->getChild(i);
+ LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
+
+ LLVector3 res;
+
+ LLVector3 center, size;
+
+ center = group->mBounds[0];
+ size = group->mBounds[1];
+
+ if (child->isInside(LLVector3d(mOrigin)) || LLRayAABB(center, size, mOrigin, mDir, res, mRadius))
+ {
+ traverse(child);
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { }
+
+};
+
+BOOL LLSpatialGroup::isVisible()
+{
+ if (sIgnoreOcclusion)
+ {
+ return !isState(CULLED);
+ }
+ else
+ {
+ return !isState(CULLED | OCCLUDED);
+ }
+}
+
+void LLSpatialGroup::validate()
+{
+#if LL_OCTREE_PARANOIA_CHECK
+
+ sg_assert(!isState(DIRTY));
+
+ LLVector3 myMin = mBounds[0] - mBounds[1];
+ LLVector3 myMax = mBounds[0] + mBounds[1];
+
+ for (U32 i = 0; i < mOctreeNode->getChildCount(); ++i)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
+
+ group->validate();
+
+ //ensure all children are enclosed in this node
+ LLVector3 center = group->mBounds[0];
+ LLVector3 size = group->mBounds[1];
+
+ LLVector3 min = center - size;
+ LLVector3 max = center + size;
+
+ for (U32 j = 0; j < 3; j++)
+ {
+ sg_assert(min.mV[j] >= myMin.mV[j]-0.02f);
+ sg_assert(max.mV[j] <= myMax.mV[j]+0.02f);
+ }
+ }
+
+#endif
+}
+
+BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ drawablep->updateSpatialExtents();
+ validate_drawable(drawablep);
+
+ if (mOctreeNode->isInside(drawablep) && mOctreeNode->contains(drawablep))
+ {
+ unbound();
+ setState(OBJECT_DIRTY);
+ validate_drawable(drawablep);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_octree)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (!from_octree)
+ {
+ mOctreeNode->insert(drawablep);
+ }
+ else
+ {
+ drawablep->setSpatialGroup(this, 0);
+ validate_drawable(drawablep);
+ }
+
+ return TRUE;
+}
+
+BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxOut)
+{
+ const OctreeState* node = mOctreeNode->getOctState();
+
+ if (node->getData().empty())
+ { //don't do anything if there are no objects
+ if (empty && mOctreeNode->getParent())
+ { //only root is allowed to be empty
+ OCT_ERRS << "Empty leaf found in octree." << llendl;
+ }
+ return FALSE;
+ }
+
+ LLVector3& newMin = mObjectExtents[0];
+ LLVector3& newMax = mObjectExtents[1];
+
+ if (isState(OBJECT_DIRTY))
+ { //calculate new bounding box
+ clearState(OBJECT_DIRTY);
+
+ //initialize bounding box to first element
+ OctreeState::const_element_iter i = node->getData().begin();
+ LLDrawable* drawablep = *i;
+ const LLVector3* minMax = drawablep->getSpatialExtents();
+
+ newMin.setVec(minMax[0]);
+ newMax.setVec(minMax[1]);
+
+ for (++i; i != node->getData().end(); ++i)
+ {
+ drawablep = *i;
+ minMax = drawablep->getSpatialExtents();
+
+ //bin up the object
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (minMax[0].mV[i] < newMin.mV[i])
+ {
+ newMin.mV[i] = minMax[0].mV[i];
+ }
+ if (minMax[1].mV[i] > newMax.mV[i])
+ {
+ newMax.mV[i] = minMax[1].mV[i];
+ }
+ }
+ }
+
+ mObjectBounds[0] = (newMin + newMax) * 0.5f;
+ mObjectBounds[1] = (newMax - newMin) * 0.5f;
+ }
+
+ if (empty)
+ {
+ minOut = newMin;
+ maxOut = newMax;
+ }
+ else
+ {
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (newMin.mV[i] < minOut.mV[i])
+ {
+ minOut.mV[i] = newMin.mV[i];
+ }
+ if (newMax.mV[i] > maxOut.mV[i])
+ {
+ maxOut.mV[i] = newMax.mV[i];
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+void LLSpatialGroup::unbound()
+{
+ if (isState(DIRTY))
+ {
+ return;
+ }
+
+ setState(DIRTY);
+
+ //all the parent nodes need to rebound this child
+ if (mOctreeNode)
+ {
+ OctreeNode* parent = (OctreeNode*) mOctreeNode->getParent();
+ while (parent != NULL)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) parent->getListener(0);
+ if (group->isState(DIRTY))
+ {
+ return;
+ }
+
+ group->setState(DIRTY);
+ parent = (OctreeNode*) parent->getParent();
+ }
+ }
+}
+
+BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ unbound();
+ if (!from_octree)
+ {
+ if (!mOctreeNode->remove(drawablep))
+ {
+ OCT_ERRS << "Could not remove drawable from spatial group" << llendl;
+ }
+ }
+ else
+ {
+ drawablep->setSpatialGroup(NULL, -1);
+ }
+ return TRUE;
+}
+
+void LLSpatialGroup::shift(const LLVector3 &offset)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLVector3d offsetd(offset);
+ mOctreeNode->setCenter(mOctreeNode->getCenter()+offsetd);
+ mOctreeNode->updateMinMax();
+ mBounds[0] += offset;
+ mExtents[0] += offset;
+ mExtents[1] += offset;
+ mObjectBounds[0] += offset;
+ mObjectExtents[0] += offset;
+ mObjectExtents[1] += offset;
+}
+
+class LLSpatialSetState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+ LLSpatialSetState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { ((LLSpatialGroup*) branch->getListener(0))->setState(mState); }
+};
+
+class LLSpatialSetStateDiff : public LLSpatialSetState
+{
+public:
+ LLSpatialSetStateDiff(U32 state) : LLSpatialSetState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (!group->isState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+void LLSpatialGroup::setState(U32 state, S32 mode)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialSetStateDiff setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ else
+ {
+ LLSpatialSetState setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ }
+ else
+ {
+ mState |= state;
+ }
+}
+
+class LLSpatialClearState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+ LLSpatialClearState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearState(mState); }
+};
+
+class LLSpatialClearStateDiff : public LLSpatialClearState
+{
+public:
+ LLSpatialClearStateDiff(U32 state) : LLSpatialClearState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (!group->isState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+
+void LLSpatialGroup::clearState(U32 state, S32 mode)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialClearStateDiff clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ else
+ {
+ LLSpatialClearState clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ }
+ else
+ {
+ mState &= ~state;
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ if (state & LLSpatialGroup::ACTIVE_OCCLUSION)
+ {
+ LLSpatialPartition* part = mSpatialPartition;
+ for (U32 i = 0; i < part->mOccludedList.size(); i++)
+ {
+ if (part->mOccludedList[i] == this)
+ {
+ llerrs << "LLSpatialGroup state error: " << mState << llendl;
+ }
+ }
+ }
+#endif
+}
+
+//======================================
+// Octree Listener Implementation
+//======================================
+
+LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part)
+: mState(0), mOctreeNode(node), mSpatialPartition(part)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ sg_assert(mOctreeNode->getListenerCount() == 0);
+ mOctreeNode->addListener(this);
+ setState(DIRTY);
+
+ mBounds[0] = LLVector3(node->getCenter());
+ mBounds[1] = LLVector3(node->getSize());
+
+ sLODSeed = (sLODSeed+1)%SG_LOD_PERIOD;
+ mLODHash = sLODSeed;
+}
+
+BOOL LLSpatialGroup::changeLOD()
+{
+ return LLDrawable::getCurrentFrame()%SG_LOD_PERIOD == mLODHash;
+}
+
+void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawable)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ addObject(drawable, FALSE, TRUE);
+ unbound();
+ setState(OBJECT_DIRTY);
+}
+
+void LLSpatialGroup::handleRemoval(const TreeNode* node, LLDrawable* drawable)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ removeObject(drawable, TRUE);
+ setState(OBJECT_DIRTY);
+}
+
+void LLSpatialGroup::handleDestruction(const TreeNode* node)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ if (mOctreeNode)
+ {
+ OctreeState* state = mOctreeNode->getOctState();
+ for (OctreeState::element_iter i = state->getData().begin(); i != state->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ if (!drawable->isDead())
+ {
+ drawable->setSpatialGroup(NULL, -1);
+ }
+ }
+ }
+
+ if (safeToDelete())
+ {
+ delete this;
+ }
+ else
+ {
+ setState(DEAD);
+ mOctreeNode = NULL;
+ }
+}
+
+void LLSpatialGroup::handleStateChange(const TreeNode* node)
+{
+ //drop bounding box upon state change
+ if (mOctreeNode != node)
+ {
+ mOctreeNode = (OctreeNode*) node;
+ }
+ unbound();
+}
+
+void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (child->getListenerCount() == 0)
+ {
+ (new LLSpatialGroup(child, mSpatialPartition))->setState(mState & SG_STATE_INHERIT_MASK);
+ }
+ else
+ {
+ OCT_ERRS << "LLSpatialGroup redundancy detected." << llendl;
+ }
+
+ unbound();
+}
+
+void LLSpatialGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child)
+{
+ unbound();
+}
+
+BOOL LLSpatialGroup::rebound()
+{
+ if (!isState(DIRTY))
+ { //return TRUE if we're not empty
+ return TRUE;
+ }
+
+ LLVector3 oldBounds[2];
+
+ if (isState(QUERY_OUT))
+ { //a query has been issued, if our bounding box changes significantly
+ //we need to discard the issued query
+ oldBounds[0] = mBounds[0];
+ oldBounds[1] = mBounds[1];
+ }
+
+ if (mOctreeNode->getChildCount() == 1 && mOctreeNode->getElementCount() == 0)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
+ group->rebound();
+
+ //copy single child's bounding box
+ mBounds[0] = group->mBounds[0];
+ mBounds[1] = group->mBounds[1];
+ mExtents[0] = group->mExtents[0];
+ mExtents[1] = group->mExtents[1];
+
+ group->setState(SKIP_FRUSTUM_CHECK);
+ }
+ else if (mOctreeNode->hasLeafState())
+ { //copy object bounding box if this is a leaf
+ boundObjects(TRUE, mExtents[0], mExtents[1]);
+ mBounds[0] = mObjectBounds[0];
+ mBounds[1] = mObjectBounds[1];
+ }
+ else
+ {
+ LLVector3& newMin = mExtents[0];
+ LLVector3& newMax = mExtents[1];
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
+ group->clearState(SKIP_FRUSTUM_CHECK);
+ group->rebound();
+ //initialize to first child
+ newMin = group->mExtents[0];
+ newMax = group->mExtents[1];
+
+ //first, rebound children
+ for (U32 i = 1; i < mOctreeNode->getChildCount(); i++)
+ {
+ group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
+ group->clearState(SKIP_FRUSTUM_CHECK);
+ group->rebound();
+ const LLVector3& max = group->mExtents[1];
+ const LLVector3& min = group->mExtents[0];
+
+ for (U32 j = 0; j < 3; j++)
+ {
+ if (max.mV[j] > newMax.mV[j])
+ {
+ newMax.mV[j] = max.mV[j];
+ }
+ if (min.mV[j] < newMin.mV[j])
+ {
+ newMin.mV[j] = min.mV[j];
+ }
+ }
+ }
+
+ boundObjects(FALSE, newMin, newMax);
+
+ mBounds[0] = (newMin + newMax)*0.5f;
+ mBounds[1] = (newMax - newMin)*0.5f;
+ }
+
+ if (isState(QUERY_OUT))
+ {
+ for (U32 i = 0; i < 3 && !isState(DISCARD_QUERY); i++)
+ {
+ if (fabsf(mBounds[0].mV[i]-oldBounds[0].mV[i]) > SG_DISCARD_TOLERANCE ||
+ fabsf(mBounds[1].mV[i]-oldBounds[1].mV[i]) > SG_DISCARD_TOLERANCE)
+ { //bounding box changed significantly, discard last issued
+ //occlusion query
+ setState(DISCARD_QUERY);
+ }
+ }
+ }
+
+ clearState(DIRTY);
+
+ return TRUE;
+}
+
+//==============================================
+
+LLSpatialPartition::LLSpatialPartition()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ mOctree = new LLSpatialGroup::OctreeNode(LLVector3d(0,0,0),
+ LLVector3d(1,1,1),
+ new LLSpatialGroup::OctreeRoot(), NULL);
+ new LLSpatialGroup(mOctree, this);
+}
+
+
+LLSpatialPartition::~LLSpatialPartition()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ delete mOctree;
+ mOctree = NULL;
+}
+
+
+LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
+
+ if (drawablep->getPositionGroup().magVecSquared() > MAX_MAG)
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "LLSpatialPartition::put Object out of range!" << llendl;
+ llinfos << drawablep->getPositionGroup() << llendl;
+
+ if (drawablep->getVObj())
+ {
+ llwarns << "Dumping debugging info: " << llendl;
+ drawablep->getVObj()->dump();
+ }
+#endif
+ return NULL;
+ }
+
+ drawablep->updateSpatialExtents();
+ validate_drawable(drawablep);
+
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+
+ assert_octree_valid(mOctree);
+ mOctree->insert(drawablep);
+ assert_octree_valid(mOctree);
+
+ LLSpatialGroup::OctreeNode* node = mOctree->getNodeAt(drawablep);
+
+ return (LLSpatialGroup*) node->getListener(0);
+}
+
+BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ drawablep->setSpatialGroup(NULL, -1);
+
+ if (!curp->removeObject(drawablep))
+ {
+ OCT_ERRS << "Failed to remove drawable from octree!" << llendl;
+ }
+
+ assert_octree_valid(mOctree);
+
+ return TRUE;
+}
+
+void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE);
+
+ if (curp && curp->mSpatialPartition != this)
+ {
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+ if (curp->mSpatialPartition->remove(drawablep, curp))
+ {
+ put(drawablep);
+ return;
+ }
+ else
+ {
+ OCT_ERRS << "Drawable lost between spatial partitions on outbound transition." << llendl;
+ }
+ }
+
+ if (curp && curp->updateInGroup(drawablep, immediate))
+ {
+ // Already updated, don't need to do anything
+ assert_octree_valid(mOctree);
+ return;
+ }
+
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+ if (curp && !remove(drawablep, curp))
+ {
+ OCT_ERRS << "Move couldn't find existing spatial group!" << llendl;
+ }
+
+ put(drawablep);
+}
+
+class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLSpatialShift(LLVector3 offset) : mOffset(offset) { }
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ ((LLSpatialGroup*) branch->getListener(0))->shift(mOffset);
+ }
+
+ LLVector3 mOffset;
+};
+
+void LLSpatialPartition::shift(const LLVector3 &offset)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ llinfos << "Shifting octree: " << offset << llendl;
+ LLSpatialShift shifter(offset);
+ shifter.traverse(mOctree);
+}
+
+BOOL LLSpatialPartition::checkOcclusion(LLSpatialGroup* group, LLCamera* camera)
+{
+ if (sIgnoreOcclusion)
+ {
+ return FALSE;
+ }
+
+ if (!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION | LLSpatialGroup::OCCLUDED) &&
+ !earlyFail(camera, group))
+ {
+ group->setState(LLSpatialGroup::ACTIVE_OCCLUSION);
+ mQueryQueue.push(group);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+class LLOctreeCull : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLOctreeCull(LLCamera* camera)
+ : mCamera(camera), mRes(0) { }
+
+ virtual bool earlyFail(const LLSpatialGroup* group)
+ {
+ if (mRes && //never occlusion cull the root node
+ !sIgnoreOcclusion && //never occlusion cull selection
+ group->isState(LLSpatialGroup::OCCLUDED))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (earlyFail(group))
+ {
+ return;
+ }
+
+ if (mRes == 2 ||
+ (mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)))
+ { //fully in, just add everything
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ mRes = mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]);
+
+ if (mRes)
+ { //at least partially in, run on down
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ lateFail(group);
+ }
+ mRes = 0;
+ }
+ }
+
+ virtual void lateFail(LLSpatialGroup* group)
+ {
+ if (!group->isState(LLSpatialGroup::CULLED))
+ { //totally culled, so are all its children
+ group->setState(LLSpatialGroup::CULLED, LLSpatialGroup::STATE_MODE_DIFF);
+ }
+ }
+
+ virtual bool checkObjects(const LLSpatialGroup::OctreeState* branch, const LLSpatialGroup* group)
+ {
+
+ if (branch->getElementCount() == 0) //no elements
+ {
+ return false;
+ }
+ else if (branch->getChildCount() == 0) //leaf state, already checked tightest bounding box
+ {
+ return true;
+ }
+ else if (mRes == 1 && !mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1])) //no objects in frustum
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual void preprocess(LLSpatialGroup* group)
+ {
+ if (group->isState(LLSpatialGroup::CULLED))
+ { //this is the first frame this node is visible
+ group->clearState(LLSpatialGroup::CULLED);
+ if (group->mOctreeNode->hasLeafState())
+ { //if it's a leaf, force it onto the active occlusion list to prevent
+ //massive frame stutters
+ group->mSpatialPartition->checkOcclusion(group, mCamera);
+ }
+ }
+ }
+
+ virtual void processDrawable(LLDrawable* drawable)
+ {
+ if (!drawable->isDead())
+ {
+ const LLVector3* extents = drawable->getSpatialExtents();
+
+ F32 objRad = drawable->getRadius();
+ objRad *= objRad;
+ F32 distSqr = ((extents[0]+extents[1])*0.5f - mCamera->getOrigin()).magVecSquared();
+ if (objRad/distSqr > SG_MIN_DIST_RATIO)
+ {
+ gPipeline.markNotCulled(drawable, *mCamera);
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
+
+ preprocess(group);
+
+ if (checkObjects(branch, group))
+ {
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ processDrawable(*i);
+ }
+ }
+ }
+
+ LLCamera *mCamera;
+ S32 mRes;
+};
+
+
+class LLOctreeSelect : public LLOctreeCull
+{
+public:
+ LLOctreeSelect(LLCamera* camera, std::vector<LLDrawable*>* results)
+ : LLOctreeCull(camera), mResults(results) { }
+
+ virtual bool earlyFail(const LLSpatialGroup* group) { return false; }
+ virtual void lateFail(LLSpatialGroup* group) { }
+ virtual void preprocess(LLSpatialGroup* group) { }
+
+ virtual void processDrawable(LLDrawable* drawable)
+ {
+ if (!drawable->isDead())
+ {
+ if (drawable->isSpatialBridge())
+ {
+ drawable->setVisible(*mCamera, mResults, TRUE);
+ }
+ else
+ {
+ mResults->push_back(drawable);
+ }
+ }
+ }
+
+ std::vector<LLDrawable*>* mResults;
+};
+
+
+void drawBox(const LLVector3& c, const LLVector3& r)
+{
+ glPushMatrix();
+ glTranslatef(c.mV[0], c.mV[1], c.mV[2]);
+ glScalef(r.mV[0], r.mV[1], r.mV[2]);
+ glCallList(sBoxList);
+ glPopMatrix();
+}
+
+void genBoxList()
+{
+ if (sBoxList != 0)
+ {
+ return;
+ }
+
+ sBoxList = glGenLists(1);
+ glNewList(sBoxList, GL_COMPILE);
+
+ LLVector3 c,r;
+ c = LLVector3(0,0,0);
+ r = LLVector3(1,1,1);
+
+ glBegin(GL_TRIANGLE_STRIP);
+ //left front
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ //right front
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
+ //right back
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
+ //left back
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
+ //left front
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ glEnd();
+
+ //bottom
+ glBegin(GL_TRIANGLE_STRIP);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
+ glEnd();
+
+ //top
+ glBegin(GL_TRIANGLE_STRIP);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
+ glEnd();
+
+ glEndList();
+}
+
+void LLSpatialPartition::restoreGL()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ mOcclusionQueries.clear();
+ sBoxList = 0;
+
+ //generate query ids
+ while (mOcclusionQueries.size() < mOccludedList.size())
+ {
+ GLuint id;
+ glGenQueriesARB(1, &id);
+ mOcclusionQueries.push_back(id);
+ }
+
+ for (U32 i = 0; i < mOccludedList.size(); i++)
+ { //previously issued queries are now invalid
+ mOccludedList[i]->setState(LLSpatialGroup::DISCARD_QUERY);
+ }
+
+ genBoxList();
+}
+
+S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* results, BOOL for_select)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
+ group->rebound();
+ }
+
+ if (for_select)
+ {
+ LLOctreeSelect selecter(&camera, results);
+ selecter.traverse(mOctree);
+ }
+ else
+ {
+ LLOctreeCull culler(&camera);
+ culler.traverse(mOctree);
+ }
+
+ sIgnoreOcclusion = !(gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery);
+ return 0;
+}
+
+class LLOctreeClearOccludedNotActive : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLOctreeClearOccludedNotActive() { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+ if ((!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION)) //|| group->isState(LLSpatialGroup::QUERY_PENDING)
+ || group->isState(LLSpatialGroup::DEACTIVATE_OCCLUSION))
+ { //the children are all occluded or culled as well
+ group->clearState(LLSpatialGroup::OCCLUDED);
+ for (U32 i = 0; i < group->mOctreeNode->getChildCount(); i++)
+ {
+ traverse(group->mOctreeNode->getChild(i));
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { }
+};
+
+class LLQueueNonCulled : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ std::queue<LLSpatialGroup*>* mQueue;
+ LLQueueNonCulled(std::queue<LLSpatialGroup*> *queue) : mQueue(queue) { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+ if (group->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED))
+ { //the children are all occluded or culled as well
+ return;
+ }
+
+ if (!group->isState(LLSpatialGroup::IN_QUEUE))
+ {
+ group->setState(LLSpatialGroup::IN_QUEUE);
+ mQueue->push(group);
+ }
+
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { }
+};
+
+BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
+{
+ LLVector3 c = group->mBounds[0];
+ LLVector3 r = group->mBounds[1] * (SG_OCCLUSION_FUDGE*2.f) + LLVector3(0.01f,0.01f,0.01f);
+
+ if (group->isState(LLSpatialGroup::CULLED) || !camera->AABBInFrustum(c, r))
+ {
+ return TRUE;
+ }
+
+ LLVector3 e = camera->getOrigin();
+
+ LLVector3 min = c - r;
+ LLVector3 max = c + r;
+
+ for (U32 j = 0; j < 3; j++)
+ {
+ if (e.mV[j] < min.mV[j] || e.mV[j] > max.mV[j])
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLSpatialPartition::processOcclusion(LLCamera* camera)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLSpatialGroup* rootGroup = (LLSpatialGroup*) mOctree->getListener(0);
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
+ rootGroup->rebound();
+ }
+
+ //update potentials
+ if (!rootGroup->isState(LLSpatialGroup::IN_QUEUE))
+ {
+ rootGroup->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(rootGroup);
+ }
+
+ const U32 MAX_PULLED = 32;
+ const U32 MAX_PUSHED = mOcclusionQueue.size();
+ U32 count = 0;
+ U32 pcount = 0;
+
+ while (pcount < MAX_PUSHED && count < MAX_PULLED && !mOcclusionQueue.empty())
+ {
+ LLFastTimer t(LLFastTimer::FTM_OCCLUSION);
+
+ LLSpatialGroup* group = mOcclusionQueue.front();
+ if (!group->isState(LLSpatialGroup::IN_QUEUE))
+ {
+ OCT_ERRS << "Spatial Group State Error. Group in queue not tagged as such." << llendl;
+ }
+
+ mOcclusionQueue.pop();
+ group->clearState(LLSpatialGroup::IN_QUEUE);
+
+ if (group->isDead())
+ {
+ if (group->safeToDelete())
+ {
+ delete group;
+ }
+ continue;
+ }
+
+ if (group->isState(LLSpatialGroup::CULLED | LLSpatialGroup::OCCLUDED))
+ { //already culled, skip it
+ continue;
+ }
+
+ //before we process, enqueue this group's children
+ for (U32 i = 0; i < group->mOctreeNode->getChildCount(); i++)
+ {
+ LLSpatialGroup* child = (LLSpatialGroup*) group->mOctreeNode->getChild(i)->getListener(0);
+
+ if (!child->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED)
+ && !child->isState(LLSpatialGroup::IN_QUEUE | LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+ child->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(child);
+ }
+ }
+
+ /*if (group->isState(LLSpatialGroup::QUERY_PENDING))
+ { //already on the pending group, put it back
+ group->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(group);
+ pcount++;
+ continue;
+ }*/
+
+ if (earlyFail(camera, group))
+ {
+ sg_assert(!group->isState(LLSpatialGroup::OCCLUDED));
+ group->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(group);
+ pcount++;
+ continue;
+ }
+
+ //add to pending queue
+ if (!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 i = 0; i < mOccludedList.size(); ++i)
+ {
+ sg_assert(mOccludedList[i] != group);
+ }
+#endif
+ //group->setState(LLSpatialGroup::QUERY_PENDING);
+ group->setState(LLSpatialGroup::ACTIVE_OCCLUSION);
+ mQueryQueue.push(group);
+ count++;
+ }
+ }
+
+ //read back results from last frame
+ for (U32 i = 0; i < mOccludedList.size(); i++)
+ {
+ LLFastTimer t(LLFastTimer::FTM_OCCLUSION_READBACK);
+
+ if (mOccludedList[i]->isDead() || !mOccludedList[i]->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+ continue;
+ }
+ GLuint res = 0;
+
+ if (mOccludedList[i]->isState(LLSpatialGroup::EARLY_FAIL | LLSpatialGroup::DISCARD_QUERY) ||
+ !mOccludedList[i]->isState(LLSpatialGroup::QUERY_OUT))
+ {
+ mOccludedList[i]->clearState(LLSpatialGroup::EARLY_FAIL);
+ mOccludedList[i]->clearState(LLSpatialGroup::DISCARD_QUERY);
+ res = 1;
+ }
+ else
+ {
+ glGetQueryObjectuivARB(mOcclusionQueries[i], GL_QUERY_RESULT_ARB, &res);
+ stop_glerror();
+ }
+
+ if (res) //NOT OCCLUDED
+ {
+ if (mOccludedList[i]->isState(LLSpatialGroup::OCCLUDED))
+ { //this node was occluded last frame
+ LLSpatialGroup::OctreeNode* node = mOccludedList[i]->mOctreeNode;
+ //add any immediate children to the queue that are not already there
+ for (U32 j = 0; j < node->getChildCount(); j++)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getChild(j)->getListener(0);
+ checkOcclusion(group, camera);
+ }
+ }
+
+ //clear occlusion status for everything not on the active list
+ LLOctreeClearOccludedNotActive clear_occluded;
+ mOccludedList[i]->setState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
+ mOccludedList[i]->clearState(LLSpatialGroup::OCCLUDED);
+ mOccludedList[i]->clearState(LLSpatialGroup::OCCLUDING);
+ clear_occluded.traverse(mOccludedList[i]->mOctreeNode);
+ }
+ else
+ { //OCCLUDED
+ if (mOccludedList[i]->isState(LLSpatialGroup::OCCLUDING))
+ {
+ if (!mOccludedList[i]->isState(LLSpatialGroup::OCCLUDED))
+ {
+ LLSpatialGroup::OctreeNode* oct_parent = (LLSpatialGroup::OctreeNode*) mOccludedList[i]->mOctreeNode->getParent();
+ if (oct_parent)
+ {
+ LLSpatialGroup* parent = (LLSpatialGroup*) oct_parent->getListener(0);
+
+ if (checkOcclusion(parent, camera))
+ { //force a guess on the parent and siblings
+
+ for (U32 i = 0; i < parent->mOctreeNode->getChildCount(); i++)
+ {
+ LLSpatialGroup* child = (LLSpatialGroup*) parent->mOctreeNode->getChild(i)->getListener(0);
+ checkOcclusion(child, camera);
+ }
+ }
+ }
+ }
+ mOccludedList[i]->setState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+ }
+ else
+ {
+ //take children off the active list
+ mOccludedList[i]->setState(LLSpatialGroup::DEACTIVATE_OCCLUSION, LLSpatialGroup::STATE_MODE_DIFF);
+
+ //keep this node on the active list
+ mOccludedList[i]->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
+
+ //this node is a top level occluder
+ mOccludedList[i]->setState(LLSpatialGroup::OCCLUDING);
+ }
+ }
+
+ mOccludedList[i]->clearState(LLSpatialGroup::QUERY_OUT);
+ }
+
+ //remove non-occluded groups from occluded list
+ for (U32 i = 0; i < mOccludedList.size(); )
+ {
+ if (mOccludedList[i]->isDead() || //needs to be deleted
+ !mOccludedList[i]->isState(LLSpatialGroup::OCCLUDING) || //is not occluding
+ mOccludedList[i]->isState(LLSpatialGroup::DEACTIVATE_OCCLUSION)) //parent is occluded
+ {
+ LLSpatialGroup* groupp = mOccludedList[i];
+ mOccludedList.erase(mOccludedList.begin()+i);
+ groupp->clearState(LLSpatialGroup::ACTIVE_OCCLUSION);
+ groupp->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
+ groupp->clearState(LLSpatialGroup::OCCLUDING);
+
+ if (groupp->isDead() && groupp->safeToDelete())
+ {
+ delete groupp;
+ }
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ //pump some non-culled items onto the occlusion list
+ //count = MAX_PULLED;
+ while (!mQueryQueue.empty())
+ {
+ LLSpatialGroup* group = mQueryQueue.front();
+ mQueryQueue.pop();
+ //group->clearState(LLSpatialGroup::QUERY_PENDING);
+ mOccludedList.push_back(group);
+ }
+
+ //generate query ids
+ while (mOcclusionQueries.size() < mOccludedList.size())
+ {
+ GLuint id;
+ glGenQueriesARB(1, &id);
+ mOcclusionQueries.push_back(id);
+ }
+}
+
+void LLSpatialPartition::doOcclusion(LLCamera* camera)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ sIgnoreOcclusion = gUseWireframe;
+ LLFastTimer t(LLFastTimer::FTM_RENDER_OCCLUSION);
+
+#if LL_OCTREE_PARANOIA_CHECK
+ LLSpatialGroup* check = (LLSpatialGroup*) mOctree->getListener(0);
+ check->validate();
+#endif
+
+ stop_glerror();
+
+ //actually perform the occlusion queries
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ glDisable(GL_TEXTURE_2D);
+ gPipeline.disableLights();
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLDisable blend(GL_BLEND);
+ LLGLDisable alpha_test(GL_ALPHA_TEST);
+ LLGLDisable fog(GL_FOG);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glColor4f(1,1,1,1);
+
+ //sort occlusion queries front to back
+ /*for (U32 i = 0; i < mOccludedList.size(); i++)
+ {
+ LLSpatialGroup* group = mOccludedList[i];
+
+ LLVector3 v = group->mOctreeNode->getCenter()-camera->getOrigin();
+ group->mDistance = v*v;
+ }
+
+ std::sort(mOccludedList.begin(), mOccludedList.end(), dist_greater());
+
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ LLGLEnable stencil(GL_STENCIL_TEST);
+ glStencilFunc(GL_GREATER, 1, 0xFFFFFFFF);
+ glStencilOp(GL_KEEP, GL_SET, GL_KEEP);*/
+
+ genBoxList();
+
+ for (U32 i = 0; i < mOccludedList.size(); i++)
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 j = i+1; j < mOccludedList.size(); j++)
+ {
+ sg_assert(mOccludedList[i] != mOccludedList[j]);
+ }
+#endif
+ LLSpatialGroup* group = mOccludedList[i];
+ if (group->isDead())
+ {
+ continue;
+ }
+
+ if (earlyFail(camera, group))
+ {
+ group->setState(LLSpatialGroup::EARLY_FAIL);
+ }
+ else
+ { //early rejection criteria passed, send some geometry to the query
+ LLVector3 c;
+ LLVector3 r;
+
+ sg_assert(!group->isState(LLSpatialGroup::DIRTY));
+
+ c = group->mBounds[0];
+ r = group->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.01f,0.01f,0.01f);
+ for (U32 k = 0; k < 3; k++)
+ {
+ r.mV[k] = llmin(group->mBounds[1].mV[k]+0.25f, r.mV[k]);
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ LLVector3 e = camera->getOrigin();
+ LLVector3 min = c - r;
+ LLVector3 max = c + r;
+ BOOL outside = FALSE;
+ for (U32 j = 0; j < 3; j++)
+ {
+ outside = outside || (e.mV[j] < min.mV[j] || e.mV[j] > max.mV[j]);
+ }
+ sg_assert(outside);
+#endif
+
+ glBeginQueryARB(GL_SAMPLES_PASSED_ARB, mOcclusionQueries[i]);
+ drawBox(c,r);
+ glEndQueryARB(GL_SAMPLES_PASSED_ARB);
+
+ group->setState(LLSpatialGroup::QUERY_OUT);
+ group->clearState(LLSpatialGroup::DISCARD_QUERY);
+ }
+ }
+ stop_glerror();
+
+ glFlush();
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_TEXTURE_2D);
+}
+
+class LLOctreeGet : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLOctreeGet(LLVector3 pos, F32 rad, LLDrawable::drawable_set_t* results, BOOL lights)
+ : mPosition(pos), mRad(rad), mResults(results), mLights(lights), mRes(0)
+ {
+
+ }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (mRes == 2)
+ { //fully in, just add everything
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ LLVector3 center, size;
+
+ center = group->mBounds[0];
+ size = group->mBounds[1];
+
+ mRes = LLSphereAABB(center, size, mPosition, mRad);
+ if (mRes > 0)
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ mRes = 0;
+ }
+ }
+
+ static BOOL skip(LLDrawable* drawable, BOOL get_lights)
+ {
+ if (get_lights != drawable->isLight())
+ {
+ return TRUE;
+ }
+ if (get_lights && drawable->getVObj()->isHUDAttachment())
+ {
+ return TRUE; // no lighting from HUD objects
+ }
+ if (get_lights && drawable->isState(LLDrawable::ACTIVE))
+ {
+ return TRUE; // ignore active lights
+ }
+ return FALSE;
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ if (!skip(drawable, mLights))
+ {
+ if (mRes == 2)
+ {
+ mResults->insert(drawable);
+ }
+ else
+ {
+ LLVector3 v = LLVector3(drawable->getPositionGroup())-mPosition;
+ float dsq = v.magVecSquared();
+ float maxd = mRad + drawable->getVisibilityRadius();
+ if (dsq <= maxd*maxd)
+ {
+ mResults->insert(drawable);
+ }
+ }
+ }
+ }
+ }
+
+ LLVector3 mPosition;
+ F32 mRad;
+ LLDrawable::drawable_set_t* mResults;
+ BOOL mLights;
+ U32 mRes;
+};
+
+S32 LLSpatialPartition::getDrawables(const LLVector3& pos, F32 rad,
+ LLDrawable::drawable_set_t &results,
+ BOOL get_lights)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ LLOctreeGet getter(pos, rad, &results, get_lights);
+ getter.traverse(mOctree);
+
+ return results.size();
+}
+
+S32 LLSpatialPartition::getObjects(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results)
+{
+ return getDrawables(pos, rad, results, FALSE);
+}
+
+S32 LLSpatialPartition::getLights(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results)
+{
+ return getDrawables(pos, rad, results, TRUE);
+}
+
+class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
+{
+public:
+ LLOctreeRenderNonOccluded() {}
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+ {
+ const LLSpatialGroup::OctreeState* state = node->getOctState();
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+
+ if (!group->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED))
+ {
+ state->accept(this);
+
+ for (U32 i = 0; i < state->getChildCount(); i++)
+ {
+ traverse(state->getChild(i));
+ }
+
+ /*if (state->getElementCount() == 0)
+ {
+ return;
+ }*/
+
+ //draw tight fit bounding box for spatial group
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+ {
+ if (node->getElementCount() == 0)
+ {
+ return;
+ }
+
+ if (node->hasLeafState())
+ {
+ glColor4f(1,1,1,1);
+ }
+ else
+ {
+ glColor4f(0,1,1,1);
+ }
+
+
+ LLVector3 pos;
+ LLVector3 size;
+
+ pos = group->mObjectBounds[0];
+ size = group->mObjectBounds[1];
+
+ LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
+ LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
+ LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
+ LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
+
+ glBegin(GL_LINE_LOOP); //top
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos+v4).mV);
+ glEnd();
+
+ glBegin(GL_LINE_LOOP); //bottom
+ glVertex3fv((pos-v1).mV);
+ glVertex3fv((pos-v2).mV);
+ glVertex3fv((pos-v3).mV);
+ glVertex3fv((pos-v4).mV);
+ glEnd();
+
+
+ glBegin(GL_LINES);
+
+ //right
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos-v3).mV);
+
+ glVertex3fv((pos+v4).mV);
+ glVertex3fv((pos-v2).mV);
+
+ //left
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos-v4).mV);
+
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos-v1).mV);
+
+ glEnd();
+
+ LLVector3 nc = LLVector3(node->getCenter());
+ LLVector3 ns = LLVector3(node->getSize());
+
+ LLVector3 nv1 = ns.scaledVec(LLVector3( 1, 1,1));
+ LLVector3 nv2 = ns.scaledVec(LLVector3(-1, 1,1));
+ LLVector3 nv3 = ns.scaledVec(LLVector3(-1,-1,1));
+ LLVector3 nv4 = ns.scaledVec(LLVector3( 1,-1,1));
+
+
+
+ /*if (node->getElementCount() > 0)
+ {
+ //spokes
+ glColor4f(1,1,0,1);
+ glVertex3fv(pos.mV);
+ glColor4f(1,1,0,0);
+ glVertex3fv(nc.mV);
+
+ glColor4f(1,1,0,1); glVertex3fv((pos+v1).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v1).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos+v2).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v2).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos+v3).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v3).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos+v4).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v4).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ }*/
+
+
+
+ /*glColor4f(0,1,0,1);
+ glBegin(GL_LINE_LOOP); //top
+ glVertex3fv((nc+nv1).mV);
+ glVertex3fv((nc+nv2).mV);
+ glVertex3fv((nc+nv3).mV);
+ glVertex3fv((nc+nv4).mV);
+ glEnd();
+
+ glBegin(GL_LINE_LOOP); //bottom
+ glVertex3fv((nc-nv1).mV);
+ glVertex3fv((nc-nv2).mV);
+ glVertex3fv((nc-nv3).mV);
+ glVertex3fv((nc-nv4).mV);
+ glEnd();
+
+
+ glBegin(GL_LINES);
+
+ //right
+ glVertex3fv((nc+nv1).mV);
+ glVertex3fv((nc-nv3).mV);
+
+ glVertex3fv((nc+nv4).mV);
+ glVertex3fv((nc-nv2).mV);
+
+ //left
+ glVertex3fv((nc+nv2).mV);
+ glVertex3fv((nc-nv4).mV);
+
+ glVertex3fv((nc+nv3).mV);
+ glVertex3fv((nc-nv1).mV);
+ glEnd();*/
+
+ glLineWidth(1);
+
+ glDepthMask(GL_FALSE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glColor4f(0.1f,0.1f,1,0.1f);
+ drawBox(group->mObjectBounds[0], group->mObjectBounds[1]*1.01f+LLVector3(0.001f, 0.001f, 0.001f));
+ glDepthMask(GL_TRUE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ /*else
+ {
+ //occlusion paranoia check
+ const LLSpatialGroup::OctreeNode* parent = node;
+ while (parent != NULL)
+ {
+ LLSpatialGroup* grp = (LLSpatialGroup*) parent->getListener(0);
+ if (grp->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+ return;
+ }
+ parent = (const LLSpatialGroup::OctreeNode*) parent->getParent();
+ }
+
+ glColor4f(1,0,1,1);
+ drawBox(group->mBounds[0], group->mBounds[1]);
+ }*/
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
+
+ if (group->isState(LLSpatialGroup::CULLED | LLSpatialGroup::OCCLUDED))
+ {
+ return;
+ }
+
+ LLVector3 nodeCenter = group->mBounds[0];
+ LLVector3 octCenter = LLVector3(group->mOctreeNode->getCenter());
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+ {
+ glBegin(GL_LINES);
+ glColor4f(1,0.5f,0,1);
+ glVertex3fv(nodeCenter.mV);
+ glColor4f(0,1,1,0);
+ glVertex3fv(octCenter.mV);
+ glEnd();
+ }
+
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+
+ if (drawable->isSpatialBridge())
+ {
+ LLSpatialBridge* bridge = (LLSpatialBridge*) drawable;
+ glPushMatrix();
+ glMultMatrixf((F32*)bridge->mDrawable->getWorldMatrix().mMatrix);
+ traverse(bridge->mOctree);
+ glPopMatrix();
+ }
+
+ if (!drawable->isVisible())
+ {
+ continue;
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
+ {
+ if (drawable->isSpatialBridge())
+ {
+ glColor4f(1,0.5f,0,1);
+ }
+ else if (drawable->getVOVolume())
+ {
+ if (drawable->isRoot())
+ {
+ glColor4f(1,1,0,1);
+ }
+ else
+ {
+ glColor4f(0,1,0,1);
+ }
+ }
+ else if (drawable->getVObj())
+ {
+ switch (drawable->getVObj()->getPCode())
+ {
+ case LLViewerObject::LL_VO_SURFACE_PATCH:
+ glColor4f(0,1,1,1);
+ break;
+ case LLViewerObject::LL_VO_CLOUDS:
+ glColor4f(0.5f,0.5f,0.5f,1.0f);
+ break;
+ case LLViewerObject::LL_VO_PART_GROUP:
+ glColor4f(0,0,1,1);
+ break;
+ case LLViewerObject::LL_VO_WATER:
+ glColor4f(0,0.5f,1,1);
+ break;
+ case LL_PCODE_LEGACY_TREE:
+ glColor4f(0,0.5f,0,1);
+ default:
+ glColor4f(1,0,1,1);
+ break;
+ }
+ }
+ else
+ {
+ glColor4f(1,0,0,1);
+ }
+
+
+ const LLVector3* ext = drawable->getSpatialExtents();
+
+ LLVector3 pos = (ext[0] + ext[1]) * 0.5f;
+ LLVector3 size = (ext[1] - ext[0]) * 0.5f;
+
+ LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
+ LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
+ LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
+ LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
+
+ glBegin(GL_LINE_LOOP); //top
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos+v4).mV);
+ glEnd();
+
+ glBegin(GL_LINE_LOOP); //bottom
+ glVertex3fv((pos-v1).mV);
+ glVertex3fv((pos-v2).mV);
+ glVertex3fv((pos-v3).mV);
+ glVertex3fv((pos-v4).mV);
+ glEnd();
+
+
+ glBegin(GL_LINES);
+
+ //right
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos-v3).mV);
+
+ glVertex3fv((pos+v4).mV);
+ glVertex3fv((pos-v2).mV);
+
+ //left
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos-v4).mV);
+
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos-v1).mV);
+
+ glEnd();
+
+ //render space partition trace
+ glBegin(GL_LINE_STRIP);
+ glColor4f(1,0,0,1);
+ glVertex3fv(pos.mV);
+ glColor4f(0,1,0,1);
+ glVertex3dv(drawable->getPositionGroup().mdV);
+ glColor4f(0,0,1,1);
+ glVertex3fv(nodeCenter.mV);
+ glColor4f(1,1,0,1);
+ glVertex3fv(octCenter.mV);
+ glEnd();
+ }
+
+ if (drawable->getVOVolume() && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_CHAINS | LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ glLineWidth(3);
+
+ for (int face=0; face<drawable->getNumFaces(); ++face)
+ {
+ LLFace *facep = drawable->getFace(face);
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_CHAINS))
+ {
+ LLGLDepthTest depth(GL_FALSE);
+ if (facep->mNextFace)
+ {
+ glBegin(GL_LINE_STRIP);
+
+ if (facep->isState(LLFace::GLOBAL))
+ {
+ glColor4f(0,1,0,1);
+ }
+ else
+ {
+ glColor4f(1,0.5f,0.25f,1);
+ }
+
+ if (drawable->isActive())
+ {
+ glVertex3fv(facep->mCenterLocal.mV);
+ glVertex3fv(facep->mNextFace->mCenterLocal.mV);
+ }
+ else
+ {
+ glVertex3fv(facep->mCenterAgent.mV);
+ glVertex3fv(facep->mNextFace->mCenterAgent.mV);
+ }
+
+ glEnd();
+ }
+ else
+ {
+ glPointSize(5);
+ glBegin(GL_POINTS);
+
+ if (!facep->isState(LLFace::GLOBAL))
+ {
+ glColor4f(1,0.75f,0,1);
+ glVertex3fv(facep->mCenterLocal.mV);
+ }
+ else
+ {
+ glColor4f(0,0.75f,1,1);
+ glVertex3fv(facep->mCenterAgent.mV);
+ }
+
+ glEnd();
+ glPointSize(1);
+ }
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ LLVector4 cold(0,0,0.25f);
+ LLVector4 hot(1,0.25f,0.25f);
+
+ LLVector4 boost_cold(0,0,0,0);
+ LLVector4 boost_hot(0,1,0,1);
+
+ LLGLDisable blend(GL_BLEND);
+
+ LLViewerImage* imagep = facep->getTexture();
+ if (imagep)
+ {
+
+ //F32 vsize = LLVOVolume::getTextureVirtualSize(facep);
+ F32 vsize = imagep->mMaxVirtualSize;
+
+ if (vsize > sCurMaxTexPriority)
+ {
+ sCurMaxTexPriority = vsize;
+ }
+
+ F32 t = vsize/sLastMaxTexPriority;
+
+ LLVector4 col = lerp(cold, hot, t);
+ glColor4fv(col.mV);
+ }
+ else
+ {
+ glColor4f(1,0,1,1);
+ }
+
+ LLVector3 center = (facep->mExtents[1]+facep->mExtents[0])*0.5f;
+ LLVector3 size = (facep->mExtents[1]-facep->mExtents[0])*0.5f + LLVector3(0.01f, 0.01f, 0.01f);
+ drawBox(center, size);
+
+ S32 boost = imagep->getBoostLevel();
+ if (boost)
+ {
+ F32 t = (F32) boost / (F32) (LLViewerImage::BOOST_MAX_LEVEL-1);
+ LLVector4 col = lerp(boost_cold, boost_hot, t);
+ LLGLEnable blend_on(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glColor4fv(col.mV);
+ drawBox(center, size);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ }
+ }
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_POINTS))
+ {
+ glPointSize(4);
+ glColor4f(1,1,1,1);
+ glBegin(GL_POINTS);
+ S32 num_faces = drawable->getNumFaces();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ LLStrider<LLVector3> vertices;
+ drawable->getFace(i)->getVertices(vertices);
+
+ LLFace* face = drawable->getFace(i);
+
+ for (S32 v = 0; v < (S32)drawable->getFace(i)->getGeomCount(); v++)
+ {
+ if (!face->getDrawable()->isActive())
+ {
+ //glVertex3fv(vertices[v].mV);
+ }
+ else
+ {
+ glVertex3fv((vertices[v]*face->getRenderMatrix()).mV);
+ }
+ }
+ }
+ glEnd();
+ glPointSize(1);
+ }
+
+ glLineWidth(1);
+ }
+ }
+};
+
+void LLSpatialPartition::renderDebug()
+{
+ if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE |
+ LLPipeline::RENDER_DEBUG_OCCLUSION |
+ LLPipeline::RENDER_DEBUG_BBOXES |
+ LLPipeline::RENDER_DEBUG_POINTS |
+ LLPipeline::RENDER_DEBUG_FACE_CHAINS |
+ LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ return;
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ //sLastMaxTexPriority = lerp(sLastMaxTexPriority, sCurMaxTexPriority, gFrameIntervalSeconds);
+ sLastMaxTexPriority = sCurMaxTexPriority;
+ sCurMaxTexPriority = 0.f;
+ }
+
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ LLGLDisable cullface(GL_CULL_FACE);
+ LLGLEnable blend(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ LLGLDisable tex(GL_TEXTURE_2D);
+ gPipeline.disableLights();
+
+ LLOctreeRenderNonOccluded render_debug;
+ render_debug.traverse(mOctree);
+
+ LLGLDisable cull_face(GL_CULL_FACE);
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ //draw frustum
+ glColor4f(0,0,1,0.5f);
+ glBegin(GL_QUADS);
+ //glVertex3fv(gCamera->mAgentFrustum[0].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[1].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[2].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[3].mV);
+
+ //glVertex3fv(gCamera->mAgentFrustum[4].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[5].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[6].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[7].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[0].mV);
+ glVertex3fv(gCamera->mAgentFrustum[1].mV);
+ glVertex3fv(gCamera->mAgentFrustum[5].mV);
+ glVertex3fv(gCamera->mAgentFrustum[4].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[1].mV);
+ glVertex3fv(gCamera->mAgentFrustum[2].mV);
+ glVertex3fv(gCamera->mAgentFrustum[6].mV);
+ glVertex3fv(gCamera->mAgentFrustum[5].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[2].mV);
+ glVertex3fv(gCamera->mAgentFrustum[3].mV);
+ glVertex3fv(gCamera->mAgentFrustum[7].mV);
+ glVertex3fv(gCamera->mAgentFrustum[6].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[3].mV);
+ glVertex3fv(gCamera->mAgentFrustum[0].mV);
+ glVertex3fv(gCamera->mAgentFrustum[4].mV);
+ glVertex3fv(gCamera->mAgentFrustum[7].mV);
+
+ glEnd();
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
+ {
+ LLGLDisable fog(GL_FOG);
+ LLGLDepthTest gls_depth(GL_FALSE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+
+ for (std::vector<LLSpatialGroup*>::iterator i = mOccludedList.begin(); i != mOccludedList.end(); ++i)
+ { //draw occluded nodes
+ LLSpatialGroup* node = *i;
+ if (node->isDead())
+ {
+ continue;
+ }
+ if (!node->isState(LLSpatialGroup::OCCLUDED))
+ {
+ continue;
+ }
+ else
+ {
+ glColor4f(0.25f,0.125f,0.1f,0.125f);
+ }
+ LLVector3 c;
+ LLVector3 r;
+
+ c = node->mBounds[0];
+ r = node->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.01f,0.01f,0.01f);;
+
+ drawBox(c,r);
+ }
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+}
+
+
+BOOL LLSpatialPartition::isVisible(const LLVector3& v)
+{
+ if (!gCamera->sphereInFrustum(v, 4.0f))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+class LLOctreePick : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLVector3 mStart;
+ LLVector3 mEnd;
+ LLDrawable* mRet;
+
+ LLOctreePick(LLVector3 start, LLVector3 end)
+ : mStart(start), mEnd(end)
+ {
+ mRet = NULL;
+ }
+
+ virtual LLDrawable* check(const LLSpatialGroup::OctreeNode* node)
+ {
+ const LLSpatialGroup::OctreeState* state = node->getOctState();
+ state->accept(this);
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ const LLSpatialGroup::OctreeNode* child = node->getChild(i);
+ LLVector3 res;
+
+ LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
+
+ LLVector3 size;
+ LLVector3 center;
+
+ size = group->mBounds[1];
+ center = group->mBounds[0];
+
+ if (LLLineSegmentAABB(mStart, mEnd, center, size))
+ {
+ check(child);
+ }
+ }
+
+ return mRet;
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ check(*i);
+ }
+ }
+
+ virtual bool check(LLDrawable* drawable)
+ {
+ LLViewerObject* vobj = drawable->getVObj();
+ if (vobj->lineSegmentIntersect(mStart, mEnd))
+ {
+ mRet = vobj->mDrawable;
+ }
+
+ return false;
+ }
+};
+
+LLDrawable* LLSpatialPartition::pickDrawable(const LLVector3& start, const LLVector3& end, LLVector3& collision)
+{
+ LLOctreePick pick(start, end);
+ LLDrawable* ret = pick.check(mOctree);
+ collision.setVec(pick.mEnd);
+ return ret;
+}
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
new file mode 100644
index 0000000000..012e3a9e82
--- /dev/null
+++ b/indra/newview/llspatialpartition.h
@@ -0,0 +1,209 @@
+/**
+ * @file llspatialpartition.h
+ * @brief LLSpatialGroup header file including definitions for supporting functions
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSPATIALPARTITION_H
+#define LL_LLSPATIALPARTITION_H
+
+#define SG_MIN_DIST_RATIO 0.00001f
+
+#include "llmemory.h"
+#include "lldrawable.h"
+#include "lloctree.h"
+#include "llgltypes.h"
+
+#include <queue>
+
+class LLCullInfo
+{
+public:
+ LLVector3 mPos;
+ F32 mRadius;
+ LLPointer<LLDrawable> mDrawablep;
+};
+
+#define SG_STATE_INHERIT_MASK (CULLED | OCCLUDED)
+class LLSpatialPartition;
+
+class LLSpatialGroup : public LLOctreeListener<LLDrawable>
+{
+ friend class LLSpatialPartition;
+public:
+
+ typedef LLOctreeListener<LLDrawable> BaseType;
+ typedef LLOctreeListener<LLDrawable> OctreeListener;
+ typedef LLTreeNode<LLDrawable> TreeNode;
+ typedef LLOctreeNode<LLDrawable> OctreeNode;
+ typedef LLOctreeRoot<LLDrawable> OctreeRoot;
+ typedef LLOctreeState<LLDrawable> OctreeState;
+ typedef LLOctreeTraveler<LLDrawable> OctreeTraveler;
+
+ typedef enum
+ {
+ IN_QUEUE = 0x00000001,
+ QUERY_PENDING = 0x00000002,
+ CULLED = 0x00000004,
+ OCCLUDED = 0x00000008,
+ DEAD = 0x00000010,
+ ACTIVE_OCCLUSION = 0x00000020,
+ EARLY_FAIL = 0x00000040,
+ DEACTIVATE_OCCLUSION = 0x00000080,
+ RESHADOW = 0x00000100,
+ RESHADOW_QUEUE = 0x00000200,
+ DIRTY = 0x00000400,
+ OBJECT_DIRTY = 0x00000800,
+ DISCARD_QUERY = 0x00001000,
+ QUERY_OUT = 0x00002000,
+ OCCLUDING = 0x00004000,
+ SKIP_FRUSTUM_CHECK = 0x00008000,
+ } eSpatialState;
+
+ typedef enum
+ {
+ STATE_MODE_SINGLE = 0, //set one node
+ STATE_MODE_BRANCH, //set entire branch
+ STATE_MODE_DIFF //set entire branch as long as current state is different
+ } eSetStateMode;
+
+ LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part);
+ BOOL safeToDelete();
+ virtual ~LLSpatialGroup();
+
+ S32 getCount() const { return mObjects.size(); }
+ BOOL isDead() { return isState(DEAD); }
+ BOOL isState(U32 state) const { return mState & state ? TRUE : FALSE; }
+ U32 getState() { return mState; }
+ void setState(U32 state) { mState |= state; }
+ void clearState(U32 state) { mState &= ~state; }
+
+ void validate();
+
+ void setState(U32 state, S32 mode);
+
+
+ void clearState(U32 state, S32 mode);
+ BOOL addObject(LLDrawable *drawablep, BOOL add_all = FALSE, BOOL from_octree = FALSE);
+ BOOL removeObject(LLDrawable *drawablep, BOOL from_octree = FALSE);
+ BOOL updateInGroup(LLDrawable *drawablep, BOOL immediate = FALSE); // Update position if it's in the group
+ BOOL isVisible();
+ void shift(const LLVector3 &offset);
+ BOOL boundObjects(BOOL empty, LLVector3& newMin, LLVector3& newMax);
+ void unbound();
+ BOOL rebound();
+ BOOL changeLOD();
+
+ //LISTENER FUNCTIONS
+ virtual void handleInsertion(const TreeNode* node, LLDrawable* face);
+ virtual void handleRemoval(const TreeNode* node, LLDrawable* face);
+ virtual void handleDestruction(const TreeNode* node);
+ virtual void handleStateChange(const TreeNode* node);
+ virtual void handleChildAddition(const OctreeNode* parent, OctreeNode* child);
+ virtual void handleChildRemoval(const OctreeNode* parent, const OctreeNode* child);
+
+protected:
+ std::vector<LLCullInfo> mObjects;
+ U32 mState;
+ S32 mLODHash;
+ static S32 sLODSeed;
+
+public:
+ OctreeNode* mOctreeNode;
+ LLSpatialPartition* mSpatialPartition;
+ LLVector3 mBounds[2];
+ LLVector3 mExtents[2];
+ LLVector3 mObjectExtents[2];
+ LLVector3 mObjectBounds[2];
+
+};
+
+class LLSpatialPartition
+{
+public:
+ LLSpatialPartition();
+ virtual ~LLSpatialPartition();
+
+ LLSpatialGroup *put(LLDrawable *drawablep);
+ BOOL remove(LLDrawable *drawablep, LLSpatialGroup *curp);
+
+ LLDrawable* pickDrawable(const LLVector3& start, const LLVector3& end, LLVector3& collision);
+
+ // If the drawable moves, move it here.
+ virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
+ void shift(const LLVector3 &offset);
+
+ S32 cull(LLCamera &camera, std::vector<LLDrawable *>* results = NULL, BOOL for_select = FALSE); // Cull on arbitrary frustum
+ BOOL checkOcclusion(LLSpatialGroup* group, LLCamera* camera);
+ void processOcclusion(LLCamera* camera);
+ void doOcclusion(LLCamera* camera);
+ BOOL isVisible(const LLVector3& v);
+
+ S32 getObjects(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results );
+ S32 getLights(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results );
+
+ void renderDebug();
+ void restoreGL();
+
+protected:
+ S32 getDrawables(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results, BOOL get_lights );
+
+ LLSpatialGroup *mLastAddedGroupp;
+
+ typedef std::set<LLSpatialGroup*> spatial_group_set_t;
+ spatial_group_set_t mSpatialGroups;
+
+ //things that might be occluded
+ std::queue<LLSpatialGroup*> mOcclusionQueue;
+
+ //things awaiting query
+ std::queue<LLSpatialGroup*> mQueryQueue;
+
+ std::vector<LLGLuint> mOcclusionQueries;
+
+public:
+ LLSpatialGroup::OctreeNode* mOctree;
+
+ //things that are occluded
+ std::vector<LLSpatialGroup*> mOccludedList;
+
+ std::queue<LLSpatialGroup*> mReshadowQueue;
+
+};
+
+// class for creating bridges between spatial partitions
+class LLSpatialBridge : public LLDrawable, public LLSpatialPartition
+{
+public:
+ LLSpatialBridge(LLDrawable* root);
+ virtual ~LLSpatialBridge();
+
+ virtual BOOL isSpatialBridge() const { return TRUE; }
+
+ virtual void updateSpatialExtents();
+ virtual void updateBinRadius();
+ virtual void setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results = NULL, BOOL for_select = FALSE);
+ virtual void updateDistance(LLCamera& camera_in);
+ virtual void makeActive();
+ virtual void makeStatic();
+ virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
+ virtual BOOL updateMove();
+ virtual void shiftPos(const LLVector3& vec);
+ virtual void cleanupReferences();
+ virtual LLSpatialPartition* asPartition() { return this; }
+ LLCamera transformCamera(LLCamera& camera);
+
+ LLDrawable* mDrawable;
+};
+
+extern const F32 SG_BOX_SIDE;
+extern const F32 SG_BOX_OFFSET;
+extern const F32 SG_BOX_RAD;
+
+extern const F32 SG_OBJ_SIDE;
+extern const F32 SG_MAX_OBJ_RAD;
+
+#endif //LL_LLSPATIALPARTITION_H
+
diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp
new file mode 100644
index 0000000000..e614134080
--- /dev/null
+++ b/indra/newview/llsprite.cpp
@@ -0,0 +1,296 @@
+/**
+ * @file llsprite.cpp
+ * @brief LLSprite class implementation
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/* -*- c++ -*-
+ * Notes:
+ * PR - Should add a creator that can take a pointer rather than handle for streaming
+ * object textures.
+ * PR - Need to add support for lit/non-lit conditions, set normals?
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <llglheaders.h>
+
+#include "llsprite.h"
+
+#include "math.h"
+
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "viewer.h"
+
+LLVector3 LLSprite::sCameraUp(0.0f,0.0f,1.0f);
+LLVector3 LLSprite::sCameraRight(1.0f,0.0f,0.0f);
+LLVector3 LLSprite::sCameraPosition(0.f, 0.f, 0.f);
+LLVector3 LLSprite::sNormal(0.0f,0.0f,0.0f);
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+// A simple initialization
+LLSprite::LLSprite(const LLUUID &image_uuid)
+{
+ mImageID = image_uuid;
+ mImagep = NULL;
+
+ setSize(1.0f, 1.0f);
+ setPosition(LLVector3(0.0f, 0.0f, 0.0f));
+ mTexMode = GL_REPLACE;
+ mColor.setVec(0.5f, 0.5f, 0.5f, 1.0f);
+ mFollow = TRUE;
+ mUseCameraUp = TRUE;
+}
+
+LLSprite::LLSprite(const LLUUID &image_uuid, const F32 width, const F32 height, const BOOL b_usemipmap)
+{
+ mImageID = image_uuid;
+ mImagep = NULL;
+
+ setSize(width,height);
+ setPosition(LLVector3(0.0f, 0.0f, 0.0f));
+ mTexMode = GL_REPLACE;
+ mColor.setVec(0.5f, 0.5f, 0.5f, 1.0f);
+ mFollow = TRUE;
+ mUseCameraUp = TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////
+LLSprite::~LLSprite()
+{
+}
+
+void LLSprite::updateFace(LLFace &face)
+{
+ LLViewerCamera &camera = *gCamera;
+
+ // First, figure out how many vertices/indices we need.
+ U32 num_vertices, num_indices;
+ U32 vertex_count = 0;
+
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> tex_coordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ // Get the total number of vertices and indices
+ if (mFollow)
+ {
+ num_vertices = 4;
+ num_indices = 6;
+ }
+ else
+ {
+ num_vertices = 4;
+ num_indices = 12;
+ }
+
+ // Setup face
+ face.setPrimType(LLTriangles);
+ face.setSize(num_vertices, num_indices);
+ index_offset = face.getGeometry(verticesp,normalsp,tex_coordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ if (mFollow)
+ {
+ sCameraUp = camera.getUpAxis();
+ sCameraRight = -camera.getLeftAxis();
+ sCameraPosition = camera.getOrigin();
+ sNormal = -camera.getAtAxis();
+ if (mUseCameraUp)
+ {
+ // these need to live here because the height/width may change between render calls
+ mScaledUp = sCameraUp;
+ mScaledRight = sCameraRight;
+
+ mScaledUp *= mHeightDiv2;
+ mScaledRight *= mWidthDiv2;
+
+ mA = mPosition + mScaledRight + mScaledUp;
+ mB = mPosition - mScaledRight + mScaledUp;
+ mC = mPosition - mScaledRight - mScaledUp;
+ mD = mPosition + mScaledRight - mScaledUp;
+ }
+ else
+ {
+ // The up vector is perpendicular to the camera vector...
+ LLVector3 camera_vec = mPosition - sCameraPosition;
+ mScaledRight = camera_vec % LLVector3(0.f, 0.f, 1.f);
+ mScaledUp = -(camera_vec % mScaledRight);
+ mScaledUp.normVec();
+ mScaledRight.normVec();
+ mScaledUp *= mHeightDiv2;
+ mScaledRight *= mWidthDiv2;
+
+ mA = mPosition + mScaledRight + mScaledUp;
+ mB = mPosition - mScaledRight + mScaledUp;
+ mC = mPosition - mScaledRight - mScaledUp;
+ mD = mPosition + mScaledRight - mScaledUp;
+ }
+ }
+ else
+ {
+ // this is equivalent to how it was done before. . .
+ // we need to establish a way to
+ // identify the orientation of a particular sprite rather than
+ // just banging it in on the x,z plane if it's not following the camera.
+
+ LLVector3 x_axis;
+ LLVector3 y_axis;
+
+ F32 dot = sNormal * LLVector3(0.f, 1.f, 0.f);
+ if (dot == 1.f || dot == -1.f)
+ {
+ x_axis.setVec(1.f, 0.f, 0.f);
+ y_axis.setVec(0.f, 1.f, 0.f);
+ }
+ else
+ {
+ x_axis = sNormal % LLVector3(0.f, -1.f, 0.f);
+ x_axis.normVec();
+
+ y_axis = sNormal % x_axis;
+ }
+
+ LLQuaternion yaw_rot(mYaw, sNormal);
+
+ // rotate axes by specified yaw
+ x_axis = x_axis * yaw_rot;
+ y_axis = y_axis * yaw_rot;
+
+ // rescale axes by width and height of sprite
+ x_axis = x_axis * mWidthDiv2;
+ y_axis = y_axis * mHeightDiv2;
+
+ mA = -x_axis + y_axis;
+ mB = x_axis + y_axis;
+ mC = x_axis - y_axis;
+ mD = -x_axis - y_axis;
+
+ mA += mPosition;
+ mB += mPosition;
+ mC += mPosition;
+ mD += mPosition;
+ }
+
+ face.setFaceColor(mColor);
+
+ *tex_coordsp = LLVector2(0.f, 0.f);
+ *verticesp = mC;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ *tex_coordsp = LLVector2(0.f, 1.f);
+ *verticesp = mB;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ *tex_coordsp = LLVector2(1.f, 1.f);
+ *verticesp = mA;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ *tex_coordsp = LLVector2(1.f, 0.0f);
+ *verticesp = mD;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ // Generate indices, since they're easy.
+ // Just a series of quads.
+ *indicesp++ = index_offset;
+ *indicesp++ = 2 + index_offset;
+ *indicesp++ = 1 + index_offset;
+
+ *indicesp++ = index_offset;
+ *indicesp++ = 3 + index_offset;
+ *indicesp++ = 2 + index_offset;
+
+ if (!mFollow)
+ {
+ *indicesp++ = 0 + index_offset;
+ *indicesp++ = 1 + index_offset;
+ *indicesp++ = 2 + index_offset;
+ *indicesp++ = 0 + index_offset;
+ *indicesp++ = 2 + index_offset;
+ *indicesp++ = 3 + index_offset;
+ }
+
+ face.mCenterAgent = mPosition;
+}
+
+void LLSprite::setPosition(const LLVector3 &position)
+{
+ mPosition = position;
+}
+
+
+void LLSprite::setPitch(const F32 pitch)
+{
+ mPitch = pitch;
+}
+
+
+void LLSprite::setSize(const F32 width, const F32 height)
+{
+ mWidth = width;
+ mHeight = height;
+ mWidthDiv2 = width/2.0f;
+ mHeightDiv2 = height/2.0f;
+}
+
+void LLSprite::setYaw(F32 yaw)
+{
+ mYaw = yaw;
+}
+
+void LLSprite::setFollow(const BOOL follow)
+{
+ mFollow = follow;
+}
+
+void LLSprite::setUseCameraUp(const BOOL use_up)
+{
+ mUseCameraUp = use_up;
+}
+
+void LLSprite::setTexMode(const LLGLenum mode)
+{
+ mTexMode = mode;
+}
+
+void LLSprite::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
+void LLSprite::setColor(const F32 r, const F32 g, const F32 b, const F32 a)
+{
+ mColor.setVec(r, g, b, a);
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/llsprite.h b/indra/newview/llsprite.h
new file mode 100644
index 0000000000..6aa24fc8db
--- /dev/null
+++ b/indra/newview/llsprite.h
@@ -0,0 +1,89 @@
+/**
+ * @file llsprite.h
+ * @brief LLSprite class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSPRITE_H
+#define LL_LLSPRITE_H
+
+////#include "vmath.h"
+//#include "llmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "v4color.h"
+#include "lluuid.h"
+#include "llgl.h"
+#include "llviewerimage.h"
+
+class LLViewerCamera;
+
+class LLFace;
+
+class LLSprite
+{
+public:
+ LLSprite(const LLUUID &image_uuid);
+ LLSprite(const LLUUID &image_uuid, const F32 width, const F32 height, const BOOL b_usemipmap = TRUE);
+ ~LLSprite();
+
+ void render(LLViewerCamera * camerap);
+
+ F32 getWidth() const { return mWidth; }
+ F32 getHeight() const { return mHeight; }
+ F32 getYaw() const { return mYaw; }
+ F32 getPitch() const { return mPitch; }
+ F32 getAlpha() const { return mColor.mV[VALPHA]; }
+
+ LLVector3 getPosition() const { return mPosition; }
+ LLColor4 getColor() const { return mColor; }
+
+ void setPosition(const LLVector3 &position);
+ void setPitch(const F32 pitch);
+ void setSize(const F32 width, const F32 height);
+ void setYaw(const F32 yaw);
+ void setFollow(const BOOL follow);
+ void setUseCameraUp(const BOOL use_up);
+
+ void setTexMode(LLGLenum mode);
+ void setColor(const LLColor4 &color);
+ void setColor(const F32 r, const F32 g, const F32 b, const F32 a);
+ void setAlpha(const F32 alpha) { mColor.mV[VALPHA] = alpha; }
+ void setNormal(const LLVector3 &normal) { sNormal = normal; sNormal.normVec();}
+
+ F32 getAlpha();
+
+ void updateFace(LLFace &face);
+
+public:
+ LLUUID mImageID;
+ LLPointer<LLViewerImage> mImagep;
+private:
+ F32 mWidth;
+ F32 mHeight;
+ F32 mWidthDiv2;
+ F32 mHeightDiv2;
+ F32 mPitch;
+ F32 mYaw;
+ LLVector3 mPosition;
+ BOOL mFollow;
+ BOOL mUseCameraUp;
+
+ LLColor4 mColor;
+ LLGLenum mTexMode;
+
+ // put
+ LLVector3 mScaledUp;
+ LLVector3 mScaledRight;
+ static LLVector3 sCameraUp;
+ static LLVector3 sCameraRight;
+ static LLVector3 sCameraPosition;
+ static LLVector3 sNormal;
+ LLVector3 mA,mB,mC,mD; // the four corners of a quad
+
+};
+
+#endif
+
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
new file mode 100644
index 0000000000..643b86e104
--- /dev/null
+++ b/indra/newview/llstartup.cpp
@@ -0,0 +1,3959 @@
+/**
+ * @file llstartup.cpp
+ * @brief startup routines.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llstartup.h"
+
+#if LL_WINDOWS
+#include <process.h> // _spawnl()
+#else
+#include <sys/stat.h> // mkdir()
+#endif
+
+#include "audioengine.h"
+
+#if LL_FMOD
+#include "audioengine_fmod.h"
+#endif
+
+#include "audiosettings.h"
+#include "llcachename.h"
+#include "llviewercontrol.h"
+#include "llcrypto.h"
+#include "lldir.h"
+#include "lleconomy.h"
+#include "llfiltersd2xmlrpc.h"
+#include "llfocusmgr.h"
+#include "imageids.h"
+#include "lllandmark.h"
+#include "llloginflags.h"
+#include "llmd5.h"
+#include "llmemorystream.h"
+#include "llregionhandle.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include "llsecondlifeurls.h"
+#include "llstring.h"
+#include "lluserrelations.h"
+#include "llversion.h"
+#include "llvfs.h"
+#include "llwindow.h" // for shell_open
+#include "message.h"
+#include "v3math.h"
+
+#include "llagent.h"
+#include "llagentpilot.h"
+#include "llasynchostbyname.h"
+#include "llfloateravatarpicker.h"
+#include "llcallbacklist.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llconsole.h"
+#include "llcontainerview.h"
+#include "lldebugview.h"
+#include "lldrawable.h"
+#include "lleventnotifier.h"
+#include "llface.h"
+#include "llfeaturemanager.h"
+#include "llfloateraccounthistory.h"
+#include "llfloaterchat.h"
+#include "llfloatergesture.h"
+#include "llfloaterland.h"
+#include "llfloatertopobjects.h"
+#include "llfloaterrate.h"
+#include "llfloatertos.h"
+#include "llfloaterworldmap.h"
+#include "llframestats.h"
+#include "llframestatview.h"
+#include "llgesturemgr.h"
+#include "llgroupmgr.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llhttpclient.h"
+#include "llimagebmp.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "llpanellogin.h"
+#include "llmutelist.h"
+#include "llnotify.h"
+#include "llpanelavatar.h"
+#include "llpaneldirbrowser.h"
+#include "llpaneldirland.h"
+#include "llpanelevent.h"
+#include "llpanelclassified.h"
+#include "llpanelpick.h"
+#include "llpanelplace.h"
+#include "llpanelgrouplandmoney.h"
+#include "llpanelgroupnotices.h"
+#include "llpreview.h"
+#include "llpreviewscript.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llstatview.h"
+#include "llsurface.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llurlwhitelist.h"
+#include "lluserauth.h"
+#include "llviewerassetstorage.h"
+#include "llviewercamera.h"
+#include "llviewerdisplay.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewermedialist.h"
+#include "llviewermenu.h"
+#include "llviewermessage.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerthrottle.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoclouds.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "llxfermanager.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llmediaengine.h"
+#include "llfasttimerview.h"
+#include "llmozlib.h"
+#include "llweb.h"
+#include "llfloaterhtml.h"
+
+#if LL_WINDOWS
+#include "llwindebug.h"
+#include "lldxhardware.h"
+#endif
+
+#if LL_QUICKTIME_ENABLED
+#if LL_DARWIN
+#include <QuickTime/QuickTime.h>
+#else
+// quicktime specific includes
+#include "MacTypes.h"
+#include "QTML.h"
+#include "Movies.h"
+#include "FixMath.h"
+#endif
+#endif
+
+//
+// exported globals
+//
+
+// HACK: Allow server to change sun and moon IDs.
+// I can't figure out how to pass the appropriate
+// information into the LLVOSky constructor. JC
+LLUUID gSunTextureID = IMG_SUN;
+LLUUID gMoonTextureID = IMG_MOON;
+LLUUID gCloudTextureID = IMG_CLOUD_POOF;
+
+const char* SCREEN_HOME_FILENAME = "screen_home.bmp";
+const char* SCREEN_LAST_FILENAME = "screen_last.bmp";
+
+//
+// Imported globals
+//
+extern LLPointer<LLImageGL> gStartImageGL;
+extern S32 gStartImageWidth;
+extern S32 gStartImageHeight;
+extern std::string gSerialNumber;
+
+//
+// local globals
+//
+
+static LLHost gAgentSimHost;
+static BOOL gSkipOptionalUpdate = FALSE;
+
+bool gQuickTimeInitialized = false;
+static bool gGotUseCircuitCodeAck = false;
+LLString gInitialOutfit;
+LLString gInitialOutfitGender; // "male" or "female"
+
+
+//
+// local function declaration
+//
+
+void login_show();
+void login_callback(S32 option, void* userdata);
+LLString load_password_from_disk();
+void save_password_to_disk(const char* hashed_password);
+BOOL is_hex_string(U8* str, S32 len);
+void show_first_run_dialog();
+void first_run_dialog_callback(S32 option, void* userdata);
+void set_startup_status(const F32 frac, const char* string, const char* msg);
+void on_userserver_name_resolved( BOOL success, const LLString& host_name, U32 ip, void* userdata );
+void login_alert_status(S32 option, void* user_data);
+void update_app(BOOL mandatory, const std::string& message);
+void update_dialog_callback(S32 option, void *userdata);
+void login_packet_failed(void**, S32 result);
+void use_circuit_callback(void**, S32 result);
+void register_viewer_callbacks(LLMessageSystem* msg);
+void init_stat_view();
+void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32);
+void dialog_choose_gender_first_start();
+void callback_choose_gender(S32 option, void* userdata);
+void init_start_screen(S32 location_id);
+void release_start_screen();
+void process_connect_to_userserver(LLMessageSystem* msg, void**);
+
+
+//
+// exported functionality
+//
+
+//
+// local classes
+//
+class LLGestureInventoryFetchObserver : public LLInventoryFetchObserver
+{
+public:
+ LLGestureInventoryFetchObserver() {}
+ virtual void done()
+ {
+ // we've downloaded all the items, so repaint the dialog
+ LLFloaterGesture::refreshAll();
+
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+
+
+// Returns FALSE to skip other idle processing. Should only return
+// TRUE when all initialization done.
+BOOL idle_startup()
+{
+ LLMemType mt1(LLMemType::MTYPE_STARTUP);
+
+ const F32 PRECACHING_DELAY = gSavedSettings.getF32("PrecachingDelay");
+ const F32 TIMEOUT_SECONDS = 5.f;
+ const S32 MAX_TIMEOUT_COUNT = 3;
+ static LLTimer timeout;
+ static S32 timeout_count = 0;
+
+ static LLTimer login_time;
+
+ // until this is encapsulated, this little hack for the
+ // auth/transform loop will do.
+ static F32 progress = 0.10f;
+
+ static std::string auth_uri;
+ static std::string auth_method;
+ static std::string auth_desc;
+ static std::string auth_message;
+ static LLString firstname;
+ static LLString lastname;
+ static LLString password;
+ static std::vector<const char*> requested_options;
+
+ static U32 region_size = 256;
+ static F32 region_scale = 1.f;
+ static U64 first_sim_handle = 0;
+ static LLHost first_sim;
+ static std::string first_sim_seed_cap;
+
+ static LLVector3 initial_sun_direction(1.f, 0.f, 0.f);
+ static LLVector3 agent_start_position_region(10.f, 10.f, 10.f); // default for when no space server
+ static LLVector3 agent_start_look_at(1.0f, 0.f, 0.f);
+ static std::string agent_start_location = "safe";
+
+ // last location by default
+ static S32 agent_location_id = START_LOCATION_ID_LAST;
+ static S32 location_which = START_LOCATION_ID_LAST;
+
+ static BOOL show_connect_box = TRUE;
+ static BOOL remember_password = TRUE;
+
+ static BOOL stipend_since_login = FALSE;
+
+ static BOOL samename = FALSE;
+
+ static BOOL did_precache = FALSE;
+
+ BOOL do_normal_idle = FALSE;
+
+ // HACK: These are things from the main loop that usually aren't done
+ // until initialization is complete, but need to be done here for things
+ // to work.
+ gIdleCallbacks.callFunctions();
+ gViewerWindow->handlePerFrameHover();
+ LLMortician::updateClass();
+
+ if (gNoRender)
+ {
+ // HACK, skip optional updates if you're running drones
+ gSkipOptionalUpdate = TRUE;
+ }
+ else
+ {
+ // Update images?
+ gImageList.updateImages(0.01f);
+ }
+
+ if (STATE_FIRST == gStartupState)
+ {
+ gViewerWindow->showCursor();
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
+
+ /////////////////////////////////////////////////
+ //
+ // Initialize stuff that doesn't need data from userserver/simulators
+ //
+
+ if (gFeatureManagerp->isSafe())
+ {
+ gViewerWindow->alertXml("DisplaySetToSafe");
+ }
+ else if (gSavedSettings.getS32("LastFeatureVersion") < gFeatureManagerp->getVersion())
+ {
+ if (gSavedSettings.getS32("LastFeatureVersion") != 0)
+ {
+ gViewerWindow->alertXml("DisplaySetToRecommended");
+ }
+ }
+ gSavedSettings.setS32("LastFeatureVersion", gFeatureManagerp->getVersion());
+
+ LLString xml_file = LLUI::locateSkin("xui_version.xml");
+ LLXMLNodePtr root;
+ bool xml_ok = false;
+ if (LLXMLNode::parseFile(xml_file, root, NULL))
+ {
+ if( (root->hasName("xui_version") ) )
+ {
+ LLString value = root->getValue();
+ F32 version = 0.0f;
+ LLString::convertToF32(value, version);
+ if (version >= 1.0f)
+ {
+ xml_ok = true;
+ }
+ }
+ }
+ if (!xml_ok)
+ {
+ // XUI:translate (maybe - very unlikely error message)
+ // Note: alerts.xml may be invalid - if this gets translated it will need to be in the code
+ LLString bad_xui_msg = "An error occured while updating Second Life. Please download the latest version from www.secondlife.com.";
+ app_early_exit(bad_xui_msg);
+ }
+ //
+ // Statistics stuff
+ //
+
+ // Load autopilot and stats stuff
+ gAgentPilot.load(gSavedSettings.getString("StatsPilotFile").c_str());
+ gFrameStats.setFilename(gSavedSettings.getString("StatsFile"));
+ gFrameStats.setSummaryFilename(gSavedSettings.getString("StatsSummaryFile"));
+
+ //gErrorStream.setTime(gSavedSettings.getBOOL("LogTimestamps"));
+
+ // Load the throttle settings
+ gViewerThrottle.load();
+
+ //
+ // Initialize messaging system
+ //
+ llinfos << "Initializing messaging system..." << llendl;
+
+ std::string message_template_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"message_template.msg");
+
+ FILE* found_template = NULL;
+ found_template = LLFile::fopen(message_template_path.c_str(), "r");
+ if (found_template)
+ {
+ fclose(found_template);
+
+ if(!start_messaging_system(
+ message_template_path,
+ gAgent.mViewerPort,
+ LL_VERSION_MAJOR,
+ LL_VERSION_MINOR,
+ LL_VERSION_PATCH,
+ FALSE,
+ std::string()))
+ {
+ std::string msg = llformat("Unable to start networking, error %d", gMessageSystem->getErrorCode());
+ app_early_exit(msg);
+ }
+ }
+ else
+ {
+ app_early_exit("Unable to initialize communications.");
+ }
+
+ if(gMessageSystem && gMessageSystem->isOK())
+ {
+ // Initialize all of the callbacks in case of bad message
+ // system data
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setExceptionFunc(MX_UNREGISTERED_MESSAGE,
+ invalid_message_callback,
+ NULL);
+ msg->setExceptionFunc(MX_PACKET_TOO_SHORT,
+ invalid_message_callback,
+ NULL);
+ msg->setExceptionFunc(MX_RAN_OFF_END_OF_PACKET,
+ invalid_message_callback,
+ NULL);
+ msg->setExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE,
+ invalid_message_callback,
+ NULL);
+
+ gErrorStream.setUTCTimestamp(gLogUTC);
+ if (gSavedSettings.getBOOL("LogMessages") || gLogMessages)
+ {
+ llinfos << "Message logging activated!" << llendl;
+ msg->startLogging();
+ }
+
+ // start the xfer system. by default, choke the downloads
+ // a lot...
+ const S32 VIEWER_MAX_XFER = 3;
+ start_xfer_manager(gVFS);
+ gXferManager->setMaxIncomingXfers(VIEWER_MAX_XFER);
+ F32 xfer_throttle_bps = gSavedSettings.getF32("XferThrottle");
+ if (xfer_throttle_bps > 1.f)
+ {
+ gXferManager->setUseAckThrottling(TRUE);
+ gXferManager->setAckThrottleBPS(xfer_throttle_bps);
+ }
+ gAssetStorage = new LLViewerAssetStorage(msg, gXferManager, gVFS, gUserServer);
+
+ msg->mPacketRing.setDropPercentage(gPacketDropPercentage);
+ if (gInBandwidth != 0.f)
+ {
+ llinfos << "Setting packetring incoming bandwidth to " << gInBandwidth << llendl;
+ msg->mPacketRing.setUseInThrottle(TRUE);
+ msg->mPacketRing.setInBandwidth(gInBandwidth);
+ }
+ if (gOutBandwidth != 0.f)
+ {
+ llinfos << "Setting packetring outgoing bandwidth to " << gOutBandwidth << llendl;
+ msg->mPacketRing.setUseOutThrottle(TRUE);
+ msg->mPacketRing.setOutBandwidth(gOutBandwidth);
+ }
+ }
+
+ // initialize the economy
+ gGlobalEconomy = new LLGlobalEconomy();
+
+ //---------------------------------------------------------------------
+ // LibXUL (Mozilla) initialization
+ //---------------------------------------------------------------------
+ #if LL_LIBXUL_ENABLED
+ set_startup_status(0.48f, "Initializing embedded web browser...", gAgent.mMOTD.c_str());
+ display_startup();
+
+ #if LL_DARWIN
+ // For Mac OS, we store both the shared libraries and the runtime files (chrome/, plugins/, etc) in
+ // Second Life.app/Contents/MacOS/. This matches the way Firefox is distributed on the Mac.
+ std::string profileBaseDir(gDirUtilp->getExecutableDir());
+ #else
+ std::string profileBaseDir( gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "" ) );
+ profileBaseDir += gDirUtilp->getDirDelimiter();
+ #ifdef LL_DEBUG
+ profileBaseDir += "mozilla_debug";
+ #else
+ profileBaseDir += "mozilla";
+ #endif
+ #endif
+ LLMozLib::getInstance()->init( profileBaseDir, gDirUtilp->getExpandedFilename( LL_PATH_MOZILLA_PROFILE, "" ) );
+
+ std::ostringstream codec;
+ codec << "[Second Life " << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH << "." << LL_VERSION_BUILD << "]";
+ LLMozLib::getInstance()->setBrowserAgentId( codec.str() );
+ #endif
+
+ //-------------------------------------------------
+ // Init audio, which may be needed for prefs dialog
+ // or audio cues in connection UI.
+ //-------------------------------------------------
+
+ if (gUseAudio)
+ {
+#if LL_FMOD
+ gAudiop = (LLAudioEngine *) new LLAudioEngine_FMOD();
+#else
+ gAudiop = NULL;
+#endif
+
+ if (gAudiop)
+ {
+#if LL_WINDOWS
+ // FMOD on Windows needs the window handle to stop playing audio
+ // when window is minimized. JC
+ void* window_handle = (void*)llwindow_get_hwnd(gViewerWindow->getWindow());
+#else
+ void* window_handle = NULL;
+#endif
+ BOOL init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle);
+ if(!init)
+ {
+ llwarns << "Unable to initialize audio engine" << llendl;
+ }
+ gAudiop->setMuted(TRUE);
+ }
+ }
+
+ if (LLTimer::knownBadTimer())
+ {
+ llwarns << "Unreliable timers detected (may be bad PCI chipset)!!" << llendl;
+ }
+
+ // Get ready to show the login dialog
+ if (!gConnectToSomething)
+ {
+ // Don't use a session token, and generate a random user id
+ gAgentID.generate();
+ gAgentSessionID = LLUUID::null;
+
+ gStartupState = STATE_WORLD_INIT;
+ return do_normal_idle;
+ }
+ else if (!gRunLocal)
+ {
+ //
+ // Log on to userserver
+ //
+ if( !gCmdLineFirstName.empty()
+ && !gCmdLineLastName.empty()
+ && !gCmdLinePassword.empty())
+ {
+ firstname = gCmdLineFirstName;
+ lastname = gCmdLineLastName;
+
+ LLMD5 pass((unsigned char*)gCmdLinePassword.c_str());
+ char md5pass[33];
+ pass.hex_digest(md5pass);
+ password = md5pass;
+
+ remember_password = gSavedSettings.getBOOL("RememberPassword");
+ show_connect_box = FALSE;
+ }
+ else if (gAutoLogin || gSavedSettings.getBOOL("AutoLogin"))
+ {
+ firstname = gSavedSettings.getString("FirstName");
+ lastname = gSavedSettings.getString("LastName");
+ password = load_password_from_disk();
+ remember_password = TRUE;
+ show_connect_box = FALSE;
+ }
+ else
+ {
+ // if not automatically logging in, display login dialog
+ // until a valid userserver is selected
+ firstname = gSavedSettings.getString("FirstName");
+ lastname = gSavedSettings.getString("LastName");
+ password = load_password_from_disk();
+ remember_password = gSavedSettings.getBOOL("RememberPassword");
+ show_connect_box = TRUE;
+ }
+
+ // Go to the next startup state
+ gStartupState++;
+ return do_normal_idle;
+ }
+ else
+ {
+ gStartupState++;
+ return do_normal_idle;
+ }
+ }
+
+ if (STATE_LOGIN_SHOW == gStartupState)
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ // Push our window frontmost
+ gViewerWindow->getWindow()->show();
+
+ timeout_count = 0;
+
+ if (gConnectToSomething && !gRunLocal)
+ {
+ if (show_connect_box)
+ {
+ if (gNoRender)
+ {
+ llerrs << "Need to autologin or use command line with norender!" << llendl;
+ }
+ // Make sure the process dialog doesn't hide things
+ gViewerWindow->setShowProgress(FALSE);
+
+ // Show the login dialog
+ login_show();
+
+ // connect dialog is already shown, so fill in the names
+ LLPanelLogin::setFields( firstname, lastname, password, remember_password );
+ LLPanelLogin::giveFocus();
+
+ gSavedSettings.setBOOL("FirstRunThisInstall", FALSE);
+
+ gStartupState++;
+ }
+ else
+ {
+ // skip directly to message template verification
+ gStartupState = STATE_LOGIN_CLEANUP;
+ }
+ }
+ else
+ {
+ gMessageSystem->setCircuitProtection(FALSE);
+ gStartupState = STATE_LOGIN_CLEANUP;
+ }
+
+ timeout.reset();
+ return do_normal_idle;
+ }
+
+ if (STATE_LOGIN_WAIT == gStartupState)
+ {
+ // Don't do anything. Wait for the login view to call the login_callback,
+ // which will push us to the next state.
+
+ // Sleep so we don't spin the CPU
+ ms_sleep(1);
+ return do_normal_idle;
+ }
+
+ if (STATE_LOGIN_CLEANUP == gStartupState)
+ {
+ if (show_connect_box)
+ {
+ // Load all the name information out of the login view
+ LLPanelLogin::getFields(firstname, lastname, password, remember_password);
+
+ // HACK: Try to make not jump on login
+ gKeyboard->resetKeys();
+ }
+
+ if (!firstname.empty() && !lastname.empty())
+ {
+ gSavedSettings.setString("FirstName", firstname);
+ gSavedSettings.setString("LastName", lastname);
+
+ llinfos << "Attempting login as: " << firstname << " " << lastname << llendl;
+ write_debug("Attempting login as: ");
+ write_debug(firstname);
+ write_debug(" ");
+ write_debug(lastname);
+ write_debug("\n");
+ }
+
+ // create necessary directories
+ // FIXME: these mkdir's should error check
+ gDirUtilp->setLindenUserDir(firstname.c_str(), lastname.c_str());
+
+
+ LLFile::mkdir(gDirUtilp->getLindenUserDir().c_str());
+
+ // the mute list is loaded in the llmutelist class.
+
+ gSavedSettings.loadFromFile(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,"overrides.xml"));
+
+ // handle the per account settings setup
+ strcpy(gPerAccountSettingsFileName, gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, DEFAULT_SETTINGS_FILE).c_str());
+
+ // per account settings. Set defaults here if not found. If we get a bunch of these, eventually move to a function.
+ gSavedPerAccountSettings.loadFromFile(gPerAccountSettingsFileName);
+
+ // Need to set the LastLogoff time here if we don't have one. LastLogoff is used for "Recent Items" calculation
+ // and startup time is close enough if we don't have a real value.
+ if (gSavedPerAccountSettings.getU32("LastLogoff") == 0)
+ {
+ gSavedPerAccountSettings.setU32("LastLogoff", time_corrected());
+ }
+
+ //Default the path if one isn't set.
+ if (gSavedPerAccountSettings.getString("InstantMessageLogPath").empty())
+ {
+ gDirUtilp->setChatLogsDir(gDirUtilp->getOSUserAppDir());
+ gSavedPerAccountSettings.setString("InstantMessageLogPath",gDirUtilp->getChatLogsDir());
+ }
+ else
+ {
+ gDirUtilp->setChatLogsDir(gSavedPerAccountSettings.getString("InstantMessageLogPath"));
+ }
+
+ gDirUtilp->setPerAccountChatLogsDir(firstname.c_str(), lastname.c_str());
+
+ LLFile::mkdir(gDirUtilp->getChatLogsDir().c_str());
+ LLFile::mkdir(gDirUtilp->getPerAccountChatLogsDir().c_str());
+
+
+#if LL_WINDOWS
+ if (gSavedSettings.getBOOL("UseDebugLogin") && show_connect_box)
+#else
+ if (show_connect_box)
+#endif
+ {
+ LLString server_label;
+ S32 domain_name_index;
+ LLPanelLogin::getServer( server_label, domain_name_index );
+ gUserServerChoice = (EUserServerDomain) domain_name_index;
+ gSavedSettings.setS32("ServerChoice", gUserServerChoice);
+ if (gUserServerChoice == USERSERVER_OTHER)
+ {
+ gUserServer.setHostByName( server_label.c_str() );
+ snprintf(gUserServerName, MAX_STRING, "%s", server_label.c_str());
+ }
+ }
+
+ if (show_connect_box)
+ {
+ LLString location;
+ LLPanelLogin::getLocation( location );
+ LLURLSimString::setString( location );
+ LLPanelLogin::close();
+ }
+
+ //For HTML parsing in text boxes.
+ LLTextEditor::setLinkColor( gSavedSettings.getColor4("HTMLLinkColor") );
+ LLTextEditor::setURLCallbacks ( &LLWeb::loadURL, &process_secondlife_url );
+
+ //-------------------------------------------------
+ // Handle startup progress screen
+ //-------------------------------------------------
+
+ // on startup the user can request to go to their home,
+ // their last location, or some URL "-url //sim/x/y[/z]"
+ // All accounts have both a home and a last location, and we don't support
+ // more locations than that. Choose the appropriate one. JC
+ if (LLURLSimString::parse())
+ {
+ // a startup URL was specified
+ agent_location_id = START_LOCATION_ID_TELEHUB;
+
+ // doesn't really matter what location_which is, since
+ // agent_start_look_at will be overwritten when the
+ // UserLoginLocationReply arrives
+ location_which = START_LOCATION_ID_LAST;
+ }
+ else if (gSavedSettings.getBOOL("LoginLastLocation"))
+ {
+ agent_location_id = START_LOCATION_ID_LAST; // last location
+ location_which = START_LOCATION_ID_LAST;
+ }
+ else
+ {
+ agent_location_id = START_LOCATION_ID_HOME; // home
+ location_which = START_LOCATION_ID_HOME;
+ }
+
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
+
+ if (!gNoRender)
+ {
+ init_start_screen(agent_location_id);
+ }
+
+ // Display the startup progress bar.
+ gViewerWindow->setShowProgress(TRUE);
+ gViewerWindow->setProgressCancelButtonVisible(TRUE, "Quit");
+
+ // Poke the VFS, which could potentially block for a while if
+ // Windows XP is acting up
+ set_startup_status(0.05f, "Verifying cache files (can take 60-90 seconds)...", NULL);
+ display_startup();
+
+ gVFS->pokeFiles();
+
+ // color init must be after saved settings loaded
+ init_colors();
+
+ // Request userserver domain name
+ set_startup_status(0.05f, "Finding Server Domain Name...", NULL);
+
+ // We're prematurely switching out of this state because the
+ // userserver name resolver can potentiallly occur before reaching the end of the
+ // switch statement. Also, if it's done at the bottom, sometimes we will
+ // skip the userserver resolved step (in the local cases) - djs 09/24/03
+ gStartupState++;
+ timeout.reset();
+
+ switch( gUserServerChoice )
+ {
+ case USERSERVER_AGNI:
+ gInProductionGrid = TRUE;
+ case USERSERVER_DMZ:
+ case USERSERVER_ADITI:
+ case USERSERVER_SIVA:
+ case USERSERVER_SHAKTI:
+ case USERSERVER_DURGA:
+ case USERSERVER_SOMA:
+ case USERSERVER_GANGA:
+ {
+ const char* host_name = gUserServerDomainName[gUserServerChoice].mName;
+ sprintf(gUserServerName,"%s", host_name);
+ llinfos << "Resolving " <<
+ gUserServerDomainName[gUserServerChoice].mLabel <<
+ " userserver domain name " << host_name << llendl;
+
+ BOOL requested_domain_name = gAsyncHostByName.startRequest( host_name, on_userserver_name_resolved, NULL );
+ if( !requested_domain_name )
+ //BOOL resolved_domain_name = gUserServer.setHostByName( host_name );
+ //if( !resolved_domain_name )
+ {
+ llwarns << "setHostByName failed" << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[HOST_NAME]"] = host_name;
+
+ gViewerWindow->alertXml("UnableToConnect", args, login_alert_done );
+ gStartupState = STATE_LOGIN_SHOW;
+ return FALSE;
+ }
+ break;
+ }
+
+ case USERSERVER_LOCAL:
+ llinfos << "Using local userserver" << llendl;
+ gUserServer.setAddress( LOOPBACK_ADDRESS_STRING );
+ gStartupState = STATE_USERSERVER_RESOLVED;
+ break;
+
+ case USERSERVER_OTHER:
+ llinfos << "Userserver set explicitly" << llendl;
+ gStartupState = STATE_USERSERVER_RESOLVED;
+ break;
+
+ case USERSERVER_NONE:
+ default:
+ llerrs << "No userserver IP address specified" << llendl;
+ break;
+ }
+ return do_normal_idle;
+ }
+
+ if (STATE_RESOLVING_USERSERVER == gStartupState)
+ {
+ // Don't do anything. Wait for LL_WM_HOST_RESOLVED which is handled by LLAsyncHostByName,
+ // which calls on_userserver_name_resolved, which will push us to the next state.
+ if (timeout.getElapsedTimeF32() > TIMEOUT_SECONDS*3.f)
+ {
+ // Cancel the pending asynchostbyname request
+
+ gViewerWindow->alertXml("CanNotFindServer",
+ login_alert_status, NULL);
+
+ // Back up to login screen
+ gStartupState = STATE_LOGIN_SHOW;
+ gViewerStats->incStat(LLViewerStats::ST_LOGIN_TIMEOUT_COUNT);
+ }
+ ms_sleep(1);
+ return do_normal_idle;
+ }
+
+ if (STATE_USERSERVER_RESOLVED == gStartupState)
+ {
+ if (!gUserServer.isOk())
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[IP_ADDRESS]"] = u32_to_ip_string( gUserServer.getAddress() );
+
+ gViewerWindow->alertXml("PleaseSelectServer", args, login_alert_done );
+
+ gStartupState = STATE_LOGIN_SHOW;
+ return FALSE;
+ }
+
+ write_debug("Userserver: ");
+ char tmp_str[256];
+ gUserServer.getIPString(tmp_str, 256);
+ write_debug(tmp_str);
+ write_debug("\n");
+
+ gStartupState++;
+ }
+
+ if (STATE_MESSAGE_TEMPLATE_SEND == gStartupState)
+ {
+ set_startup_status(0.10f, "Verifying protocol version...", NULL);
+
+ LLHost mt_host;
+ if (!gRunLocal)
+ {
+ // open up user server circuit (trusted)
+ gMessageSystem->enableCircuit(gUserServer, TRUE);
+ mt_host = gUserServer;
+ }
+ else
+ {
+ mt_host = gAgentSimHost;
+ }
+
+ llinfos << "Verifying message template..." << llendl;
+
+ // register with the message system so it knows we're
+ // expecting this message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setHandlerFuncFast(_PREHASH_TemplateChecksumReply, null_message_callback, NULL);
+ msg->newMessageFast(_PREHASH_SecuredTemplateChecksumRequest);
+ msg->nextBlockFast(_PREHASH_TokenBlock);
+ lldebugs << "random token: " << gTemplateToken << llendl;
+ msg->addUUIDFast(_PREHASH_Token, gTemplateToken);
+ msg->sendReliable(mt_host);
+
+ timeout.reset();
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ if (STATE_MESSAGE_TEMPLATE_WAIT == gStartupState)
+ {
+ U32 remote_template_checksum = 0;
+
+ U8 major_version = 0;
+ U8 minor_version = 0;
+ U8 patch_version = 0;
+ U8 server_version = 0;
+ U32 flags = 0x0;
+
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ if (msg->isMessageFast(_PREHASH_TemplateChecksumReply))
+ {
+ LLUUID token;
+ msg->getUUID("TokenBlock", "Token", token);
+ if(token != gTemplateToken)
+ {
+ llwarns << "Incorrect token in template checksum reply: "
+ << token << llendl;
+ return do_normal_idle;
+ }
+ msg->getU32("DataBlock", "Checksum", remote_template_checksum);
+ msg->getU8 ("DataBlock", "MajorVersion", major_version);
+ msg->getU8 ("DataBlock", "MinorVersion", minor_version);
+ msg->getU8 ("DataBlock", "PatchVersion", patch_version);
+ msg->getU8 ("DataBlock", "ServerVersion", server_version);
+ msg->getU32("DataBlock", "Flags", flags);
+
+ BOOL update_available = FALSE;
+ BOOL mandatory = FALSE;
+
+ if (remote_template_checksum != msg->mMessageFileChecksum)
+ {
+ llinfos << "Message template out of sync" << llendl;
+ // Mandatory update -- message template checksum doesn't match
+ update_available = TRUE;
+ mandatory = TRUE;
+ }
+
+ BOOL quit = FALSE;
+ if (update_available)
+ {
+ if (show_connect_box)
+ {
+ update_app(mandatory, "");
+ gStartupState = STATE_UPDATE_CHECK;
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ // Bail out and clean up circuit
+ if (quit)
+ {
+ msg->newMessageFast(_PREHASH_CloseCircuit);
+ msg->sendMessage( msg->getSender() );
+ app_force_quit(NULL);
+ return FALSE;
+ }
+
+ // If we get here, we've got a compatible message template
+
+ if (!mandatory)
+ {
+ llinfos << "Message template is current!" << llendl;
+ }
+ gStartupState = STATE_LOGIN_AUTH_INIT;
+ timeout.reset();
+ // unregister with the message system so it knows we're no longer expecting this message
+ msg->setHandlerFuncFast(_PREHASH_TemplateChecksumReply, NULL, NULL);
+
+ msg->newMessageFast(_PREHASH_CloseCircuit);
+ msg->sendMessage(gUserServer);
+ msg->disableCircuit(gUserServer);
+ if (gRunLocal)
+ {
+ msg->enableCircuit(gAgentSimHost, TRUE);
+
+ // Don't use a session token, and generate a random user id
+ gAgentID.generate();
+ gAgentSessionID = LLUUID::null;
+
+ // Skip userserver queries.
+ gStartupState = STATE_WORLD_INIT;
+ }
+ }
+ }
+ gMessageSystem->processAcks();
+
+ if (timeout.getElapsedTimeF32() > TIMEOUT_SECONDS)
+ {
+ if (timeout_count > MAX_TIMEOUT_COUNT)
+ {
+ gViewerWindow->alertXml("SystemMayBeDown",
+ login_alert_status,
+ NULL);
+
+ // Back up to login screen
+ gStartupState = STATE_LOGIN_SHOW;
+ gViewerStats->incStat(LLViewerStats::ST_LOGIN_TIMEOUT_COUNT);
+ }
+ else
+ {
+ llinfos << "Resending on timeout" << llendl;
+ gStartupState--;
+ timeout_count++;
+ }
+ }
+
+ return do_normal_idle;
+ }
+
+ if (STATE_UPDATE_CHECK == gStartupState)
+ {
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_AUTH_INIT == gStartupState)
+ {
+//#define LL_MINIMIAL_REQUESTED_OPTIONS
+ lldebugs << "STATE_LOGIN_AUTH_INIT" << llendl;
+ if (!gUserAuthp)
+ {
+ gUserAuthp = new LLUserAuth();
+ }
+ requested_options.clear();
+ requested_options.push_back("inventory-root");
+ requested_options.push_back("inventory-skeleton");
+ //requested_options.push_back("inventory-meat");
+ //requested_options.push_back("inventory-skel-targets");
+#if (!defined LL_MINIMIAL_REQUESTED_OPTIONS)
+ if(gRequestInventoryLibrary)
+ {
+ requested_options.push_back("inventory-lib-root");
+ requested_options.push_back("inventory-lib-owner");
+ requested_options.push_back("inventory-skel-lib");
+ // requested_options.push_back("inventory-meat-lib");
+ }
+
+ requested_options.push_back("initial-outfit");
+ requested_options.push_back("gestures");
+ requested_options.push_back("event_categories");
+ requested_options.push_back("event_notifications");
+ requested_options.push_back("classified_categories");
+ //requested_options.push_back("inventory-targets");
+ requested_options.push_back("buddy-list");
+ requested_options.push_back("ui-config");
+#endif
+ requested_options.push_back("login-flags");
+ requested_options.push_back("global-textures");
+ if(gGodConnect)
+ {
+ gSavedSettings.setBOOL("UseDebugMenus", TRUE);
+ requested_options.push_back("god-connect");
+ }
+ auth_uri = getLoginURI();
+ auth_method = "login_to_simulator";
+ auth_desc = "Logging in. ";
+ auth_desc += gSecondLife;
+ auth_desc += " may appear frozen. Please wait.";
+ ++gStartupState;
+ }
+
+ if (STATE_LOGIN_AUTHENTICATE == gStartupState)
+ {
+ lldebugs << "STATE_LOGIN_AUTHENTICATE" << llendl;
+ set_startup_status(progress, auth_desc.c_str(), auth_message.c_str());
+ progress += 0.02f;
+ display_startup();
+
+ std::stringstream start;
+ if (LLURLSimString::parse())
+ {
+ // a startup URL was specified
+ std::stringstream unescaped_start;
+ unescaped_start << "uri:"
+ << LLURLSimString::sInstance.mSimName << "&"
+ << LLURLSimString::sInstance.mX << "&"
+ << LLURLSimString::sInstance.mY << "&"
+ << LLURLSimString::sInstance.mZ;
+ start << xml_escape_string(unescaped_start.str().c_str());
+ }
+ else if (gSavedSettings.getBOOL("LoginLastLocation"))
+ {
+ start << "last";
+ }
+ else
+ {
+ start << "home";
+ }
+
+ char hashed_mac_string[MD5HEX_STR_SIZE];
+ LLMD5 hashed_mac;
+ hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES );
+ hashed_mac.finalize();
+ hashed_mac.hex_digest(hashed_mac_string);
+
+ gUserAuthp->authenticate(
+ auth_uri.c_str(),
+ auth_method.c_str(),
+ firstname.c_str(),
+ lastname.c_str(),
+ password.c_str(),
+ start.str().c_str(),
+ gSkipOptionalUpdate,
+ gAcceptTOS,
+ gAcceptCriticalMessage,
+ gViewerDigest,
+ gLastExecFroze,
+ requested_options,
+ hashed_mac_string,
+ gSerialNumber);
+ // reset globals
+ gAcceptTOS = FALSE;
+ gAcceptCriticalMessage = FALSE;
+ ++gStartupState;
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_NO_DATA_YET == gStartupState)
+ {
+ //lldebugs << "STATE_LOGIN_NO_DATA_YET" << llendl;
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth in STATE_LOGIN_NO_DATA_YET!" << llendl;
+ }
+ // Process messages to keep from dropping circuit.
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ }
+ msg->processAcks();
+ LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
+ if(LLUserAuth::E_NO_RESPONSE_YET == error)
+ {
+ //llinfos << "waiting..." << llendl;
+ return do_normal_idle;
+ }
+ ++gStartupState;
+ progress += 0.01f;
+ set_startup_status(progress, auth_desc.c_str(), auth_message.c_str());
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_DOWNLOADING == gStartupState)
+ {
+ lldebugs << "STATE_LOGIN_DOWNLOADING" << llendl;
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth in STATE_LOGIN_DOWNLOADING!" << llendl;
+ }
+ // Process messages to keep from dropping circuit.
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ }
+ msg->processAcks();
+ LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
+ if(LLUserAuth::E_DOWNLOADING == error)
+ {
+ //llinfos << "downloading..." << llendl;
+ return do_normal_idle;
+ }
+ ++gStartupState;
+ progress += 0.01f;
+ set_startup_status(progress, "Processing Response...", auth_message.c_str());
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_PROCESS_RESPONSE == gStartupState)
+ {
+ lldebugs << "STATE_LOGIN_PROCESS_RESPONSE" << llendl;
+ std::ostringstream emsg;
+ BOOL quit = FALSE;
+ const char* login_response = NULL;
+ const char* reason_response = NULL;
+ const char* message_response = NULL;
+ BOOL successful_login = FALSE;
+ LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
+ // reset globals
+ gAcceptTOS = FALSE;
+ gAcceptCriticalMessage = FALSE;
+ switch(error)
+ {
+ case LLUserAuth::E_OK:
+ login_response = gUserAuthp->getResponse("login");
+ if(login_response && (0 == strcmp(login_response, "true")))
+ {
+ // Yay, login!
+ successful_login = TRUE;
+ }
+ else if(login_response && (0 == strcmp(login_response, "indeterminate")))
+ {
+ llinfos << "Indeterminate login..." << llendl;
+ auth_uri = gUserAuthp->getResponse("next_url");
+ auth_method = gUserAuthp->getResponse("next_method");
+ auth_message = gUserAuthp->getResponse("message");
+ if(auth_method.substr(0, 5) == "login")
+ {
+ auth_desc.assign("Authenticating...");
+ }
+ else
+ {
+ auth_desc.assign("Performing account maintenance...");
+ }
+ // ignoring the duration & options array for now.
+ // Go back to authenticate.
+ gStartupState = STATE_LOGIN_AUTHENTICATE;
+ return do_normal_idle;
+ }
+ else
+ {
+ emsg << "Login failed.\n";
+ reason_response = gUserAuthp->getResponse("reason");
+ message_response = gUserAuthp->getResponse("message");
+
+ if (gHideLinks && reason_response && (0 == strcmp(reason_response, "disabled")))
+ {
+ emsg << gDisabledMessage;
+ }
+ else if (message_response)
+ {
+ emsg << message_response;
+ }
+
+ if(reason_response && (0 == strcmp(reason_response, "tos")))
+ {
+ if (show_connect_box)
+ {
+ llinfos << "Need tos agreement" << llendl;
+ gStartupState = STATE_UPDATE_CHECK;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,
+ message_response);
+ tos_dialog->startModal();
+ // LLFloaterTOS deletes itself.
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ if(reason_response && (0 == strcmp(reason_response, "critical")))
+ {
+ if (show_connect_box)
+ {
+ llinfos << "Need critical message" << llendl;
+ gStartupState = STATE_UPDATE_CHECK;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_CRITICAL_MESSAGE,
+ message_response);
+ tos_dialog->startModal();
+ // LLFloaterTOS deletes itself.
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ if(reason_response && (0 == strcmp(reason_response, "key")))
+ {
+ // Couldn't login because user/password is wrong
+ // Clear the password
+ password = "";
+ }
+ if(reason_response && (0 == strcmp(reason_response, "update")))
+ {
+ auth_message = gUserAuthp->getResponse("message");
+ if (show_connect_box)
+ {
+ update_app(TRUE, auth_message);
+ gStartupState = STATE_UPDATE_CHECK;
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ if(reason_response && (0 == strcmp(reason_response, "optional")))
+ {
+ llinfos << "Login got optional update" << llendl;
+ auth_message = gUserAuthp->getResponse("message");
+ if (show_connect_box)
+ {
+ update_app(FALSE, auth_message);
+ gStartupState = STATE_UPDATE_CHECK;
+ gSkipOptionalUpdate = TRUE;
+ return FALSE;
+ }
+ }
+ }
+ break;
+ case LLUserAuth::E_COULDNT_RESOLVE_HOST:
+ case LLUserAuth::E_SSL_PEER_CERTIFICATE:
+ case LLUserAuth::E_UNHANDLED_ERROR:
+ default:
+ emsg << "Unable to connect to " << gSecondLife << ".\n";
+ emsg << gUserAuthp->errorMessage();
+ break;
+ case LLUserAuth::E_SSL_CACERT:
+ case LLUserAuth::E_SSL_CONNECT_ERROR:
+ emsg << "Unable to establish a secure connection to the login server.\n";
+ emsg << gUserAuthp->errorMessage();
+ break;
+ }
+
+ // Version update and we're not showing the dialog
+ if(quit)
+ {
+ delete gUserAuthp;
+ gUserAuthp = NULL;
+ app_force_quit(NULL);
+ return FALSE;
+ }
+
+ if(successful_login)
+ {
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth on successful login!" << llendl;
+ }
+
+ // unpack login data needed by the application
+ const char* text;
+ text = gUserAuthp->getResponse("agent_id");
+ if(text) gAgentID.set(text);
+ write_debug("AgentID: ");
+ write_debug(text);
+ write_debug("\n");
+
+ text = gUserAuthp->getResponse("session_id");
+ if(text) gAgentSessionID.set(text);
+ write_debug("SessionID: ");
+ write_debug(text);
+ write_debug("\n");
+
+ text = gUserAuthp->getResponse("secure_session_id");
+ if(text) gAgent.mSecureSessionID.set(text);
+
+ text = gUserAuthp->getResponse("first_name");
+ if(text)
+ {
+ // Remove quotes from string. Login.cgi sends these to force
+ // names that look like numbers into strings.
+ firstname.assign(text);
+ LLString::replaceChar(firstname, '"', ' ');
+ LLString::trim(firstname);
+ }
+ text = gUserAuthp->getResponse("last_name");
+ if(text) lastname.assign(text);
+ gSavedSettings.setString("FirstName", firstname);
+ gSavedSettings.setString("LastName", lastname);
+ if (remember_password)
+ {
+ save_password_to_disk(password.c_str());
+ }
+ else
+ {
+ save_password_to_disk(NULL);
+ }
+ gSavedSettings.setBOOL("RememberPassword", remember_password);
+ gSavedSettings.setBOOL("LoginLastLocation", gSavedSettings.getBOOL("LoginLastLocation"));
+ gSavedSettings.setBOOL("LoggedIn", TRUE);
+
+ text = gUserAuthp->getResponse("agent_access");
+ if(text && (text[0] == 'M'))
+ {
+ gAgent.mAccess = SIM_ACCESS_MATURE;
+ }
+ else
+ {
+ gAgent.mAccess = SIM_ACCESS_PG;
+ }
+
+ text = gUserAuthp->getResponse("start_location");
+ if(text) agent_start_location.assign(text);
+ text = gUserAuthp->getResponse("circuit_code");
+ if(text)
+ {
+ gMessageSystem->mOurCircuitCode = strtoul(text, NULL, 10);
+ }
+ const char* sim_ip_str = gUserAuthp->getResponse("sim_ip");
+ const char* sim_port_str = gUserAuthp->getResponse("sim_port");
+ if(sim_ip_str && sim_port_str)
+ {
+ U32 sim_port = strtoul(sim_port_str, NULL, 10);
+ first_sim.set(sim_ip_str, sim_port);
+ if (first_sim.isOk())
+ {
+ gMessageSystem->enableCircuit(first_sim, TRUE);
+ }
+ }
+ const char* region_x_str = gUserAuthp->getResponse("region_x");
+ const char* region_y_str = gUserAuthp->getResponse("region_y");
+ if(region_x_str && region_y_str)
+ {
+ U32 region_x = strtoul(region_x_str, NULL, 10);
+ U32 region_y = strtoul(region_y_str, NULL, 10);
+ first_sim_handle = to_region_handle(region_x, region_y);
+ }
+
+ const char* look_at_str = gUserAuthp->getResponse("look_at");
+ if (look_at_str)
+ {
+ LLMemoryStream mstr((U8*)look_at_str, strlen(look_at_str));
+ LLSD sd = LLSDNotationParser::parse(mstr);
+ agent_start_look_at = ll_vector3_from_sd(sd);
+ }
+
+ text = gUserAuthp->getResponse("seed_capability");
+ if (text) first_sim_seed_cap = text;
+
+ text = gUserAuthp->getResponse("seconds_since_epoch");
+ if(text)
+ {
+ U32 server_utc_time = strtoul(text, NULL, 10);
+ if(server_utc_time)
+ {
+ time_t now = time(NULL);
+ gUTCOffset = (server_utc_time - now);
+ }
+ }
+
+ const char* home_location = gUserAuthp->getResponse("home");
+ if(home_location)
+ {
+ LLMemoryStream mstr((U8*)home_location, strlen(home_location));
+ LLSD sd = LLSDNotationParser::parse(mstr);
+ S32 region_x = sd["region_handle"][0].asInteger();
+ S32 region_y = sd["region_handle"][1].asInteger();
+ U64 region_handle = to_region_handle(region_x, region_y);
+ LLVector3 position = ll_vector3_from_sd(sd["position"]);
+ gAgent.setHomePosRegion(region_handle, position);
+ }
+
+ gAgent.mMOTD.assign(gUserAuthp->getResponse("message"));
+ LLUserAuth::options_t options;
+ if(gUserAuthp->getOptions("inventory-root", options))
+ {
+ LLUserAuth::response_t::iterator it;
+ it = options[0].find("folder_id");
+ if(it != options[0].end())
+ {
+ gAgent.mInventoryRootID.set((*it).second.c_str());
+ //gInventory.mock(gAgent.getInventoryRootID());
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("login-flags", options))
+ {
+ LLUserAuth::response_t::iterator it;
+ LLUserAuth::response_t::iterator no_flag = options[0].end();
+ it = options[0].find("ever_logged_in");
+ if(it != no_flag)
+ {
+ if((*it).second == "N") gAgent.setFirstLogin(TRUE);
+ else gAgent.setFirstLogin(FALSE);
+ }
+ it = options[0].find("stipend_since_login");
+ if(it != no_flag)
+ {
+ if((*it).second == "Y") stipend_since_login = TRUE;
+ }
+ it = options[0].find("gendered");
+ if(it != no_flag)
+ {
+ if((*it).second == "Y") gAgent.setGenderChosen(TRUE);
+ }
+ it = options[0].find("daylight_savings");
+ if(it != no_flag)
+ {
+ if((*it).second == "Y") gPacificDaylightTime = TRUE;
+ else gPacificDaylightTime = FALSE;
+ }
+ }
+ options.clear();
+ if (gUserAuthp->getOptions("initial-outfit", options)
+ && !options.empty())
+ {
+ LLUserAuth::response_t::iterator it;
+ LLUserAuth::response_t::iterator it_end = options[0].end();
+ it = options[0].find("folder_name");
+ if(it != it_end)
+ {
+ gInitialOutfit = (*it).second;
+ }
+ it = options[0].find("gender");
+ if (it != it_end)
+ {
+ gInitialOutfitGender = (*it).second;
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("global-textures", options))
+ {
+ // Extract sun and moon texture IDs. These are used
+ // in the LLVOSky constructor, but I can't figure out
+ // how to pass them in. JC
+ LLUserAuth::response_t::iterator it;
+ LLUserAuth::response_t::iterator no_texture = options[0].end();
+ it = options[0].find("sun_texture_id");
+ if(it != no_texture)
+ {
+ gSunTextureID.set((*it).second.c_str());
+ }
+ it = options[0].find("moon_texture_id");
+ if(it != no_texture)
+ {
+ gMoonTextureID.set((*it).second.c_str());
+ }
+ it = options[0].find("cloud_texture_id");
+ if(it != no_texture)
+ {
+ gCloudTextureID.set((*it).second.c_str());
+ }
+ }
+
+
+ // JC: gesture loading done below, when we have an asset system
+ // in place. Don't delete/clear user_credentials until then.
+
+ if(gAgentID.notNull()
+ && gAgentSessionID.notNull()
+ && gMessageSystem->mOurCircuitCode
+ && first_sim.isOk()
+ && gAgent.mInventoryRootID.notNull())
+ {
+ ++gStartupState;
+ }
+ else
+ {
+ if (gNoRender)
+ {
+ llinfos << "Bad login - missing return values" << llendl;
+ llinfos << emsg << llendl;
+ exit(0);
+ }
+ // Bounce back to the login screen.
+ LLStringBase<char>::format_map_t args;
+ args["[ERROR_MESSAGE]"] = emsg.str();
+ gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+ }
+ else
+ {
+ if (gNoRender)
+ {
+ llinfos << "Failed to login!" << llendl;
+ llinfos << emsg << llendl;
+ exit(0);
+ }
+ // Bounce back to the login screen.
+ LLStringBase<char>::format_map_t args;
+ args["[ERROR_MESSAGE]"] = emsg.str();
+ gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // World Init
+ //---------------------------------------------------------------------
+ if (STATE_WORLD_INIT == gStartupState)
+ {
+ set_startup_status(0.40f, "Initializing World...", gAgent.mMOTD.c_str());
+ display_startup();
+ // We should have an agent id by this point.
+ llassert(!(gAgentID == LLUUID::null));
+
+ // Finish agent initialization. (Requires gSavedSettings, builds camera)
+ gAgent.init();
+
+ // Since we connected, save off the settings so the user doesn't have to
+ // type the name/password again if we crash.
+ gSavedSettings.saveToFile(gSettingsFileName, TRUE);
+
+ // Create selection manager
+ // Must be done before menus created, because many enabled callbacks
+ // require its existance.
+ gSelectMgr = new LLSelectMgr();
+ gParcelMgr = new LLViewerParcelMgr();
+ gHUDManager = new LLHUDManager();
+ gMuteListp = new LLMuteList();
+
+ //
+ // Initialize classes w/graphics stuff.
+ //
+ LLSurface::initClasses();
+
+ LLFace::initClass();
+
+ LLDrawable::initClass();
+
+ // RN: don't initialize VO classes in drone mode, they are too closely tied to rendering
+ LLViewerObject::initVOClasses();
+
+ display_startup();
+
+ // World initialization must be done after above window init
+ gWorldp = new LLWorld(region_size, region_scale);
+
+ // User might have overridden far clip
+ gWorldp->setLandFarClip( gAgent.mDrawDistance );
+
+ if (!gRunLocal)
+ {
+ // Before we create the first region, we need to set the agent's mOriginGlobal
+ // This is necessary because creating objects before this is set will result in a
+ // bad mPositionAgent cache.
+
+ gAgent.initOriginGlobal(from_region_handle(first_sim_handle));
+
+ gWorldp->addRegion(first_sim_handle, first_sim);
+
+ LLViewerRegion *regionp = gWorldp->getRegionFromHandle(first_sim_handle);
+ llinfos << "Adding initial simulator " << regionp->getOriginGlobal() << llendl;
+
+ regionp->setSeedCapability(first_sim_seed_cap);
+
+ // Set agent's initial region to be the one we just created.
+ gAgent.setRegion(regionp);
+
+ // Set agent's initial position, which will be read by LLVOAvatar when the avatar
+ // object is created. I think this must be done after setting the region. JC
+ gAgent.setPositionAgent(agent_start_position_region);
+ }
+ else
+ {
+ // With one simulator, assume region is at 0,0, hence has regionHandle 0
+ // VEFFECT: Login
+ gWorldp->addRegion(0, gAgentSimHost);
+ gAgent.setRegion(gWorldp->getRegionFromHandle(0));
+ }
+
+ display_startup();
+
+ // Initialize UI
+ if (!gNoRender)
+ {
+ // Initialize all our tools. Must be done after saved settings loaded.
+ gToolMgr = new LLToolMgr();
+ gToolMgr->initTools();
+ // Quickly get something onscreen to look at.
+ gViewerWindow->initWorldUI();
+
+ // Move the progress view in front of the UI
+ gViewerWindow->moveProgressViewToFront();
+
+ gErrorStream.setFixedBuffer(gDebugView->mDebugConsolep);
+ // set initial visibility of debug console
+ gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole"));
+ gDebugView->mStatViewp->setVisible(gSavedSettings.getBOOL("ShowDebugStats"));
+ }
+
+ //
+ // Set message handlers
+ //
+ llinfos << "Initializing communications..." << llendl;
+
+ // register callbacks for messages. . . do this after initial handshake to make sure that we don't catch any unwanted
+ register_viewer_callbacks(gMessageSystem);
+
+ // Debugging info parameters
+ gMessageSystem->setMaxMessageTime( 0.5f ); // Spam if decoding all msgs takes more than 500 ms
+
+ #ifndef LL_RELEASE_FOR_DOWNLOAD
+ gMessageSystem->setTimeDecodes( TRUE ); // Time the decode of each msg
+ gMessageSystem->setTimeDecodesSpamThreshold( 0.05f ); // Spam if a single msg takes over 50ms to decode
+ #endif
+
+ gXferManager->registerCallbacks(gMessageSystem);
+
+ gCacheName = new LLCacheName(gMessageSystem);
+ gCacheName->addObserver(callback_cache_name);
+
+ // Load stored cache if possible
+ load_name_cache();
+
+ // Data storage for map of world.
+ gWorldMap = new LLWorldMap();
+
+ // register null callbacks for audio until the audio system is initialized
+ gMessageSystem->setHandlerFuncFast(_PREHASH_SoundTrigger, null_message_callback, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_AttachedSound, null_message_callback, NULL);
+
+ //reset statistics
+ gViewerStats->resetStats();
+
+ if (!gNoRender)
+ {
+ //
+ // Set up all of our statistics UI stuff.
+ //
+ init_stat_view();
+ }
+
+ display_startup();
+ //
+ // Set up region and surface defaults
+ //
+
+
+ // Sets up the parameters for the first simulator
+
+ llinfos << "Initializing camera..." << llendl;
+ gFrameTime = totalTime();
+ F32 last_time = gFrameTimeSeconds;
+ gFrameTimeSeconds = (S64)(gFrameTime - gStartTime)/SEC_TO_MICROSEC;
+
+ gFrameIntervalSeconds = gFrameTimeSeconds - last_time;
+ if (gFrameIntervalSeconds < 0.f)
+ {
+ gFrameIntervalSeconds = 0.f;
+ }
+
+ // Make sure agent knows correct aspect ratio
+ gCamera->setViewHeightInPixels(gViewerWindow->getWindowDisplayHeight());
+ if (gViewerWindow->mWindow->getFullscreen())
+ {
+ gCamera->setAspect(gViewerWindow->getDisplayAspectRatio());
+ }
+ else
+ {
+ gCamera->setAspect( (F32) gViewerWindow->getWindowWidth() / (F32) gViewerWindow->getWindowHeight());
+ }
+
+ // Move agent to starting location. The position handed to us by
+ // the space server is in global coordinates, but the agent frame
+ // is in region local coordinates. Therefore, we need to adjust
+ // the coordinates handed to us to fit in the local region.
+
+ gAgent.setPositionAgent(agent_start_position_region);
+ gAgent.resetAxes(agent_start_look_at);
+ gAgent.stopCameraAnimation();
+ gAgent.resetCamera();
+
+ // Initialize global class data needed for surfaces (i.e. textures)
+ if (!gNoRender)
+ {
+ llinfos << "Initializing sky..." << llendl;
+ // Initialize all of the viewer object classes for the first time (doing things like texture fetches.
+ gSky.init(initial_sun_direction);
+ }
+
+ set_startup_status(0.45f, "Decoding UI images...", gAgent.mMOTD.c_str());
+ display_startup();
+ llinfos << "Decoding images..." << llendl;
+ // For all images pre-loaded into viewer cache, decode them.
+ // Need to do this AFTER we init the sky
+ gImageList.decodeAllImages();
+ gStartupState++;
+
+ // JC - Do this as late as possible to increase likelihood Purify
+ // will run.
+ if (!gRunLocal)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ if (!msg->mOurCircuitCode)
+ {
+ llwarns << "Attempting to connect to simulator with a zero circuit code!" << llendl;
+ }
+ msg->enableCircuit(first_sim, TRUE);
+ // now, use the circuit info to tell simulator about us!
+ llinfos << "viewer: UserLoginLocationReply() Enabling " << first_sim << " with code " << msg->mOurCircuitCode << llendl;
+ msg->newMessageFast(_PREHASH_UseCircuitCode);
+ msg->nextBlockFast(_PREHASH_CircuitCode);
+ msg->addU32Fast(_PREHASH_Code, msg->mOurCircuitCode);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_ID, gAgent.getID());
+ msg->sendReliable(
+ first_sim,
+ MAX_TIMEOUT_COUNT,
+ FALSE,
+ TIMEOUT_SECONDS,
+ use_circuit_callback,
+ NULL);
+ }
+
+ timeout.reset();
+ if (!gConnectToSomething)
+ {
+ gStartupState = STATE_MISC;
+ }
+
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // LLMediaEngine Init
+ //---------------------------------------------------------------------
+ if (STATE_QUICKTIME_INIT == gStartupState)
+ {
+ if (gViewerWindow)
+ {
+ if (gSavedSettings.getBOOL("MuteAudio"))
+ {
+ LLMediaEngine::updateClass( 0.0f );
+ }
+ else
+ {
+ LLMediaEngine::updateClass( gSavedSettings.getF32( "MediaAudioVolume" ) );
+ }
+ }
+
+ #if LL_QUICKTIME_ENABLED // windows only right now but will be ported to mac
+ if (!gQuickTimeInitialized)
+ {
+ // initialize quicktime libraries (fails gracefully if quicktime not installed ($QUICKTIME)
+ llinfos << "Initializing QuickTime...." << llendl;
+ set_startup_status(0.47f, "Initializing QuickTime...", gAgent.mMOTD.c_str());
+ display_startup();
+ #if LL_WINDOWS
+ // Only necessary/available on Windows.
+ if ( InitializeQTML ( 0L ) != noErr )
+ {
+ // quicktime init failed - turn off media engine support
+ LLMediaEngine::getInstance ()->setAvailable ( FALSE );
+ llinfos << "...not found - unable to initialize." << llendl;
+ set_startup_status(0.47f, "QuickTime not found - unable to initialize.", gAgent.mMOTD.c_str());
+ }
+ else
+ {
+ llinfos << ".. initialized successfully." << llendl;
+ set_startup_status(0.47f, "QuickTime initialized successfully.", gAgent.mMOTD.c_str());
+ };
+ #endif
+ EnterMovies ();
+ gQuickTimeInitialized = true;
+ }
+ #endif
+
+ // Get list of URLs approved for usage
+ // CP: removed since they're not useful without Mozilla enabled
+ #if LL_MOZILLA_ENABLED
+ LLUrlWhiteList::getInstance()->load();
+ #endif
+
+ // initialize mozilla if we're using web page on a prim or not using an external browser for floater
+ BOOL use_web_pages_on_prims = gSavedSettings.getBOOL("UseWebPagesOnPrims");
+ BOOL use_external_browser = gSavedSettings.getBOOL("UseExternalBrowser");
+
+use_external_browser = false;
+
+ if (use_web_pages_on_prims || !use_external_browser)
+ {
+ //llinfos << "Initializing web browser...." << llendl;
+ //set_startup_status(0.48f, "Initializing web browser...", gAgent.mMOTD.c_str());
+ //display_startup();
+ // initialize mozilla
+ LLString mozilla_path = gDirUtilp->getExecutableDir();
+ mozilla_path.append( gDirUtilp->getDirDelimiter() );
+#if LL_DEBUG
+ mozilla_path.append( "mozilla_debug" );
+#else
+ mozilla_path.append( "mozilla" );
+#endif
+
+#if LL_MOZILLA_ENABLED
+ if (!gMozillaInitialized)
+ {
+ void* platform_window = gViewerWindow->getPlatformWindow();
+ mozilla_init_embedding(platform_window, mozilla_path);
+ }
+#endif
+
+ if (use_web_pages_on_prims)
+ {
+ gMediaList = new LLViewerMediaList(2);
+ }
+ }
+
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Agent Send
+ //---------------------------------------------------------------------
+ if(STATE_WORLD_WAIT == gStartupState)
+ {
+ //llinfos << "Waiting for simulator ack...." << llendl;
+ set_startup_status(0.49f, "Waiting for region handshake...", gAgent.mMOTD.c_str());
+ if(gGotUseCircuitCodeAck)
+ {
+ ++gStartupState;
+ }
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ }
+ msg->processAcks();
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Agent Send
+ //---------------------------------------------------------------------
+ if (STATE_AGENT_SEND == gStartupState)
+ {
+ llinfos << "Connecting to region..." << llendl;
+ set_startup_status(0.50f, "Connecting to region...", gAgent.mMOTD.c_str());
+ // register with the message system so it knows we're
+ // expecting this message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setHandlerFuncFast(
+ _PREHASH_AgentMovementComplete,
+ process_agent_movement_complete,
+ NULL);
+ LLViewerRegion* regionp = gAgent.getRegion();
+ if(!gRunLocal && regionp)
+ {
+ send_complete_agent_movement(regionp->getHost());
+ gAssetStorage->setUpstream(regionp->getHost());
+ gCacheName->setUpstream(regionp->getHost());
+ msg->newMessageFast(_PREHASH_EconomyDataRequest);
+ gAgent.sendReliableMessage();
+ }
+ else
+ {
+ gStartupState++;
+ }
+
+ // Create login effect
+ // But not on first login, because you can't see your avatar then
+ if (!gAgent.isFirstLogin())
+ {
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(gAgent.getPositionGlobal());
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gHUDManager->sendEffects();
+ }
+
+ gStartupState++;
+
+ timeout.reset();
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Agent Wait
+ //---------------------------------------------------------------------
+ if (STATE_AGENT_WAIT == gStartupState)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ if (msg->isMessageFast(_PREHASH_AgentMovementComplete))
+ {
+ gStartupState++;
+ // Sometimes we have more than one message in the
+ // queue. break out of this loop and continue
+ // processing. If we don't, then this could skip one
+ // or more login steps.
+ break;
+ }
+ else
+ {
+ //llinfos << "Awaiting AvatarInitComplete, got "
+ //<< msg->getMessageName() << llendl;
+ }
+ }
+ msg->processAcks();
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Inventory Send
+ //---------------------------------------------------------------------
+ if (STATE_INVENTORY_SEND == gStartupState)
+ {
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth in STATE_INVENTORY_SEND!" << llendl;
+ }
+
+ if (gConnectToSomething && !gRunLocal)
+ {
+ // unpack thin inventory
+ LLUserAuth::options_t options;
+ options.clear();
+ //bool dump_buffer = false;
+
+ if(gUserAuthp->getOptions("inventory-lib-root", options)
+ && !options.empty())
+ {
+ // should only be one
+ LLUserAuth::response_t::iterator it;
+ it = options[0].find("folder_id");
+ if(it != options[0].end())
+ {
+ gInventoryLibraryRoot.set((*it).second.c_str());
+ }
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("inventory-lib-owner", options)
+ && !options.empty())
+ {
+ // should only be one
+ LLUserAuth::response_t::iterator it;
+ it = options[0].find("agent_id");
+ if(it != options[0].end())
+ {
+ gInventoryLibraryOwner.set((*it).second.c_str());
+ }
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("inventory-skel-lib", options)
+ && gInventoryLibraryOwner.notNull())
+ {
+ if(!gInventory.loadSkeleton(options, gInventoryLibraryOwner))
+ {
+ llwarns << "Problem loading inventory-skel-lib" << llendl;
+ }
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("inventory-skeleton", options))
+ {
+ if(!gInventory.loadSkeleton(options, gAgent.getID()))
+ {
+ llwarns << "Problem loading inventory-skel-targets"
+ << llendl;
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("buddy-list", options))
+ {
+ LLUserAuth::options_t::iterator it = options.begin();
+ LLUserAuth::options_t::iterator end = options.end();
+ LLAvatarTracker::buddy_map_t list;
+ LLUUID agent_id;
+ S32 has_rights = 0, given_rights = 0;
+ for (; it != end; ++it)
+ {
+ LLUserAuth::response_t::const_iterator option_it;
+ option_it = (*it).find("buddy_id");
+ if(option_it != (*it).end())
+ {
+ agent_id.set((*option_it).second.c_str());
+ }
+ option_it = (*it).find("buddy_rights_has");
+ if(option_it != (*it).end())
+ {
+ has_rights = atoi((*option_it).second.c_str());
+ }
+ option_it = (*it).find("buddy_rights_given");
+ if(option_it != (*it).end())
+ {
+ given_rights = atoi((*option_it).second.c_str());
+ }
+ list[agent_id] = new LLRelationship(given_rights, has_rights, false);
+ }
+ LLAvatarTracker::instance().addBuddyList(list);
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("ui-config", options))
+ {
+ LLUserAuth::options_t::iterator it = options.begin();
+ LLUserAuth::options_t::iterator end = options.end();
+ for (; it != end; ++it)
+ {
+ LLUserAuth::response_t::const_iterator option_it;
+ option_it = (*it).find("allow_first_life");
+ if(option_it != (*it).end())
+ {
+ if (option_it->second == "Y")
+ {
+ LLPanelAvatar::sAllowFirstLife = TRUE;
+ }
+ }
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("event_categories", options))
+ {
+ LLEventInfo::loadCategories(options);
+ }
+ if(gUserAuthp->getOptions("event_notifications", options))
+ {
+ gEventNotifier.load(options);
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("classified_categories", options))
+ {
+ LLClassifiedInfo::loadCategories(options);
+ }
+ gInventory.buildParentChildMap();
+ gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null);
+ gInventory.notifyObservers();
+
+ // set up callbacks
+ LLMessageSystem* msg = gMessageSystem;
+ LLInventoryModel::registerCallbacks(msg);
+ LLAvatarTracker::instance().registerCallbacks(msg);
+ LLLandmark::registerCallbacks(msg);
+
+ // request mute list
+ gMuteListp->requestFromServer(gAgent.getID());
+
+ // Get money and ownership credit information
+ msg->newMessageFast(_PREHASH_MoneyBalanceRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null );
+ gAgent.sendReliableMessage();
+
+ // request all group information
+ // *FIX: This will not do the right thing if the message
+ // gets there before the requestuserserverconnection
+ // circuit is completed.
+ gAgent.sendAgentDataUpdateRequest();
+
+
+ // NOTE: removed as part of user-privacy
+ // enhancements. this information should be available from
+ // login. 2006-10-16 Phoenix.
+ // get the users that have been granted modify powers
+ //msg->newMessageFast(_PREHASH_RequestGrantedProxies);
+ //msg->nextBlockFast(_PREHASH_AgentData);
+ //msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ //msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ //gAgent.sendReliableMessage();
+
+ BOOL shown_at_exit = gSavedSettings.getBOOL("ShowInventory");
+
+ // Create the inventory views
+ LLInventoryView::showAgentInventory();
+
+ // Hide the inventory if it wasn't shown at exit
+ if(!shown_at_exit)
+ {
+ LLInventoryView::toggleVisibility(NULL);
+ }
+ }
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Assert agent to userserver
+ //---------------------------------------------------------------------
+ if (STATE_CONNECT_USERSERVER == gStartupState)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->enableCircuit(gUserServer, TRUE);
+ msg->newMessage("ConnectAgentToUserserver");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->sendReliable(gUserServer);
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Misc
+ //---------------------------------------------------------------------
+ if (STATE_MISC == gStartupState)
+ {
+ // Create a few objects if we don't actually have a world
+ if (!gConnectToSomething)
+ {
+ // could add them here
+ }
+
+ // We have a region, and just did a big inventory download.
+ // We can estimate the user's connection speed, and set their
+ // max bandwidth accordingly. JC
+ if (gSavedSettings.getBOOL("FirstLoginThisInstall")
+ && gUserAuthp)
+ {
+ // This is actually a pessimistic computation, because TCP may not have enough
+ // time to ramp up on the (small) default inventory file to truly measure max
+ // bandwidth. JC
+ F64 rate_bps = gUserAuthp->getLastTransferRateBPS();
+ const F32 FAST_RATE_BPS = 600.f * 1024.f;
+ const F32 FASTER_RATE_BPS = 750.f * 1024.f;
+ F32 max_bandwidth = gViewerThrottle.getMaxBandwidth();
+ if (rate_bps > FASTER_RATE_BPS
+ && rate_bps > max_bandwidth)
+ {
+ llinfos << "Fast network connection, increasing max bandwidth to "
+ << FASTER_RATE_BPS/1024.f
+ << " Kbps" << llendl;
+ gViewerThrottle.setMaxBandwidth(FASTER_RATE_BPS / 1024.f);
+ }
+ else if (rate_bps > FAST_RATE_BPS
+ && rate_bps > max_bandwidth)
+ {
+ llinfos << "Fast network connection, increasing max bandwidth to "
+ << FAST_RATE_BPS/1024.f
+ << " Kbps" << llendl;
+ gViewerThrottle.setMaxBandwidth(FAST_RATE_BPS / 1024.f);
+ }
+ }
+
+ // We're successfully logged in.
+ gSavedSettings.setBOOL("FirstLoginThisInstall", FALSE);
+
+
+ // based on the comments, we've successfully logged in so we can delete the 'forced'
+ // URL that the updater set in settings.ini (in a mostly paranoid fashion)
+ LLString nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
+ if ( nextLoginLocation.length() )
+ {
+ // clear it
+ gSavedSettings.setString( "NextLoginLocation", "" );
+
+ // and make sure it's saved
+ gSavedSettings.saveToFile( gSettingsFileName, TRUE );
+ };
+
+ if (!gNoRender)
+ {
+ // JC: Initializing audio requests many sounds for download.
+ init_audio();
+
+ // JC: Initialize "active" gestures. This may also trigger
+ // many gesture downloads, if this is the user's first
+ // time on this machine or -purge has been run.
+ LLUserAuth::options_t gesture_options;
+ if (gUserAuthp->getOptions("gestures", gesture_options))
+ {
+ llinfos << "Gesture Manager loading " << gesture_options.size()
+ << llendl;
+ std::vector<LLUUID> item_ids;
+ LLUserAuth::options_t::iterator resp_it;
+ for (resp_it = gesture_options.begin();
+ resp_it != gesture_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+ LLUUID item_id;
+ LLUUID asset_id;
+ LLUserAuth::response_t::const_iterator option_it;
+
+ option_it = response.find("item_id");
+ if (option_it != response.end())
+ {
+ const std::string& uuid_string = (*option_it).second;
+ item_id.set(uuid_string.c_str());
+ }
+ option_it = response.find("asset_id");
+ if (option_it != response.end())
+ {
+ const std::string& uuid_string = (*option_it).second;
+ asset_id.set(uuid_string.c_str());
+ }
+
+ if (item_id.notNull() && asset_id.notNull())
+ {
+ // Could schedule and delay these for later.
+ const BOOL no_inform_server = FALSE;
+ const BOOL no_deactivate_similar = FALSE;
+ gGestureManager.activateGestureWithAsset(item_id, asset_id,
+ no_inform_server,
+ no_deactivate_similar);
+ // We need to fetch the inventory items for these gestures
+ // so we have the names to populate the UI.
+ item_ids.push_back(item_id);
+ }
+ }
+
+ LLGestureInventoryFetchObserver* fetch = new LLGestureInventoryFetchObserver();
+ fetch->fetchItems(item_ids);
+ // deletes itself when done
+ gInventory.addObserver(fetch);
+ }
+ }
+ gDisplaySwapBuffers = TRUE;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setHandlerFuncFast(_PREHASH_SoundTrigger, process_sound_trigger);
+ msg->setHandlerFuncFast(_PREHASH_PreloadSound, process_preload_sound);
+ msg->setHandlerFuncFast(_PREHASH_AttachedSound, process_attached_sound);
+ msg->setHandlerFuncFast(_PREHASH_AttachedSoundGainChange, process_attached_sound_gain_change);
+ //msg->setHandlerFuncFast(_PREHASH_AttachedSoundCutoffRadius, process_attached_sound_cutoff_radius);
+ msg->setHandlerFunc(
+ "ConnectToUserserver",
+ process_connect_to_userserver);
+
+ llinfos << "Initialization complete" << llendl;
+ gInitializationComplete = TRUE;
+
+ gRenderStartTime.reset();
+
+ // HACK: Inform simulator of window size.
+ // Do this here so it's less likely to race with RegisterNewAgent.
+ // TODO: Put this into RegisterNewAgent
+ // JC - 7/20/2002
+ gViewerWindow->sendShapeToSim();
+
+ // if needed, show the money history window
+ if (stipend_since_login && !gNoRender)
+ {
+ LLFloaterAccountHistory::show(NULL);
+ }
+
+ if (!gAgent.isFirstLogin())
+ {
+ bool url_ok = LLURLSimString::sInstance.parse();
+ if (!((agent_start_location == "url" && url_ok) ||
+ (!url_ok && ((agent_start_location == "last" && gSavedSettings.getBOOL("LoginLastLocation")) ||
+ (agent_start_location == "home" && !gSavedSettings.getBOOL("LoginLastLocation"))))))
+ {
+ // The reason we show the alert is because we want to
+ // reduce confusion for when you log in and your provided
+ // location is not your expected location. So, if this is
+ // your first login, then you do not have an expectation,
+ // thus, do not show this alert.
+ LLString::format_map_t args;
+ if (url_ok)
+ {
+ args["[TYPE]"] = "desired";
+ args["[HELP]"] = " ";
+ }
+ else if (gSavedSettings.getBOOL("LoginLastLocation"))
+ {
+ args["[TYPE]"] = "last";
+ args["[HELP]"] = " \n ";
+ }
+ else
+ {
+ args["[TYPE]"] = "home";
+ args["[HELP]"] = " \nYou may want to set a new home location.\n ";
+ }
+ gViewerWindow->alertXml("AvatarMoved", args);
+ }
+ else
+ {
+ if (samename)
+ {
+ // restore old camera pos
+ gAgent.setFocusOnAvatar(FALSE, FALSE);
+ gAgent.setCameraPosAndFocusGlobal(gSavedSettings.getVector3d("CameraPosOnLogout"), gSavedSettings.getVector3d("FocusPosOnLogout"), LLUUID::null);
+ BOOL limit_hit = FALSE;
+ gAgent.calcCameraPositionTargetGlobal(&limit_hit);
+ if (limit_hit)
+ {
+ gAgent.setFocusOnAvatar(TRUE, FALSE);
+ }
+ gAgent.stopCameraAnimation();
+ }
+ }
+ }
+
+ gStartupState++;
+ timeout.reset();
+ return do_normal_idle;
+ }
+
+ if (STATE_PRECACHE == gStartupState)
+ {
+ do_normal_idle = TRUE;
+ if (!did_precache)
+ {
+ did_precache = TRUE;
+ // Don't preload map information! The amount of data for all the
+ // map items (icons for classifieds, avatar locations, etc.) is
+ // huge, and not throttled. This overflows the downstream
+ // pipe during startup, when lots of information is being sent.
+ // The problem manifests itself as invisible avatars on login. JC
+ //gWorldMap->setCurrentLayer(0); // pre-load layer 0 of the world map
+
+ gImageList.doPreloadImages(); // pre-load some images from static VFS
+ }
+
+ F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY;
+ // wait precache-delay and for agent's avatar or a lot longer.
+ if(((timeout_frac > 1.f) && gAgent.getAvatarObject())
+ || (timeout_frac > 3.f))
+ {
+ gStartupState++;
+ }
+ else
+ {
+ set_startup_status(0.50f + 0.50f * timeout_frac, "Precaching...",
+ gAgent.mMOTD.c_str());
+ }
+
+ return do_normal_idle;
+ }
+
+ if (STATE_WEARABLES_WAIT == gStartupState)
+ {
+ do_normal_idle = TRUE;
+
+ static LLFrameTimer wearables_timer;
+
+ const F32 wearables_time = wearables_timer.getElapsedTimeF32();
+ const F32 MAX_WEARABLES_TIME = 10.f;
+
+ if(gAgent.getWearablesLoaded() || !gAgent.isGenderChosen())
+ {
+ gStartupState++;
+ }
+ else if (wearables_time > MAX_WEARABLES_TIME)
+ {
+ gViewerWindow->alertXml("ClothingLoading");
+ gViewerStats->incStat(LLViewerStats::ST_WEARABLES_TOO_LONG);
+ gStartupState++;
+ }
+ else
+ {
+ set_startup_status(0.f + 0.25f * wearables_time / MAX_WEARABLES_TIME,
+ "Downloading clothing...",
+ gAgent.mMOTD.c_str());
+ }
+ return do_normal_idle;
+ }
+
+ if (STATE_CLEANUP == gStartupState)
+ {
+ set_startup_status(1.0, "", NULL);
+
+ do_normal_idle = TRUE;
+
+ // Let the map know about the inventory.
+ if(gFloaterWorldMap)
+ {
+ gFloaterWorldMap->observeInventory(&gInventory);
+ gFloaterWorldMap->observeFriends();
+ }
+
+ gViewerWindow->showCursor();
+ gViewerWindow->getWindow()->resetBusyCount();
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ //llinfos << "Done releasing bitmap" << llendl;
+ gViewerWindow->setShowProgress(FALSE);
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "");
+
+ // We're not away from keyboard, even though login might have taken
+ // a while. JC
+ gAgent.clearAFK();
+
+ // Have the agent start watching the friends list so we can update proxies
+ gAgent.observeFriends();
+ if (gSavedSettings.getBOOL("LoginAsGod"))
+ {
+ gAgent.requestEnterGodMode();
+ }
+
+ // On first start, ask user for gender
+ dialog_choose_gender_first_start();
+
+ // Start automatic replay if the flag is set.
+ if (gSavedSettings.getBOOL("StatsAutoRun"))
+ {
+ LLUUID id;
+ llinfos << "Starting automatic playback" << llendl;
+ gAgentPilot.startPlayback();
+ }
+
+ // ok, if we've gotten this far and have a startup URL
+ if (LLURLSimString::sInstance.parse())
+ {
+ // kick off request for landmark to startup URL
+ if(gFloaterWorldMap)
+ {
+ LLVector3 pos = gAgent.getPositionAgent();
+ if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName()
+ || LLURLSimString::sInstance.mX != llfloor(pos[VX])
+ || LLURLSimString::sInstance.mY != llfloor(pos[VY]) )
+ {
+ LLFloaterWorldMap::show(NULL, TRUE);
+ gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName,
+ LLURLSimString::sInstance.mX,
+ LLURLSimString::sInstance.mY,
+ LLURLSimString::sInstance.mZ);
+ }
+ }
+ }
+
+ // Clean up the userauth stuff.
+ if (gUserAuthp)
+ {
+ delete gUserAuthp;
+ gUserAuthp = NULL;
+ }
+
+ gStartupState++;
+ //RN: unmute audio now that we are entering world
+ //JC: But only if the user wants audio working.
+ if (gAudiop)
+ {
+ BOOL mute = gSavedSettings.getBOOL("MuteAudio");
+ gAudiop->setMuted(mute);
+ }
+
+ // reset keyboard focus to sane state of pointing at world
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+
+#if 0 // sjb: enable for auto-enabling timer display
+ gDebugView->mFastTimerView->setVisible(TRUE);
+#endif
+ return do_normal_idle;
+ }
+
+ llwarns << "Reached end of idle_startup for state " << gStartupState << llendl;
+ return do_normal_idle;
+}
+
+//
+// local function definition
+//
+
+void unsupported_graphics_callback(S32 option, void* userdata)
+{
+ if (0 == option)
+ {
+ llinfos << "User cancelled after driver check" << llendl;
+ std::string help_path;
+ help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
+ "unsupported_card.html");
+ app_force_quit( help_path.c_str() );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+void check_driver_callback(S32 option, void* userdata)
+{
+ if (0 == option)
+ {
+ llinfos << "User cancelled after driver check" << llendl;
+ std::string help_path;
+ help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
+ "graphics_driver_update.html");
+ app_force_quit( help_path.c_str() );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+void login_show()
+{
+ LLPanelLogin::show( gViewerWindow->getVirtualWindowRect(),
+ gSavedSettings.getBOOL("UseDebugLogin"),
+ login_callback, NULL );
+
+ // Make sure all the UI textures are present and decoded.
+ gImageList.decodeAllImages();
+
+ if( USERSERVER_OTHER == gUserServerChoice )
+ {
+ LLPanelLogin::addServer( gUserServerName, USERSERVER_OTHER );
+ }
+ else
+ {
+ LLPanelLogin::addServer( gUserServerDomainName[gUserServerChoice].mLabel, gUserServerChoice );
+ }
+
+ // Arg! We hate loops!
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_DMZ].mLabel, USERSERVER_DMZ );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_LOCAL].mLabel, USERSERVER_LOCAL );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_AGNI].mLabel, USERSERVER_AGNI );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_ADITI].mLabel, USERSERVER_ADITI );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SIVA].mLabel, USERSERVER_SIVA );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_DURGA].mLabel, USERSERVER_DURGA );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SHAKTI].mLabel, USERSERVER_SHAKTI );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_GANGA].mLabel, USERSERVER_GANGA );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SOMA].mLabel, USERSERVER_SOMA );
+}
+
+// Callback for when login screen is closed. Option 0 = connect, option 1 = quit.
+void login_callback(S32 option, void *userdata)
+{
+ const S32 CONNECT_OPTION = 0;
+ const S32 QUIT_OPTION = 1;
+
+ if (CONNECT_OPTION == option)
+ {
+ gStartupState++;
+ return;
+ }
+ else if (QUIT_OPTION == option)
+ {
+ // Make sure we don't save the password if the user is trying to clear it.
+ LLString first, last, password;
+ BOOL remember = TRUE;
+ LLPanelLogin::getFields(first, last, password, remember);
+ if (!remember)
+ {
+ // turn off the setting and write out to disk
+ gSavedSettings.setBOOL("RememberPassword", FALSE);
+ gSavedSettings.saveToFile(gSettingsFileName, TRUE);
+
+ // stomp the saved password on disk
+ save_password_to_disk(NULL);
+ }
+
+ LLPanelLogin::close();
+
+ // Next iteration through main loop should shut down the app cleanly.
+ gQuit = TRUE;
+
+ return;
+ }
+ else
+ {
+ llwarns << "Unknown login button clicked" << llendl;
+ }
+}
+
+LLString load_password_from_disk()
+{
+ LLString hashed_password("");
+
+ // Look for legacy "marker" password from settings.ini
+ hashed_password = gSavedSettings.getString("Marker");
+ if (!hashed_password.empty())
+ {
+ // Stomp the Marker entry.
+ gSavedSettings.setString("Marker", "");
+
+ // Return that password.
+ return hashed_password;
+ }
+
+ std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
+ "password.dat");
+ FILE* fp = LLFile::fopen(filepath.c_str(), "rb");
+ if (!fp)
+ {
+ return hashed_password;
+ }
+
+ // UUID is 16 bytes, written into ASCII is 32 characters
+ // without trailing \0
+ const S32 HASHED_LENGTH = 32;
+ U8 buffer[HASHED_LENGTH+1];
+
+ if (1 != fread(buffer, HASHED_LENGTH, 1, fp))
+ {
+ return hashed_password;
+ }
+
+ fclose(fp);
+
+ // Decipher with MAC address
+ LLXORCipher cipher(gMACAddress, 6);
+ cipher.decrypt(buffer, HASHED_LENGTH);
+
+ buffer[HASHED_LENGTH] = '\0';
+
+ // Check to see if the mac address generated a bad hashed
+ // password. It should be a hex-string or else the mac adress has
+ // changed. This is a security feature to make sure that if you
+ // get someone's password.dat file, you cannot hack their account.
+ if(is_hex_string(buffer, HASHED_LENGTH))
+ {
+ hashed_password.assign((char*)buffer);
+ }
+
+ return hashed_password;
+}
+
+void save_password_to_disk(const char* hashed_password)
+{
+ std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
+ "password.dat");
+ if (!hashed_password)
+ {
+ // No password, remove the file.
+ LLFile::remove(filepath.c_str());
+ }
+ else
+ {
+ FILE* fp = LLFile::fopen(filepath.c_str(), "wb");
+ if (!fp)
+ {
+ return;
+ }
+
+ // Encipher with MAC address
+ const S32 HASHED_LENGTH = 32;
+ U8 buffer[HASHED_LENGTH+1];
+
+ LLString::copy((char*)buffer, hashed_password, HASHED_LENGTH+1);
+
+ LLXORCipher cipher(gMACAddress, 6);
+ cipher.encrypt(buffer, HASHED_LENGTH);
+
+ fwrite(buffer, HASHED_LENGTH, 1, fp);
+
+ fclose(fp);
+ }
+}
+
+BOOL is_hex_string(U8* str, S32 len)
+{
+ BOOL rv = TRUE;
+ U8* c = str;
+ while(rv && len--)
+ {
+ switch(*c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ ++c;
+ break;
+ default:
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+}
+
+void show_first_run_dialog()
+{
+ gViewerWindow->alertXml("FirstRun", first_run_dialog_callback, NULL);
+}
+
+void first_run_dialog_callback(S32 option, void* userdata)
+{
+ if (0 == option)
+ {
+ llinfos << "First run dialog cancelling" << llendl;
+ LLWeb::loadURL( CREATE_ACCOUNT_URL );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+
+
+void set_startup_status(const F32 frac, const char *string, const char* msg)
+{
+ gViewerWindow->setProgressPercent(frac*100);
+ gViewerWindow->setProgressString(string);
+
+ gViewerWindow->setProgressMessage(msg);
+}
+
+void on_userserver_name_resolved( BOOL success, const LLString& host_name, U32 ip, void* userdata )
+{
+ if( STATE_RESOLVING_USERSERVER != gStartupState )
+ {
+ llwarns << "Userserver name callback returned during invalid state!" << llendl;
+ return;
+ }
+
+ if( success )
+ {
+ gUserServer.setAddress( ip );
+ llinfos << "...Userserver resolved to " << gUserServer << llendl;
+ gStartupState = STATE_USERSERVER_RESOLVED;
+ }
+ else
+ {
+ llwarns << "setHostByName failed" << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[HOST_NAME]"] = host_name;
+ gViewerWindow->alertXml("SetByHostFail", args, login_alert_done );
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+}
+
+void login_alert_status(S32 option, void* user_data)
+{
+ if (0 == option)
+ {
+ // OK button
+ }
+ else if (1 == option)
+ {
+ // Help button
+ std::string help_path;
+ help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP, "unable_to_connect.html");
+ load_url_local_file(help_path.c_str() );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+void update_app(BOOL mandatory, const std::string& auth_msg)
+{
+ // store off config state, as we might quit soon
+ gSavedSettings.saveToFile(gSettingsFileName, TRUE);
+
+ std::ostringstream message;
+
+ //XUI:translate
+ std::string msg;
+ if (!auth_msg.empty())
+ {
+ msg = "(" + auth_msg + ") \n";
+ }
+ LLStringBase<char>::format_map_t args;
+ args["[MESSAGE]"] = msg;
+
+ BOOL *mandatoryp = new BOOL(mandatory);
+
+#if LL_WINDOWS
+ if (mandatory)
+ {
+ gViewerWindow->alertXml("DownloadWindowsMandatory", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+ }
+ else
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ gViewerWindow->alertXml("DownloadWindowsReleaseForDownload", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#else
+ gViewerWindow->alertXml("DownloadWindows", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#endif
+ }
+#else
+ if (mandatory)
+ {
+ gViewerWindow->alertXml("DownloadMacMandatory", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+ }
+ else
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ gViewerWindow->alertXml("DownloadMacReleaseForDownload", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#else
+ gViewerWindow->alertXml("DownloadMac", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#endif
+ }
+#endif
+
+}
+
+
+void update_dialog_callback(S32 option, void *userdata)
+{
+ std::string update_exe_path;
+ BOOL mandatory = *(BOOL *)userdata;
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+ if (option == 2)
+ {
+ gStartupState++;
+ return;
+ }
+#endif
+
+ if (option == 1)
+ {
+ // ...user doesn't want to do it
+ if (mandatory)
+ {
+ app_force_quit();
+ // Bump them back to the login screen.
+ //gStartupState = STATE_LOGIN_SHOW;
+ }
+ else
+ {
+ gStartupState++;
+ }
+ return;
+ }
+
+#if LL_WINDOWS
+ char ip[MAX_STRING];
+
+ update_exe_path = gDirUtilp->getTempFilename();
+ if (update_exe_path.empty())
+ {
+ // We're hosed, bail
+ llwarns << "LLDir::getTempFilename() failed" << llendl;
+ app_force_quit(NULL);
+ return;
+ }
+
+ update_exe_path += ".exe";
+
+ std::string updater_source = gDirUtilp->getAppRODataDir();
+ updater_source += gDirUtilp->getDirDelimiter();
+ updater_source += "updater.exe";
+
+ llinfos << "Calling CopyFile source: " << updater_source.c_str()
+ << " dest: " << update_exe_path
+ << llendl;
+
+
+ if (!CopyFileA(updater_source.c_str(), update_exe_path.c_str(), FALSE))
+ {
+ llinfos << "Unable to copy the updater!" << llendl;
+ app_force_quit(NULL);
+ return;
+ }
+ u32_to_ip_string(gUserServer.getAddress(), ip);
+
+ std::ostringstream params;
+ params << "-userserver " << gUserServerName;
+
+ // if a sim name was passed in via command line parameter (typically through a SLURL)
+ if ( LLURLSimString::sInstance.mSimString.length() )
+ {
+ // record the location to start at next time
+ gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString );
+ };
+
+ if (gHideLinks)
+ {
+ // Figure out the program name.
+ const char* data_dir = gDirUtilp->getAppRODataDir().c_str();
+ // Roll back from the end, stopping at the first '\'
+ const char* program_name = data_dir + strlen(data_dir);
+ while ( (data_dir != --program_name) &&
+ *(program_name) != '\\');
+
+ if ( *(program_name) == '\\')
+ {
+ // We found a '\'.
+ program_name++;
+ }
+ else
+ {
+ // Oops.
+ program_name = "SecondLife";
+ }
+
+ params << " -silent -name \"" << gSecondLife << "\" -program \"" << program_name << "\"";
+ }
+
+ llinfos << "Calling updater: " << update_exe_path << " " << params.str() << llendl;
+
+ remove_marker_file(); // In case updater fails
+
+ // Use spawn() to run asynchronously
+ int retval = _spawnl(_P_NOWAIT, update_exe_path.c_str(), update_exe_path.c_str(), params.str().c_str(), NULL);
+ llinfos << "Spawn returned " << retval << llendl;
+
+#elif LL_DARWIN
+ // if a sim name was passed in via command line parameter (typically through a SLURL)
+ if ( LLURLSimString::sInstance.mSimString.length() )
+ {
+ // record the location to start at next time
+ gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString );
+ };
+
+ update_exe_path = "'";
+ update_exe_path += gDirUtilp->getAppRODataDir();
+ update_exe_path += "/AutoUpdater.app/Contents/MacOS/AutoUpdater' -userserver ";
+ update_exe_path += gUserServerName;
+ update_exe_path += " -name \"";
+ update_exe_path += gSecondLife;
+ update_exe_path += "\" &";
+
+ llinfos << "Calling updater: " << update_exe_path << llendl;
+
+ remove_marker_file(); // In case updater fails
+
+ // Run the auto-updater.
+ system(update_exe_path.c_str());
+
+#elif LL_LINUX
+ OSMessageBox("Automatic updating is not yet implemented for Linux.\n"
+ "Please download the latest version from www.secondlife.com.",
+ NULL, OSMB_OK);
+ remove_marker_file();
+
+#endif
+ app_force_quit(NULL);
+}
+
+void use_circuit_callback(void**, S32 result)
+{
+ // bail if we're quitting.
+ if(gQuit) return;
+ static bool called = false;
+ if(!called)
+ {
+ called = true;
+ if (result)
+ {
+ // Make sure user knows something bad happened. JC
+ llinfos << "Backing up to login screen!" << llendl;
+ gViewerWindow->alertXml("LoginPacketNeverReceived",
+ login_alert_status, NULL);
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+ else
+ {
+ gGotUseCircuitCodeAck = true;
+ }
+ }
+}
+
+void register_viewer_callbacks(LLMessageSystem* msg)
+{
+ msg->setHandlerFuncFast(_PREHASH_LayerData, process_layer_data );
+ msg->setHandlerFuncFast(_PREHASH_ImageData, LLViewerImage::receiveImage );
+ msg->setHandlerFuncFast(_PREHASH_ImagePacket, LLViewerImage::receiveImagePacket );
+ msg->setHandlerFuncFast(_PREHASH_ObjectUpdate, process_object_update );
+ msg->setHandlerFunc("ObjectUpdateCompressed", process_compressed_object_update );
+ msg->setHandlerFunc("ObjectUpdateCached", process_cached_object_update );
+ msg->setHandlerFuncFast(_PREHASH_ImprovedTerseObjectUpdate, process_terse_object_update_improved );
+ msg->setHandlerFunc("SimStats", process_sim_stats);
+ msg->setHandlerFuncFast(_PREHASH_HealthMessage, process_health_message );
+ msg->setHandlerFuncFast(_PREHASH_EconomyData, process_economy_data);
+ msg->setHandlerFunc("RegionInfo", LLViewerRegion::processRegionInfo);
+
+ msg->setHandlerFuncFast(_PREHASH_ChatFromSimulator, process_chat_from_simulator);
+ msg->setHandlerFuncFast(_PREHASH_KillObject, process_kill_object, NULL);
+ msg->setHandlerFuncFast(_PREHASH_SimulatorViewerTimeMessage, process_time_synch, NULL);
+ msg->setHandlerFuncFast(_PREHASH_EnableSimulator, process_enable_simulator);
+ msg->setHandlerFuncFast(_PREHASH_DisableSimulator, process_disable_simulator);
+ msg->setHandlerFuncFast(_PREHASH_KickUser, process_kick_user, NULL);
+
+ msg->setHandlerFunc("CrossedRegion", process_crossed_region);
+ msg->setHandlerFuncFast(_PREHASH_TeleportFinish, process_teleport_finish);
+
+ msg->setHandlerFuncFast(_PREHASH_AlertMessage, process_alert_message);
+ msg->setHandlerFunc("AgentAlertMessage", process_agent_alert_message);
+ msg->setHandlerFuncFast(_PREHASH_MeanCollisionAlert, process_mean_collision_alert_message, NULL);
+ msg->setHandlerFunc("ViewerFrozenMessage", process_frozen_message);
+
+ msg->setHandlerFuncFast(_PREHASH_RequestAvatarInfo, process_avatar_info_request);
+ msg->setHandlerFuncFast(_PREHASH_NameValuePair, process_name_value);
+ msg->setHandlerFuncFast(_PREHASH_RemoveNameValuePair, process_remove_name_value);
+ msg->setHandlerFuncFast(_PREHASH_AvatarAnimation, process_avatar_animation);
+ msg->setHandlerFuncFast(_PREHASH_AvatarAppearance, process_avatar_appearance);
+ msg->setHandlerFunc("AgentCachedTextureResponse", LLAgent::processAgentCachedTextureResponse);
+ msg->setHandlerFunc("RebakeAvatarTextures", LLVOAvatar::processRebakeAvatarTextures);
+ msg->setHandlerFuncFast(_PREHASH_CameraConstraint, process_camera_constraint);
+ msg->setHandlerFuncFast(_PREHASH_AvatarSitResponse, process_avatar_sit_response);
+ msg->setHandlerFunc("SetFollowCamProperties", process_set_follow_cam_properties);
+ msg->setHandlerFunc("ClearFollowCamProperties", process_clear_follow_cam_properties);
+
+ msg->setHandlerFuncFast(_PREHASH_ImprovedInstantMessage, process_improved_im);
+ msg->setHandlerFuncFast(_PREHASH_ScriptQuestion, process_script_question);
+ msg->setHandlerFuncFast(_PREHASH_ObjectProperties, LLSelectMgr::processObjectProperties, NULL);
+ msg->setHandlerFuncFast(_PREHASH_ObjectPropertiesFamily, LLSelectMgr::processObjectPropertiesFamily, NULL);
+ msg->setHandlerFunc("ForceObjectSelect", LLSelectMgr::processForceObjectSelect);
+
+ msg->setHandlerFuncFast(_PREHASH_MoneyBalanceReply, process_money_balance_reply, NULL);
+ msg->setHandlerFuncFast(_PREHASH_CoarseLocationUpdate, LLWorld::processCoarseUpdate, NULL);
+ msg->setHandlerFuncFast(_PREHASH_ReplyTaskInventory, LLViewerObject::processTaskInv, NULL);
+ msg->setHandlerFuncFast(_PREHASH_DerezContainer, process_derez_container, NULL);
+ msg->setHandlerFuncFast(_PREHASH_ScriptRunningReply,
+ &LLLiveLSLEditor::processScriptRunningReply);
+
+ msg->setHandlerFuncFast(_PREHASH_DeRezAck, process_derez_ack);
+
+ msg->setHandlerFunc("LogoutReply", process_logout_reply);
+
+ //msg->setHandlerFuncFast(_PREHASH_AddModifyAbility,
+ // &LLAgent::processAddModifyAbility);
+ //msg->setHandlerFuncFast(_PREHASH_RemoveModifyAbility,
+ // &LLAgent::processRemoveModifyAbility);
+ msg->setHandlerFuncFast(_PREHASH_AgentDataUpdate,
+ &LLAgent::processAgentDataUpdate);
+ msg->setHandlerFuncFast(_PREHASH_AgentGroupDataUpdate,
+ &LLAgent::processAgentGroupDataUpdate);
+ msg->setHandlerFunc("AgentDropGroup",
+ &LLAgent::processAgentDropGroup);
+ // land ownership messages
+ msg->setHandlerFuncFast(_PREHASH_ParcelOverlay,
+ LLViewerParcelMgr::processParcelOverlay);
+ msg->setHandlerFuncFast(_PREHASH_ParcelProperties,
+ LLViewerParcelMgr::processParcelProperties);
+ msg->setHandlerFunc("ParcelAccessListReply",
+ LLViewerParcelMgr::processParcelAccessListReply);
+ msg->setHandlerFunc("ParcelDwellReply",
+ LLViewerParcelMgr::processParcelDwellReply);
+
+ msg->setHandlerFunc("AvatarPropertiesReply",
+ LLPanelAvatar::processAvatarPropertiesReply);
+ msg->setHandlerFunc("AvatarInterestsReply",
+ LLPanelAvatar::processAvatarInterestsReply);
+ msg->setHandlerFunc("AvatarGroupsReply",
+ LLPanelAvatar::processAvatarGroupsReply);
+ msg->setHandlerFuncFast(_PREHASH_AvatarStatisticsReply,
+ LLPanelAvatar::processAvatarStatisticsReply);
+ msg->setHandlerFunc("AvatarNotesReply",
+ LLPanelAvatar::processAvatarNotesReply);
+ msg->setHandlerFunc("AvatarPicksReply",
+ LLPanelAvatar::processAvatarPicksReply);
+ msg->setHandlerFunc("AvatarClassifiedReply",
+ LLPanelAvatar::processAvatarClassifiedReply);
+
+ msg->setHandlerFuncFast(_PREHASH_CreateGroupReply,
+ LLGroupMgr::processCreateGroupReply);
+ msg->setHandlerFuncFast(_PREHASH_JoinGroupReply,
+ LLGroupMgr::processJoinGroupReply);
+ msg->setHandlerFuncFast(_PREHASH_EjectGroupMemberReply,
+ LLGroupMgr::processEjectGroupMemberReply);
+ msg->setHandlerFuncFast(_PREHASH_LeaveGroupReply,
+ LLGroupMgr::processLeaveGroupReply);
+ msg->setHandlerFuncFast(_PREHASH_GroupProfileReply,
+ LLGroupMgr::processGroupPropertiesReply);
+
+ msg->setHandlerFuncFast(_PREHASH_ReputationIndividualReply,
+ LLFloaterRate::processReputationIndividualReply);
+
+ msg->setHandlerFuncFast(_PREHASH_AgentWearablesUpdate,
+ LLAgent::processAgentInitialWearablesUpdate );
+
+ msg->setHandlerFunc("ScriptControlChange",
+ LLAgent::processScriptControlChange );
+
+ msg->setHandlerFuncFast(_PREHASH_GestureUpdate,
+ LLViewerGestureList::processGestureUpdate);
+
+ msg->setHandlerFuncFast(_PREHASH_ViewerEffect, LLHUDManager::processViewerEffect);
+
+ msg->setHandlerFuncFast(_PREHASH_GrantGodlikePowers, process_grant_godlike_powers);
+
+ msg->setHandlerFuncFast(_PREHASH_MoneySummaryReply,
+ LLFloaterAccountHistory::processMoneySummaryReply);
+ msg->setHandlerFuncFast(_PREHASH_MoneyDetailsReply,
+ LLFloaterAccountHistory::processMoneyDetailsReply);
+ msg->setHandlerFuncFast(_PREHASH_MoneyTransactionsReply,
+ LLFloaterAccountHistory::processMoneyTransactionsReply);
+
+ // ASDF
+ msg->setHandlerFuncFast(_PREHASH_GroupAccountSummaryReply,
+ LLGroupMoneyPlanningTabEventHandler::processGroupAccountSummaryReply);
+ msg->setHandlerFuncFast(_PREHASH_GroupAccountDetailsReply,
+ LLGroupMoneyDetailsTabEventHandler::processGroupAccountDetailsReply);
+ msg->setHandlerFuncFast(_PREHASH_GroupAccountTransactionsReply,
+ LLGroupMoneySalesTabEventHandler::processGroupAccountTransactionsReply);
+
+ msg->setHandlerFuncFast(_PREHASH_UserInfoReply,
+ process_user_info_reply);
+
+ msg->setHandlerFunc("RegionHandshake", process_region_handshake, NULL);
+
+ msg->setHandlerFunc("TeleportStart", process_teleport_start );
+ msg->setHandlerFunc("TeleportProgress", process_teleport_progress);
+ msg->setHandlerFunc("TeleportFailed", process_teleport_failed, NULL);
+ msg->setHandlerFunc("TeleportLocal", process_teleport_local, NULL);
+
+ msg->setHandlerFunc("ImageNotInDatabase", LLViewerImageList::processImageNotInDatabase, NULL);
+
+ msg->setHandlerFuncFast(_PREHASH_GroupMembersReply,
+ LLGroupMgr::processGroupMembersReply);
+ msg->setHandlerFunc("GroupRoleDataReply",
+ LLGroupMgr::processGroupRoleDataReply);
+ msg->setHandlerFunc("GroupRoleMembersReply",
+ LLGroupMgr::processGroupRoleMembersReply);
+ msg->setHandlerFunc("GroupTitlesReply",
+ LLGroupMgr::processGroupTitlesReply);
+ // Special handler as this message is sometimes used for group land.
+ msg->setHandlerFunc("PlacesReply", process_places_reply);
+ msg->setHandlerFunc("GroupNoticesListReply", LLPanelGroupNotices::processGroupNoticesListReply);
+
+ msg->setHandlerFunc("DirPlacesReply", LLPanelDirBrowser::processDirPlacesReply);
+ msg->setHandlerFunc("DirPeopleReply", LLPanelDirBrowser::processDirPeopleReply);
+ msg->setHandlerFunc("DirEventsReply", LLPanelDirBrowser::processDirEventsReply);
+ msg->setHandlerFunc("DirGroupsReply", LLPanelDirBrowser::processDirGroupsReply);
+ //msg->setHandlerFunc("DirPicksReply", LLPanelDirBrowser::processDirPicksReply);
+ msg->setHandlerFunc("DirClassifiedReply", LLPanelDirBrowser::processDirClassifiedReply);
+ msg->setHandlerFunc("DirLandReply", LLPanelDirBrowser::processDirLandReply);
+ msg->setHandlerFunc("DirPopularReply",LLPanelDirBrowser::processDirPopularReply);
+
+ msg->setHandlerFunc("AvatarPickerReply", LLFloaterAvatarPicker::processAvatarPickerReply);
+
+ msg->setHandlerFunc("MapLayerReply", LLWorldMap::processMapLayerReply);
+ msg->setHandlerFunc("MapBlockReply", LLWorldMap::processMapBlockReply);
+ msg->setHandlerFunc("MapItemReply", LLWorldMap::processMapItemReply);
+
+ msg->setHandlerFunc("EventInfoReply", LLPanelEvent::processEventInfoReply);
+ msg->setHandlerFunc("PickInfoReply", LLPanelPick::processPickInfoReply);
+ msg->setHandlerFunc("ClassifiedInfoReply", LLPanelClassified::processClassifiedInfoReply);
+ msg->setHandlerFunc("ParcelInfoReply", LLPanelPlace::processParcelInfoReply);
+ msg->setHandlerFunc("ScriptDialog", process_script_dialog);
+ msg->setHandlerFunc("LoadURL", process_load_url);
+ msg->setHandlerFunc("ScriptTeleportRequest", process_script_teleport_request);
+ msg->setHandlerFunc("EstateCovenantReply", process_covenant_reply);
+
+ // calling cards
+ msg->setHandlerFunc("OfferCallingCard", process_offer_callingcard);
+ msg->setHandlerFunc("AcceptCallingCard", process_accept_callingcard);
+ msg->setHandlerFunc("DeclineCallingCard", process_decline_callingcard);
+
+ msg->setHandlerFunc("ParcelObjectOwnersReply", LLPanelLandObjects::processParcelObjectOwnersReply);
+
+ // Reponse to the "Refresh" button on land objects floater.
+ if (gSavedSettings.getBOOL("AudioStreamingVideo"))
+ {
+ msg->setHandlerFunc("ParcelMediaCommandMessage", LLMediaEngine::process_parcel_media);
+ msg->setHandlerFunc ( "ParcelMediaUpdate", LLMediaEngine::process_parcel_media_update );
+ }
+ else
+ {
+ msg->setHandlerFunc("ParcelMediaCommandMessage", null_message_callback);
+ gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", null_message_callback );
+ }
+
+ msg->setHandlerFunc("InitiateDownload", process_initiate_download);
+ msg->setHandlerFunc("LandStatReply", LLFloaterTopObjects::handle_land_reply);
+ msg->setHandlerFunc("GenericMessage", process_generic_message);
+
+ msg->setHandlerFuncFast(_PREHASH_FeatureDisabled, process_feature_disabled_message);
+}
+
+
+void init_stat_view()
+{
+ LLFrameStatView *frameviewp = gDebugView->mFrameStatView;
+ frameviewp->setup(gFrameStats);
+ frameviewp->mShowPercent = FALSE;
+
+ LLRect rect;
+ LLStatBar *stat_barp;
+ rect = gDebugView->mStatViewp->getRect();
+
+ //
+ // Viewer advanced stats
+ //
+ LLStatView *stat_viewp = NULL;
+
+ //
+ // Viewer Basic
+ //
+ stat_viewp = new LLStatView("basic stat view", "Basic", "OpenDebugStatBasic", rect);
+ gDebugView->mStatViewp->addChildAtEnd(stat_viewp);
+
+ stat_barp = stat_viewp->addStat("FPS", &(gViewerStats->mFPSStat));
+ stat_barp->setUnitLabel(" fps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 45.f;
+ stat_barp->mTickSpacing = 7.5f;
+ stat_barp->mLabelSpacing = 15.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = TRUE;
+
+ stat_barp = stat_viewp->addStat("Bandwidth", &(gViewerStats->mKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 900.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 300.f;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = FALSE;
+
+ stat_barp = stat_viewp->addStat("Packet Loss", &(gViewerStats->mPacketsLostPercentStat));
+ stat_barp->setUnitLabel(" %%");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 5.f;
+ stat_barp->mTickSpacing = 1.f;
+ stat_barp->mLabelSpacing = 1.f;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayMean = TRUE;
+ stat_barp->mPrecision = 1;
+
+ stat_barp = stat_viewp->addStat("Ping Sim", &(gViewerStats->mSimPingStat));
+ stat_barp->setUnitLabel(" msec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = stat_viewp->addStat("Ping User", &(gViewerStats->mUserserverPingStat));
+ stat_barp->setUnitLabel(" msec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+
+ stat_viewp = new LLStatView("advanced stat view", "Advanced", "OpenDebugStatAdvanced", rect);
+ gDebugView->mStatViewp->addChildAtEnd(stat_viewp);
+
+
+ LLStatView *render_statviewp;
+ render_statviewp = new LLStatView("render stat view", "Render", "OpenDebugStatRender", rect);
+ stat_viewp->addChildAtEnd(render_statviewp);
+
+ stat_barp = render_statviewp->addStat("KTris Drawn", &(gPipeline.mTrianglesDrawnStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 500.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = render_statviewp->addStat("KTris Drawn", &(gPipeline.mTrianglesDrawnStat));
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 3000.f;
+ stat_barp->mTickSpacing = 250.f;
+ stat_barp->mLabelSpacing = 1000.f;
+ stat_barp->mPrecision = 1;
+
+ stat_barp = render_statviewp->addStat("Total Objs", &(gObjectList.mNumObjectsStat));
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 10000.f;
+ stat_barp->mTickSpacing = 2500.f;
+ stat_barp->mLabelSpacing = 5000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = render_statviewp->addStat("New Objs", &(gObjectList.mNumNewObjectsStat));
+ stat_barp->setLabel("New Objs");
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = TRUE;
+ stat_barp->mDisplayBar = FALSE;
+
+
+ // Pipeline statistics
+ LLStatView *pipeline_statviewp;
+ pipeline_statviewp = new LLStatView("pipeline stat view", "Pipeline", "", rect);
+ render_statviewp->addChildAtEnd(pipeline_statviewp);
+
+ stat_barp = pipeline_statviewp->addStat("Visible Drawables", &(gPipeline.mNumVisibleDrawablesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 10000.f;
+ stat_barp->mTickSpacing = 1000.f;
+ stat_barp->mLabelSpacing = 5000.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("Visible Faces", &(gPipeline.mNumVisibleFacesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40000.f;
+ stat_barp->mTickSpacing = 5000.f;
+ stat_barp->mLabelSpacing = 10000.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("DirtyGeom", &(gPipeline.mGeometryChangesStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("DirtyLight", &(gPipeline.mLightingChangesStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("MoveList", &(gPipeline.mMoveChangesStat));
+ stat_barp->setUnitLabel("dr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("Compiles", &(gPipeline.mCompilesStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("Verts Relit", &(gPipeline.mVerticesRelitStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40000.f;
+ stat_barp->mTickSpacing = 10000.f;
+ stat_barp->mLabelSpacing = 20000.f;
+ stat_barp->mPerSec = FALSE;
+
+ // Texture statistics
+ LLStatView *texture_statviewp;
+ texture_statviewp = new LLStatView("texture stat view", "Texture", "", rect);
+ render_statviewp->addChildAtEnd(texture_statviewp);
+
+ stat_barp = texture_statviewp->addStat("Count", &(gImageList.sNumImagesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 8000.f;
+ stat_barp->mTickSpacing = 2000.f;
+ stat_barp->mLabelSpacing = 4000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Raw Count", &(gImageList.sNumRawImagesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 8000.f;
+ stat_barp->mTickSpacing = 2000.f;
+ stat_barp->mLabelSpacing = 4000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = texture_statviewp->addStat("GL Mem", &(gImageList.sGLTexMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Formatted Mem", &(gImageList.sFormattedMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Raw Mem", &(gImageList.sRawMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Bound Mem", &(gImageList.sGLBoundMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+
+ // Network statistics
+ LLStatView *net_statviewp;
+ net_statviewp = new LLStatView("network stat view", "Network", "OpenDebugStatNet", rect);
+ stat_viewp->addChildAtEnd(net_statviewp);
+
+ stat_barp = net_statviewp->addStat("Packets In", &(gViewerStats->mPacketsInStat));
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Packets Out", &(gViewerStats->mPacketsOutStat));
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Objects", &(gViewerStats->mObjectKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Texture", &(gViewerStats->mTextureKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Asset", &(gViewerStats->mAssetKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Layers", &(gViewerStats->mLayersKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Actual In", &(gViewerStats->mActualInKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1024.f;
+ stat_barp->mTickSpacing = 128.f;
+ stat_barp->mLabelSpacing = 256.f;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = FALSE;
+
+ stat_barp = net_statviewp->addStat("Actual Out", &(gViewerStats->mActualOutKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 512.f;
+ stat_barp->mTickSpacing = 128.f;
+ stat_barp->mLabelSpacing = 256.f;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = FALSE;
+
+ stat_barp = net_statviewp->addStat("VFS Pending Ops", &(gViewerStats->mVFSPendingOperations));
+ stat_barp->setUnitLabel(" ");
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+
+ // Simulator stats
+ LLStatView *sim_statviewp = new LLStatView("sim stat view", "Simulator", "OpenDebugStatSim", rect);
+ gDebugView->mStatViewp->addChildAtEnd(sim_statviewp);
+
+ stat_barp = sim_statviewp->addStat("Time Dilation", &(gViewerStats->mSimTimeDilation));
+ stat_barp->mPrecision = 2;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1.f;
+ stat_barp->mTickSpacing = 0.25f;
+ stat_barp->mLabelSpacing = 0.5f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Sim FPS", &(gViewerStats->mSimFPS));
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 200.f;
+ stat_barp->mTickSpacing = 20.f;
+ stat_barp->mLabelSpacing = 100.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Physics FPS", &(gViewerStats->mSimPhysicsFPS));
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 66.f;
+ stat_barp->mTickSpacing = 33.f;
+ stat_barp->mLabelSpacing = 33.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Agent Updates/Sec", &(gViewerStats->mSimAgentUPS));
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100.f;
+ stat_barp->mTickSpacing = 25.f;
+ stat_barp->mLabelSpacing = 50.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Main Agents", &(gViewerStats->mSimMainAgents));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 80.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 40.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Child Agents", &(gViewerStats->mSimChildAgents));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 5.f;
+ stat_barp->mLabelSpacing = 10.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Objects", &(gViewerStats->mSimObjects));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 30000.f;
+ stat_barp->mTickSpacing = 5000.f;
+ stat_barp->mLabelSpacing = 10000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Active Objects", &(gViewerStats->mSimActiveObjects));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 800.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Active Scripts", &(gViewerStats->mSimActiveScripts));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 800.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Script Perf", &(gViewerStats->mSimLSLIPS));
+ stat_barp->setUnitLabel(" ips");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100000.f;
+ stat_barp->mTickSpacing = 25000.f;
+ stat_barp->mLabelSpacing = 50000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Packets In", &(gViewerStats->mSimInPPS));
+ stat_barp->setUnitLabel(" pps");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 2000.f;
+ stat_barp->mTickSpacing = 250.f;
+ stat_barp->mLabelSpacing = 1000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Packets Out", &(gViewerStats->mSimOutPPS));
+ stat_barp->setUnitLabel(" pps");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 2000.f;
+ stat_barp->mTickSpacing = 250.f;
+ stat_barp->mLabelSpacing = 1000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Pending Downloads", &(gViewerStats->mSimPendingDownloads));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 800.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Pending Uploads", &(gViewerStats->mSimPendingUploads));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100.f;
+ stat_barp->mTickSpacing = 25.f;
+ stat_barp->mLabelSpacing = 50.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Total Unacked Bytes", &(gViewerStats->mSimTotalUnackedBytes));
+ stat_barp->setUnitLabel(" kb");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100000.f;
+ stat_barp->mTickSpacing = 25000.f;
+ stat_barp->mLabelSpacing = 50000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ LLStatView *sim_time_viewp;
+ sim_time_viewp = new LLStatView("sim perf view", "Time (ms)", "", rect);
+ sim_statviewp->addChildAtEnd(sim_time_viewp);
+
+ stat_barp = sim_time_viewp->addStat("Total Frame Time", &(gViewerStats->mSimFrameMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Net Time", &(gViewerStats->mSimNetMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Sim Time (Physics)", &(gViewerStats->mSimSimPhysicsMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Sim Time (Other)", &(gViewerStats->mSimSimOtherMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Agent Time", &(gViewerStats->mSimAgentMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Images Time", &(gViewerStats->mSimImagesMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Script Time", &(gViewerStats->mSimScriptMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ LLRect r = gDebugView->mStatViewp->getRect();
+
+ // Reshape based on the parameters we set.
+ gDebugView->mStatViewp->reshape(r.getWidth(), r.getHeight());
+}
+
+void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32)
+{
+ // nothing
+}
+
+// *HACK: Must match name in Library or agent inventory
+const char* COMMON_GESTURES_FOLDER = "Common Gestures";
+const char* MALE_GESTURES_FOLDER = "Male Gestures";
+const char* FEMALE_GESTURES_FOLDER = "Female Gestures";
+const char* MALE_OUTFIT_FOLDER = "Male Shape & Outfit";
+const char* FEMALE_OUTFIT_FOLDER = "Female Shape & Outfit";
+const S32 OPT_USE_INITIAL_OUTFIT = -2;
+const S32 OPT_CLOSED_WINDOW = -1;
+const S32 OPT_MALE = 0;
+const S32 OPT_FEMALE = 1;
+
+void callback_choose_gender(S32 option, void* userdata)
+{
+ S32 gender = OPT_FEMALE;
+ const char* outfit = FEMALE_OUTFIT_FOLDER;
+ const char* gestures = FEMALE_GESTURES_FOLDER;
+ const char* common_gestures = COMMON_GESTURES_FOLDER;
+ if (!gInitialOutfit.empty())
+ {
+ outfit = gInitialOutfit.c_str();
+ if (gInitialOutfitGender == "male")
+ {
+ gender = OPT_MALE;
+ gestures = MALE_GESTURES_FOLDER;
+ }
+ else
+ {
+ gender = OPT_FEMALE;
+ gestures = FEMALE_GESTURES_FOLDER;
+ }
+ }
+ else
+ {
+ switch(option)
+ {
+ case OPT_MALE:
+ gender = OPT_MALE;
+ outfit = MALE_OUTFIT_FOLDER;
+ gestures = MALE_GESTURES_FOLDER;
+ break;
+
+ case OPT_FEMALE:
+ case OPT_CLOSED_WINDOW:
+ default:
+ gender = OPT_FEMALE;
+ outfit = FEMALE_OUTFIT_FOLDER;
+ gestures = FEMALE_GESTURES_FOLDER;
+ break;
+ }
+ }
+
+ // try to find the outfit - if not there, create some default
+ // wearables.
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLNameCategoryCollector has_name(outfit);
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ has_name);
+ if (0 == cat_array.count())
+ {
+ gAgent.createStandardWearables(gender);
+ }
+ else
+ {
+ wear_outfit_by_name(outfit);
+ }
+ wear_outfit_by_name(gestures);
+ wear_outfit_by_name(common_gestures);
+
+ typedef std::map<LLUUID, LLMultiGesture*> item_map_t;
+ item_map_t::iterator gestureIterator;
+
+ // Must be here so they aren't invisible if they close the window.
+ gAgent.setGenderChosen(TRUE);
+}
+
+// XUI:translate
+void dialog_choose_gender_first_start()
+{
+ if (!gNoRender
+ && (!gAgent.isGenderChosen()))
+ {
+ if (!gInitialOutfit.empty())
+ {
+ gViewerWindow->alertXml("WelcomeNoClothes",
+ callback_choose_gender, NULL);
+ }
+ else
+ {
+ gViewerWindow->alertXml("WelcomeChooseSex",
+ callback_choose_gender, NULL);
+
+ }
+ }
+}
+
+// Loads a bitmap to display during load
+// location_id = 0 => last position
+// location_id = 1 => home position
+void init_start_screen(S32 location_id)
+{
+ if (gStartImageGL)
+ {
+ gStartImageGL = NULL;
+ llinfos << "re-initializing start screen" << llendl;
+ }
+
+ llinfos << "Loading startup bitmap..." << llendl;
+
+ LLString temp_str = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter();
+
+ if ((S32)START_LOCATION_ID_LAST == location_id)
+ {
+ temp_str += SCREEN_LAST_FILENAME;
+ }
+ else
+ {
+ temp_str += SCREEN_HOME_FILENAME;
+ }
+
+ LLPointer<LLImageBMP> start_image_bmp = new LLImageBMP;
+ if( !start_image_bmp->load(temp_str) )
+ {
+ llinfos << "Bitmap load failed" << llendl;
+ return;
+ }
+
+ gStartImageGL = new LLImageGL(FALSE);
+ gStartImageWidth = start_image_bmp->getWidth();
+ gStartImageHeight = start_image_bmp->getHeight();
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ if (!start_image_bmp->decode(raw))
+ {
+ llinfos << "Bitmap decode failed" << llendl;
+ gStartImageGL = NULL;
+ return;
+ }
+
+ raw->expandToPowerOfTwo();
+ gStartImageGL->createGLTexture(0, raw);
+}
+
+
+// frees the bitmap
+void release_start_screen()
+{
+ //llinfos << "Releasing bitmap..." << llendl;
+ gStartImageGL = NULL;
+}
+
+void process_connect_to_userserver(LLMessageSystem* msg, void**)
+{
+ // Sent unreliably since if we've become disconnected, the
+ // userserver will get back to us eventually. By sending reliable,
+ // we also may accidently induce two separate validations under
+ // conditions where the userserver is already lagged.
+ msg->newMessage("ConnectAgentToUserserver");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->sendMessage(gUserServer);
+}
+
+bool LLStartUp::canGoFullscreen()
+{
+ return gStartupState >= STATE_WORLD_INIT;
+}
+
+
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
new file mode 100644
index 0000000000..c7b3bc13d5
--- /dev/null
+++ b/indra/newview/llstartup.h
@@ -0,0 +1,72 @@
+/**
+ * @file llstartup.h
+ * @brief startup routines and logic declaration
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTARTUP_H
+#define LL_LLSTARTUP_H
+
+// functions
+BOOL idle_startup();
+void cleanup_app();
+LLString load_password_from_disk();
+
+// constants, variables, & enumerations
+extern const char* SCREEN_HOME_FILENAME;
+extern const char* SCREEN_LAST_FILENAME;
+
+enum EStartupState{
+ STATE_FIRST,
+ STATE_LOGIN_SHOW,
+ STATE_LOGIN_WAIT,
+ STATE_LOGIN_CLEANUP,
+ STATE_RESOLVING_USERSERVER,
+ STATE_USERSERVER_RESOLVED,
+ STATE_MESSAGE_TEMPLATE_SEND,
+ STATE_MESSAGE_TEMPLATE_WAIT,
+ STATE_UPDATE_CHECK,
+ STATE_LOGIN_AUTH_INIT,
+ STATE_LOGIN_AUTHENTICATE,
+ STATE_LOGIN_NO_DATA_YET,
+ STATE_LOGIN_DOWNLOADING,
+ STATE_LOGIN_PROCESS_RESPONSE,
+ //STATE_USERSERVER_SEND,
+ //STATE_USERSERVER_WAIT,
+ //STATE_LOCATION_SEND,
+ //STATE_LOCATION_WAIT,
+ STATE_WORLD_INIT,
+ STATE_QUICKTIME_INIT,
+ STATE_WORLD_WAIT,
+ STATE_AGENT_SEND,
+ STATE_AGENT_WAIT,
+ STATE_INVENTORY_SEND,
+ STATE_CONNECT_USERSERVER,
+ STATE_MISC,
+ STATE_PRECACHE,
+ STATE_WEARABLES_WAIT,
+ //STATE_INVENTORY_WAIT,
+ //STATE_TOS_AGREEMENT_START,
+ //STATE_TOS_AGREEMENT_WAIT,
+ //STATE_CRITICAL_MESSAGE_START,
+ //STATE_CRITICAL_MESSAGE_WAIT,
+ STATE_CLEANUP,
+ STATE_STARTED
+};
+
+// exorted symbol
+extern S32 gStartupState;
+extern bool gQuickTimeInitialized;
+
+class LLStartUp
+{
+public:
+ static bool canGoFullscreen();
+ // returns true if we are far enough along in startup to allow
+ // going full screen
+};
+
+
+#endif // LL_LLSTARTUP_H
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
new file mode 100644
index 0000000000..f4d87e5c6f
--- /dev/null
+++ b/indra/newview/llstatusbar.cpp
@@ -0,0 +1,612 @@
+/**
+ * @file llstatusbar.cpp
+ * @brief LLStatusBar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llstatusbar.h"
+
+#include <iomanip>
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llparcel.h"
+#include "llstring.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloaterchat.h"
+#include "llfloaterland.h"
+#include "llfloaterregioninfo.h"
+#include "llfloaterscriptdebug.h"
+#include "llhudicon.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llnotify.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llframetimer.h"
+#include "llvoavatar.h"
+#include "llresmgr.h"
+#include "llworld.h"
+#include "llstatgraph.h"
+#include "llviewermenu.h" // for gMenuBarView
+#include "llviewerparcelmgr.h"
+#include "llviewerthrottle.h"
+#include "llvieweruictrlfactory.h"
+
+#include "lltoolmgr.h"
+#include "llfocusmgr.h"
+#include "viewer.h"
+
+//
+// Globals
+//
+LLStatusBar *gStatusBar = NULL;
+S32 STATUS_BAR_HEIGHT = 0;
+extern S32 MENU_BAR_HEIGHT;
+
+// TODO: these values ought to be in the XML too
+const S32 MENU_PARCEL_SPACING = 1; // Distance from right of menu item to parcel information
+const S32 SIM_STAT_WIDTH = 8;
+const F32 SIM_WARN_FRACTION = 0.75f;
+const F32 SIM_FULL_FRACTION = 0.98f;
+const LLColor4 SIM_OK_COLOR(0.f, 1.f, 0.f, 1.f);
+const LLColor4 SIM_WARN_COLOR(1.f, 1.f, 0.f, 1.f);
+const LLColor4 SIM_FULL_COLOR(1.f, 0.f, 0.f, 1.f);
+const F32 ICON_TIMER_EXPIRY = 3.f; // How long the balance and health icons should flash after a change.
+const F32 ICON_FLASH_FREQUENCY = 2.f;
+const S32 GRAPHIC_FUDGE = 4;
+const S32 TEXT_HEIGHT = 18;
+
+LLStatusBar::LLStatusBar(const std::string& name, const LLRect& rect)
+: LLPanel(name, LLRect(), FALSE), // not mouse opaque
+ mBalance(0),
+ mHealth(100),
+ mSquareMetersCredit(0),
+ mSquareMetersCommitted(0)
+{
+ // status bar can possible overlay menus?
+ mMouseOpaque = FALSE;
+ setIsChrome(TRUE);
+
+ mBalanceTimer = new LLFrameTimer();
+ mHealthTimer = new LLFrameTimer();
+
+ gUICtrlFactory->buildPanel(this,"panel_status_bar.xml");
+
+ // status bar can never get a tab
+ setFocusRoot(FALSE);
+
+ mBtnScriptOut = LLUICtrlFactory::getButtonByName( this, "scriptout" );
+ mBtnHealth = LLUICtrlFactory::getButtonByName( this, "health" );
+ mBtnFly = LLUICtrlFactory::getButtonByName( this, "fly" );
+ mBtnBuild = LLUICtrlFactory::getButtonByName( this, "build" );
+ mBtnScripts = LLUICtrlFactory::getButtonByName( this, "scripts" );
+ mBtnPush = LLUICtrlFactory::getButtonByName( this, "restrictpush" );
+ mBtnBuyLand = LLUICtrlFactory::getButtonByName( this, "buyland" );
+ mBtnBuyCurrency = LLUICtrlFactory::getButtonByName( this, "buycurrency" );
+
+ mTextParcelName = LLUICtrlFactory::getTextBoxByName( this, "ParcelNameText" );
+ mTextBalance = LLUICtrlFactory::getTextBoxByName( this, "BalanceText" );
+
+ mTextHealth = LLUICtrlFactory::getTextBoxByName( this, "HealthText" );
+ mTextTime = LLUICtrlFactory::getTextBoxByName( this, "TimeText" );
+
+ S32 x = mRect.getWidth() - 2;
+ S32 y = 0;
+ LLRect r;
+ r.set( x-SIM_STAT_WIDTH, y+MENU_BAR_HEIGHT-1, x, y+1);
+ mSGBandwidth = new LLStatGraph("BandwidthGraph", r);
+ mSGBandwidth->setFollows(FOLLOWS_BOTTOM | FOLLOWS_RIGHT);
+ mSGBandwidth->setStat(&gViewerStats->mKBitStat);
+ LLString text = childGetText("bandwidth_tooltip") + " ";
+ LLUIString bandwidth_tooltip = text; // get the text from XML until this widget is XML driven
+ mSGBandwidth->setLabel(bandwidth_tooltip.getString().c_str());
+ mSGBandwidth->setUnits("Kbps");
+ mSGBandwidth->setPrecision(0);
+ addChild(mSGBandwidth);
+ x -= SIM_STAT_WIDTH + 2;
+
+ r.set( x-SIM_STAT_WIDTH, y+MENU_BAR_HEIGHT-1, x, y+1);
+ mSGPacketLoss = new LLStatGraph("PacketLossPercent", r);
+ mSGPacketLoss->setFollows(FOLLOWS_BOTTOM | FOLLOWS_RIGHT);
+ mSGPacketLoss->setStat(&gViewerStats->mPacketsLostPercentStat);
+ text = childGetText("packet_loss_tooltip") + " ";
+ LLUIString packet_loss_tooltip = text; // get the text from XML until this widget is XML driven
+ mSGPacketLoss->setLabel(packet_loss_tooltip.getString().c_str());
+ mSGPacketLoss->setUnits("%");
+ mSGPacketLoss->setMin(0.f);
+ mSGPacketLoss->setMax(5.f);
+ mSGPacketLoss->setThreshold(0, 0.5f);
+ mSGPacketLoss->setThreshold(1, 1.f);
+ mSGPacketLoss->setThreshold(2, 3.f);
+ mSGPacketLoss->setPrecision(1);
+ mSGPacketLoss->mPerSec = FALSE;
+ addChild(mSGPacketLoss);
+
+}
+
+LLStatusBar::~LLStatusBar()
+{
+ delete mBalanceTimer;
+ mBalanceTimer = NULL;
+
+ delete mHealthTimer;
+ mHealthTimer = NULL;
+
+ // LLView destructor cleans up children
+}
+
+BOOL LLStatusBar::postBuild()
+{
+ childSetAction("scriptout", onClickScriptDebug, this);
+ childSetAction("health", onClickHealth, this);
+ childSetAction("fly", onClickFly, this);
+ childSetAction("buyland", onClickBuyLand, this );
+ childSetAction("buycurrency", onClickBuyCurrency, this );
+ childSetAction("build", onClickBuild, this );
+ childSetAction("scripts", onClickScripts, this );
+ childSetAction("restrictpush", onClickPush, this );
+
+ childSetActionTextbox("ParcelNameText", onClickParcelInfo );
+ childSetActionTextbox("BalanceText", onClickBalance );
+
+ return TRUE;
+}
+
+EWidgetType LLStatusBar::getWidgetType() const
+{
+ return WIDGET_TYPE_STATUS_BAR;
+}
+
+LLString LLStatusBar::getWidgetTag() const
+{
+ return LL_STATUS_BAR_TAG;
+}
+
+//-----------------------------------------------------------------------
+// Overrides
+//-----------------------------------------------------------------------
+
+// virtual
+void LLStatusBar::draw()
+{
+ refresh();
+
+ LLView::draw();
+}
+
+
+// Per-frame updates of visibility
+void LLStatusBar::refresh()
+{
+ F32 bwtotal = gViewerThrottle.getMaxBandwidth() / 1000.f;
+ mSGBandwidth->setMin(0.f);
+ mSGBandwidth->setMax(bwtotal*1.25f);
+ mSGBandwidth->setThreshold(0, bwtotal*0.75f);
+ mSGBandwidth->setThreshold(1, bwtotal);
+ mSGBandwidth->setThreshold(2, bwtotal);
+
+ // Get current UTC time, adjusted for the user's clock
+ // being off.
+ U32 utc_time;
+ utc_time = time_corrected();
+
+ // There's only one internal tm buffer.
+ struct tm* internal_time;
+
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ internal_time = utc_to_pacific_time(utc_time, gPacificDaylightTime);
+
+ S32 hour = internal_time->tm_hour;
+ S32 min = internal_time->tm_min;
+
+ std::string am_pm = "AM";
+ if (hour > 11)
+ {
+ hour -= 12;
+ am_pm = "PM";
+ }
+
+ std::string tz = "PST";
+ if (gPacificDaylightTime)
+ {
+ tz = "PDT";
+ }
+ // Zero hour is 12 AM
+ if (hour == 0) hour = 12;
+ std::ostringstream t;
+ t << std::setfill(' ') << std::setw(2) << hour << ":"
+ << std::setfill('0') << std::setw(2) << min
+ << " " << am_pm << " " << tz;
+ mTextTime->setText(t.str().c_str());
+
+ LLRect r;
+ const S32 MENU_RIGHT = gMenuBarView->getRightmostMenuEdge();
+ S32 x = MENU_RIGHT + MENU_PARCEL_SPACING;
+ S32 y = 0;
+
+ LLViewerRegion *region = gAgent.getRegion();
+ LLParcel *parcel = gParcelMgr->getAgentParcel();
+
+ LLRect buttonRect;
+
+ if (LLHUDIcon::iconsNearby())
+ {
+ childGetRect( "scriptout", buttonRect );
+ r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnScriptOut->setRect(r);
+ mBtnScriptOut->setVisible(TRUE);
+ x += buttonRect.getWidth();
+ }
+ else
+ {
+ mBtnScriptOut->setVisible(FALSE);
+ }
+
+ if ((region && region->getAllowDamage()) ||
+ (parcel && parcel->getAllowDamage()) )
+ {
+ // set visibility based on flashing
+ if( mHealthTimer->hasExpired() )
+ {
+ mBtnHealth->setVisible( TRUE );
+ }
+ else
+ {
+ BOOL flash = S32(mHealthTimer->getElapsedSeconds() * ICON_FLASH_FREQUENCY) & 1;
+ mBtnHealth->setVisible( flash );
+ }
+ mTextHealth->setVisible(TRUE);
+
+ // Health
+ childGetRect( "health", buttonRect );
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnHealth->setRect(r);
+ x += buttonRect.getWidth();
+
+ const S32 health_width = S32( LLFontGL::sSansSerifSmall->getWidth("100%") );
+ r.set(x, y+TEXT_HEIGHT - 2, x+health_width, y);
+ mTextHealth->setRect(r);
+ x += health_width;
+ }
+ else
+ {
+ // invisible if region doesn't allow damage
+ mBtnHealth->setVisible(FALSE);
+ mTextHealth->setVisible(FALSE);
+ }
+
+ if ((region && region->getBlockFly()) ||
+ (parcel && !parcel->getAllowFly()) )
+ {
+ // No Fly Zone
+ childGetRect( "fly", buttonRect );
+ mBtnFly->setVisible(TRUE);
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnFly->setRect(r);
+ x += buttonRect.getWidth();
+ }
+ else
+ {
+ mBtnFly->setVisible(FALSE);
+ }
+
+ BOOL no_build = parcel && !parcel->getAllowModify();
+ mBtnBuild->setVisible( no_build );
+ if (no_build)
+ {
+ childGetRect( "build", buttonRect );
+ // No Build Zone
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnBuild->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ BOOL no_scripts = FALSE;
+ if((region
+ && ((region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS)
+ || (region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)))
+ || (parcel && !parcel->getAllowOtherScripts()))
+ {
+ no_scripts = TRUE;
+ }
+ mBtnScripts->setVisible( no_scripts );
+ if (no_scripts)
+ {
+ // No scripts
+ childGetRect( "scripts", buttonRect );
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnScripts->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ BOOL no_region_push = (region && region->getRestrictPushObject());
+ BOOL no_push = no_region_push || (parcel && parcel->getRestrictPushObject());
+ mBtnPush->setVisible( no_push );
+ if (no_push)
+ {
+ childGetRect( "restrictpush", buttonRect );
+ // No Push Zone
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnPush->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ BOOL canBuyLand = parcel
+ && !parcel->isPublic()
+ && gParcelMgr->canAgentBuyParcel(parcel, false);
+ mBtnBuyLand->setVisible(canBuyLand);
+ if (canBuyLand)
+ {
+ childGetRect( "buyland", buttonRect );
+ r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnBuyLand->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ LLString location_name;
+ if (region)
+ {
+ const LLVector3& agent_pos_region = gAgent.getPositionAgent();
+ S32 pos_x = lltrunc( agent_pos_region.mV[VX] );
+ S32 pos_y = lltrunc( agent_pos_region.mV[VY] );
+ S32 pos_z = lltrunc( agent_pos_region.mV[VZ] );
+
+ // Round the numbers based on the velocity
+ LLVector3 agent_velocity = gAgent.getVelocity();
+ F32 velocity_mag_sq = agent_velocity.magVecSquared();
+
+ const F32 FLY_CUTOFF = 6.f; // meters/sec
+ const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF;
+ const F32 WALK_CUTOFF = 1.5f; // meters/sec
+ const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF;
+
+ if (velocity_mag_sq > FLY_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 4;
+ pos_y -= pos_y % 4;
+ }
+ else if (velocity_mag_sq > WALK_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 2;
+ pos_y -= pos_y % 2;
+ }
+
+ if (parcel && parcel->getName())
+ {
+ location_name = region->getName()
+ + llformat(" %d, %d, %d (%s) - %s",
+ pos_x, pos_y, pos_z,
+ region->getSimAccessString(),
+ parcel->getName());
+ }
+ else
+ {
+ location_name = region->getName()
+ + llformat(" %d, %d, %d (%s)",
+ pos_x, pos_y, pos_z,
+ region->getSimAccessString());
+ }
+ }
+ else
+ {
+ // no region
+ location_name = "(Unknown)";
+ }
+ mTextParcelName->setText(location_name);
+
+ // Adjust region name and parcel name
+ x += 4;
+
+ const S32 PARCEL_RIGHT = llmin(mTextTime->getRect().mLeft, mTextParcelName->getTextPixelWidth() + x + 5);
+ r.set(x+4, mRect.getHeight() - 2, PARCEL_RIGHT, 0);
+ mTextParcelName->setRect(r);
+}
+
+void LLStatusBar::setVisibleForMouselook(bool visible)
+{
+ mTextBalance->setVisible(visible);
+ mTextTime->setVisible(visible);
+ mSGBandwidth->setVisible(visible);
+ mSGPacketLoss->setVisible(visible);
+ mBtnBuyCurrency->setVisible(visible);
+}
+
+void LLStatusBar::debitBalance(S32 debit)
+{
+ setBalance(getBalance() - debit);
+}
+
+void LLStatusBar::creditBalance(S32 credit)
+{
+ setBalance(getBalance() + credit);
+}
+
+void LLStatusBar::setBalance(S32 balance)
+{
+ LLString balance_str;
+ gResMgr->getMonetaryString( balance_str, balance );
+ mTextBalance->setText( balance_str );
+
+ if (mBalance && (fabs((F32)(mBalance - balance)) > gSavedSettings.getF32("UISndMoneyChangeThreshold")))
+ {
+ if (mBalance > balance)
+ make_ui_sound("UISndMoneyChangeDown");
+ else
+ make_ui_sound("UISndMoneyChangeUp");
+ }
+
+ if( balance != mBalance )
+ {
+ mBalanceTimer->reset();
+ mBalanceTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );
+ mBalance = balance;
+ }
+}
+
+void LLStatusBar::setHealth(S32 health)
+{
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d%%", health);
+ //llinfos << "Setting health to: " << buffer << llendl;
+ mTextHealth->setText(buffer);
+
+ if( mHealth > health )
+ {
+ if (mHealth > (health + gSavedSettings.getF32("UISndHealthReductionThreshold")))
+ {
+ LLVOAvatar *me;
+
+ if ((me = gAgent.getAvatarObject()))
+ {
+ if (me->getSex() == SEX_FEMALE)
+ {
+ make_ui_sound("UISndHealthReductionF");
+ }
+ else
+ {
+ make_ui_sound("UISndHealthReductionM");
+ }
+ }
+ }
+
+ mHealthTimer->reset();
+ mHealthTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );
+ }
+
+ mHealth = health;
+}
+
+S32 LLStatusBar::getBalance() const
+{
+ return mBalance;
+}
+
+
+S32 LLStatusBar::getHealth() const
+{
+ return mHealth;
+}
+
+void LLStatusBar::setLandCredit(S32 credit)
+{
+ mSquareMetersCredit = credit;
+}
+void LLStatusBar::setLandCommitted(S32 committed)
+{
+ mSquareMetersCommitted = committed;
+}
+
+BOOL LLStatusBar::isUserTiered() const
+{
+ return (mSquareMetersCredit > 0);
+}
+
+S32 LLStatusBar::getSquareMetersCredit() const
+{
+ return mSquareMetersCredit;
+}
+
+S32 LLStatusBar::getSquareMetersCommitted() const
+{
+ return mSquareMetersCommitted;
+}
+
+S32 LLStatusBar::getSquareMetersLeft() const
+{
+ return mSquareMetersCredit - mSquareMetersCommitted;
+}
+
+// static
+void LLStatusBar::onClickParcelInfo(void* data)
+{
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+
+ LLFloaterLand::show();
+}
+
+// static
+void LLStatusBar::onClickBalance(void* data)
+{
+ LLFloaterBuyCurrency::buyCurrency();
+}
+
+// static
+void LLStatusBar::onClickBuyCurrency(void* data)
+{
+ LLFloaterBuyCurrency::buyCurrency();
+}
+
+// static
+void LLStatusBar::onClickHealth(void* )
+{
+ LLNotifyBox::showXml("NotSafe");
+}
+
+// static
+void LLStatusBar::onClickScriptDebug(void*)
+{
+ LLFloaterScriptDebug::show(LLUUID::null);
+}
+
+// static
+void LLStatusBar::onClickFly(void* )
+{
+ LLNotifyBox::showXml("NoFly");
+}
+
+// static
+void LLStatusBar::onClickPush(void* )
+{
+ LLNotifyBox::showXml("PushRestricted");
+}
+
+// static
+void LLStatusBar::onClickBuild(void*)
+{
+ LLNotifyBox::showXml("NoBuild");
+}
+
+// static
+void LLStatusBar::onClickScripts(void*)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
+ {
+ LLNotifyBox::showXml("ScriptsStopped");
+ }
+ else if(region && region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS)
+ {
+ LLNotifyBox::showXml("ScriptsNotRunning");
+ }
+ else
+ {
+ LLNotifyBox::showXml("NoOutsideScripts");
+ }
+}
+
+// static
+void LLStatusBar::onClickBuyLand(void*)
+{
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+ gParcelMgr->startBuyLand();
+}
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
new file mode 100644
index 0000000000..c3f520315c
--- /dev/null
+++ b/indra/newview/llstatusbar.h
@@ -0,0 +1,108 @@
+/**
+ * @file llstatusbar.h
+ * @brief LLStatusBar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTATUSBAR_H
+#define LL_LLSTATUSBAR_H
+
+#include "llpanel.h"
+
+// "Constants" loaded from settings.xml at start time
+extern S32 STATUS_BAR_HEIGHT;
+
+class LLButton;
+class LLLineEditor;
+class LLMessageSystem;
+class LLTextBox;
+class LLTextEditor;
+class LLUICtrl;
+class LLUUID;
+class LLFrameTimer;
+class LLStatGraph;
+
+class LLStatusBar
+: public LLPanel
+{
+public:
+ LLStatusBar(const std::string& name, const LLRect& rect );
+ ~LLStatusBar();
+ virtual BOOL postBuild();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // OVERRIDES
+ virtual void draw();
+
+ // MANIPULATORS
+ void setBalance(S32 balance);
+ void debitBalance(S32 debit);
+ void creditBalance(S32 credit);
+
+ void setHealth(S32 percent);
+
+ void setLandCredit(S32 credit);
+ void setLandCommitted(S32 committed);
+
+ void refresh();
+ void setVisibleForMouselook(bool visible);
+ // some elements should hide in mouselook
+
+ // ACCESSORS
+ S32 getBalance() const;
+ S32 getHealth() const;
+
+ BOOL isUserTiered() const;
+ S32 getSquareMetersCredit() const;
+ S32 getSquareMetersCommitted() const;
+ S32 getSquareMetersLeft() const;
+
+protected:
+ static void onClickParcelInfo(void*);
+ static void onClickBalance(void*);
+ static void onClickBuyCurrency(void*);
+ static void onClickRegionInfo(void*);
+ static void onClickHealth(void*);
+ static void onClickFly(void*);
+ static void onClickPush(void*);
+ static void onClickBuild(void*);
+ static void onClickScripts(void*);
+ static void onClickBuyLand(void*);
+ static void onClickScriptDebug(void*);
+
+protected:
+ LLTextBox *mTextBalance;
+ LLTextBox *mTextHealth;
+ LLTextBox *mTextTime;
+
+ LLButton *mBtnScriptOut;
+ LLButton *mBtnHealth;
+ LLButton *mBtnFly;
+ LLButton *mBtnBuild;
+ LLButton *mBtnScripts;
+ LLButton *mBtnPush;
+ LLButton *mBtnBuyLand;
+
+
+ LLTextBox* mTextParcelName;
+
+ LLStatGraph *mSGBandwidth;
+ LLStatGraph *mSGPacketLoss;
+
+ LLButton *mBtnBuyCurrency;
+
+ S32 mBalance;
+ S32 mHealth;
+ S32 mSquareMetersCredit;
+ S32 mSquareMetersCommitted;
+ LLFrameTimer* mBalanceTimer;
+ LLFrameTimer* mHealthTimer;
+};
+
+extern LLStatusBar *gStatusBar;
+
+#endif
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
new file mode 100644
index 0000000000..fc0c46bbe1
--- /dev/null
+++ b/indra/newview/llsurface.cpp
@@ -0,0 +1,1337 @@
+/**
+ * @file llsurface.cpp
+ * @brief Implementation of LLSurface class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsurface.h"
+
+#include "llviewerimagelist.h"
+#include "llpatchvertexarray.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+#include "bitpack.h"
+#include "llviewerobjectlist.h"
+#include "llregionhandle.h"
+#include "llagent.h"
+#include "viewer.h"
+#include "llworld.h"
+#include "llviewercontrol.h"
+#include "llviewerimage.h"
+#include "llsurfacepatch.h"
+#include "llvosurfacepatch.h"
+#include "llvowater.h"
+#include "pipeline.h"
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "noise.h"
+#include "llviewercamera.h"
+#include "llglheaders.h"
+#include "lldrawpool.h"
+#include "lldrawable.h"
+
+extern LLPipeline gPipeline;
+
+LLColor4U MAX_WATER_COLOR(0, 48, 96, 240);
+
+
+S32 LLSurface::sTextureSize = 256;
+S32 LLSurface::sTexelsUpdated = 0;
+F32 LLSurface::sTextureUpdateTime = 0.f;
+LLStat LLSurface::sTexelsUpdatedPerSecStat;
+
+extern void bad_network_handler();
+
+// ---------------- LLSurface:: Public Members ---------------
+
+LLSurface::LLSurface(U32 type, LLViewerRegion *regionp) :
+ mGridsPerEdge(0),
+ mOOGridsPerEdge(0.f),
+ mPatchesPerEdge(0),
+ mType(type),
+ mOriginGlobal(0.0, 0.0, 0.0),
+ mSTexturep(NULL),
+ mWaterTexturep(NULL),
+ mGridsPerPatchEdge(0),
+ mMetersPerGrid(1.0f),
+ mMetersPerEdge(1.0f),
+ mRegionp(regionp)
+{
+ // Surface data
+ mSurfaceZ = NULL;
+ mNorm = NULL;
+
+ // Patch data
+ mPatchList = NULL;
+
+ // One of each for each camera
+ mVisiblePatchCount = 0;
+
+ mHasZData = FALSE;
+ // "uninitialized" min/max z
+ mMinZ = 10000.f;
+ mMaxZ = -10000.f;
+
+ mWaterObjp = NULL;
+
+ // In here temporarily.
+ mSurfacePatchUpdateCount = 0;
+
+ for (S32 i = 0; i < 8; i++)
+ {
+ mNeighbors[i] = NULL;
+ }
+}
+
+
+LLSurface::~LLSurface()
+{
+ delete [] mSurfaceZ;
+ mSurfaceZ = NULL;
+
+ delete [] mNorm;
+
+ mGridsPerEdge = 0;
+ mGridsPerPatchEdge = 0;
+ mPatchesPerEdge = 0;
+ mNumberOfPatches = 0;
+ destroyPatchData();
+
+ LLDrawPool *poolp = gPipeline.findPool(LLDrawPool::POOL_TERRAIN, mSTexturep);
+ if (!poolp)
+ {
+ llwarns << "No pool for terrain on destruction!" << llendl;
+ }
+ else if (poolp->mReferences.empty())
+ {
+ gPipeline.removePool(poolp);
+ // Don't enable this until we blitz the draw pool for it as well. -- djs
+ if (mSTexturep)
+ {
+ gImageList.deleteImage(mSTexturep);
+ mSTexturep = NULL;
+ }
+ if (mWaterTexturep)
+ {
+ gImageList.deleteImage(mWaterTexturep);
+ mWaterTexturep = NULL;
+ }
+ }
+ else
+ {
+ llerrs << "Terrain pool not empty!" << llendl;
+ }
+}
+
+void LLSurface::initClasses()
+{
+}
+
+void LLSurface::setRegion(LLViewerRegion *regionp)
+{
+ mRegionp = regionp;
+}
+
+// Assumes that arguments are powers of 2, and that
+// grids_per_edge / grids_per_patch_edge = power of 2
+void LLSurface::create(const S32 grids_per_edge,
+ const S32 grids_per_patch_edge,
+ const LLVector3d &origin_global,
+ const F32 width)
+{
+ // Initialize various constants for the surface
+ mGridsPerEdge = grids_per_edge + 1; // Add 1 for the east and north buffer
+ mOOGridsPerEdge = 1.f / mGridsPerEdge;
+ mGridsPerPatchEdge = grids_per_patch_edge;
+ mPatchesPerEdge = (mGridsPerEdge - 1) / mGridsPerPatchEdge;
+ mNumberOfPatches = mPatchesPerEdge * mPatchesPerEdge;
+ mMetersPerGrid = width / ((F32)(mGridsPerEdge - 1));
+ mMetersPerEdge = mMetersPerGrid * (mGridsPerEdge - 1);
+
+ mOriginGlobal.setVec(origin_global);
+
+ mPVArray.create(mGridsPerEdge, mGridsPerPatchEdge, gWorldPointer->getRegionScale());
+
+ S32 number_of_grids = mGridsPerEdge * mGridsPerEdge;
+
+ /////////////////////////////////////
+ //
+ // Initialize data arrays for surface
+ ///
+ mSurfaceZ = new F32[number_of_grids];
+ mNorm = new LLVector3[number_of_grids];
+
+ // Reset the surface to be a flat square grid
+ for(S32 i=0; i < number_of_grids; i++)
+ {
+ // Surface is flat and zero
+ // Normals all point up
+ mSurfaceZ[i] = 0.0f;
+ mNorm[i].setVec(0.f, 0.f, 1.f);
+ }
+
+
+ mVisiblePatchCount = 0;
+
+
+ ///////////////////////
+ //
+ // Initialize textures
+ //
+
+ initTextures();
+
+ // Has to be done after texture initialization
+ createPatchData();
+}
+
+LLViewerImage* LLSurface::getSTexture()
+{
+ if (mSTexturep.notNull() && !mSTexturep->getHasGLTexture())
+ {
+ createSTexture();
+ }
+ return mSTexturep;
+}
+
+LLViewerImage* LLSurface::getWaterTexture()
+{
+ if (mWaterTexturep.notNull() && !mWaterTexturep->getHasGLTexture())
+ {
+ createWaterTexture();
+ }
+ return mWaterTexturep;
+}
+
+void LLSurface::createSTexture()
+{
+ if (!mSTexturep)
+ {
+ // Fill with dummy gray data.
+ LLPointer<LLImageRaw> raw = new LLImageRaw(sTextureSize, sTextureSize, 3);
+ U8 *default_texture = raw->getData();
+ for (S32 i = 0; i < sTextureSize; i++)
+ {
+ for (S32 j = 0; j < sTextureSize; j++)
+ {
+ *(default_texture + (i*sTextureSize + j)*3) = 128;
+ *(default_texture + (i*sTextureSize + j)*3 + 1) = 128;
+ *(default_texture + (i*sTextureSize + j)*3 + 2) = 128;
+ }
+ }
+
+ mSTexturep = new LLViewerImage(raw, FALSE);
+ mSTexturep->dontDiscard();
+ mSTexturep->bind();
+ mSTexturep->setClamp(TRUE, TRUE);
+ gImageList.addImage(mSTexturep);
+ }
+}
+
+void LLSurface::createWaterTexture()
+{
+ if (!mWaterTexturep)
+ {
+ // Create the water texture
+ LLPointer<LLImageRaw> raw = new LLImageRaw(sTextureSize/2, sTextureSize/2, 4);
+ U8 *default_texture = raw->getData();
+ for (S32 i = 0; i < sTextureSize/2; i++)
+ {
+ for (S32 j = 0; j < sTextureSize/2; j++)
+ {
+ *(default_texture + (i*sTextureSize/2 + j)*4) = MAX_WATER_COLOR.mV[0];
+ *(default_texture + (i*sTextureSize/2 + j)*4 + 1) = MAX_WATER_COLOR.mV[1];
+ *(default_texture + (i*sTextureSize/2 + j)*4 + 2) = MAX_WATER_COLOR.mV[2];
+ *(default_texture + (i*sTextureSize/2 + j)*4 + 3) = MAX_WATER_COLOR.mV[3];
+ }
+ }
+ mWaterTexturep = new LLViewerImage(raw, FALSE);
+ mWaterTexturep->dontDiscard();
+ mWaterTexturep->bind();
+ mWaterTexturep->setClamp(TRUE, TRUE);
+ gImageList.addImage(mWaterTexturep);
+ }
+}
+
+void LLSurface::initTextures()
+{
+ ///////////////////////
+ //
+ // Main surface texture
+ //
+ createSTexture();
+
+ ///////////////////////
+ //
+ // Water texture
+ //
+ if (gSavedSettings.getBOOL("RenderWater") )
+ {
+ createWaterTexture();
+ mWaterObjp = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER, mRegionp);
+ gPipeline.addObject(mWaterObjp);
+ LLVector3d water_pos_global = from_region_handle(mRegionp->getHandle());
+ water_pos_global += LLVector3d(128.0, 128.0, DEFAULT_WATER_HEIGHT);
+ mWaterObjp->setPositionGlobal(water_pos_global);
+ }
+}
+
+
+void LLSurface::setOriginGlobal(const LLVector3d &origin_global)
+{
+ LLVector3d new_origin_global;
+ mOriginGlobal = origin_global;
+ LLSurfacePatch *patchp;
+ S32 i, j;
+ // Need to update the southwest corners of the patches
+ for (j=0; j<mPatchesPerEdge; j++)
+ {
+ for (i=0; i<mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, j);
+
+ new_origin_global = patchp->getOriginGlobal();
+
+ new_origin_global.mdV[0] = mOriginGlobal.mdV[0] + i * mMetersPerGrid * mGridsPerPatchEdge;
+ new_origin_global.mdV[1] = mOriginGlobal.mdV[1] + j * mMetersPerGrid * mGridsPerPatchEdge;
+ patchp->setOriginGlobal(new_origin_global);
+ }
+ }
+
+ // Hack!
+ if (mWaterObjp.notNull() && mWaterObjp->mDrawable.notNull())
+ {
+ const F64 x = origin_global.mdV[VX] + 128.0;
+ const F64 y = origin_global.mdV[VY] + 128.0;
+ const F64 z = mWaterObjp->getPositionGlobal().mdV[VZ];
+
+ LLVector3d water_origin_global(x, y, z);
+
+ mWaterObjp->setPositionGlobal(water_origin_global);
+ gPipeline.markMoved(mWaterObjp->mDrawable);
+ }
+}
+
+
+void LLSurface::connectNeighbor(LLSurface *neighborp, U32 direction)
+{
+ S32 i;
+ LLSurfacePatch *patchp, *neighbor_patchp;
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ mNeighbors[direction] = neighborp;
+ neighborp->mNeighbors[gDirOpposite[direction]] = this;
+
+ // Connect patches
+ if (NORTHEAST == direction)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(0, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ patchp->updateNorthEdge(); // Only update one of north or east.
+ patchp->dirtyZ();
+ }
+ else if (NORTHWEST == direction)
+ {
+ patchp = getPatch(0, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+ }
+ else if (SOUTHWEST == direction)
+ {
+ patchp = getPatch(0, 0);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ neighbor_patchp->updateNorthEdge(); // Only update one of north or east.
+ neighbor_patchp->dirtyZ();
+ }
+ else if (SOUTHEAST == direction)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, 0);
+ neighbor_patchp = neighborp->getPatch(0, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+ }
+ else if (EAST == direction)
+ {
+ // Do east/west connections, first
+ for (i = 0; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, i);
+ neighbor_patchp = neighborp->getPatch(0, i);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ patchp->updateEastEdge();
+ patchp->dirtyZ();
+ }
+
+ // Now do northeast/southwest connections
+ for (i = 0; i < (S32)mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, i);
+ neighbor_patchp = neighborp->getPatch(0, i+1);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHWEST);
+ }
+ // Now do southeast/northwest connections
+ for (i = 1; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, i);
+ neighbor_patchp = neighborp->getPatch(0, i-1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHWEST);
+ }
+ }
+ else if (NORTH == direction)
+ {
+ // Do north/south connections, first
+ for (i = 0; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(i, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ patchp->updateNorthEdge();
+ patchp->dirtyZ();
+ }
+
+ // Do northeast/southwest connections
+ for (i = 0; i < (S32)mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(i, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(i+1, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHWEST);
+ }
+ // Do southeast/northwest connections
+ for (i = 1; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(i-1, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHEAST);
+ }
+ }
+ else if (WEST == direction)
+ {
+ // Do east/west connections, first
+ for (i = 0; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(0, i);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, i);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ neighbor_patchp->updateEastEdge();
+ neighbor_patchp->dirtyZ();
+ }
+
+ // Now do northeast/southwest connections
+ for (i = 1; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(0, i);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, i - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHEAST);
+ }
+
+ // Now do northwest/southeast connections
+ for (i = 0; i < mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(0, i);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, i + 1);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHEAST);
+ }
+ }
+ else if (SOUTH == direction)
+ {
+ // Do north/south connections, first
+ for (i = 0; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, 0);
+ neighbor_patchp = neighborp->getPatch(i, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ neighbor_patchp->updateNorthEdge();
+ neighbor_patchp->dirtyZ();
+ }
+
+ // Now do northeast/southwest connections
+ for (i = 1; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, 0);
+ neighbor_patchp = neighborp->getPatch(i - 1, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHEAST);
+ }
+ // Now do northeast/southwest connections
+ for (i = 0; i < mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(i, 0);
+ neighbor_patchp = neighborp->getPatch(i + 1, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHWEST);
+ }
+ }
+}
+
+void LLSurface::disconnectNeighbor(LLSurface *surfacep)
+{
+ S32 i;
+ for (i = 0; i < 8; i++)
+ {
+ if (surfacep == mNeighbors[i])
+ {
+ mNeighbors[i] = NULL;
+ }
+ }
+
+ // Iterate through surface patches, removing any connectivity to removed surface.
+ for (i = 0; i < mNumberOfPatches; i++)
+ {
+ (mPatchList + i)->disconnectNeighbor(surfacep);
+ }
+}
+
+
+void LLSurface::disconnectAllNeighbors()
+{
+ S32 i;
+ for (i = 0; i < 8; i++)
+ {
+ if (mNeighbors[i])
+ {
+ mNeighbors[i]->disconnectNeighbor(this);
+ mNeighbors[i] = NULL;
+ }
+ }
+}
+
+
+
+const LLVector3d &LLSurface::getOriginGlobal() const
+{
+ return mOriginGlobal;
+}
+
+LLVector3 LLSurface::getOriginAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mOriginGlobal);
+}
+
+F32 LLSurface::getMetersPerGrid() const
+{
+ return mMetersPerGrid;
+}
+
+S32 LLSurface::getGridsPerEdge() const
+{
+ return mGridsPerEdge;
+}
+
+S32 LLSurface::getPatchesPerEdge() const
+{
+ return mPatchesPerEdge;
+}
+
+S32 LLSurface::getGridsPerPatchEdge() const
+{
+ return mGridsPerPatchEdge;
+}
+
+void LLSurface::moveZ(const S32 x, const S32 y, const F32 delta)
+{
+ llassert(x >= 0);
+ llassert(y >= 0);
+ llassert(x < mGridsPerEdge);
+ llassert(y < mGridsPerEdge);
+ mSurfaceZ[x + y*mGridsPerEdge] += delta;
+}
+
+
+void LLSurface::updatePatchVisibilities(LLAgent &agent)
+{
+ LLVector3 pos_region = mRegionp->getPosRegionFromGlobal(gAgent.getCameraPositionGlobal());
+
+ LLSurfacePatch *patchp;
+
+ mVisiblePatchCount = 0;
+ for (S32 i=0; i<mNumberOfPatches; i++)
+ {
+ patchp = mPatchList + i;
+
+ patchp->updateVisibility();
+ if (patchp->getVisible())
+ {
+ mVisiblePatchCount++;
+ patchp->updateCameraDistanceRegion(pos_region);
+ }
+ }
+}
+
+
+
+BOOL LLSurface::idleUpdate()
+{
+ if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
+ {
+ return TRUE;
+ }
+
+ // Perform idle time update of non-critical stuff.
+ // In this case, texture and normal updates.
+ LLTimer update_timer;
+ LLSurfacePatch *patchp = NULL;
+
+ // If the Z height data has changed, we need to rebuild our
+ // property line vertex arrays.
+ if (mDirtyPatchList.count() > 0)
+ {
+ getRegion()->dirtyHeights();
+ }
+
+ S32 i = 0;
+ while (i < mDirtyPatchList.count())
+ {
+ patchp = mDirtyPatchList[i];
+ patchp->updateNormals();
+ patchp->updateVerticalStats();
+
+ if ((update_timer.getElapsedTimeF32() < 0.05f) && patchp->updateTexture())
+ {
+ patchp->clearDirty();
+ mDirtyPatchList.remove(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+ return TRUE;
+}
+
+// TODO -- move this to LLViewerRegion class
+void LLSurface::renderSurfaceBounds()
+{
+ // Shows the edge of the surface, so that visibility across regions can be seen
+ LLVector3 origin_agent = getOriginAgent();
+
+ glPushMatrix();
+ LLGLSNoTexture no_texture;
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+ glTranslatef(origin_agent.mV[VX] + (region_width_meters * 0.005f),
+ origin_agent.mV[VY] + (region_width_meters * 0.005f), 0.f);
+
+ glColor4ub(0, 128, 0, 64);
+
+ F32 length = region_width_meters * 0.995f;
+ F32 height = length/8.0f;
+
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+ glEnd();
+
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+
+ glEnd();
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+ glEnd();
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+ glEnd();
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+
+ glPopMatrix();
+}
+
+
+void LLSurface::decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch)
+{
+
+ LLPatchHeader ph;
+ S32 j, i;
+ S32 patch[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+ LLSurfacePatch *patchp;
+
+ init_patch_decompressor(gopp->patch_size);
+ gopp->stride = mGridsPerEdge;
+ set_group_of_patch_header(gopp);
+
+ while (1)
+ {
+ decode_patch_header(bitpack, &ph);
+ if (ph.quant_wbits == END_OF_PATCHES)
+ {
+ break;
+ }
+
+ i = ph.patchids >> 5;
+ j = ph.patchids & 0x1F;
+
+ if ((i >= mPatchesPerEdge) || (j >= mPatchesPerEdge))
+ {
+ llwarns << "Received invalid terrain packet - patch header patch ID incorrect!"
+ << " patches per edge " << mPatchesPerEdge
+ << " i " << i
+ << " j " << j
+ << " dc_offset " << ph.dc_offset
+ << " range " << (S32)ph.range
+ << " quant_wbits " << (S32)ph.quant_wbits
+ << " patchids " << (S32)ph.patchids
+ << llendl;
+ bad_network_handler();
+ return;
+ }
+
+ patchp = &mPatchList[j*mPatchesPerEdge + i];
+
+
+ decode_patch(bitpack, patch);
+ decompress_patch(patchp->getDataZ(), patch, &ph);
+
+ // Update edges for neighbors. Need to guarantee that this gets done before we generate vertical stats.
+ patchp->updateNorthEdge();
+ patchp->updateEastEdge();
+ if (patchp->getNeighborPatch(WEST))
+ {
+ patchp->getNeighborPatch(WEST)->updateEastEdge();
+ }
+ if (patchp->getNeighborPatch(SOUTHWEST))
+ {
+ patchp->getNeighborPatch(SOUTHWEST)->updateEastEdge();
+ patchp->getNeighborPatch(SOUTHWEST)->updateNorthEdge();
+ }
+ if (patchp->getNeighborPatch(SOUTH))
+ {
+ patchp->getNeighborPatch(SOUTH)->updateNorthEdge();
+ }
+
+ // Dirty patch statistics, and flag that the patch has data.
+ patchp->dirtyZ();
+ patchp->setHasReceivedData();
+ }
+}
+
+
+// Retrurns TRUE if "position" is within the bounds of surface.
+// "position" is region-local
+BOOL LLSurface::containsPosition(const LLVector3 &position)
+{
+ if (position.mV[VX] < 0.0f || position.mV[VX] > mMetersPerEdge ||
+ position.mV[VY] < 0.0f || position.mV[VY] > mMetersPerEdge)
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+F32 LLSurface::resolveHeightRegion(const F32 x, const F32 y) const
+{
+ F32 height = 0.0f;
+ F32 oometerspergrid = 1.f/mMetersPerGrid;
+
+ // Check to see if v is actually above surface
+ // We use (mGridsPerEdge-1) below rather than (mGridsPerEdge)
+ // becuase of the east and north buffers
+
+ if (x >= 0.f &&
+ x <= mMetersPerEdge &&
+ y >= 0.f &&
+ y <= mMetersPerEdge)
+ {
+ const S32 left = llfloor(x * oometerspergrid);
+ const S32 bottom = llfloor(y * oometerspergrid);
+
+ // Don't walk off the edge of the array!
+ const S32 right = ( left+1 < (S32)mGridsPerEdge-1 ? left+1 : left );
+ const S32 top = ( bottom+1 < (S32)mGridsPerEdge-1 ? bottom+1 : bottom );
+
+ // Figure out if v is in first or second triangle of the square
+ // and calculate the slopes accordingly
+ // | |
+ // -(i,j+1)---(i+1,j+1)--
+ // | 1 / | ^
+ // | / 2 | |
+ // | / | j
+ // --(i,j)----(i+1,j)--
+ // | |
+ //
+ // i ->
+ // where N = mGridsPerEdge
+
+ const F32 left_bottom = getZ( left, bottom );
+ const F32 right_bottom = getZ( right, bottom );
+ const F32 left_top = getZ( left, top );
+ const F32 right_top = getZ( right, top );
+
+ // dx and dy are incremental steps from (mSurface + k)
+ F32 dx = x - left * mMetersPerGrid;
+ F32 dy = y - bottom * mMetersPerGrid;
+
+ if (dy > dx)
+ {
+ // triangle 1
+ dy *= left_top - left_bottom;
+ dx *= right_top - left_top;
+ }
+ else
+ {
+ // triangle 2
+ dx *= right_bottom - left_bottom;
+ dy *= right_top - right_bottom;
+ }
+ height = left_bottom + (dx + dy) * oometerspergrid;
+ }
+ return height;
+}
+
+
+F32 LLSurface::resolveHeightGlobal(const LLVector3d& v) const
+{
+ if (!mRegionp)
+ {
+ return 0.f;
+ }
+
+ LLVector3 pos_region = mRegionp->getPosRegionFromGlobal(v);
+
+ return resolveHeightRegion(pos_region);
+}
+
+
+LLVector3 LLSurface::resolveNormalGlobal(const LLVector3d& pos_global) const
+{
+ if (!mSurfaceZ)
+ {
+ // Hmm. Uninitialized surface!
+ return LLVector3::z_axis;
+ }
+ //
+ // Returns the vector normal to a surface at location specified by vector v
+ //
+ F32 oometerspergrid = 1.f/mMetersPerGrid;
+ LLVector3 normal;
+ F32 dzx, dzy;
+
+ if (pos_global.mdV[VX] >= mOriginGlobal.mdV[VX] &&
+ pos_global.mdV[VX] < mOriginGlobal.mdV[VX] + mMetersPerEdge &&
+ pos_global.mdV[VY] >= mOriginGlobal.mdV[VY] &&
+ pos_global.mdV[VY] < mOriginGlobal.mdV[VY] + mMetersPerEdge)
+ {
+ U32 i, j, k;
+ F32 dx, dy;
+ i = (U32) ((pos_global.mdV[VX] - mOriginGlobal.mdV[VX]) * oometerspergrid);
+ j = (U32) ((pos_global.mdV[VY] - mOriginGlobal.mdV[VY]) * oometerspergrid );
+ k = i + j*mGridsPerEdge;
+
+ // Figure out if v is in first or second triangle of the square
+ // and calculate the slopes accordingly
+ // | |
+ // -(k+N)---(k+1+N)--
+ // | 1 / | ^
+ // | / 2 | |
+ // | / | j
+ // --(k)----(k+1)--
+ // | |
+ //
+ // i ->
+ // where N = mGridsPerEdge
+
+ // dx and dy are incremental steps from (mSurface + k)
+ dx = (F32)(pos_global.mdV[VX] - i*mMetersPerGrid - mOriginGlobal.mdV[VX]);
+ dy = (F32)(pos_global.mdV[VY] - j*mMetersPerGrid - mOriginGlobal.mdV[VY]);
+ if (dy > dx)
+ { // triangle 1
+ dzx = *(mSurfaceZ + k + 1 + mGridsPerEdge) - *(mSurfaceZ + k + mGridsPerEdge);
+ dzy = *(mSurfaceZ + k) - *(mSurfaceZ + k + mGridsPerEdge);
+ normal.setVec(-dzx,dzy,1);
+ }
+ else
+ { // triangle 2
+ dzx = *(mSurfaceZ + k) - *(mSurfaceZ + k + 1);
+ dzy = *(mSurfaceZ + k + 1 + mGridsPerEdge) - *(mSurfaceZ + k + 1);
+ normal.setVec(dzx,-dzy,1);
+ }
+ }
+ normal.normVec();
+ return normal;
+
+
+}
+
+LLSurfacePatch *LLSurface::resolvePatchRegion(const F32 x, const F32 y) const
+{
+// x and y should be region-local coordinates.
+// If x and y are outside of the surface, then the returned
+// index will be for the nearest boundary patch.
+//
+// 12 | 13| 14| 15
+// | | |
+// +---+---+---+---+
+// | 12| 13| 14| 15|
+// ----+---+---+---+---+-----
+// 8 | 8 | 9 | 10| 11| 11
+// ----+---+---+---+---+-----
+// 4 | 4 | 5 | 6 | 7 | 7
+// ----+---+---+---+---+-----
+// | 0 | 1 | 2 | 3 |
+// +---+---+---+---+
+// | | |
+// 0 | 1 | 2 | 3
+//
+
+// When x and y are not region-local do the following first
+
+ S32 i, j;
+ if (x < 0.0f)
+ {
+ i = 0;
+ }
+ else if (x >= mMetersPerEdge)
+ {
+ i = mPatchesPerEdge - 1;
+ }
+ else
+ {
+ i = (U32) (x / (mMetersPerGrid * mGridsPerPatchEdge));
+ }
+
+ if (y < 0.0f)
+ {
+ j = 0;
+ }
+ else if (y >= mMetersPerEdge)
+ {
+ j = mPatchesPerEdge - 1;
+ }
+ else
+ {
+ j = (U32) (y / (mMetersPerGrid * mGridsPerPatchEdge));
+ }
+
+ // *NOTE: Super paranoia code follows.
+ S32 index = i + j * mPatchesPerEdge;
+ if((index < 0) || (index >= mNumberOfPatches))
+ {
+ if(0 == mNumberOfPatches)
+ {
+ llwarns << "No patches for current region!" << llendl;
+ return NULL;
+ }
+ S32 old_index = index;
+ index = llclamp(old_index, 0, (mNumberOfPatches - 1));
+ llwarns << "Clamping out of range patch index " << old_index
+ << " to " << index << llendl;
+ }
+ return &(mPatchList[index]);
+}
+
+
+LLSurfacePatch *LLSurface::resolvePatchRegion(const LLVector3 &pos_region) const
+{
+ return resolvePatchRegion(pos_region.mV[VX], pos_region.mV[VY]);
+}
+
+
+LLSurfacePatch *LLSurface::resolvePatchGlobal(const LLVector3d &pos_global) const
+{
+ LLVector3 pos_region = mRegionp->getPosRegionFromGlobal(pos_global);
+ return resolvePatchRegion(pos_region);
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLSurface &S)
+{
+ s << "{ \n";
+ s << " mGridsPerEdge = " << S.mGridsPerEdge - 1 << " + 1\n";
+ s << " mGridsPerPatchEdge = " << S.mGridsPerPatchEdge << "\n";
+ s << " mPatchesPerEdge = " << S.mPatchesPerEdge << "\n";
+ s << " mOriginGlobal = " << S.mOriginGlobal << "\n";
+ s << " mMetersPerGrid = " << S.mMetersPerGrid << "\n";
+ s << " mVisiblePatchCount = " << S.mVisiblePatchCount << "\n";
+ s << "}";
+ return s;
+}
+
+
+// ---------------- LLSurface:: Protected ----------------
+
+void LLSurface::createPatchData()
+{
+ // Assumes mGridsPerEdge, mGridsPerPatchEdge, and mPatchesPerEdge have been properly set
+ // TODO -- check for create() called when surface is not empty
+ S32 i, j;
+ LLSurfacePatch *patchp;
+
+ // Allocate memory
+ mPatchList = new LLSurfacePatch[mNumberOfPatches];
+
+ // One of each for each camera
+ mVisiblePatchCount = mNumberOfPatches;
+
+ for (j=0; j<mPatchesPerEdge; j++)
+ {
+ for (i=0; i<mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, j);
+ patchp->setSurface(this);
+ }
+ }
+
+ for (j=0; j<mPatchesPerEdge; j++)
+ {
+ for (i=0; i<mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, j);
+ patchp->mHasReceivedData = FALSE;
+ patchp->mSTexUpdate = TRUE;
+
+ S32 data_offset = i * mGridsPerPatchEdge + j * mGridsPerPatchEdge * mGridsPerEdge;
+
+ patchp->setDataZ(mSurfaceZ + data_offset);
+ patchp->setDataNorm(mNorm + data_offset);
+
+
+ // We make each patch point to its neighbors so we can do resolution checking
+ // when butting up different resolutions. Patches that don't have neighbors
+ // somewhere will point to NULL on that side.
+ if (i < mPatchesPerEdge-1)
+ {
+ patchp->setNeighborPatch(EAST,getPatch(i+1, j));
+ }
+ else
+ {
+ patchp->setNeighborPatch(EAST, NULL);
+ }
+
+ if (j < mPatchesPerEdge-1)
+ {
+ patchp->setNeighborPatch(NORTH, getPatch(i, j+1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(NORTH, NULL);
+ }
+
+ if (i > 0)
+ {
+ patchp->setNeighborPatch(WEST, getPatch(i - 1, j));
+ }
+ else
+ {
+ patchp->setNeighborPatch(WEST, NULL);
+ }
+
+ if (j > 0)
+ {
+ patchp->setNeighborPatch(SOUTH, getPatch(i, j-1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(SOUTH, NULL);
+ }
+
+ if (i < (mPatchesPerEdge-1) && j < (mPatchesPerEdge-1))
+ {
+ patchp->setNeighborPatch(NORTHEAST, getPatch(i + 1, j + 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(NORTHEAST, NULL);
+ }
+
+ if (i > 0 && j < (mPatchesPerEdge-1))
+ {
+ patchp->setNeighborPatch(NORTHWEST, getPatch(i - 1, j + 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(NORTHWEST, NULL);
+ }
+
+ if (i > 0 && j > 0)
+ {
+ patchp->setNeighborPatch(SOUTHWEST, getPatch(i - 1, j - 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(SOUTHWEST, NULL);
+ }
+
+ if (i < (mPatchesPerEdge-1) && j > 0)
+ {
+ patchp->setNeighborPatch(SOUTHEAST, getPatch(i + 1, j - 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(SOUTHEAST, NULL);
+ }
+
+ LLVector3d origin_global;
+ origin_global.mdV[0] = mOriginGlobal.mdV[0] + i * mMetersPerGrid * mGridsPerPatchEdge;
+ origin_global.mdV[1] = mOriginGlobal.mdV[0] + j * mMetersPerGrid * mGridsPerPatchEdge;
+ origin_global.mdV[2] = 0.f;
+ patchp->setOriginGlobal(origin_global);
+ }
+ }
+}
+
+
+void LLSurface::destroyPatchData()
+{
+ // Delete all of the cached patch data for these patches.
+
+ delete [] mPatchList;
+ mPatchList = NULL;
+ mVisiblePatchCount = 0;
+}
+
+
+void LLSurface::setTextureSize(const S32 texture_size)
+{
+ sTextureSize = texture_size;
+}
+
+
+U32 LLSurface::getRenderLevel(const U32 render_stride) const
+{
+ return mPVArray.mRenderLevelp[render_stride];
+}
+
+
+U32 LLSurface::getRenderStride(const U32 render_level) const
+{
+ return mPVArray.mRenderStridep[render_level];
+}
+
+
+LLSurfacePatch *LLSurface::getPatch(const S32 x, const S32 y) const
+{
+ if ((x < 0) || (x >= mPatchesPerEdge))
+ {
+ llerrs << "Asking for patch out of bounds" << llendl;
+ return NULL;
+ }
+ if ((y < 0) || (y >= mPatchesPerEdge))
+ {
+ llerrs << "Asking for patch out of bounds" << llendl;
+ return NULL;
+ }
+
+ return mPatchList + x + y*mPatchesPerEdge;
+}
+
+
+void LLSurface::dirtyAllPatches()
+{
+ S32 i;
+ for (i = 0; i < mNumberOfPatches; i++)
+ {
+ mPatchList[i].dirtyZ();
+ }
+}
+
+void LLSurface::dirtySurfacePatch(LLSurfacePatch *patchp)
+{
+ // Put surface patch on dirty surface patch list
+ if (-1 == mDirtyPatchList.find(patchp))
+ {
+ mDirtyPatchList.put(patchp);
+ }
+}
+
+
+void LLSurface::setWaterHeight(F32 height)
+{
+ if (!mWaterObjp.isNull())
+ {
+ LLVector3 water_pos_region = mWaterObjp->getPositionRegion();
+ water_pos_region.mV[VZ] = height;
+ mWaterObjp->setPositionRegion(water_pos_region);
+ }
+ else
+ {
+ llwarns << "LLSurface::setWaterHeight with no water object!" << llendl;
+ }
+}
+
+F32 LLSurface::getWaterHeight() const
+{
+ if (!mWaterObjp.isNull())
+ {
+ // we have a water object, the usual case
+ return mWaterObjp->getPositionRegion().mV[VZ];
+ }
+ else
+ {
+ return DEFAULT_WATER_HEIGHT;
+ }
+}
+
+
+BOOL LLSurface::generateWaterTexture(const F32 x, const F32 y,
+ const F32 width, const F32 height)
+{
+ if (!getWaterTexture())
+ {
+ return FALSE;
+ }
+
+ S32 tex_width = mWaterTexturep->getWidth();
+ S32 tex_height = mWaterTexturep->getHeight();
+ S32 tex_comps = mWaterTexturep->getComponents();
+ S32 tex_stride = tex_width * tex_comps;
+ LLPointer<LLImageRaw> raw = new LLImageRaw(tex_width, tex_height, tex_comps);
+ U8 *rawp = raw->getData();
+
+ F32 scale = 256.f * getMetersPerGrid() / (F32)tex_width;
+ F32 scale_inv = 1.f / scale;
+
+ S32 x_begin, y_begin, x_end, y_end;
+
+ x_begin = llround(x * scale_inv);
+ y_begin = llround(y * scale_inv);
+ x_end = llround((x + width) * scale_inv);
+ y_end = llround((y + width) * scale_inv);
+
+ if (x_end > tex_width)
+ {
+ x_end = tex_width;
+ }
+ if (y_end > tex_width)
+ {
+ y_end = tex_width;
+ }
+
+ LLVector3d origin_global = from_region_handle(getRegion()->getHandle());
+
+ // OK, for now, just have the composition value equal the height at the point.
+ LLVector3 location;
+ LLColor4U coloru;
+
+ const F32 WATER_HEIGHT = getWaterHeight();
+
+ S32 i, j, offset;
+ for (j = y_begin; j < y_end; j++)
+ {
+ for (i = x_begin; i < x_end; i++)
+ {
+ //F32 nv[2];
+ //nv[0] = i/256.f;
+ //nv[1] = j/256.f;
+ // const S32 modulation = noise2(nv)*40;
+ offset = j*tex_stride + i*tex_comps;
+ location.mV[VX] = i*scale;
+ location.mV[VY] = j*scale;
+
+ // Sample multiple points
+ const F32 height = resolveHeightRegion(location);
+
+ if (height > WATER_HEIGHT)
+ {
+ // Above water...
+ coloru = MAX_WATER_COLOR;
+ coloru.mV[3] = ABOVE_WATERLINE_ALPHA;
+ *(rawp + offset++) = coloru.mV[0];
+ *(rawp + offset++) = coloru.mV[1];
+ *(rawp + offset++) = coloru.mV[2];
+ *(rawp + offset++) = coloru.mV[3];
+ }
+ else
+ {
+ // Want non-linear curve for transparency gradient
+ coloru = MAX_WATER_COLOR;
+ const F32 frac = 1.f - 2.f/(2.f - (height - WATER_HEIGHT));
+ S32 alpha = 64 + llround((255-64)*frac);
+
+ alpha = llmin(llround((F32)MAX_WATER_COLOR.mV[3]), alpha);
+ alpha = llmax(64, alpha);
+
+ coloru.mV[3] = alpha;
+ *(rawp + offset++) = coloru.mV[0];
+ *(rawp + offset++) = coloru.mV[1];
+ *(rawp + offset++) = coloru.mV[2];
+ *(rawp + offset++) = coloru.mV[3];
+ }
+ }
+ }
+
+ mWaterTexturep->setSubImage(raw, x_begin, y_begin, x_end - x_begin, y_end - y_begin);
+ return TRUE;
+}
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
new file mode 100644
index 0000000000..7698c272ab
--- /dev/null
+++ b/indra/newview/llsurface.h
@@ -0,0 +1,249 @@
+/**
+ * @file llsurface.h
+ * @brief Description of LLSurface class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSURFACE_H
+#define LL_LLSURFACE_H
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+
+#include "v4coloru.h"
+#include "v4color.h"
+
+#include "llvowater.h"
+#include "llpatchvertexarray.h"
+#include "lldarray.h"
+#include "llviewerimage.h"
+
+class LLTimer;
+class LLUUID;
+class LLAgent;
+class LLStat;
+
+static const U8 NO_EDGE = 0x00;
+static const U8 EAST_EDGE = 0x01;
+static const U8 NORTH_EDGE = 0x02;
+static const U8 WEST_EDGE = 0x04;
+static const U8 SOUTH_EDGE = 0x08;
+
+static const S32 ONE_MORE_THAN_NEIGHBOR = 1;
+static const S32 EQUAL_TO_NEIGHBOR = 0;
+static const S32 ONE_LESS_THAN_NEIGHBOR = -1;
+
+const S32 ABOVE_WATERLINE_ALPHA = 32; // The alpha of water when the land elevation is above the waterline.
+
+class LLViewerRegion;
+class LLSurfacePatch;
+class LLBitPack;
+class LLGroupHeader;
+
+class LLSurface
+{
+public:
+ LLSurface(U32 type, LLViewerRegion *regionp = NULL);
+ virtual ~LLSurface();
+
+ static void initClasses(); // Do class initialization for LLSurface and its child classes.
+
+ void create(const S32 surface_grid_width,
+ const S32 surface_patch_width,
+ const LLVector3d &origin_global,
+ const F32 width); // Allocates and initializes surface
+
+ void setRegion(LLViewerRegion *regionp);
+
+ void setOriginGlobal(const LLVector3d &origin_global);
+
+ void connectNeighbor(LLSurface *neighborp, U32 direction);
+ void disconnectNeighbor(LLSurface *neighborp);
+ void disconnectAllNeighbors();
+
+ virtual void decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch);
+ virtual void updatePatchVisibilities(LLAgent &agent);
+
+ inline F32 getZ(const U32 k) const { return mSurfaceZ[k]; }
+ inline F32 getZ(const S32 i, const S32 j) const { return mSurfaceZ[i + j*mGridsPerEdge]; }
+
+ LLVector3 getOriginAgent() const;
+ const LLVector3d &getOriginGlobal() const;
+ F32 getMetersPerGrid() const;
+ S32 getGridsPerEdge() const;
+ S32 getPatchesPerEdge() const;
+ S32 getGridsPerPatchEdge() const;
+ U32 getRenderStride(const U32 render_level) const;
+ U32 getRenderLevel(const U32 render_stride) const;
+
+ // Returns the height of the surface immediately above (or below) location,
+ // or if location is not above surface returns zero.
+ F32 resolveHeightRegion(const F32 x, const F32 y) const;
+ F32 resolveHeightRegion(const LLVector3 &location) const
+ { return resolveHeightRegion( location.mV[VX], location.mV[VY] ); }
+ F32 resolveHeightGlobal(const LLVector3d &position_global) const;
+ LLVector3 resolveNormalGlobal(const LLVector3d& v) const; // Returns normal to surface
+
+ LLSurfacePatch *resolvePatchRegion(const F32 x, const F32 y) const;
+ LLSurfacePatch *resolvePatchRegion(const LLVector3 &position_region) const;
+ LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
+
+ // Update methods (called during idle, normally)
+ BOOL idleUpdate();
+
+ void renderSurfaceBounds();
+
+ BOOL containsPosition(const LLVector3 &position);
+
+ void moveZ(const S32 x, const S32 y, const F32 delta);
+
+ LLViewerRegion *getRegion() const { return mRegionp; }
+
+ F32 getMinZ() const { return mMinZ; }
+ F32 getMaxZ() const { return mMaxZ; }
+
+ void setWaterHeight(F32 height);
+ F32 getWaterHeight() const;
+
+ LLViewerImage *getSTexture();
+ LLViewerImage *getWaterTexture();
+ BOOL hasZData() const { return mHasZData; }
+
+ void dirtyAllPatches(); // Use this to dirty all patches when changing terrain parameters
+
+ void dirtySurfacePatch(LLSurfacePatch *patchp);
+ LLVOWater *getWaterObj() { return mWaterObjp; }
+
+ static void setTextureSize(const S32 texture_size);
+
+ friend class LLSurfacePatch;
+ friend std::ostream& operator<<(std::ostream &s, const LLSurface &S);
+public:
+ // Number of grid points on one side of a region, including +1 buffer for
+ // north and east edge.
+ S32 mGridsPerEdge;
+
+ F32 mOOGridsPerEdge; // Inverse of grids per edge
+
+ S32 mPatchesPerEdge; // Number of patches on one side of a region
+ S32 mNumberOfPatches; // Total number of patches
+
+
+ // Each surface points at 8 neighbors (or NULL)
+ // +---+---+---+
+ // |NW | N | NE|
+ // +---+---+---+
+ // | W | 0 | E |
+ // +---+---+---+
+ // |SW | S | SE|
+ // +---+---+---+
+ LLSurface *mNeighbors[8]; // Adjacent patches
+
+ U32 mType; // Useful for identifying derived classes
+
+ F32 mDetailTextureScale; // Number of times to repeat detail texture across this surface
+
+ static F32 sTextureUpdateTime;
+ static S32 sTexelsUpdated;
+ static LLStat sTexelsUpdatedPerSecStat;
+
+protected:
+ void createSTexture();
+ void createWaterTexture();
+ void initTextures();
+ void initWater();
+
+
+ void createPatchData(); // Allocates memory for patches.
+ void destroyPatchData(); // Deallocates memory for patches.
+
+ BOOL generateWaterTexture(const F32 x, const F32 y,
+ const F32 width, const F32 height); // Generate texture from composition values.
+
+ //F32 updateTexture(LLSurfacePatch *ppatch);
+
+ LLSurfacePatch *getPatch(const S32 x, const S32 y) const;
+
+protected:
+ LLVector3d mOriginGlobal; // In absolute frame
+ LLSurfacePatch *mPatchList; // Array of all patches
+
+ // Array of grid data, mGridsPerEdge * mGridsPerEdge
+ F32 *mSurfaceZ;
+
+ // Array of grid normals, mGridsPerEdge * mGridsPerEdge
+ LLVector3 *mNorm;
+
+ LLDynamicArray<LLSurfacePatch *> mDirtyPatchList;
+
+
+ // The textures should never be directly initialized - use the setter methods!
+ LLPointer<LLViewerImage> mSTexturep; // Texture for surface
+ LLPointer<LLViewerImage> mWaterTexturep; // Water texture
+
+ LLPointer<LLVOWater> mWaterObjp;
+
+ // When we want multiple cameras we'll need one of each these for each camera
+ S32 mVisiblePatchCount;
+
+ U32 mGridsPerPatchEdge; // Number of grid points on a side of a patch
+ F32 mMetersPerGrid; // Converts (i,j) indecies to distance
+ F32 mMetersPerEdge; // = mMetersPerGrid * (mGridsPerEdge-1)
+
+ F32 mSurfaceTexScale; // Scale factors for automatic tex coord generation
+ F32 mDetailTexScale;
+
+ LLPatchVertexArray mPVArray;
+
+ BOOL mHasZData; // We've received any patch data for this surface.
+ F32 mMinZ; // min z for this region (during the session)
+ F32 mMaxZ; // max z for this region (during the session)
+
+ S32 mSurfacePatchUpdateCount; // Number of frames since last update.
+
+private:
+ LLViewerRegion *mRegionp; // Patch whose coordinate system this surface is using.
+ static S32 sTextureSize; // Size of the surface texture
+};
+
+
+
+// . __.
+// Z /|\ /| Y North
+// | /
+// | / |<----------------- mGridsPerSurfaceEdge --------------->|
+// | / __________________________________________________________
+// |/______\ X /_______________________________________________________ /
+// / / / / / / / /M*M-2 /M*M-1 / /
+// /______/______/______/______/______/______/______/______/ /
+// / / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ /
+// / / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ /
+// West / / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ / East
+// /... / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ /
+// _. / 2M / / / / / / / / /
+// /| /______/______/______/______/______/______/______/______/ /
+// / / M / M+1 / M+2 / ... / / / / 2M-1 / /
+// j /______/______/______/______/______/______/______/______/ /
+// / 0 / 1 / 2 / ... / / / / M-1 / /
+// /______/______/______/______/______/______/______/______/_/
+// South |<-L->|
+// i -->
+//
+// where M = mSurfPatchWidth
+// and L = mPatchGridWidth
+//
+// Notice that mGridsPerSurfaceEdge = a power of two + 1
+// This provides a buffer on the east and north edges that will allow us to
+// fill the cracks between adjacent surfaces when rendering.
+#endif
diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp
new file mode 100644
index 0000000000..ac0add8ae3
--- /dev/null
+++ b/indra/newview/llsurfacepatch.cpp
@@ -0,0 +1,974 @@
+/**
+ * @file llsurfacepatch.cpp
+ * @brief LLSurfacePatch class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsurfacepatch.h"
+#include "llpatchvertexarray.h"
+#include "llviewerobjectlist.h"
+#include "llvosurfacepatch.h"
+#include "llsurface.h"
+#include "pipeline.h"
+#include "llagent.h"
+#include "timing.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+
+// For getting composition values
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "lldrawpool.h"
+#include "noise.h"
+
+extern U64 gFrameTime;
+extern LLPipeline gPipeline;
+
+LLSurfacePatch::LLSurfacePatch() :
+ mDataZ(NULL),
+ mVObjp(NULL),
+ mLastUpdateTime(0),
+ mSurfacep(NULL)
+{
+ // This flag is used to communicate between adjacent surfaces and is set
+ // to non-zero values by higher classes.
+ mConnectedEdge = NO_EDGE;
+ mCenterRegion = LLVector3(0.f, 0.f, 0.f);
+ mOriginRegion = LLVector3(0.f, 0.f, 0.f);
+ mHasReceivedData = FALSE;
+ mMinZ = 0.0f;
+ mMaxZ = 0.0f;
+ mMeanZ = 0.0f;
+ mMinComposition = 0.f;
+ mMeanComposition = 0.f;
+ mMaxComposition = 0.f;
+ mRadius = 0.f;
+ mDirty = FALSE;
+ mDirtyZStats = TRUE;
+ mHeightsGenerated = FALSE;
+
+ S32 i;
+ for (i = 0; i < 8; i++)
+ {
+ setNeighborPatch(i, NULL);
+ }
+ for (i = 0; i < 9; i++)
+ {
+ mNormalsInvalid[i] = TRUE;
+ }
+}
+
+
+LLSurfacePatch::~LLSurfacePatch()
+{
+ mVObjp = NULL;
+}
+
+
+void LLSurfacePatch::dirty()
+{
+ // These are outside of the loop in case we're still waiting for a dirty from the
+ // texture being updated...
+ if (mVObjp)
+ {
+ mVObjp->dirtyGeom();
+ }
+ else
+ {
+ llwarns << "No viewer object for this surface patch!" << llendl;
+ }
+
+ mDirtyZStats = TRUE;
+ mHeightsGenerated = FALSE;
+
+ if (!mDirty)
+ {
+ mDirty = TRUE;
+ mSurfacep->dirtySurfacePatch(this);
+ }
+}
+
+
+void LLSurfacePatch::setSurface(LLSurface *surfacep)
+{
+ mSurfacep = surfacep;
+ if (mVObjp == (LLVOSurfacePatch *)NULL)
+ {
+ llassert(mSurfacep->mType == 'l');
+
+ mVObjp = (LLVOSurfacePatch *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_SURFACE_PATCH, mSurfacep->getRegion());
+ mVObjp->setPatch(this);
+ mVObjp->setPositionRegion(mCenterRegion);
+ gPipeline.addObject(mVObjp);
+ }
+}
+
+void LLSurfacePatch::disconnectNeighbor(LLSurface *surfacep)
+{
+ U32 i;
+ for (i = 0; i < 8; i++)
+ {
+ if (getNeighborPatch(i))
+ {
+ if (getNeighborPatch(i)->mSurfacep == surfacep)
+ {
+ setNeighborPatch(i, NULL);
+ mNormalsInvalid[i] = TRUE;
+ }
+ }
+ }
+
+ // Clean up connected edges
+ if (getNeighborPatch(EAST))
+ {
+ if (getNeighborPatch(EAST)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~EAST_EDGE;
+ }
+ }
+ if (getNeighborPatch(NORTH))
+ {
+ if (getNeighborPatch(NORTH)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~NORTH_EDGE;
+ }
+ }
+ if (getNeighborPatch(WEST))
+ {
+ if (getNeighborPatch(WEST)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~WEST_EDGE;
+ }
+ }
+ if (getNeighborPatch(SOUTH))
+ {
+ if (getNeighborPatch(SOUTH)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~SOUTH_EDGE;
+ }
+ }
+}
+
+LLVector3 LLSurfacePatch::getPointAgent(const U32 x, const U32 y) const
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ U32 point_offset = x + y*surface_stride;
+ LLVector3 pos;
+ pos = getOriginAgent();
+ pos.mV[VX] += x * mSurfacep->getMetersPerGrid();
+ pos.mV[VY] += y * mSurfacep->getMetersPerGrid();
+ pos.mV[VZ] = *(mDataZ + point_offset);
+ return pos;
+}
+
+LLVector2 LLSurfacePatch::getTexCoords(const U32 x, const U32 y) const
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ U32 point_offset = x + y*surface_stride;
+ LLVector3 pos, rel_pos;
+ pos = getOriginAgent();
+ pos.mV[VX] += x * mSurfacep->getMetersPerGrid();
+ pos.mV[VY] += y * mSurfacep->getMetersPerGrid();
+ pos.mV[VZ] = *(mDataZ + point_offset);
+ rel_pos = pos - mSurfacep->getOriginAgent();
+ rel_pos *= 1.f/surface_stride;
+ return LLVector2(rel_pos.mV[VX], rel_pos.mV[VY]);
+}
+
+
+void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 *vertex, LLVector3 *normal,
+ LLVector2 *tex0, LLVector2 *tex1)
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ U32 point_offset = x + y*surface_stride;
+
+ *normal = getNormal(x, y);
+
+ LLVector3 pos_agent = getOriginAgent();
+ pos_agent.mV[VX] += x * mSurfacep->getMetersPerGrid();
+ pos_agent.mV[VY] += y * mSurfacep->getMetersPerGrid();
+ pos_agent.mV[VZ] = *(mDataZ + point_offset);
+ *vertex = pos_agent;
+
+ LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent();
+ LLVector3 tex_pos = rel_pos * (1.f/surface_stride);
+ tex0->mV[0] = tex_pos.mV[0];
+ tex0->mV[1] = tex_pos.mV[1];
+ tex1->mV[0] = mSurfacep->getRegion()->getCompositionXY(llfloor(mOriginRegion.mV[0])+x, llfloor(mOriginRegion.mV[1])+y);
+
+ const F32 xyScale = 4.9215f*7.f; //0.93284f;
+ const F32 xyScaleInv = (1.f / xyScale)*(0.2222222222f);
+
+ F32 vec[3] = {
+ fmod((F32)(mOriginGlobal.mdV[0] + x)*xyScaleInv, 256.f),
+ fmod((F32)(mOriginGlobal.mdV[1] + y)*xyScaleInv, 256.f),
+ 0.f
+ };
+ F32 rand_val = llclamp(noise2(vec)* 0.75f + 0.5f, 0.f, 1.f);
+ tex1->mV[1] = rand_val;
+
+
+}
+
+
+void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
+{
+ U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+
+ const F32 mpg = mSurfacep->getMetersPerGrid() * stride;
+
+ S32 poffsets[2][2][2];
+ poffsets[0][0][0] = x - stride;
+ poffsets[0][0][1] = y - stride;
+
+ poffsets[0][1][0] = x - stride;
+ poffsets[0][1][1] = y + stride;
+
+ poffsets[1][0][0] = x + stride;
+ poffsets[1][0][1] = y - stride;
+
+ poffsets[1][1][0] = x + stride;
+ poffsets[1][1][1] = y + stride;
+
+ const LLSurfacePatch *ppatches[2][2];
+
+ // LLVector3 p1, p2, p3, p4;
+
+ ppatches[0][0] = this;
+ ppatches[0][1] = this;
+ ppatches[1][0] = this;
+ ppatches[1][1] = this;
+
+ U32 i, j;
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 2; j++)
+ {
+ if (poffsets[i][j][0] < 0)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(WEST))
+ {
+ poffsets[i][j][0] = 0;
+ }
+ else
+ {
+ poffsets[i][j][0] += patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(WEST);
+ }
+ }
+ if (poffsets[i][j][1] < 0)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(SOUTH))
+ {
+ poffsets[i][j][1] = 0;
+ }
+ else
+ {
+ poffsets[i][j][1] += patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(SOUTH);
+ }
+ }
+ if (poffsets[i][j][0] >= (S32)patch_width)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(EAST))
+ {
+ poffsets[i][j][0] = patch_width - 1;
+ }
+ else
+ {
+ poffsets[i][j][0] -= patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(EAST);
+ }
+ }
+ if (poffsets[i][j][1] >= (S32)patch_width)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(NORTH))
+ {
+ poffsets[i][j][1] = patch_width - 1;
+ }
+ else
+ {
+ poffsets[i][j][1] -= patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(NORTH);
+ }
+ }
+ }
+ }
+
+ LLVector3 p00(-mpg,-mpg,
+ *(ppatches[0][0]->mDataZ
+ + poffsets[0][0][0]
+ + poffsets[0][0][1]*surface_stride));
+ LLVector3 p01(-mpg,+mpg,
+ *(ppatches[0][1]->mDataZ
+ + poffsets[0][1][0]
+ + poffsets[0][1][1]*surface_stride));
+ LLVector3 p10(+mpg,-mpg,
+ *(ppatches[1][0]->mDataZ
+ + poffsets[1][0][0]
+ + poffsets[1][0][1]*surface_stride));
+ LLVector3 p11(+mpg,+mpg,
+ *(ppatches[1][1]->mDataZ
+ + poffsets[1][1][0]
+ + poffsets[1][1][1]*surface_stride));
+
+ LLVector3 c1 = p11 - p00;
+ LLVector3 c2 = p01 - p10;
+
+ LLVector3 normal = c1;
+ normal %= c2;
+ normal.normVec();
+
+ *(mDataNorm + surface_stride * y + x) = normal;
+}
+
+const LLVector3 &LLSurfacePatch::getNormal(const U32 x, const U32 y) const
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ return *(mDataNorm + surface_stride * y + x);
+}
+
+
+void LLSurfacePatch::updateCameraDistanceRegion(const LLVector3 &pos_region)
+{
+ LLVector3 dv = pos_region;
+ dv -= mCenterRegion;
+ mVisInfo.mDistance = llmax(0.f, (F32)(dv.magVec() - mRadius));
+}
+
+F32 LLSurfacePatch::getDistance() const
+{
+ return mVisInfo.mDistance;
+}
+
+
+// Called when a patch has changed its height field
+// data.
+void LLSurfacePatch::updateVerticalStats()
+{
+ if (!mDirtyZStats)
+ {
+ return;
+ }
+
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+ F32 meters_per_grid = mSurfacep->getMetersPerGrid();
+
+ U32 i, j, k;
+ F32 z, total;
+
+ z = *(mDataZ);
+
+ mMinZ = z;
+ mMaxZ = z;
+
+ k = 0;
+ total = 0.0f;
+
+ // Iterate to +1 because we need to do the edges correctly.
+ for (j=0; j<(grids_per_patch_edge+1); j++)
+ {
+ for (i=0; i<(grids_per_patch_edge+1); i++)
+ {
+ z = *(mDataZ + i + j*grids_per_edge);
+
+ if (z < mMinZ)
+ {
+ mMinZ = z;
+ }
+ if (z > mMaxZ)
+ {
+ mMaxZ = z;
+ }
+ total += z;
+ k++;
+ }
+ }
+ mMeanZ = total / (F32) k;
+ mCenterRegion.mV[VZ] = 0.5f * (mMinZ + mMaxZ);
+
+ LLVector3 diam_vec(meters_per_grid*grids_per_patch_edge,
+ meters_per_grid*grids_per_patch_edge,
+ mMaxZ - mMinZ);
+ mRadius = diam_vec.magVec() * 0.5f;
+
+ mSurfacep->mMaxZ = llmax(mMaxZ, mSurfacep->mMaxZ);
+ mSurfacep->mMinZ = llmin(mMinZ, mSurfacep->mMinZ);
+ mSurfacep->mHasZData = TRUE;
+ mSurfacep->getRegion()->calculateCenterGlobal();
+
+ if (mVObjp)
+ {
+ mVObjp->dirtyPatch();
+ }
+ mDirtyZStats = FALSE;
+}
+
+
+void LLSurfacePatch::updateNormals()
+{
+ if (mSurfacep->mType == 'w')
+ {
+ return;
+ }
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+
+ BOOL dirty_patch = FALSE;
+
+ U32 i, j;
+ // update the east edge
+ if (mNormalsInvalid[EAST] || mNormalsInvalid[NORTHEAST] || mNormalsInvalid[SOUTHEAST])
+ {
+ for (j = 0; j <= grids_per_patch_edge; j++)
+ {
+ calcNormal(grids_per_patch_edge, j, 2);
+ calcNormal(grids_per_patch_edge - 1, j, 2);
+ calcNormal(grids_per_patch_edge - 2, j, 2);
+ }
+
+ dirty_patch = TRUE;
+ }
+
+ // update the north edge
+ if (mNormalsInvalid[NORTHEAST] || mNormalsInvalid[NORTH] || mNormalsInvalid[NORTHWEST])
+ {
+ for (i = 0; i <= grids_per_patch_edge; i++)
+ {
+ calcNormal(i, grids_per_patch_edge, 2);
+ calcNormal(i, grids_per_patch_edge - 1, 2);
+ calcNormal(i, grids_per_patch_edge - 2, 2);
+ }
+
+ dirty_patch = TRUE;
+ }
+
+ // update the west edge
+ if (mNormalsInvalid[NORTHWEST] || mNormalsInvalid[WEST] || mNormalsInvalid[SOUTHWEST])
+ {
+ for (j = 0; j < grids_per_patch_edge; j++)
+ {
+ calcNormal(0, j, 2);
+ calcNormal(1, j, 2);
+ }
+ dirty_patch = TRUE;
+ }
+
+ // update the south edge
+ if (mNormalsInvalid[SOUTHWEST] || mNormalsInvalid[SOUTH] || mNormalsInvalid[SOUTHEAST])
+ {
+ for (i = 0; i < grids_per_patch_edge; i++)
+ {
+ calcNormal(i, 0, 2);
+ calcNormal(i, 1, 2);
+ }
+ dirty_patch = TRUE;
+ }
+
+ // Invalidating the northeast corner is different, because depending on what the adjacent neighbors are,
+ // we'll want to do different things.
+ if (mNormalsInvalid[NORTHEAST])
+ {
+ if (!getNeighborPatch(NORTHEAST))
+ {
+ if (!getNeighborPatch(NORTH))
+ {
+ if (!getNeighborPatch(EAST))
+ {
+ // No north or east neighbors. Pull from the diagonal in your own patch.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ else
+ {
+ if (getNeighborPatch(EAST)->getHasReceivedData())
+ {
+ // East, but not north. Pull from your east neighbor's northwest point.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(getNeighborPatch(EAST)->mDataZ + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ else
+ {
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ }
+ }
+ else
+ {
+ // We have a north.
+ if (getNeighborPatch(EAST))
+ {
+ // North and east neighbors, but not northeast.
+ // Pull from diagonal in your own patch.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ else
+ {
+ if (getNeighborPatch(NORTH)->getHasReceivedData())
+ {
+ // North, but not east. Pull from your north neighbor's southeast corner.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(getNeighborPatch(NORTH)->mDataZ + (grids_per_patch_edge - 1));
+ }
+ else
+ {
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ }
+ }
+ }
+ else if (getNeighborPatch(NORTHEAST)->mSurfacep != mSurfacep)
+ {
+ if (
+ (!getNeighborPatch(NORTH) || (getNeighborPatch(NORTH)->mSurfacep != mSurfacep))
+ &&
+ (!getNeighborPatch(EAST) || (getNeighborPatch(EAST)->mSurfacep != mSurfacep)))
+ {
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(getNeighborPatch(NORTHEAST)->mDataZ);
+ }
+ }
+ else
+ {
+ // We've got a northeast patch in the same surface.
+ // The z and normals will be handled by that patch.
+ }
+ calcNormal(grids_per_patch_edge, grids_per_patch_edge, 2);
+ calcNormal(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
+ calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
+ calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
+ dirty_patch = TRUE;
+ }
+
+ // update the middle normals
+ if (mNormalsInvalid[MIDDLE])
+ {
+ for (j=2; j < grids_per_patch_edge - 2; j++)
+ {
+ for (i=2; i < grids_per_patch_edge - 2; i++)
+ {
+ calcNormal(i, j, 2);
+ }
+ }
+ dirty_patch = TRUE;
+ }
+
+ if (dirty_patch)
+ {
+ mSurfacep->dirtySurfacePatch(this);
+ }
+
+ for (i = 0; i < 9; i++)
+ {
+ mNormalsInvalid[i] = FALSE;
+ }
+}
+
+void LLSurfacePatch::updateEastEdge()
+{
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+
+ U32 j, k;
+ F32 *west_surface, *east_surface;
+
+ if (!getNeighborPatch(EAST))
+ {
+ west_surface = mDataZ + grids_per_patch_edge;
+ east_surface = mDataZ + grids_per_patch_edge - 1;
+ }
+ else if (mConnectedEdge & EAST_EDGE)
+ {
+ west_surface = mDataZ + grids_per_patch_edge;
+ east_surface = getNeighborPatch(EAST)->mDataZ;
+ }
+ else
+ {
+ return;
+ }
+
+ // If patchp is on the east edge of its surface, then we update the east
+ // side buffer
+ for (j=0; j < grids_per_patch_edge; j++)
+ {
+ k = j * grids_per_edge;
+ *(west_surface + k) = *(east_surface + k); // update buffer Z
+ }
+}
+
+
+void LLSurfacePatch::updateNorthEdge()
+{
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+
+ U32 i;
+ F32 *south_surface, *north_surface;
+
+ if (!getNeighborPatch(NORTH))
+ {
+ south_surface = mDataZ + grids_per_patch_edge*grids_per_edge;
+ north_surface = mDataZ + (grids_per_patch_edge - 1) * grids_per_edge;
+ }
+ else if (mConnectedEdge & NORTH_EDGE)
+ {
+ south_surface = mDataZ + grids_per_patch_edge*grids_per_edge;
+ north_surface = getNeighborPatch(NORTH)->mDataZ;
+ }
+ else
+ {
+ return;
+ }
+
+ // Update patchp's north edge ...
+ for (i=0; i<grids_per_patch_edge; i++)
+ {
+ *(south_surface + i) = *(north_surface + i); // update buffer Z
+ }
+}
+
+
+BOOL LLSurfacePatch::updateTexture()
+{
+ if (mSTexUpdate) // Update texture as needed
+ {
+ F32 meters_per_grid = getSurface()->getMetersPerGrid();
+ F32 grids_per_patch_edge = (F32)getSurface()->getGridsPerPatchEdge();
+
+ if ((!getNeighborPatch(EAST) || getNeighborPatch(EAST)->getHasReceivedData())
+ && (!getNeighborPatch(WEST) || getNeighborPatch(WEST)->getHasReceivedData())
+ && (!getNeighborPatch(SOUTH) || getNeighborPatch(SOUTH)->getHasReceivedData())
+ && (!getNeighborPatch(NORTH) || getNeighborPatch(NORTH)->getHasReceivedData()))
+ {
+ LLViewerRegion *regionp = getSurface()->getRegion();
+ LLVector3d origin_region = getOriginGlobal() - getSurface()->getOriginGlobal();
+
+ // Have to figure out a better way to deal with these edge conditions...
+ LLVLComposition* comp = regionp->getComposition();
+ if (!mHeightsGenerated)
+ {
+ F32 patch_size = meters_per_grid*(grids_per_patch_edge+1);
+ if (comp->generateHeights((F32)origin_region[VX], (F32)origin_region[VY],
+ patch_size, patch_size))
+ {
+ mHeightsGenerated = TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ if (comp->generateComposition())
+ {
+ if (mVObjp)
+ {
+ mVObjp->dirtyGeom();
+ }
+ updateCompositionStats();
+ F32 tex_patch_size = meters_per_grid*grids_per_patch_edge;
+ if (comp->generateTexture((F32)origin_region[VX], (F32)origin_region[VY],
+ tex_patch_size, tex_patch_size))
+ {
+ mSTexUpdate = FALSE;
+
+ // Also generate the water texture
+ mSurfacep->generateWaterTexture((F32)origin_region.mdV[VX], (F32)origin_region.mdV[VY],
+ tex_patch_size, tex_patch_size);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+
+void LLSurfacePatch::dirtyZ()
+{
+ mSTexUpdate = TRUE;
+
+ // Invalidate all normals in this patch
+ U32 i;
+ for (i = 0; i < 9; i++)
+ {
+ mNormalsInvalid[i] = TRUE;
+ }
+
+ // Invalidate normals in this and neighboring patches
+ for (i = 0; i < 8; i++)
+ {
+ if (getNeighborPatch(i))
+ {
+ getNeighborPatch(i)->mNormalsInvalid[gDirOpposite[i]] = TRUE;
+ getNeighborPatch(i)->dirty();
+ if (i < 4)
+ {
+ getNeighborPatch(i)->mNormalsInvalid[gDirAdjacent[gDirOpposite[i]][0]] = TRUE;
+ getNeighborPatch(i)->mNormalsInvalid[gDirAdjacent[gDirOpposite[i]][1]] = TRUE;
+ }
+ }
+ }
+
+ dirty();
+ mLastUpdateTime = gFrameTime;
+}
+
+
+const U64 &LLSurfacePatch::getLastUpdateTime() const
+{
+ return mLastUpdateTime;
+}
+
+F32 LLSurfacePatch::getMaxZ() const
+{
+ return mMaxZ;
+}
+
+F32 LLSurfacePatch::getMinZ() const
+{
+ return mMinZ;
+}
+
+void LLSurfacePatch::setOriginGlobal(const LLVector3d &origin_global)
+{
+ mOriginGlobal = origin_global;
+
+ LLVector3 origin_region;
+ origin_region.setVec(mOriginGlobal - mSurfacep->getOriginGlobal());
+
+ mOriginRegion = origin_region;
+ mCenterRegion.mV[VX] = origin_region.mV[VX] + 0.5f*mSurfacep->getGridsPerPatchEdge()*mSurfacep->getMetersPerGrid();
+ mCenterRegion.mV[VY] = origin_region.mV[VY] + 0.5f*mSurfacep->getGridsPerPatchEdge()*mSurfacep->getMetersPerGrid();
+
+ mVisInfo.mbIsVisible = FALSE;
+ mVisInfo.mDistance = 512.0f;
+ mVisInfo.mRenderLevel = 0;
+ mVisInfo.mRenderStride = mSurfacep->getGridsPerPatchEdge();
+
+}
+
+void LLSurfacePatch::connectNeighbor(LLSurfacePatch *neighbor_patchp, const U32 direction)
+{
+ llassert(neighbor_patchp);
+ mNormalsInvalid[direction] = TRUE;
+ neighbor_patchp->mNormalsInvalid[gDirOpposite[direction]] = TRUE;
+
+ setNeighborPatch(direction, neighbor_patchp);
+ neighbor_patchp->setNeighborPatch(gDirOpposite[direction], this);
+
+ if (EAST == direction)
+ {
+ mConnectedEdge |= EAST_EDGE;
+ neighbor_patchp->mConnectedEdge |= WEST_EDGE;
+ }
+ else if (NORTH == direction)
+ {
+ mConnectedEdge |= NORTH_EDGE;
+ neighbor_patchp->mConnectedEdge |= SOUTH_EDGE;
+ }
+ else if (WEST == direction)
+ {
+ mConnectedEdge |= WEST_EDGE;
+ neighbor_patchp->mConnectedEdge |= EAST_EDGE;
+ }
+ else if (SOUTH == direction)
+ {
+ mConnectedEdge |= SOUTH_EDGE;
+ neighbor_patchp->mConnectedEdge |= NORTH_EDGE;
+ }
+}
+
+void LLSurfacePatch::updateVisibility()
+{
+ if (mVObjp == (LLVOSurfacePatch*)NULL)
+ {
+ return;
+ }
+
+ const F32 DEFAULT_DELTA_ANGLE = (0.15f);
+ U32 old_render_stride, max_render_stride;
+ U32 new_render_level;
+ F32 stride_per_distance = DEFAULT_DELTA_ANGLE / mSurfacep->getMetersPerGrid();
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+
+ // sphere in frustum on global coordinates
+ if (gCamera->sphereInFrustum(mCenterRegion + mSurfacep->getOriginAgent(), mRadius) )
+ {
+ // We now need to calculate the render stride based on patchp's distance
+ // from LLCamera render_stride is governed by a relation something like this...
+ //
+ // delta_angle * patch.distance
+ // render_stride <= ----------------------------------------
+ // mMetersPerGrid
+ //
+ // where 'delta_angle' is the desired solid angle of the average polgon on a patch.
+ //
+ // Any render_stride smaller than the RHS would be 'satisfactory'. Smaller
+ // strides give more resolution, but efficiency suggests that we use the largest
+ // of the render_strides that obey the relation. Flexibility is achieved by
+ // modulating 'delta_angle' until we have an acceptable number of triangles.
+
+ old_render_stride = mVisInfo.mRenderStride;
+
+ // Calculate the render_stride using information in agent
+ max_render_stride = lltrunc(mVisInfo.mDistance * stride_per_distance);
+ max_render_stride = llmin(max_render_stride , 2*grids_per_patch_edge);
+
+ // We only use render_strides that are powers of two, so we use look-up tables to figure out
+ // the render_level and corresponding render_stride
+ new_render_level = mVisInfo.mRenderLevel = mSurfacep->getRenderLevel(max_render_stride);
+ mVisInfo.mRenderStride = mSurfacep->getRenderStride(new_render_level);
+
+ if ((mVisInfo.mRenderStride != old_render_stride))
+ // The reason we check !mbIsVisible is because non-visible patches normals
+ // are not updated when their data is changed. When this changes we can get
+ // rid of mbIsVisible altogether.
+ {
+ if (mVObjp)
+ {
+ mVObjp->dirtyGeom();
+ if (getNeighborPatch(WEST))
+ {
+ getNeighborPatch(WEST)->mVObjp->dirtyGeom();
+ }
+ if (getNeighborPatch(SOUTH))
+ {
+ getNeighborPatch(SOUTH)->mVObjp->dirtyGeom();
+ }
+ }
+ }
+ mVisInfo.mbIsVisible = TRUE;
+ }
+ else
+ {
+ mVisInfo.mbIsVisible = FALSE;
+ }
+}
+
+
+const LLVector3d &LLSurfacePatch::getOriginGlobal() const
+{
+ return mOriginGlobal;
+}
+
+LLVector3 LLSurfacePatch::getOriginAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mOriginGlobal);
+}
+
+BOOL LLSurfacePatch::getVisible() const
+{
+ return mVisInfo.mbIsVisible;
+}
+
+U32 LLSurfacePatch::getRenderStride() const
+{
+ return mVisInfo.mRenderStride;
+}
+
+S32 LLSurfacePatch::getRenderLevel() const
+{
+ return mVisInfo.mRenderLevel;
+}
+
+void LLSurfacePatch::setHasReceivedData()
+{
+ mHasReceivedData = TRUE;
+}
+
+BOOL LLSurfacePatch::getHasReceivedData() const
+{
+ return mHasReceivedData;
+}
+
+const LLVector3 &LLSurfacePatch::getCenterRegion() const
+{
+ return mCenterRegion;
+}
+
+
+void LLSurfacePatch::updateCompositionStats()
+{
+ LLViewerLayer *vlp = mSurfacep->getRegion()->getComposition();
+
+ F32 x, y, width, height, mpg, min, mean, max;
+
+ LLVector3 origin = getOriginAgent() - mSurfacep->getOriginAgent();
+ mpg = mSurfacep->getMetersPerGrid();
+ x = origin.mV[VX];
+ y = origin.mV[VY];
+ width = mpg*(mSurfacep->getGridsPerPatchEdge()+1);
+ height = mpg*(mSurfacep->getGridsPerPatchEdge()+1);
+
+ mean = 0.f;
+ min = vlp->getValueScaled(x, y);
+ max= min;
+ U32 count = 0;
+ F32 i, j;
+ for (j = 0; j < height; j += mpg)
+ {
+ for (i = 0; i < width; i += mpg)
+ {
+ F32 comp = vlp->getValueScaled(x + i, y + j);
+ mean += comp;
+ min = llmin(min, comp);
+ max = llmax(max, comp);
+ count++;
+ }
+ }
+ mean /= count;
+
+ mMinComposition = min;
+ mMeanComposition = mean;
+ mMaxComposition = max;
+}
+
+F32 LLSurfacePatch::getMeanComposition() const
+{
+ return mMeanComposition;
+}
+
+F32 LLSurfacePatch::getMinComposition() const
+{
+ return mMinComposition;
+}
+
+F32 LLSurfacePatch::getMaxComposition() const
+{
+ return mMaxComposition;
+}
+
+void LLSurfacePatch::setNeighborPatch(const U32 direction, LLSurfacePatch *neighborp)
+{
+ mNeighborPatches[direction] = neighborp;
+ mNormalsInvalid[direction] = TRUE;
+ if (direction < 4)
+ {
+ mNormalsInvalid[gDirAdjacent[direction][0]] = TRUE;
+ mNormalsInvalid[gDirAdjacent[direction][1]] = TRUE;
+ }
+}
+
+LLSurfacePatch *LLSurfacePatch::getNeighborPatch(const U32 direction) const
+{
+ return mNeighborPatches[direction];
+}
+
+void LLSurfacePatch::clearVObj()
+{
+ mVObjp = NULL;
+}
diff --git a/indra/newview/llsurfacepatch.h b/indra/newview/llsurfacepatch.h
new file mode 100644
index 0000000000..7835b3f598
--- /dev/null
+++ b/indra/newview/llsurfacepatch.h
@@ -0,0 +1,158 @@
+/**
+ * @file llsurfacepatch.h
+ * @brief LLSurfacePatch class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSURFACEPATCH_H
+#define LL_LLSURFACEPATCH_H
+
+#include "v3math.h"
+#include "v3dmath.h"
+#include "llmemory.h"
+
+class LLSurface;
+class LLVOSurfacePatch;
+class LLVector2;
+class LLColor4U;
+class LLAgent;
+
+// A patch shouldn't know about its visibility since that really depends on the
+// camera that is looking (or not looking) at it. So, anything about a patch
+// that is specific to a camera should be in the struct below.
+struct LLPatchVisibilityInfo
+{
+ BOOL mbIsVisible;
+ F32 mDistance; // Distance from camera
+ S32 mRenderLevel;
+ U32 mRenderStride;
+};
+
+
+
+class LLSurfacePatch
+{
+public:
+ LLSurfacePatch();
+ ~LLSurfacePatch();
+
+ void reset(const U32 id);
+ void connectNeighbor(LLSurfacePatch *neighborp, const U32 direction);
+ void disconnectNeighbor(LLSurface *surfacep);
+
+ void setNeighborPatch(const U32 direction, LLSurfacePatch *neighborp);
+ LLSurfacePatch *getNeighborPatch(const U32 direction) const;
+
+ void colorPatch(const U8 r, const U8 g, const U8 b);
+
+ BOOL updateTexture();
+
+ void updateVerticalStats();
+ void updateCompositionStats();
+ void updateNormals();
+
+ void updateEastEdge();
+ void updateNorthEdge();
+
+ void updateCameraDistanceRegion( const LLVector3 &pos_region);
+ void updateVisibility();
+
+ void dirtyZ(); // Dirty the z values of this patch
+ void setHasReceivedData();
+ BOOL getHasReceivedData() const;
+
+ F32 getDistance() const;
+ F32 getMaxZ() const;
+ F32 getMinZ() const;
+ F32 getMeanComposition() const;
+ F32 getMinComposition() const;
+ F32 getMaxComposition() const;
+ const LLVector3 &getCenterRegion() const;
+ const U64 &getLastUpdateTime() const;
+ LLSurface *getSurface() const { return mSurfacep; }
+ LLVector3 getPointAgent(const U32 x, const U32 y) const; // get the point at the offset.
+ LLVector2 getTexCoords(const U32 x, const U32 y) const;
+
+ void calcNormal(const U32 x, const U32 y, const U32 stride);
+ const LLVector3 &getNormal(const U32 x, const U32 y) const;
+
+ void eval(const U32 x, const U32 y, const U32 stride,
+ LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex0, LLVector2 *tex1);
+
+
+
+ LLVector3 getOriginAgent() const;
+ const LLVector3d &getOriginGlobal() const;
+ void setOriginGlobal(const LLVector3d &origin_global);
+
+ // connectivity -- each LLPatch points at 5 neighbors (or NULL)
+ // +---+---+---+
+ // | | 2 | 5 |
+ // +---+---+---+
+ // | 3 | 0 | 1 |
+ // +---+---+---+
+ // | 6 | 4 | |
+ // +---+---+---+
+
+
+ BOOL getVisible() const;
+ U32 getRenderStride() const;
+ S32 getRenderLevel() const;
+
+ void setSurface(LLSurface *surfacep);
+ void setDataZ(F32 *data_z) { mDataZ = data_z; }
+ void setDataNorm(LLVector3 *data_norm) { mDataNorm = data_norm; }
+ F32 *getDataZ() const { return mDataZ; }
+
+ void dirty(); // Mark this surface patch as dirty...
+ void clearDirty() { mDirty = FALSE; }
+
+ void clearVObj();
+
+public:
+ BOOL mHasReceivedData; // has the patch EVER received height data?
+ BOOL mSTexUpdate; // Does the surface texture need to be updated?
+
+protected:
+ LLSurfacePatch *mNeighborPatches[8]; // Adjacent patches
+ BOOL mNormalsInvalid[9]; // Which normals are invalid
+
+ BOOL mDirty;
+ BOOL mDirtyZStats;
+ BOOL mHeightsGenerated;
+
+ U32 mDataOffset;
+ F32 *mDataZ;
+ LLVector3 *mDataNorm;
+
+ // Pointer to the LLVOSurfacePatch object which is used in the new renderer.
+ LLPointer<LLVOSurfacePatch> mVObjp;
+
+ // All of the camera-dependent stuff should be in its own structure...
+ LLPatchVisibilityInfo mVisInfo;
+
+ // pointers to beginnings of patch data fields
+ LLVector3d mOriginGlobal;
+ LLVector3 mOriginRegion;
+
+
+ // height field stats
+ LLVector3 mCenterRegion; // Center in region-local coords
+ F32 mMinZ, mMaxZ, mMeanZ;
+ F32 mRadius;
+
+ F32 mMinComposition;
+ F32 mMaxComposition;
+ F32 mMeanComposition;
+
+ U8 mConnectedEdge; // This flag is non-zero iff patch is on at least one edge
+ // of LLSurface that is "connected" to another LLSurface
+ U64 mLastUpdateTime; // Time patch was last updated
+
+ LLSurface *mSurfacep; // Pointer to "parent" surface
+};
+
+
+#endif // LL_LLSURFACEPATCH_H
diff --git a/indra/newview/lltable.h b/indra/newview/lltable.h
new file mode 100644
index 0000000000..9f3b536483
--- /dev/null
+++ b/indra/newview/lltable.h
@@ -0,0 +1,48 @@
+/**
+ * @file lltable.h
+ * @brief Description of LLTable template class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTABLE_H
+#define LL_LLTABLE_H
+
+template<class T> class LLTable
+{
+private:
+ T *_tab;
+ U32 _w;
+ U32 _h;
+ U32 _size;
+public:
+ LLTable(U32 w, U32 h = 0) : _tab(0), _w(w), _h(h)
+ {
+ if (_w < 0) _w = 0;
+ if (_h < 0) _h = 0;
+ if (0 == h)
+ _h = _w;
+ _size = _w * _h;
+ if ((_w > 0) && (_h > 0))
+ _tab = new T[_size];
+ }
+
+ ~LLTable()
+ {
+ delete[] _tab;
+ _tab = NULL;
+ }
+
+ void init(const T& t)
+ {
+ for (U32 i = 0; i < _size; ++i)
+ _tab[i] = t;
+ }
+ const T& at(U32 w, U32 h) const { return _tab[h * _w + w]; }
+ T& at(U32 w, U32 h) { return _tab[h * _w + w]; }
+ U32 size() const { return _size; }
+ U32 w() const { return _w; }
+ U32 h() const { return _h; }
+};
+#endif // LL_LLTABLE_H
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
new file mode 100644
index 0000000000..da468d4ab1
--- /dev/null
+++ b/indra/newview/lltexlayer.cpp
@@ -0,0 +1,2839 @@
+/**
+ * @file lltexlayer.cpp
+ * @brief A texture layer. Used for avatars.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "imageids.h"
+#include "llagent.h"
+#include "llcrc.h"
+#include "lldir.h"
+#include "llglheaders.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "llpolymorph.h"
+#include "llquantize.h"
+#include "lltexlayer.h"
+#include "llui.h"
+#include "llvfile.h"
+#include "llviewerimagelist.h"
+#include "llviewerimagelist.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llxmltree.h"
+#include "pipeline.h"
+#include "v4coloru.h"
+#include "viewer.h"
+
+//#include "../tools/imdebug/imdebug.h"
+
+
+// SJB: We really always want to use the GL cache;
+// let GL page textures in and out of video RAM instead of trying to do so by hand.
+// const U32 USE_AVATAR_GL_CACHE_THRESHOLD = 1024 * 1024 * 35; // 35 MB
+BOOL gUseAvatarGLCache = TRUE; //FALSE;
+
+LLGradientPaletteList gGradientPaletteList;
+
+// static
+S32 LLTexLayerSetBuffer::sGLByteCount = 0;
+S32 LLTexLayerSetBuffer::sGLBumpByteCount = 0;
+
+//-----------------------------------------------------------------------------
+// LLBakedUploadData()
+//-----------------------------------------------------------------------------
+LLBakedUploadData::LLBakedUploadData( LLVOAvatar* avatar, LLTexLayerSetBuffer* layerset_buffer ) :
+ mAvatar( avatar ),
+ mLayerSetBuffer( layerset_buffer )
+{
+ mID.generate();
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ LLWearable* wearable = gAgent.getWearable( (EWearableType)i);
+ if( wearable )
+ {
+ mWearableAssets[i] = wearable->getID();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetBuffer
+// The composite image that a LLTexLayerSet writes to. Each LLTexLayerSet has one.
+//-----------------------------------------------------------------------------
+LLTexLayerSetBuffer::LLTexLayerSetBuffer( LLTexLayerSet* owner, S32 width, S32 height, BOOL has_bump )
+ :
+ // ORDER_LAST => must render these after the hints are created.
+ LLDynamicTexture( width, height, 4, LLDynamicTexture::ORDER_LAST, TRUE ),
+ mNeedsUpdate( TRUE ),
+ mNeedsUpload( FALSE ),
+ mUploadPending( FALSE ), // Not used for any logic here, just to sync sending of updates
+ mTexLayerSet( owner ),
+ mInitialized( FALSE ),
+ mBumpTexName(0)
+{
+ LLTexLayerSetBuffer::sGLByteCount += getSize();
+
+ if( has_bump )
+ {
+ LLGLSUIDefault gls_ui;
+ glGenTextures(1, (GLuint*) &mBumpTexName);
+
+ LLImageGL::bindExternalTexture(mBumpTexName, 0, GL_TEXTURE_2D);
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ stop_glerror();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ LLImageGL::sGlobalTextureMemory += mWidth * mHeight * 4;
+ LLTexLayerSetBuffer::sGLBumpByteCount += mWidth * mHeight * 4;
+ }
+}
+
+LLTexLayerSetBuffer::~LLTexLayerSetBuffer()
+{
+ LLTexLayerSetBuffer::sGLByteCount -= getSize();
+
+ if( mBumpTexName )
+ {
+ glDeleteTextures(1, (GLuint*) &mBumpTexName);
+ stop_glerror();
+ mBumpTexName = 0;
+
+ LLImageGL::sGlobalTextureMemory -= mWidth * mHeight * 4;
+ LLTexLayerSetBuffer::sGLBumpByteCount -= mWidth * mHeight * 4;
+ }
+}
+
+// static
+void LLTexLayerSetBuffer::dumpTotalByteCount()
+{
+ llinfos << "Composite System GL Buffers: " << (LLTexLayerSetBuffer::sGLByteCount/1024) << "KB" << llendl;
+ llinfos << "Composite System GL Bump Buffers: " << (LLTexLayerSetBuffer::sGLBumpByteCount/1024) << "KB" << llendl;
+}
+
+void LLTexLayerSetBuffer::requestUpdate()
+{
+ mNeedsUpdate = TRUE;
+
+ // If we're in the middle of uploading a baked texture, we don't care about it any more.
+ // When it's downloaded, ignore it.
+ mUploadID.setNull();
+}
+
+void LLTexLayerSetBuffer::requestUpload()
+{
+ if (!mNeedsUpload)
+ {
+ mNeedsUpload = TRUE;
+ mUploadPending = TRUE;
+ }
+}
+
+void LLTexLayerSetBuffer::cancelUpload()
+{
+ if (mNeedsUpload)
+ {
+ mNeedsUpload = FALSE;
+ }
+ mUploadPending = FALSE;
+}
+
+void LLTexLayerSetBuffer::pushProjection()
+{
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+}
+
+void LLTexLayerSetBuffer::popProjection()
+{
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+BOOL LLTexLayerSetBuffer::needsRender()
+{
+ LLVOAvatar* avatar = mTexLayerSet->getAvatar();
+ BOOL upload_now = mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal();
+ BOOL needs_update = gAgent.mNumPendingQueries == 0 && (mNeedsUpdate || upload_now) && !avatar->mAppearanceAnimating;
+ if (needs_update)
+ {
+ BOOL invalid_skirt = avatar->getBakedTE(mTexLayerSet) == LLVOAvatar::TEX_SKIRT_BAKED && !avatar->isWearingWearableType(WT_SKIRT);
+ if (invalid_skirt)
+ {
+ // we were trying to create a skirt texture
+ // but we're no longer wearing a skirt...
+ needs_update = FALSE;
+ cancelUpload();
+ }
+ else
+ {
+ needs_update &= (avatar->isSelf() || (avatar->isVisible() && !avatar->isCulled()));
+ needs_update &= mTexLayerSet->isLocalTextureDataAvailable();
+ }
+ }
+ return needs_update;
+}
+
+void LLTexLayerSetBuffer::preRender(BOOL clear_depth)
+{
+ // Set up an ortho projection
+ pushProjection();
+
+ // keep depth buffer, we don't need to clear it
+ LLDynamicTexture::preRender(FALSE);
+}
+
+void LLTexLayerSetBuffer::postRender(BOOL success)
+{
+ popProjection();
+
+ LLDynamicTexture::postRender(success);
+}
+
+BOOL LLTexLayerSetBuffer::render()
+{
+ U8* baked_bump_data = NULL;
+
+// gUseAvatarGLCache = ( gImageList.getMaxResidentTexMem() > USE_AVATAR_GL_CACHE_THRESHOLD );
+
+ // do we need to upload, and do we have sufficient data to create an uploadable composite?
+ // When do we upload the texture if gAgent.mNumPendingQueries is non-zero?
+ BOOL upload_now = (gAgent.mNumPendingQueries == 0 && mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal());
+ BOOL success = TRUE;
+
+ // Composite bump
+ if( mBumpTexName )
+ {
+ // Composite the bump data
+ success &= mTexLayerSet->renderBump( mOrigin.mX, mOrigin.mY, mWidth, mHeight );
+ stop_glerror();
+
+ if (success)
+ {
+ LLGLSUIDefault gls_ui;
+
+ // read back into texture (this is done externally for the color data)
+ LLImageGL::bindExternalTexture( mBumpTexName, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mOrigin.mX, mOrigin.mY, mWidth, mHeight);
+ stop_glerror();
+
+ // if we need to upload the data, read it back into a buffer
+ if( upload_now )
+ {
+ baked_bump_data = new U8[ mWidth * mHeight * 4 ];
+ glReadPixels(mOrigin.mX, mOrigin.mY, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_bump_data );
+ stop_glerror();
+ }
+ }
+ }
+
+ // Composite the color data
+ LLGLSUIDefault gls_ui;
+ success &= mTexLayerSet->render( mOrigin.mX, mOrigin.mY, mWidth, mHeight );
+
+ if( upload_now )
+ {
+ if (!success)
+ {
+ delete baked_bump_data;
+ llinfos << "Failed attempt to bake " << mTexLayerSet->getBodyRegion() << llendl;
+ mUploadPending = FALSE;
+ }
+ else
+ {
+ readBackAndUpload(baked_bump_data);
+ }
+ }
+
+ // reset GL state
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+ // we have valid texture data now
+ mInitialized = TRUE;
+ mNeedsUpdate = FALSE;
+
+ return success;
+}
+
+BOOL LLTexLayerSetBuffer::updateImmediate()
+{
+ mNeedsUpdate = TRUE;
+ BOOL result = FALSE;
+
+ if (needsRender())
+ {
+ preRender(FALSE);
+ result = render();
+ postRender(result);
+ }
+
+ return result;
+}
+
+void LLTexLayerSetBuffer::readBackAndUpload(U8* baked_bump_data)
+{
+ // pointers for storing data to upload
+ U8* baked_color_data = new U8[ mWidth * mHeight * 4 ];
+
+ glReadPixels(mOrigin.mX, mOrigin.mY, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_color_data );
+ stop_glerror();
+
+ llinfos << "Baked " << mTexLayerSet->getBodyRegion() << llendl;
+ gViewerStats->incStat(LLViewerStats::ST_TEX_BAKES);
+
+ llassert( gAgent.getAvatarObject() == mTexLayerSet->getAvatar() );
+
+ // We won't need our caches since we're baked now. (Techically, we won't
+ // really be baked until this image is sent to the server and the Avatar
+ // Appearance message is received.)
+ mTexLayerSet->deleteCaches();
+
+ LLGLSUIDefault gls_ui;
+
+ LLPointer<LLImageRaw> baked_mask_image = new LLImageRaw(mWidth, mHeight, 1 );
+ U8* baked_mask_data = baked_mask_image->getData();
+
+ mTexLayerSet->gatherAlphaMasks(baked_mask_data, mWidth, mHeight);
+// imdebug("lum b=8 w=%d h=%d %p", mWidth, mHeight, baked_mask_data);
+
+
+ // writes into baked_color_data
+ const char* comment_text = NULL;
+
+ S32 baked_image_components = mBumpTexName ? 5 : 4; // red green blue [bump] clothing
+ LLPointer<LLImageRaw> baked_image = new LLImageRaw( mWidth, mHeight, baked_image_components );
+ U8* baked_image_data = baked_image->getData();
+
+ if( mBumpTexName )
+ {
+ comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // 5 channels: rgb, heightfield/alpha, mask
+
+ // Hide the alpha for the eyelashes in a corner of the bump map
+ if (mTexLayerSet->getBodyRegion() == "head")
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[5*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[5*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[5*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[5*i + 3] = baked_color_data[4*i + 3] < 255 ? baked_color_data[4*i + 3] : baked_bump_data[4*i];
+ baked_image_data[5*i + 4] = baked_mask_data[i];
+ i++;
+ }
+ }
+ }
+ else
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[5*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[5*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[5*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[5*i + 3] = baked_bump_data[4*i];
+ baked_image_data[5*i + 4] = baked_mask_data[i];
+ i++;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (mTexLayerSet->getBodyRegion() == "skirt")
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[4*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[4*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[4*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[4*i + 3] = baked_color_data[4*i + 3]; // Use alpha, not bump
+ i++;
+ }
+ }
+ }
+ else
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[4*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[4*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[4*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[4*i + 3] = baked_mask_data[i];
+ i++;
+ }
+ }
+ }
+ }
+
+ LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C;
+ compressedImage->setRate(0.f);
+ LLTransactionID tid;
+ LLAssetID asset_id;
+ tid.generate();
+ asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ BOOL res = false;
+ if( compressedImage->encode(baked_image, comment_text))
+ {
+ res = LLVFile::writeFile(compressedImage->getData(), compressedImage->getDataSize(),
+ gVFS, asset_id, LLAssetType::AT_TEXTURE);
+ if (res)
+ {
+ LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
+ BOOL valid = FALSE;
+ S32 file_size;
+ U8* data = LLVFile::readFile(gVFS, asset_id, LLAssetType::AT_TEXTURE, &file_size);
+ if (data)
+ {
+ valid = integrity_test->validate(data, file_size); // integrity_test will delete 'data'
+ }
+ else
+ {
+ integrity_test->setLastError("Unable to read entire file");
+ }
+
+ if( valid )
+ {
+ LLBakedUploadData* baked_upload_data = new LLBakedUploadData( gAgent.getAvatarObject(), this );
+ mUploadID = baked_upload_data->mID;
+
+ gAssetStorage->storeAssetData(tid,
+ LLAssetType::AT_TEXTURE,
+ LLTexLayerSetBuffer::onTextureUploadComplete,
+ baked_upload_data,
+ TRUE, // temp_file
+ FALSE, // is_priority
+ TRUE); // store_local
+
+ mNeedsUpload = FALSE;
+ }
+ else
+ {
+ mUploadPending = FALSE;
+ llinfos << "unable to create baked upload file: corrupted" << llendl;
+ LLVFile file(gVFS, asset_id, LLAssetType::AT_TEXTURE, LLVFile::WRITE);
+ file.remove();
+ }
+ }
+ }
+ if (!res)
+ {
+ mUploadPending = FALSE;
+ llinfos << "unable to create baked upload file" << llendl;
+ }
+
+ delete [] baked_color_data;
+ delete [] baked_bump_data;
+}
+
+
+// static
+void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, void* userdata, S32 result) // StoreAssetData callback (not fixed)
+{
+ LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)(U32)userdata;
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+
+ if (0 == result && avatar && !avatar->isDead())
+ {
+ // Sanity check: only the user's avatar should be uploading textures.
+ if( baked_upload_data->mAvatar == avatar )
+ {
+ // Because the avatar is still valid, it's layerset buffers should be valid also.
+ LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mLayerSetBuffer;
+ layerset_buffer->mUploadPending = FALSE;
+
+ if (layerset_buffer->mUploadID.isNull())
+ {
+ // The upload got canceled, we should be in the process of baking a new texture
+ // so request an upload with the new data
+ layerset_buffer->requestUpload();
+ }
+ else if( baked_upload_data->mID == layerset_buffer->mUploadID )
+ {
+ // This is the upload we're currently waiting for.
+ layerset_buffer->mUploadID.setNull();
+
+ if( result >= 0 )
+ {
+ LLVOAvatar::ETextureIndex baked_te = avatar->getBakedTE( layerset_buffer->mTexLayerSet );
+ if( !gAgent.cameraCustomizeAvatar() )
+ {
+ avatar->setNewBakedTexture( baked_te, uuid );
+ }
+ else
+ {
+ llinfos << "LLTexLayerSetBuffer::onTextureUploadComplete() when in Customize Avatar" << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Baked upload failed. Reason: " << result << llendl;
+ //FIXME: retry upload after n seconds, asset server could be busy
+ }
+ }
+ else
+ {
+ llinfos << "Received baked texture out of date, ignored." << llendl;
+ }
+
+ avatar->dirtyMesh();
+ }
+ }
+ else
+ {
+ // Baked texture failed to upload, but since we didn't set the new baked texture, it means that they'll
+ // try and rebake it at some point in the future (after login?)
+ llwarns << "Baked upload failed" << llendl;
+ }
+
+ delete baked_upload_data;
+}
+
+
+void LLTexLayerSetBuffer::bindTexture()
+{
+ if( mInitialized )
+ {
+ LLDynamicTexture::bindTexture();
+ }
+ else
+ {
+ gImageList.getImage(IMG_DEFAULT)->bind();
+ }
+}
+
+void LLTexLayerSetBuffer::bindBumpTexture( U32 stage )
+{
+ if( mBumpTexName )
+ {
+ LLImageGL::bindExternalTexture(mBumpTexName, stage, GL_TEXTURE_2D);
+
+ if( mLastBindTime != LLImageGL::sLastFrameTime )
+ {
+ mLastBindTime = LLImageGL::sLastFrameTime;
+ LLImageGL::updateBoundTexMem(mWidth * mHeight * 4);
+ }
+ }
+ else
+ {
+ LLImageGL::unbindTexture(stage, GL_TEXTURE_2D);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+LLTexLayerSetInfo::LLTexLayerSetInfo( )
+ :
+ mBodyRegion( "" ),
+ mWidth( 512 ),
+ mHeight( 512 ),
+ mClearAlpha( TRUE )
+{
+}
+
+LLTexLayerSetInfo::~LLTexLayerSetInfo( )
+{
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexLayerSetInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer_set" ) );
+ if( !node->hasName( "layer_set" ) )
+ {
+ return FALSE;
+ }
+
+ // body_region
+ static LLStdStringHandle body_region_string = LLXmlTree::addAttributeString("body_region");
+ if( !node->getFastAttributeString( body_region_string, mBodyRegion ) )
+ {
+ llwarns << "<layer_set> is missing body_region attribute" << llendl;
+ return FALSE;
+ }
+
+ // width, height
+ static LLStdStringHandle width_string = LLXmlTree::addAttributeString("width");
+ if( !node->getFastAttributeS32( width_string, mWidth ) )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle height_string = LLXmlTree::addAttributeString("height");
+ if( !node->getFastAttributeS32( height_string, mHeight ) )
+ {
+ return FALSE;
+ }
+
+ // Optional alpha component to apply after all compositing is complete.
+ static LLStdStringHandle alpha_tga_file_string = LLXmlTree::addAttributeString("alpha_tga_file");
+ node->getFastAttributeString( alpha_tga_file_string, mStaticAlphaFileName );
+
+ static LLStdStringHandle clear_alpha_string = LLXmlTree::addAttributeString("clear_alpha");
+ node->getFastAttributeBOOL( clear_alpha_string, mClearAlpha );
+
+ // <layer>
+ for (LLXmlTreeNode* child = node->getChildByName( "layer" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLTexLayerInfo* info = new LLTexLayerInfo();
+ if( !info->parseXml( child ))
+ {
+ delete info;
+ return FALSE;
+ }
+ mLayerInfoList.push_back( info );
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::sHasCaches = FALSE;
+
+LLTexLayerSet::LLTexLayerSet( LLVOAvatar* avatar )
+ :
+ mComposite( NULL ),
+ mAvatar( avatar ),
+ mUpdatesEnabled( FALSE ),
+ mHasBump( FALSE ),
+ mInfo( NULL )
+{
+}
+
+LLTexLayerSet::~LLTexLayerSet()
+{
+ std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer());
+ delete mComposite;
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::setInfo(LLTexLayerSetInfo *info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ LLTexLayerSetInfo::layer_info_list_t::iterator iter;
+ mLayerList.reserve(info->mLayerInfoList.size());
+ for (iter = info->mLayerInfoList.begin(); iter != info->mLayerInfoList.end(); iter++)
+ {
+ LLTexLayer* layer = new LLTexLayer( this );
+ if (!layer->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mLayerList.push_back( layer );
+ }
+
+ requestUpdate();
+
+ stop_glerror();
+
+ return TRUE;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::parseData(LLXmlTreeNode* node)
+{
+ LLTexLayerSetInfo *info = new LLTexLayerSetInfo;
+
+ if (!info->parseXml(node))
+ {
+ delete info;
+ return FALSE;
+ }
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+void LLTexLayerSet::deleteCaches()
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ layer->deleteCaches();
+ }
+}
+
+// Returns TRUE if at least one packet of data has been received for each of the textures that this layerset depends on.
+BOOL LLTexLayerSet::isLocalTextureDataAvailable()
+{
+ return mAvatar->isLocalTextureDataAvailable( this );
+}
+
+
+// Returns TRUE if all of the data for the textures that this layerset depends on have arrived.
+BOOL LLTexLayerSet::isLocalTextureDataFinal()
+{
+ return mAvatar->isLocalTextureDataFinal( this );
+}
+
+
+BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height )
+{
+ BOOL success = TRUE;
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ // composite color layers
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ if( layer->getRenderPass() == RP_COLOR )
+ {
+ success &= layer->render( x, y, width, height );
+ }
+ }
+
+ // (Optionally) replace alpha with a single component image from a tga file.
+ if( !getInfo()->mStaticAlphaFileName.empty() )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+ glBlendFunc( GL_ONE, GL_ZERO );
+
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticAlphaFileName, TRUE );
+ if( image_gl )
+ {
+ LLGLSUIDefault gls_ui;
+ image_gl->bind();
+ gl_rect_2d_simple_tex( width, height );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ LLImageRaw* image_raw = gTexStaticImageList.getImageRaw( getInfo()->mStaticAlphaFileName );
+ if( image_raw )
+ {
+ GLenum format = GL_ALPHA;
+ if( mAvatar->bindScratchTexture(format) )
+ {
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, image_raw->getWidth(), image_raw->getHeight(), format, GL_UNSIGNED_BYTE, image_raw->getData() );
+ stop_glerror();
+
+ gl_rect_2d_simple_tex( width, height );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ }
+ else
+ if( getInfo()->mClearAlpha )
+ {
+ // Set the alpha channel to one (clean up after previous blending)
+ LLGLSNoTextureNoAlphaTest gls_no_alpha;
+ glColor4f( 0.f, 0.f, 0.f, 1.f );
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+
+ gl_rect_2d_simple( width, height );
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ }
+ stop_glerror();
+
+ return success;
+}
+
+BOOL LLTexLayerSet::renderBump( S32 x, S32 y, S32 width, S32 height )
+{
+ BOOL success = TRUE;
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ //static S32 bump_layer_count = 1;
+
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ if( layer->getRenderPass() == RP_BUMP )
+ {
+ success &= layer->render( x, y, width, height );
+ }
+ }
+
+ // Set the alpha channel to one (clean up after previous blending)
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha;
+ glColor4f( 0.f, 0.f, 0.f, 1.f );
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+
+ gl_rect_2d_simple( width, height );
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ stop_glerror();
+
+ return success;
+}
+
+void LLTexLayerSet::requestUpdate()
+{
+ if( mUpdatesEnabled )
+ {
+ createComposite();
+ mComposite->requestUpdate();
+ }
+}
+
+void LLTexLayerSet::requestUpload()
+{
+ createComposite();
+ mComposite->requestUpload();
+}
+
+void LLTexLayerSet::cancelUpload()
+{
+ if(mComposite)
+ {
+ mComposite->cancelUpload();
+ }
+}
+
+void LLTexLayerSet::createComposite()
+{
+ if( !mComposite )
+ {
+ S32 width = mInfo->mWidth;
+ S32 height = mInfo->mHeight;
+ // Composite other avatars at reduced resolution
+ if( !mAvatar->mIsSelf )
+ {
+ width /= 2;
+ height /= 2;
+ }
+ mComposite = new LLTexLayerSetBuffer( this, width, height, mHasBump );
+ }
+}
+
+void LLTexLayerSet::destroyComposite()
+{
+ if( mComposite )
+ {
+ delete mComposite;
+ mComposite = NULL;
+ }
+}
+
+void LLTexLayerSet::setUpdatesEnabled( BOOL b )
+{
+ mUpdatesEnabled = b;
+}
+
+
+void LLTexLayerSet::updateComposite()
+{
+ createComposite();
+ mComposite->updateImmediate();
+}
+
+LLTexLayerSetBuffer* LLTexLayerSet::getComposite()
+{
+ createComposite();
+ return mComposite;
+}
+
+void LLTexLayerSet::gatherAlphaMasks(U8 *data, S32 width, S32 height)
+{
+ S32 size = width * height;
+
+ memset(data, 255, width * height);
+
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ U8* alphaData = layer->getAlphaData();
+ if (!alphaData && layer->hasAlphaParams())
+ {
+ LLColor4 net_color;
+ layer->findNetColor( &net_color );
+ layer->invalidateMorphMasks();
+ layer->renderAlphaMasks(mComposite->getOriginX(), mComposite->getOriginY(), width, height, &net_color);
+ alphaData = layer->getAlphaData();
+ }
+ if (alphaData)
+ {
+ for( S32 i = 0; i < size; i++ )
+ {
+ U8 curAlpha = data[i];
+ U16 resultAlpha = curAlpha;
+ resultAlpha *= (alphaData[i] + 1);
+ resultAlpha = resultAlpha >> 8;
+ data[i] = (U8)resultAlpha;
+ }
+ }
+ }
+}
+
+void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components)
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ layer->applyMorphMask(tex_data, width, height, num_components);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerInfo
+//-----------------------------------------------------------------------------
+LLTexLayerInfo::LLTexLayerInfo( )
+ :
+ mWriteAllChannels( FALSE ),
+ mRenderPass( RP_COLOR ),
+ mFixedColor( 0.f, 0.f, 0.f, 0.f ),
+ mLocalTexture( -1 ),
+ mStaticImageIsMask( FALSE ),
+ mUseLocalTextureAlphaOnly( FALSE )
+{
+}
+
+LLTexLayerInfo::~LLTexLayerInfo( )
+{
+ std::for_each(mColorInfoList.begin(), mColorInfoList.end(), DeletePointer());
+ std::for_each(mAlphaInfoList.begin(), mAlphaInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexLayerInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer" ) );
+
+ // name attribute
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle write_all_channels_string = LLXmlTree::addAttributeString("write_all_channels");
+ node->getFastAttributeBOOL( write_all_channels_string, mWriteAllChannels );
+
+ LLString render_pass_name;
+ static LLStdStringHandle render_pass_string = LLXmlTree::addAttributeString("render_pass");
+ if( node->getFastAttributeString( render_pass_string, render_pass_name ) )
+ {
+ if( render_pass_name == "bump" )
+ {
+ mRenderPass = RP_BUMP;
+ }
+ }
+
+ // Note: layers can have either a "global_color" attrib, a "fixed_color" attrib, or a <param_color> child.
+ // global color attribute (optional)
+ static LLStdStringHandle global_color_string = LLXmlTree::addAttributeString("global_color");
+ node->getFastAttributeString( global_color_string, mGlobalColor );
+
+ // color attribute (optional)
+ LLColor4U color4u;
+ static LLStdStringHandle fixed_color_string = LLXmlTree::addAttributeString("fixed_color");
+ if( node->getFastAttributeColor4U( fixed_color_string, color4u ) )
+ {
+ mFixedColor.setVec( color4u );
+ }
+
+ // <texture> optional sub-element
+ for (LLXmlTreeNode* texture_node = node->getChildByName( "texture" );
+ texture_node;
+ texture_node = node->getNextNamedChild())
+ {
+ LLString local_texture;
+ static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file");
+ static LLStdStringHandle local_texture_string = LLXmlTree::addAttributeString("local_texture");
+ static LLStdStringHandle file_is_mask_string = LLXmlTree::addAttributeString("file_is_mask");
+ static LLStdStringHandle local_texture_alpha_only_string = LLXmlTree::addAttributeString("local_texture_alpha_only");
+ if( texture_node->getFastAttributeString( tga_file_string, mStaticImageFileName ) )
+ {
+ texture_node->getFastAttributeBOOL( file_is_mask_string, mStaticImageIsMask );
+ }
+ else if( texture_node->getFastAttributeString( local_texture_string, local_texture ) )
+ {
+ texture_node->getFastAttributeBOOL( local_texture_alpha_only_string, mUseLocalTextureAlphaOnly );
+
+ if( "upper_shirt" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_SHIRT;
+ }
+ else if( "upper_bodypaint" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_BODYPAINT;
+ }
+ else if( "lower_pants" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_PANTS;
+ }
+ else if( "lower_bodypaint" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_BODYPAINT;
+ }
+ else if( "lower_shoes" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_SHOES;
+ }
+ else if( "head_bodypaint" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_HEAD_BODYPAINT;
+ }
+ else if( "lower_socks" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_SOCKS;
+ }
+ else if( "upper_jacket" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_JACKET;
+ }
+ else if( "lower_jacket" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_JACKET;
+ }
+ else if( "upper_gloves" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_GLOVES;
+ }
+ else if( "upper_undershirt" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_UNDERSHIRT;
+ }
+ else if( "lower_underpants" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_UNDERPANTS;
+ }
+ else if( "eyes_iris" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_EYES_IRIS;
+ }
+ else if( "skirt" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_SKIRT;
+ }
+ else
+ {
+ llwarns << "<texture> element has invalid local_texure attribute: " << mName << " " << local_texture << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<texture> element is missing a required attribute. " << mName << llendl;
+ return FALSE;
+ }
+ }
+
+ for (LLXmlTreeNode* maskNode = node->getChildByName( "morph_mask" );
+ maskNode;
+ maskNode = node->getNextNamedChild())
+ {
+ LLString morph_name;
+ static LLStdStringHandle morph_name_string = LLXmlTree::addAttributeString("morph_name");
+ if (maskNode->getFastAttributeString(morph_name_string, morph_name))
+ {
+ BOOL invert = FALSE;
+ static LLStdStringHandle invert_string = LLXmlTree::addAttributeString("invert");
+ maskNode->getFastAttributeBOOL(invert_string, invert);
+ mMorphNameList.push_back(std::pair<LLString,BOOL>(morph_name,invert));
+ }
+ }
+
+ // <param> optional sub-element (color or alpha params)
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if( child->getChildByName( "param_color" ) )
+ {
+ // <param><param_color/></param>
+ LLTexParamColorInfo* info = new LLTexParamColorInfo( );
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mColorInfoList.push_back( info );
+ }
+ else if( child->getChildByName( "param_alpha" ) )
+ {
+ // <param><param_alpha/></param>
+ LLTexLayerParamAlphaInfo* info = new LLTexLayerParamAlphaInfo( );
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mAlphaInfoList.push_back( info );
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayer
+// A single texture layer, consisting of:
+// * color, consisting of either
+// * one or more color parameters (weighted colors)
+// * a reference to a global color
+// * a fixed color with non-zero alpha
+// * opaque white (the default)
+// * (optional) a texture defined by either
+// * a GUID
+// * a texture entry index (TE)
+// * (optional) one or more alpha parameters (weighted alpha textures)
+//-----------------------------------------------------------------------------
+LLTexLayer::LLTexLayer( LLTexLayerSet* layer_set )
+ :
+ mTexLayerSet( layer_set ),
+ mMorphMasksValid( FALSE ),
+ mStaticImageInvalid( FALSE ),
+ mInfo( NULL )
+{
+}
+
+LLTexLayer::~LLTexLayer()
+{
+ // mParamAlphaList and mParamColorList are LLViewerVisualParam's and get
+ // deleted with ~LLCharacter()
+ //std::for_each(mParamAlphaList.begin(), mParamAlphaList.end(), DeletePointer());
+ //std::for_each(mParamColorList.begin(), mParamColorList.end(), DeletePointer());
+
+ for( alpha_cache_t::iterator iter = mAlphaCache.begin();
+ iter != mAlphaCache.end(); iter++ )
+ {
+ U8* alpha_data = iter->second;
+ delete [] alpha_data;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayer::setInfo(LLTexLayerInfo* info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ if (info->mRenderPass == RP_BUMP)
+ mTexLayerSet->setBump(TRUE);
+
+ {
+ LLTexLayerInfo::morph_name_list_t::iterator iter;
+ for (iter = mInfo->mMorphNameList.begin(); iter != mInfo->mMorphNameList.end(); iter++)
+ {
+ // FIXME: we assume that the referenced visual param is a morph target,
+ // need a better way of actually looking this up
+ LLPolyMorphTarget *morph_param;
+ LLString *name = &(iter->first);
+ morph_param = (LLPolyMorphTarget *)(getTexLayerSet()->getAvatar()->getVisualParam(name->c_str()));
+ if (morph_param)
+ {
+ BOOL invert = iter->second;
+ addMaskedMorph(morph_param, invert);
+ }
+ }
+ }
+
+ {
+ LLTexLayerInfo::color_info_list_t::iterator iter;
+ mParamColorList.reserve(mInfo->mColorInfoList.size());
+ for (iter = mInfo->mColorInfoList.begin(); iter != mInfo->mColorInfoList.end(); iter++)
+ {
+ LLTexParamColor* param_color = new LLTexParamColor( this );
+ if (!param_color->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mParamColorList.push_back( param_color );
+ }
+ }
+ {
+ LLTexLayerInfo::alpha_info_list_t::iterator iter;
+ mParamAlphaList.reserve(mInfo->mAlphaInfoList.size());
+ for (iter = mInfo->mAlphaInfoList.begin(); iter != mInfo->mAlphaInfoList.end(); iter++)
+ {
+ LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha( this );
+ if (!param_alpha->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mParamAlphaList.push_back( param_alpha );
+ }
+ }
+
+ return TRUE;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData
+//-----------------------------------------------------------------------------
+BOOL LLTexLayer::parseData( LLXmlTreeNode* node )
+{
+ LLTexLayerInfo *info = new LLTexLayerInfo;
+
+ if (!info->parseXml(node))
+ {
+ delete info;
+ return FALSE;
+ }
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+
+BOOL LLTexLayer::loadStaticImageRaw()
+{
+ if( mStaticImageRaw.isNull() && !mStaticImageInvalid)
+ {
+ mStaticImageRaw = gTexStaticImageList.getImageRaw( getInfo()->mStaticImageFileName );
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mStaticImageRaw.notNull() ? TRUE : FALSE;
+ if( mStaticImageRaw.isNull() )
+ {
+ llwarns << "Unable to load static file: " << getInfo()->mStaticImageFileName << llendl;
+ mStaticImageInvalid = TRUE; // don't try again.
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLTexLayer::deleteCaches()
+{
+ for( alpha_list_t::iterator iter = mParamAlphaList.begin();
+ iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ param->deleteCaches();
+ }
+ mStaticImageRaw = NULL;
+}
+
+BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height )
+{
+ LLGLEnable color_mat(GL_COLOR_MATERIAL);
+ gPipeline.disableLights();
+
+ BOOL success = TRUE;
+
+ BOOL color_specified = FALSE;
+ BOOL alpha_mask_specified = FALSE;
+
+ LLColor4 net_color;
+ color_specified = findNetColor( &net_color );
+
+ // If you can't see the layer, don't render it.
+ if( is_approx_zero( net_color.mV[VW] ) )
+ {
+ return success;
+ }
+
+ alpha_list_t::iterator iter = mParamAlphaList.begin();
+ if( iter != mParamAlphaList.end() )
+ {
+ // If we have alpha masks, but we're skipping all of them, skip the whole layer.
+ // However, we can't do this optimization if we have morph masks that need updating.
+ if( mMaskedMorphs.empty() )
+ {
+ BOOL skip_layer = TRUE;
+
+ while( iter != mParamAlphaList.end() )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+
+ if( !param->getSkip() )
+ {
+ skip_layer = FALSE;
+ break;
+ }
+
+ iter++;
+ }
+
+ if( skip_layer )
+ {
+ return success;
+ }
+ }
+
+ renderAlphaMasks( x, y, width, height, &net_color );
+ alpha_mask_specified = TRUE;
+ glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA );
+ }
+
+ glColor4fv( net_color.mV);
+
+ if( getInfo()->mWriteAllChannels )
+ {
+ glBlendFunc( GL_ONE, GL_ZERO );
+ }
+
+ if( (getInfo()->mLocalTexture != -1) && !getInfo()->mUseLocalTextureAlphaOnly )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = NULL;
+ if( mTexLayerSet->getAvatar()->getLocalTextureGL( getInfo()->mLocalTexture, &image_gl ) )
+ {
+ if( image_gl )
+ {
+ LLGLDisable alpha_test(getInfo()->mWriteAllChannels ? GL_ALPHA_TEST : 0);
+
+ BOOL old_clamps = image_gl->getClampS();
+ BOOL old_clampt = image_gl->getClampT();
+
+ image_gl->bind();
+ image_gl->setClamp(TRUE, TRUE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ image_gl->setClamp(old_clamps, old_clampt);
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( mTexLayerSet->getAvatar()->getLocalTextureRaw( getInfo()->mLocalTexture, image_raw ) )
+ {
+ success &= renderImageRaw( image_raw->getData(),
+ image_raw->getWidth(),
+ image_raw->getHeight(),
+ image_raw->getComponents(),
+ width,
+ height,
+ FALSE );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
+ if( image_gl )
+ {
+ image_gl->bind();
+ gl_rect_2d_simple_tex( width, height );
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ // Don't load the image file until we actually need it the first time. Like now.
+ if (!loadStaticImageRaw())
+ {
+ success = FALSE;
+ }
+ if( mStaticImageRaw.notNull() )
+ {
+ success &= renderImageRaw(
+ mStaticImageRaw->getData(),
+ mStaticImageRaw->getWidth(),
+ mStaticImageRaw->getHeight(),
+ mStaticImageRaw->getComponents(), width, height, getInfo()->mStaticImageIsMask );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ if( ((-1 == getInfo()->mLocalTexture) ||
+ getInfo()->mUseLocalTextureAlphaOnly) &&
+ getInfo()->mStaticImageFileName.empty() &&
+ color_specified )
+ {
+ LLGLSNoTextureNoAlphaTest gls;
+ glColor4fv( net_color.mV);
+ gl_rect_2d_simple( width, height );
+ }
+
+ if( alpha_mask_specified || getInfo()->mWriteAllChannels )
+ {
+ // Restore standard blend func value
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ stop_glerror();
+ }
+
+ if( !success )
+ {
+ llinfos << "LLTexLayer::render() partial: " << getInfo()->mName << llendl;
+ }
+ return success;
+}
+
+U8* LLTexLayer::getAlphaData()
+{
+ LLCRC alpha_mask_crc;
+ const LLUUID& uuid = mTexLayerSet->getAvatar()->getLocalTextureID(getInfo()->mLocalTexture);
+ alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES);
+
+ for( alpha_list_t::iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ F32 param_weight = param->getWeight();
+ alpha_mask_crc.update((U8*)&param_weight, sizeof(F32));
+ }
+
+ U32 cache_index = alpha_mask_crc.getCRC();
+
+ alpha_cache_t::iterator iter2 = mAlphaCache.find(cache_index);
+ return (iter2 == mAlphaCache.end()) ? 0 : iter2->second;
+}
+
+BOOL LLTexLayer::findNetColor( LLColor4* net_color )
+{
+ // Color is either:
+ // * one or more color parameters (weighted colors) (which may make use of a global color or fixed color)
+ // * a reference to a global color
+ // * a fixed color with non-zero alpha
+ // * opaque white (the default)
+
+ if( !mParamColorList.empty() )
+ {
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatar()->getGlobalColor( getInfo()->mGlobalColor ) );
+ }
+ else
+ if( getInfo()->mFixedColor.mV[VW] )
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ }
+ else
+ {
+ net_color->setVec( 0.f, 0.f, 0.f, 0.f );
+ }
+
+ for( color_list_t::iterator iter = mParamColorList.begin();
+ iter != mParamColorList.end(); iter++ )
+ {
+ LLTexParamColor* param = *iter;
+ LLColor4 param_net = param->getNetColor();
+ switch( param->getOperation() )
+ {
+ case OP_ADD:
+ *net_color += param_net;
+ break;
+ case OP_MULTIPLY:
+ net_color->mV[VX] *= param_net.mV[VX];
+ net_color->mV[VY] *= param_net.mV[VY];
+ net_color->mV[VZ] *= param_net.mV[VZ];
+ net_color->mV[VW] *= param_net.mV[VW];
+ break;
+ case OP_BLEND:
+ net_color->setVec( lerp(*net_color, param_net, param->getWeight()) );
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+ }
+ return TRUE;
+ }
+
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatar()->getGlobalColor( getGlobalColor() ) );
+ return TRUE;
+ }
+
+ if( getInfo()->mFixedColor.mV[VW] )
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ return TRUE;
+ }
+
+ net_color->setToWhite();
+
+ return FALSE; // No need to draw a separate colored polygon
+}
+
+
+BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4* colorp )
+{
+ BOOL success = TRUE;
+
+ llassert( !mParamAlphaList.empty() );
+
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+
+ alpha_list_t::iterator iter = mParamAlphaList.begin();
+ LLTexLayerParamAlpha* first_param = *iter;
+
+ // Note: if the first param is a mulitply, multiply against the current buffer's alpha
+ if( !first_param || !first_param->getMultiplyBlend() )
+ {
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha_test;
+
+ // Clear the alpha
+ glBlendFunc( GL_ONE, GL_ZERO );
+
+ glColor4f( 0.f, 0.f, 0.f, 0.f );
+ gl_rect_2d_simple( width, height );
+ }
+
+ // Accumulate alphas
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ glColor4f( 1.f, 1.f, 1.f, 1.f );
+
+ for( iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ success &= param->render( x, y, width, height );
+ }
+
+ // Approximates a min() function
+ glBlendFunc( GL_DST_ALPHA, GL_ZERO );
+
+ // Accumulate the alpha component of the texture
+ if( getInfo()->mLocalTexture != -1 )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = NULL;
+ if( mTexLayerSet->getAvatar()->getLocalTextureGL( getInfo()->mLocalTexture, &image_gl ) )
+ {
+ if( image_gl && (image_gl->getComponents() == 4) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ BOOL old_clamps = image_gl->getClampS();
+ BOOL old_clampt = image_gl->getClampT();
+ image_gl->bind();
+ image_gl->setClamp(TRUE, TRUE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ image_gl->setClamp(old_clamps, old_clampt);
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( mTexLayerSet->getAvatar()->getLocalTextureRaw( getInfo()->mLocalTexture, image_raw ) )
+ {
+ if(image_raw->getComponents() == 4)
+ {
+ success &= renderImageRaw(
+ image_raw->getData(),
+ image_raw->getWidth(),
+ image_raw->getHeight(),
+ image_raw->getComponents(), width, height, FALSE );
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
+ if( image_gl )
+ {
+ if( (image_gl->getComponents() == 4) ||
+ ( (image_gl->getComponents() == 1) && getInfo()->mStaticImageIsMask ) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ image_gl->bind();
+ gl_rect_2d_simple_tex( width, height );
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ // Don't load the image file until we actually need it the first time. Like now.
+ if (!loadStaticImageRaw())
+ {
+ success = FALSE;
+ }
+
+ if( mStaticImageRaw.notNull() )
+ {
+ if( (mStaticImageRaw->getComponents() == 4) ||
+ ( (mStaticImageRaw->getComponents() == 1) && getInfo()->mStaticImageIsMask ) )
+ {
+ success &= renderImageRaw(
+ mStaticImageRaw->getData(),
+ mStaticImageRaw->getWidth(),
+ mStaticImageRaw->getHeight(),
+ mStaticImageRaw->getComponents(), width, height, getInfo()->mStaticImageIsMask );
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ // Draw a rectangle with the layer color to multiply the alpha by that color's alpha.
+ // Note: we're still using glBlendFunc( GL_DST_ALPHA, GL_ZERO );
+ if( colorp->mV[VW] != 1.f )
+ {
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha_test;
+ glColor4fv( colorp->mV );
+ gl_rect_2d_simple( width, height );
+ }
+
+
+ LLGLSUIDefault gls_ui;
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+
+ if (!mMorphMasksValid && !mMaskedMorphs.empty())
+ {
+ LLCRC alpha_mask_crc;
+ const LLUUID& uuid = mTexLayerSet->getAvatar()->getLocalTextureID(getInfo()->mLocalTexture);
+ alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES);
+
+ for( alpha_list_t::iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ F32 param_weight = param->getWeight();
+ alpha_mask_crc.update((U8*)&param_weight, sizeof(F32));
+ }
+
+ U32 cache_index = alpha_mask_crc.getCRC();
+
+ alpha_cache_t::iterator iter2 = mAlphaCache.find(cache_index);
+ U8* alpha_data;
+ if (iter2 != mAlphaCache.end())
+ {
+ alpha_data = iter2->second;
+ }
+ else
+ {
+ // clear out a slot if we have filled our cache
+ S32 max_cache_entries = getTexLayerSet()->getAvatar()->mIsSelf ? 4 : 1;
+ while ((S32)mAlphaCache.size() >= max_cache_entries)
+ {
+ iter2 = mAlphaCache.begin(); // arbitrarily grab the first entry
+ alpha_data = iter2->second;
+ delete [] alpha_data;
+ mAlphaCache.erase(iter2);
+ }
+ alpha_data = new U8[width * height];
+ mAlphaCache[cache_index] = alpha_data;
+ glReadPixels(x, y, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_data);
+ }
+
+ getTexLayerSet()->getAvatar()->dirtyMesh();
+
+ mMorphMasksValid = TRUE;
+
+ for( morph_list_t::iterator iter3 = mMaskedMorphs.begin();
+ iter3 != mMaskedMorphs.end(); iter3++ )
+ {
+ LLMaskedMorph* maskedMorph = &(*iter3);
+ maskedMorph->mMorphTarget->applyMask(alpha_data, width, height, 1, maskedMorph->mInvert);
+ }
+ }
+
+ return success;
+}
+
+void LLTexLayer::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components)
+{
+ for( morph_list_t::iterator iter = mMaskedMorphs.begin();
+ iter != mMaskedMorphs.end(); iter++ )
+ {
+ LLMaskedMorph* maskedMorph = &(*iter);
+ maskedMorph->mMorphTarget->applyMask(tex_data, width, height, num_components, maskedMorph->mInvert);
+ }
+}
+
+// Returns TRUE on success.
+BOOL LLTexLayer::renderImageRaw( U8* in_data, S32 in_width, S32 in_height, S32 in_components, S32 width, S32 height, BOOL is_mask )
+{
+ if (!in_data)
+ {
+ return FALSE;
+ }
+ GLenum format_options[4] = { GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
+ GLenum format = format_options[in_components-1];
+ if( is_mask )
+ {
+ llassert( 1 == in_components );
+ format = GL_ALPHA;
+ }
+
+ if( (in_width != VOAVATAR_SCRATCH_TEX_WIDTH) || (in_height != VOAVATAR_SCRATCH_TEX_HEIGHT) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ GLenum internal_format_options[4] = { GL_LUMINANCE8, GL_LUMINANCE8_ALPHA8, GL_RGB8, GL_RGBA8 };
+ GLenum internal_format = internal_format_options[in_components-1];
+ if( is_mask )
+ {
+ llassert( 1 == in_components );
+ internal_format = GL_ALPHA8;
+ }
+
+ GLuint name = 0;
+ glGenTextures(1, &name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, internal_format,
+ in_width, in_height,
+ 0, format, GL_UNSIGNED_BYTE, in_data );
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glDeleteTextures(1, &name );
+ stop_glerror();
+ }
+ else
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ if( !mTexLayerSet->getAvatar()->bindScratchTexture(format) )
+ {
+ return FALSE;
+ }
+
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, in_width, in_height, format, GL_UNSIGNED_BYTE, in_data );
+ stop_glerror();
+
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+
+ return TRUE;
+}
+
+void LLTexLayer::requestUpdate()
+{
+ mTexLayerSet->requestUpdate();
+}
+
+void LLTexLayer::addMaskedMorph(LLPolyMorphTarget* morph_target, BOOL invert)
+{
+ mMaskedMorphs.push_front(LLMaskedMorph(morph_target, invert));
+}
+
+void LLTexLayer::invalidateMorphMasks()
+{
+ mMorphMasksValid = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlphaInfo
+//-----------------------------------------------------------------------------
+LLTexLayerParamAlphaInfo::LLTexLayerParamAlphaInfo( )
+ :
+ mMultiplyBlend( FALSE ),
+ mSkipIfZeroWeight( FALSE ),
+ mDomain( 0.f )
+{
+}
+
+BOOL LLTexLayerParamAlphaInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_alpha" ) );
+
+ if( !LLViewerVisualParamInfo::parseXml(node) )
+ return FALSE;
+
+ LLXmlTreeNode* param_alpha_node = node->getChildByName( "param_alpha" );
+ if( !param_alpha_node )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file");
+ if( param_alpha_node->getFastAttributeString( tga_file_string, mStaticImageFileName ) )
+ {
+ // Don't load the image file until it's actually needed.
+ }
+// else
+// {
+// llwarns << "<param_alpha> element is missing tga_file attribute." << llendl;
+// }
+
+ static LLStdStringHandle multiply_blend_string = LLXmlTree::addAttributeString("multiply_blend");
+ param_alpha_node->getFastAttributeBOOL( multiply_blend_string, mMultiplyBlend );
+
+ static LLStdStringHandle skip_if_zero_string = LLXmlTree::addAttributeString("skip_if_zero");
+ param_alpha_node->getFastAttributeBOOL( skip_if_zero_string, mSkipIfZeroWeight );
+
+ static LLStdStringHandle domain_string = LLXmlTree::addAttributeString("domain");
+ param_alpha_node->getFastAttributeF32( domain_string, mDomain );
+
+ gGradientPaletteList.initPalette(mDomain);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlpha
+//-----------------------------------------------------------------------------
+
+// static
+LLTexLayerParamAlpha::param_alpha_ptr_list_t LLTexLayerParamAlpha::sInstances;
+
+// static
+void LLTexLayerParamAlpha::dumpCacheByteCount()
+{
+ S32 gl_bytes = 0;
+ getCacheByteCount( &gl_bytes );
+ llinfos << "Processed Alpha Texture Cache GL:" << (gl_bytes/1024) << "KB" << llendl;
+}
+
+// static
+void LLTexLayerParamAlpha::getCacheByteCount( S32* gl_bytes )
+{
+ *gl_bytes = 0;
+
+ for( param_alpha_ptr_list_t::iterator iter = sInstances.begin();
+ iter != sInstances.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* instance = *iter;
+ LLImageGL* image_gl = instance->mCachedProcessedImageGL;
+ if( image_gl )
+ {
+ S32 bytes = (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents();
+
+ if( image_gl->getHasGLTexture() )
+ {
+ *gl_bytes += bytes;
+ }
+ }
+ }
+}
+
+LLTexLayerParamAlpha::LLTexLayerParamAlpha( LLTexLayer* layer )
+ :
+ mCachedProcessedImageGL( NULL ),
+ mTexLayer( layer ),
+ mNeedsCreateTexture( FALSE ),
+ mStaticImageInvalid( FALSE ),
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mCachedEffectiveWeight(0.f)
+{
+ sInstances.push_front( this );
+}
+
+LLTexLayerParamAlpha::~LLTexLayerParamAlpha()
+{
+ deleteCaches();
+ sInstances.remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+BOOL LLTexLayerParamAlpha::setInfo(LLTexLayerParamAlphaInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+
+ mTexLayer->getTexLayerSet()->getAvatar()->addVisualParam( this );
+ setWeight(getDefaultWeight(), FALSE );
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+
+void LLTexLayerParamAlpha::deleteCaches()
+{
+ mStaticImageTGA = NULL; // deletes image
+ mCachedProcessedImageGL = NULL;
+ mStaticImageRaw = NULL;
+ mNeedsCreateTexture = FALSE;
+}
+
+void LLTexLayerParamAlpha::setWeight(F32 weight, BOOL set_by_user)
+{
+ if (mIsAnimating)
+ {
+ return;
+ }
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ F32 new_weight = llclamp(weight, min_weight, max_weight);
+ U8 cur_u8 = F32_to_U8( mCurWeight, min_weight, max_weight );
+ U8 new_u8 = F32_to_U8( new_weight, min_weight, max_weight );
+ if( cur_u8 != new_u8)
+ {
+ mCurWeight = new_weight;
+
+ LLVOAvatar* avatar = mTexLayer->getTexLayerSet()->getAvatar();
+ if( avatar->getSex() & getSex() )
+ {
+ avatar->invalidateComposite( mTexLayer->getTexLayerSet(), set_by_user );
+ mTexLayer->invalidateMorphMasks();
+ }
+ }
+}
+
+void LLTexLayerParamAlpha::setAnimationTarget(F32 target_value, BOOL set_by_user)
+{
+ mTargetWeight = target_value;
+ setWeight(target_value, set_by_user);
+ mIsAnimating = TRUE;
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, set_by_user);
+ }
+}
+
+void LLTexLayerParamAlpha::animate(F32 delta, BOOL set_by_user)
+{
+ if (mNext)
+ {
+ mNext->animate(delta, set_by_user);
+ }
+}
+
+BOOL LLTexLayerParamAlpha::getSkip()
+{
+ LLVOAvatar *avatar = mTexLayer->getTexLayerSet()->getAvatar();
+
+ if( getInfo()->mSkipIfZeroWeight )
+ {
+ F32 effective_weight = ( avatar->getSex() & getSex() ) ? mCurWeight : getDefaultWeight();
+ if (is_approx_zero( effective_weight ))
+ {
+ return TRUE;
+ }
+ }
+
+ EWearableType type = (EWearableType)getWearableType();
+ if( (type != WT_INVALID) && !avatar->isWearingWearableType( type ) )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL LLTexLayerParamAlpha::render( S32 x, S32 y, S32 width, S32 height )
+{
+ BOOL success = TRUE;
+
+ F32 effective_weight = ( mTexLayer->getTexLayerSet()->getAvatar()->getSex() & getSex() ) ? mCurWeight : getDefaultWeight();
+ BOOL weight_changed = effective_weight != mCachedEffectiveWeight;
+ if( getSkip() )
+ {
+ return success;
+ }
+
+ if( getInfo()->mMultiplyBlend )
+ {
+ glBlendFunc( GL_DST_ALPHA, GL_ZERO ); // Multiplication: approximates a min() function
+ }
+ else
+ {
+ glBlendFunc( GL_ONE, GL_ONE ); // Addition: approximates a max() function
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() && !mStaticImageInvalid)
+ {
+ if( mStaticImageTGA.isNull() )
+ {
+ // Don't load the image file until we actually need it the first time. Like now.
+ mStaticImageTGA = gTexStaticImageList.getImageTGA( getInfo()->mStaticImageFileName );
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mStaticImageTGA.notNull() ? TRUE : FALSE;
+
+ if( mStaticImageTGA.isNull() )
+ {
+ llwarns << "Unable to load static file: " << getInfo()->mStaticImageFileName << llendl;
+ mStaticImageInvalid = TRUE; // don't try again.
+ return FALSE;
+ }
+ }
+
+ const S32 image_tga_width = mStaticImageTGA->getWidth();
+ const S32 image_tga_height = mStaticImageTGA->getHeight();
+ if( !mCachedProcessedImageGL ||
+ (mCachedProcessedImageGL->getWidth() != image_tga_width) ||
+ (mCachedProcessedImageGL->getHeight() != image_tga_height) ||
+ (weight_changed && !(gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))) ||
+ (!gUseAvatarGLCache) )
+ {
+// llinfos << "Building Cached Alpha: " << mName << ": (" << mStaticImageRaw->getWidth() << ", " << mStaticImageRaw->getHeight() << ") " << effective_weight << llendl;
+ mCachedEffectiveWeight = effective_weight;
+
+ if( !mCachedProcessedImageGL )
+ {
+ mCachedProcessedImageGL = new LLImageGL( image_tga_width, image_tga_height, 1, FALSE);
+
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mCachedProcessedImageGL ? TRUE : FALSE;
+
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ // interpret luminance values as color index table
+ mCachedProcessedImageGL->setExplicitFormat( GL_COLOR_INDEX8_EXT, GL_COLOR_INDEX );
+ }
+ else
+ {
+ mCachedProcessedImageGL->setExplicitFormat( GL_ALPHA8, GL_ALPHA );
+ }
+ }
+
+ // Applies domain and effective weight to data as it is decoded. Also resizes the raw image if needed.
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ mStaticImageRaw = NULL;
+ mStaticImageRaw = new LLImageRaw;
+ mStaticImageTGA->decode(mStaticImageRaw);
+ mNeedsCreateTexture = TRUE;
+ }
+ else
+ {
+ mStaticImageRaw = NULL;
+ mStaticImageRaw = new LLImageRaw;
+ mStaticImageTGA->decodeAndProcess( mStaticImageRaw, getInfo()->mDomain, effective_weight );
+ mNeedsCreateTexture = TRUE;
+ }
+ }
+
+ if( mCachedProcessedImageGL )
+ {
+ if( gUseAvatarGLCache ) // 64 MB
+ {
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ if( mNeedsCreateTexture )
+ {
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+ mNeedsCreateTexture = FALSE;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+ }
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ mCachedProcessedImageGL->bind();
+ gGradientPaletteList.setHardwarePalette( getInfo()->mDomain, effective_weight );
+ gl_rect_2d_simple_tex( width, height );
+ mCachedProcessedImageGL->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ // Create the GL texture, and then hang onto it for future use.
+ if( mNeedsCreateTexture )
+ {
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+ mNeedsCreateTexture = FALSE;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+ }
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ mCachedProcessedImageGL->bind();
+ gl_rect_2d_simple_tex( width, height );
+ mCachedProcessedImageGL->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ stop_glerror();
+ }
+ else
+ {
+ if( (mCachedProcessedImageGL->getWidth() != VOAVATAR_SCRATCH_TEX_WIDTH) ||
+ (mCachedProcessedImageGL->getHeight() != VOAVATAR_SCRATCH_TEX_HEIGHT) )
+ {
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+
+ gGradientPaletteList.setHardwarePalette( getInfo()->mDomain, effective_weight );
+ gl_rect_2d_simple_tex( width, height );
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ mCachedProcessedImageGL->destroyGLTexture();
+ }
+ else
+ {
+ // Create the GL texture, bind it and draw a rect, and then immediately destroy it.
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ mCachedProcessedImageGL->destroyGLTexture();
+ }
+ stop_glerror();
+ }
+ else
+ {
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ // Write into a pre-existing GL Image, and then bind and render that.
+ // Faster than creating a new GL Image and then destroying it.
+ if( mTexLayer->getTexLayerSet()->getAvatar()->bindScratchTexture( GL_COLOR_INDEX ) )
+ {
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
+ mCachedProcessedImageGL->getWidth(),
+ mCachedProcessedImageGL->getHeight(),
+ GL_COLOR_INDEX, GL_UNSIGNED_BYTE,
+ mStaticImageRaw->getData() );
+ stop_glerror();
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ gGradientPaletteList.setHardwarePalette( getInfo()->mDomain, effective_weight );
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ // Write into a pre-existing GL Image, and then bind and render that.
+ // Faster than creating a new GL Image and then destroying it.
+ if( mTexLayer->getTexLayerSet()->getAvatar()->bindScratchTexture( GL_ALPHA ) )
+ {
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
+ mCachedProcessedImageGL->getWidth(),
+ mCachedProcessedImageGL->getHeight(),
+ GL_ALPHA, GL_UNSIGNED_BYTE,
+ mStaticImageRaw->getData() );
+ stop_glerror();
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ // Don't keep the cache for other people's avatars
+ // (It's not really a "cache" in that case, but the logic is the same)
+ if( !mTexLayer->getTexLayerSet()->getAvatar()->mIsSelf )
+ {
+ mCachedProcessedImageGL = NULL;
+ }
+ }
+ else
+ {
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha_test;
+ glColor4f( 0.f, 0.f, 0.f, effective_weight );
+ gl_rect_2d_simple( width, height );
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColorInfo
+//-----------------------------------------------------------------------------
+
+LLTexGlobalColorInfo::LLTexGlobalColorInfo()
+{
+}
+
+
+LLTexGlobalColorInfo::~LLTexGlobalColorInfo()
+{
+ for_each(mColorInfoList.begin(), mColorInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexGlobalColorInfo::parseXml(LLXmlTreeNode* node)
+{
+ // name attribute
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ llwarns << "<global_color> element is missing name attribute." << llendl;
+ return FALSE;
+ }
+ // <param> sub-element
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if( child->getChildByName( "param_color" ) )
+ {
+ // <param><param_color/></param>
+ LLTexParamColorInfo* info = new LLTexParamColorInfo();
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mColorInfoList.push_back( info );
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColor
+//-----------------------------------------------------------------------------
+
+LLTexGlobalColor::LLTexGlobalColor( LLVOAvatar* avatar )
+ :
+ mAvatar( avatar ),
+ mInfo( NULL )
+{
+}
+
+
+LLTexGlobalColor::~LLTexGlobalColor()
+{
+ // mParamList are LLViewerVisualParam's and get deleted with ~LLCharacter()
+ //std::for_each(mParamList.begin(), mParamList.end(), DeletePointer());
+}
+
+BOOL LLTexGlobalColor::setInfo(LLTexGlobalColorInfo *info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ LLTexGlobalColorInfo::color_info_list_t::iterator iter;
+ mParamList.reserve(mInfo->mColorInfoList.size());
+ for (iter = mInfo->mColorInfoList.begin(); iter != mInfo->mColorInfoList.end(); iter++)
+ {
+ LLTexParamColor* param_color = new LLTexParamColor( this );
+ if (!param_color->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mParamList.push_back( param_color );
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+
+LLColor4 LLTexGlobalColor::getColor()
+{
+ // Sum of color params
+ if( !mParamList.empty() )
+ {
+ LLColor4 net_color( 0.f, 0.f, 0.f, 0.f );
+
+ for( param_list_t::iterator iter = mParamList.begin();
+ iter != mParamList.end(); iter++ )
+ {
+ LLTexParamColor* param = *iter;
+ LLColor4 param_net = param->getNetColor();
+ switch( param->getOperation() )
+ {
+ case OP_ADD:
+ net_color += param_net;
+ break;
+ case OP_MULTIPLY:
+ net_color.mV[VX] *= param_net.mV[VX];
+ net_color.mV[VY] *= param_net.mV[VY];
+ net_color.mV[VZ] *= param_net.mV[VZ];
+ net_color.mV[VW] *= param_net.mV[VW];
+ break;
+ case OP_BLEND:
+ net_color = lerp(net_color, param_net, param->getWeight());
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+ }
+
+ net_color.mV[VX] = llclampf( net_color.mV[VX] );
+ net_color.mV[VY] = llclampf( net_color.mV[VY] );
+ net_color.mV[VZ] = llclampf( net_color.mV[VZ] );
+ net_color.mV[VW] = llclampf( net_color.mV[VW] );
+
+ return net_color;
+ }
+ return LLColor4( 1.f, 1.f, 1.f, 1.f );
+}
+
+//-----------------------------------------------------------------------------
+// LLTexParamColorInfo
+//-----------------------------------------------------------------------------
+LLTexParamColorInfo::LLTexParamColorInfo()
+ :
+ mOperation( OP_ADD ),
+ mNumColors( 0 )
+{
+}
+
+BOOL LLTexParamColorInfo::parseXml(LLXmlTreeNode *node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_color" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ LLXmlTreeNode* param_color_node = node->getChildByName( "param_color" );
+ if( !param_color_node )
+ {
+ return FALSE;
+ }
+
+ LLString op_string;
+ static LLStdStringHandle operation_string = LLXmlTree::addAttributeString("operation");
+ if( param_color_node->getFastAttributeString( operation_string, op_string ) )
+ {
+ LLString::toLower(op_string);
+ if ( op_string == "add" ) mOperation = OP_ADD;
+ else if ( op_string == "multiply" ) mOperation = OP_MULTIPLY;
+ else if ( op_string == "blend" ) mOperation = OP_BLEND;
+ }
+
+ mNumColors = 0;
+
+ LLColor4U color4u;
+ for (LLXmlTreeNode* child = param_color_node->getChildByName( "value" );
+ child;
+ child = param_color_node->getNextNamedChild())
+ {
+ if( (mNumColors < MAX_COLOR_VALUES) )
+ {
+ static LLStdStringHandle color_string = LLXmlTree::addAttributeString("color");
+ if( child->getFastAttributeColor4U( color_string, color4u ) )
+ {
+ mColors[ mNumColors ].setVec(color4u);
+ mNumColors++;
+ }
+ }
+ }
+ if( !mNumColors )
+ {
+ llwarns << "<param_color> is missing <value> sub-elements" << llendl;
+ return FALSE;
+ }
+
+ if( (mOperation == OP_BLEND) && (mNumColors != 1) )
+ {
+ llwarns << "<param_color> with operation\"blend\" must have exactly one <value>" << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexParamColor
+//-----------------------------------------------------------------------------
+LLTexParamColor::LLTexParamColor( LLTexGlobalColor* tex_global_color )
+ :
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mTexGlobalColor( tex_global_color ),
+ mTexLayer( NULL ),
+ mAvatar( tex_global_color->getAvatar() )
+{
+}
+
+LLTexParamColor::LLTexParamColor( LLTexLayer* layer )
+ :
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mTexGlobalColor( NULL ),
+ mTexLayer( layer ),
+ mAvatar( layer->getTexLayerSet()->getAvatar() )
+{
+}
+
+
+LLTexParamColor::~LLTexParamColor()
+{
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+
+BOOL LLTexParamColor::setInfo(LLTexParamColorInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mID = info->mID;
+ mInfo = info;
+
+ mAvatar->addVisualParam( this );
+ setWeight( getDefaultWeight(), FALSE );
+
+ return TRUE;
+}
+
+LLColor4 LLTexParamColor::getNetColor()
+{
+ llassert( getInfo()->mNumColors >= 1 );
+
+ F32 effective_weight = ( mAvatar && (mAvatar->getSex() & getSex()) ) ? mCurWeight : getDefaultWeight();
+
+ S32 index_last = getInfo()->mNumColors - 1;
+ F32 scaled_weight = effective_weight * index_last;
+ S32 index_start = (S32) scaled_weight;
+ S32 index_end = index_start + 1;
+ if( index_start == index_last )
+ {
+ return getInfo()->mColors[index_last];
+ }
+ else
+ {
+ F32 weight = scaled_weight - index_start;
+ const LLColor4 *start = &getInfo()->mColors[ index_start ];
+ const LLColor4 *end = &getInfo()->mColors[ index_end ];
+ return LLColor4(
+ (1.f - weight) * start->mV[VX] + weight * end->mV[VX],
+ (1.f - weight) * start->mV[VY] + weight * end->mV[VY],
+ (1.f - weight) * start->mV[VZ] + weight * end->mV[VZ],
+ (1.f - weight) * start->mV[VW] + weight * end->mV[VW] );
+ }
+}
+
+void LLTexParamColor::setWeight(F32 weight, BOOL set_by_user)
+{
+ if (mIsAnimating)
+ {
+ return;
+ }
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ F32 new_weight = llclamp(weight, min_weight, max_weight);
+ U8 cur_u8 = F32_to_U8( mCurWeight, min_weight, max_weight );
+ U8 new_u8 = F32_to_U8( new_weight, min_weight, max_weight );
+ if( cur_u8 != new_u8)
+ {
+ mCurWeight = new_weight;
+
+ if( getInfo()->mNumColors <= 0 )
+ {
+ // This will happen when we set the default weight the first time.
+ return;
+ }
+
+ if( mAvatar->getSex() & getSex() )
+ {
+ if( mTexGlobalColor )
+ {
+ mAvatar->onGlobalColorChanged( mTexGlobalColor, set_by_user );
+ }
+ else
+ if( mTexLayer )
+ {
+ mAvatar->invalidateComposite( mTexLayer->getTexLayerSet(), set_by_user );
+ }
+ }
+// llinfos << "param " << mName << " = " << new_weight << llendl;
+ }
+}
+
+void LLTexParamColor::setAnimationTarget(F32 target_value, BOOL set_by_user)
+{
+ // set value first then set interpolating flag to ignore further updates
+ mTargetWeight = target_value;
+ setWeight(target_value, set_by_user);
+ mIsAnimating = TRUE;
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, set_by_user);
+ }
+}
+
+void LLTexParamColor::animate(F32 delta, BOOL set_by_user)
+{
+ if (mNext)
+ {
+ mNext->animate(delta, set_by_user);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexStaticImageList
+//-----------------------------------------------------------------------------
+
+// static
+LLTexStaticImageList gTexStaticImageList;
+LLStringTable LLTexStaticImageList::sImageNames(16384);
+
+LLTexStaticImageList::LLTexStaticImageList()
+ :
+ mRawBytes( 0 ),
+ mGLBytes( 0 ),
+ mTGABytes( 0 )
+{}
+
+LLTexStaticImageList::~LLTexStaticImageList()
+{
+ deleteCachedImages();
+}
+
+void LLTexStaticImageList::dumpByteCount()
+{
+ llinfos << "Avatar Static Textures " <<
+ " Raw:" << (mRawBytes / 1024) <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << llendl;
+}
+
+void LLTexStaticImageList::deleteCachedImages()
+{
+ if( mRawBytes || mGLBytes || mTGABytes )
+ {
+ llinfos << "Clearing Static Textures " <<
+ " Raw:" << (mRawBytes / 1024) <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << llendl;
+
+ //mStaticImageLists uses LLPointers, clear() will cause deletion
+
+ mStaticImageListRaw.clear();
+ mStaticImageListTGA.clear();
+ mStaticImageListGL.clear();
+
+ mRawBytes = 0;
+ mGLBytes = 0;
+ mTGABytes = 0;
+ }
+}
+
+// Note: in general, for a given image image we'll call either getImageTga(), getImageRaw() or getImageGL().
+// We call getImageTga() if the image is used as an alpha gradient.
+// Otherwise, we call getImageRaw() if we have 32 MB or less of video RAM or less and getImageGL() if we have
+// more video RAM than that.
+
+// Returns an LLImageTGA that contains the encoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLImageTGA* LLTexStaticImageList::getImageTGA(const LLString& file_name)
+{
+ const char *namekey = sImageNames.addString(file_name);
+ image_tga_map_t::iterator iter = mStaticImageListTGA.find(namekey);
+ if( iter != mStaticImageListTGA.end() )
+ {
+ return iter->second;
+ }
+ else
+ {
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name.c_str());
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ mStaticImageListTGA[ namekey ] = image_tga;
+ mTGABytes += image_tga->getDataSize();
+ return image_tga;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+}
+
+
+
+// Returns an LLImageRaw that contains the decoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLImageRaw* LLTexStaticImageList::getImageRaw(const LLString& file_name)
+{
+ LLPointer<LLImageRaw> image_raw;
+ const char *namekey = sImageNames.addString(file_name);
+ image_raw_map_t::iterator iter = mStaticImageListRaw.find(namekey);
+ if( iter != mStaticImageListRaw.end() )
+ {
+ image_raw = iter->second;
+ }
+ else
+ {
+ image_raw = new LLImageRaw();
+ if( loadImageRaw( file_name, image_raw ) )
+ {
+ mStaticImageListRaw[ namekey ] = image_raw;
+ mRawBytes += image_raw->getDataSize();
+ }
+ else
+ {
+ image_raw = NULL;
+ }
+ }
+
+ return image_raw;
+}
+
+// Returns a GL Image (without a backing ImageRaw) that contains the decoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLImageGL* LLTexStaticImageList::getImageGL(const LLString& file_name, BOOL is_mask )
+{
+ LLPointer<LLImageGL> image_gl;
+ const char *namekey = sImageNames.addString(file_name);
+
+ image_gl_map_t::iterator iter = mStaticImageListGL.find(namekey);
+ if( iter != mStaticImageListGL.end() )
+ {
+ image_gl = iter->second;
+ }
+ else
+ {
+ image_gl = new LLImageGL( FALSE );
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( loadImageRaw( file_name, image_raw ) )
+ {
+ if( (image_raw->getComponents() == 1) && is_mask )
+ {
+ // Note: these are static, unchanging images so it's ok to assume
+ // that once an image is a mask it's always a mask.
+ image_gl->setExplicitFormat( GL_ALPHA8, GL_ALPHA );
+ }
+ image_gl->createGLTexture(0, image_raw);
+
+ image_gl->bind();
+ image_gl->setClamp(TRUE, TRUE);
+
+ mStaticImageListGL [ namekey ] = image_gl;
+ mGLBytes += (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents();
+ }
+ else
+ {
+ image_gl = NULL;
+ }
+ }
+
+ return image_gl;
+}
+
+// Reads a .tga file, decodes it, and puts the decoded data in image_raw.
+// Returns TRUE if successful.
+BOOL LLTexStaticImageList::loadImageRaw( const LLString& file_name, LLImageRaw* image_raw )
+{
+ BOOL success = FALSE;
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name.c_str());
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ // Copy data from tga to raw.
+ success = image_tga->decode( image_raw );
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// LLMaskedMorph()
+//-----------------------------------------------------------------------------
+LLMaskedMorph::LLMaskedMorph( LLPolyMorphTarget *morph_target, BOOL invert ) : mMorphTarget(morph_target), mInvert(invert)
+{
+ morph_target->addPendingMorphMask();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLGradientPaletteList
+//-----------------------------------------------------------------------------
+
+LLGradientPaletteList::~LLGradientPaletteList()
+{
+ // Note: can't just call deleteAllData() because the data values are arrays.
+ for( palette_map_t::iterator iter = mPaletteMap.begin();
+ iter != mPaletteMap.end(); iter++ )
+ {
+ U8* data = iter->second;
+ delete []data;
+ }
+}
+
+void LLGradientPaletteList::initPalette(F32 domain)
+{
+ palette_map_t::iterator iter = mPaletteMap.find( domain );
+ if( iter == mPaletteMap.end() )
+ {
+ U8 *palette = new U8[512 * 4];
+ mPaletteMap[domain] = palette;
+ S32 ramp_start = 255 - llfloor(domain * 255.f);
+ S32 ramp_end = 255;
+ F32 ramp_factor = (ramp_end == ramp_start) ? 0.f : (255.f / ((F32)ramp_end - (F32)ramp_start));
+
+ //FIXME: move conditionals outside of loop, since this really is just a sequential process
+ for (S32 i = 0; i < 512; i++)
+ {
+ palette[(i * 4) + 1] = 0;
+ palette[(i * 4) + 2] = 0;
+ if (i <= ramp_start)
+ {
+ palette[(i * 4)] = 0;
+ palette[(i * 4) + 3] = 0;
+ }
+ else if (i < ramp_end)
+ {
+ palette[(i * 4)] = llfloor(((F32)i - (F32)ramp_start) * ramp_factor);
+ palette[(i * 4) + 3] = llfloor(((F32)i - (F32)ramp_start) * ramp_factor);
+ }
+ else
+ {
+ palette[(i * 4)] = 255;
+ palette[(i * 4) + 3] = 255;
+ }
+ }
+ }
+}
+
+void LLGradientPaletteList::setHardwarePalette( F32 domain, F32 effective_weight )
+{
+ palette_map_t::iterator iter = mPaletteMap.find( domain );
+ if( iter != mPaletteMap.end() )
+ {
+ U8* palette = iter->second;
+ set_palette( palette + llfloor(effective_weight * (255.f * (1.f - domain))) * 4);
+ }
+ else
+ {
+ llwarns << "LLGradientPaletteList::setHardwarePalette() missing domain " << domain << llendl;
+ }
+}
diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h
new file mode 100644
index 0000000000..5c1a11cdf1
--- /dev/null
+++ b/indra/newview/lltexlayer.h
@@ -0,0 +1,566 @@
+/**
+ * @file lltexlayer.h
+ * @brief A texture layer. Used for avatars.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXLAYER_H
+#define LL_LLTEXLAYER_H
+
+#include <deque>
+#include "llassetstorage.h"
+#include "lldynamictexture.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "llviewerimage.h"
+#include "llviewervisualparam.h"
+#include "llvoavatar.h"
+#include "llwearable.h"
+#include "v4color.h"
+#include "llfloater.h"
+
+class LLTexLayerSetInfo;
+class LLTexLayerSet;
+class LLTexLayerInfo;
+class LLTexLayer;
+class LLImageGL;
+class LLImageTGA;
+class LLTexGlobalColorInfo;
+class LLTexLayerParamAlphaInfo;
+class LLTexLayerParamAlpha;
+class LLTexParamColorInfo;
+class LLTexParamColor;
+class LLPolyMesh;
+class LLXmlTreeNode;
+class LLImageRaw;
+class LLPolyMorphTarget;
+
+class LLTextureCtrl;
+class LLVOAvatar;
+
+
+enum EColorOperation
+{
+ OP_ADD = 0,
+ OP_MULTIPLY = 1,
+ OP_BLEND = 2,
+ OP_COUNT = 3 // Number of operations
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlphaInfo
+//-----------------------------------------------------------------------------
+class LLTexLayerParamAlphaInfo : public LLViewerVisualParamInfo
+{
+ friend class LLTexLayerParamAlpha;
+public:
+ LLTexLayerParamAlphaInfo();
+ /*virtual*/ ~LLTexLayerParamAlphaInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mStaticImageFileName;
+ BOOL mMultiplyBlend;
+ BOOL mSkipIfZeroWeight;
+ F32 mDomain;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexParamColorInfo
+//-----------------------------------------------------------------------------
+class LLTexParamColorInfo : public LLViewerVisualParamInfo
+{
+ friend class LLTexParamColor;
+
+public:
+ LLTexParamColorInfo();
+ virtual ~LLTexParamColorInfo() {};
+ BOOL parseXml( LLXmlTreeNode* node );
+
+protected:
+ enum { MAX_COLOR_VALUES = 20 };
+ EColorOperation mOperation;
+ LLColor4 mColors[MAX_COLOR_VALUES];
+ S32 mNumColors;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColorInfo
+//-----------------------------------------------------------------------------
+class LLTexGlobalColorInfo
+{
+ friend class LLTexGlobalColor;
+public:
+ LLTexGlobalColorInfo();
+ ~LLTexGlobalColorInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ typedef std::vector<LLTexParamColorInfo *> color_info_list_t;
+ color_info_list_t mColorInfoList;
+ LLString mName;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetInfo
+// Containes shared layer set data
+//-----------------------------------------------------------------------------
+class LLTexLayerSetInfo
+{
+ friend class LLTexLayerSet;
+public:
+ LLTexLayerSetInfo();
+ ~LLTexLayerSetInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mBodyRegion;
+ S32 mWidth;
+ S32 mHeight;
+ LLString mStaticAlphaFileName;
+ BOOL mClearAlpha; // Set alpha to 1 for this layerset (if there is no mStaticAlphaFileName)
+
+ typedef std::vector<LLTexLayerInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerInfo
+//-----------------------------------------------------------------------------
+enum ERenderPass
+{
+ RP_COLOR,
+ RP_BUMP,
+ RP_SHINE
+};
+
+class LLTexLayerInfo
+{
+ friend class LLTexLayer;
+public:
+ LLTexLayerInfo();
+ ~LLTexLayerInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mName;
+
+ BOOL mWriteAllChannels; // Don't use masking. Just write RGBA into buffer,
+ ERenderPass mRenderPass;
+
+ LLString mGlobalColor;
+ LLColor4 mFixedColor;
+
+ S32 mLocalTexture;
+ LLString mStaticImageFileName;
+ BOOL mStaticImageIsMask;
+ BOOL mUseLocalTextureAlphaOnly; // Ignore RGB channels from the input texture. Use alpha as a mask
+
+ typedef std::vector<std::pair<LLString,BOOL> > morph_name_list_t;
+ morph_name_list_t mMorphNameList;
+
+ typedef std::vector<LLTexParamColorInfo*> color_info_list_t;
+ color_info_list_t mColorInfoList;
+
+ typedef std::vector<LLTexLayerParamAlphaInfo*> alpha_info_list_t;
+ alpha_info_list_t mAlphaInfoList;
+
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetBuffer
+// The composite image that a LLTexLayerSet writes to. Each LLTexLayerSet has one.
+//-----------------------------------------------------------------------------
+class LLTexLayerSetBuffer : public LLDynamicTexture
+{
+public:
+ LLTexLayerSetBuffer( LLTexLayerSet* owner, S32 width, S32 height, BOOL has_bump );
+ virtual ~LLTexLayerSetBuffer();
+
+ virtual void preRender(BOOL clear_depth);
+ virtual void postRender(BOOL success);
+ virtual BOOL render();
+ BOOL updateImmediate();
+ virtual void bindTexture();
+ void bindBumpTexture( U32 stage );
+ BOOL isInitialized() { return mInitialized; } // Initialized here means that we've done at least one render
+ BOOL needsRender();
+ void requestUpdate();
+ void requestUpload();
+ void cancelUpload();
+ BOOL uploadPending() { return mUploadPending; }
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ void readBackAndUpload(U8* baked_bump_data);
+ static void onTextureUploadComplete( const LLUUID& uuid,
+ void* userdata,
+ S32 result);
+ static void dumpTotalByteCount();
+
+private:
+ void pushProjection();
+ void popProjection();
+
+private:
+ BOOL mNeedsUpdate;
+ BOOL mNeedsUpload;
+ BOOL mUploadPending;
+ LLUUID mUploadID; // Identifys the current upload process (null if none). Used to avoid overlaps (eg, when the user rapidly makes two changes outside of Face Edit)
+ LLTexLayerSet* mTexLayerSet;
+ BOOL mInitialized;
+ LLGLuint mBumpTexName; // zero if none
+
+ static S32 sGLByteCount;
+ static S32 sGLBumpByteCount;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+class LLTexLayerSet
+{
+public:
+ LLTexLayerSet( LLVOAvatar* avatar );
+ ~LLTexLayerSet();
+
+ //BOOL parseData(LLXmlTreeNode* node);
+ LLTexLayerSetInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexLayerSetInfo *info);
+
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ BOOL renderBump( S32 x, S32 y, S32 width,S32 height );
+ BOOL isBodyRegion( const char* region ) { return mInfo->mBodyRegion == region; }
+ LLTexLayerSetBuffer* getComposite();
+ void requestUpdate();
+ void requestUpload();
+ void cancelUpload();
+ LLVOAvatar* getAvatar() { return mAvatar; }
+ void updateComposite();
+ BOOL isLocalTextureDataAvailable();
+ BOOL isLocalTextureDataFinal();
+ void createComposite();
+ void destroyComposite();
+ void setUpdatesEnabled( BOOL b );
+ BOOL getUpdatesEnabled() { return mUpdatesEnabled; }
+ void deleteCaches();
+ void gatherAlphaMasks(U8 *data, S32 width, S32 height);
+ void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components);
+ const LLString& getBodyRegion() { return mInfo->mBodyRegion; }
+ BOOL hasComposite() { return (mComposite != NULL); }
+ void setBump( BOOL b ) { mHasBump = b; }
+ BOOL hasBump() { return mHasBump; }
+
+public:
+ static BOOL sHasCaches;
+
+protected:
+ typedef std::vector<LLTexLayer *> layer_list_t;
+ layer_list_t mLayerList;
+ LLTexLayerSetBuffer* mComposite;
+ LLVOAvatar* mAvatar;
+ BOOL mUpdatesEnabled;
+ BOOL mHasBump;
+
+ LLTexLayerSetInfo *mInfo;
+};
+
+//-----------------------------------------------------------------------------
+// LLMaskedMorph
+//-----------------------------------------------------------------------------
+
+class LLMaskedMorph
+{
+public:
+ LLMaskedMorph( LLPolyMorphTarget *morph_target, BOOL invert );
+
+public:
+ LLPolyMorphTarget *mMorphTarget;
+ BOOL mInvert;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayer
+// A single texture layer
+//-----------------------------------------------------------------------------
+class LLTexLayer
+{
+public:
+ LLTexLayer( LLTexLayerSet* layer_set );
+ ~LLTexLayer();
+
+ //BOOL parseData(LLXmlTreeNode* node);
+ LLTexLayerInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexLayerInfo *info);
+
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ void requestUpdate();
+ LLTexLayerSet* getTexLayerSet() { return mTexLayerSet; }
+
+ const std::string& getName() { return mInfo->mName; }
+
+ void addMaskedMorph(LLPolyMorphTarget* morph_target, BOOL invert);
+ void deleteCaches();
+ U8* getAlphaData();
+ void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components);
+
+ void invalidateMorphMasks();
+ ERenderPass getRenderPass() { return mInfo->mRenderPass; }
+ const LLString& getGlobalColor() { return mInfo->mGlobalColor; }
+ BOOL findNetColor( LLColor4* color );
+ BOOL renderImageRaw( U8* in_data, S32 in_width, S32 in_height, S32 in_components, S32 width, S32 height, BOOL is_mask );
+ BOOL renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4* colorp );
+ BOOL hasAlphaParams() { return (!mParamAlphaList.empty());}
+
+protected:
+ BOOL loadStaticImageRaw();
+
+protected:
+ LLTexLayerSet* mTexLayerSet;
+ LLPointer<LLImageRaw> mStaticImageRaw;
+
+ // Layers can have either mParamColorList, mGlobalColor, or mFixedColor. They are looked for in that order.
+ typedef std::vector<LLTexParamColor *> color_list_t;
+ color_list_t mParamColorList;
+ // mGlobalColor name stored in mInfo
+ // mFixedColor value stored in mInfo
+
+ typedef std::vector<LLTexLayerParamAlpha *> alpha_list_t;
+ alpha_list_t mParamAlphaList;
+
+
+ typedef std::deque<LLMaskedMorph> morph_list_t;
+ morph_list_t mMaskedMorphs;
+ typedef std::map<U32, U8*> alpha_cache_t;
+ alpha_cache_t mAlphaCache;
+ BOOL mMorphMasksValid;
+ BOOL mStaticImageInvalid;
+
+ LLTexLayerInfo *mInfo;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlpha
+//-----------------------------------------------------------------------------
+class LLTexLayerParamAlpha : public LLViewerVisualParam
+{
+public:
+ LLTexLayerParamAlpha( LLTexLayer* layer );
+ /*virtual*/ ~LLTexLayerParamAlpha();
+
+ // Special: These functions are overridden by child classes
+ LLTexLayerParamAlphaInfo* getInfo() const { return (LLTexLayerParamAlphaInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexLayerParamAlphaInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex avatar_sex ) {}
+ /*virtual*/ void setWeight(F32 weight, BOOL set_by_user);
+ /*virtual*/ void setAnimationTarget(F32 target_value, BOOL set_by_user);
+ /*virtual*/ void animate(F32 delta, BOOL set_by_user);
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 1.f; }
+ /*virtual*/ const LLVector3& getAvgDistortion() { return mAvgDistortionVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 3.f; }
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) { return LLVector3(1.f, 1.f, 1.f);}
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return NULL;};
+
+ // New functions
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ BOOL getSkip();
+ void deleteCaches();
+ LLTexLayer* getTexLayer() { return mTexLayer; }
+ BOOL getMultiplyBlend() { return getInfo()->mMultiplyBlend; }
+
+protected:
+ LLPointer<LLImageGL> mCachedProcessedImageGL;
+ LLTexLayer* mTexLayer;
+ LLPointer<LLImageTGA> mStaticImageTGA;
+ LLPointer<LLImageRaw> mStaticImageRaw;
+ BOOL mNeedsCreateTexture;
+ BOOL mStaticImageInvalid;
+ LLVector3 mAvgDistortionVec;
+ F32 mCachedEffectiveWeight;
+
+public:
+ // Global list of instances for gathering statistics
+ static void dumpCacheByteCount();
+ static void getCacheByteCount( S32* gl_bytes );
+
+ typedef std::list< LLTexLayerParamAlpha* > param_alpha_ptr_list_t;
+ static param_alpha_ptr_list_t sInstances;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColor
+//-----------------------------------------------------------------------------
+class LLTexGlobalColor
+{
+public:
+ LLTexGlobalColor( LLVOAvatar* avatar );
+ ~LLTexGlobalColor();
+
+ //BOOL parseData(LLXmlTreeNode* node);
+ LLTexGlobalColorInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexGlobalColorInfo *info);
+
+ void requstUpdate();
+ LLVOAvatar* getAvatar() { return mAvatar; }
+ LLColor4 getColor();
+ const std::string& getName() { return mInfo->mName; }
+
+protected:
+ typedef std::vector<LLTexParamColor *> param_list_t;
+ param_list_t mParamList;
+ LLVOAvatar* mAvatar;
+
+ LLTexGlobalColorInfo *mInfo;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTexParamColor
+//-----------------------------------------------------------------------------
+class LLTexParamColor : public LLViewerVisualParam
+{
+public:
+ LLTexParamColor( LLTexGlobalColor* tex_color );
+ LLTexParamColor( LLTexLayer* layer );
+ /* virtual */ ~LLTexParamColor();
+
+ // Special: These functions are overridden by child classes
+ LLTexParamColorInfo* getInfo() const { return (LLTexParamColorInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexParamColorInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex avatar_sex ) {}
+ /*virtual*/ void setWeight(F32 weight, BOOL set_by_user);
+ /*virtual*/ void setAnimationTarget(F32 target_value, BOOL set_by_user);
+ /*virtual*/ void animate(F32 delta, BOOL set_by_user);
+
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 1.f; }
+ /*virtual*/ const LLVector3& getAvgDistortion() { return mAvgDistortionVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 3.f; }
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) { return LLVector3(1.f, 1.f, 1.f); }
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return NULL;};
+
+ // New functions
+ LLColor4 getNetColor();
+ EColorOperation getOperation() const { return getInfo()->mOperation; }
+
+
+protected:
+ LLVector3 mAvgDistortionVec;
+ LLTexGlobalColor* mTexGlobalColor; // either has mTexGlobalColor or mTexLayer as its parent
+ LLTexLayer* mTexLayer;
+ LLVOAvatar* mAvatar; // redundant, but simplifies the code
+};
+
+//-----------------------------------------------------------------------------
+// LLTexStaticImageList
+//-----------------------------------------------------------------------------
+
+class LLTexStaticImageList
+{
+public:
+ LLTexStaticImageList();
+ ~LLTexStaticImageList();
+
+ LLImageRaw* getImageRaw( const LLString& file_name );
+ LLImageGL* getImageGL( const LLString& file_name, BOOL is_mask );
+ LLImageTGA* getImageTGA( const LLString& file_name );
+
+ void deleteCachedImages();
+ void dumpByteCount();
+
+private:
+ BOOL loadImageRaw( const LLString& file_name, LLImageRaw* image_raw );
+
+private:
+ static LLStringTable sImageNames;
+
+ typedef std::map< const char *, LLPointer<LLImageRaw> > image_raw_map_t;
+ typedef std::map< const char *, LLPointer<LLImageGL> > image_gl_map_t;
+ typedef std::map< const char *, LLPointer<LLImageTGA> > image_tga_map_t;
+ image_raw_map_t mStaticImageListRaw;
+ image_gl_map_t mStaticImageListGL;
+ image_tga_map_t mStaticImageListTGA;
+
+public:
+ S32 mRawBytes;
+ S32 mGLBytes;
+ S32 mTGABytes;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLGradientPaletteList
+// A static set of ramp grayscale palettes. The "effective_weight" is used
+// to determine the x position of the ramp (offset)
+//
+// "Domain" isn't really the right word. It refers to the width of the
+// ramp portion of the function that relates input and output pixel values.
+// A domain of 0 gives a step function.
+//
+// | /----------------
+// O| / |
+// u| / |
+// t| / |
+// p|------------------/ |
+// u| | |
+// t|<---------------->|<-->|
+// | "offset" "domain"
+// |
+// --+---Input--------------------------------
+// |
+//-----------------------------------------------------------------------------
+class LLGradientPaletteList
+{
+public:
+ LLGradientPaletteList() {}
+ ~LLGradientPaletteList();
+
+ void initPalette(F32 domain);
+ void setHardwarePalette(F32 domain, F32 effective_weight);
+
+private:
+ typedef std::map<F32, U8*> palette_map_t;
+ palette_map_t mPaletteMap;
+};
+
+// Used by LLTexLayerSetBuffer for a callback.
+class LLBakedUploadData
+{
+public:
+ LLBakedUploadData( LLVOAvatar* avatar, LLTexLayerSetBuffer* layerset_buffer );
+ ~LLBakedUploadData() {}
+
+ LLUUID mID;
+ LLVOAvatar* mAvatar;
+ LLTexLayerSetBuffer* mLayerSetBuffer;
+ LLUUID mWearableAssets[WT_COUNT];
+};
+
+extern LLTexStaticImageList gTexStaticImageList;
+
+
+#endif // LL_LLTEXLAYER_H
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
new file mode 100644
index 0000000000..8a098f4482
--- /dev/null
+++ b/indra/newview/lltexturectrl.cpp
@@ -0,0 +1,1368 @@
+/**
+ * @file lltexturectrl.cpp
+ * @author Richard Nelson, James Cook
+ * @brief LLTextureCtrl class implementation including related functions
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltexturectrl.h"
+
+#include "llagent.h"
+#include "llviewerimagelist.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llbutton.h"
+#include "lldraghandle.h"
+#include "llfocusmgr.h"
+#include "llviewerimage.h"
+#include "llfolderview.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "lllineeditor.h"
+#include "llui.h"
+#include "llviewerinventory.h"
+#include "llpermissions.h"
+#include "llsaleinfo.h"
+#include "llassetstorage.h"
+#include "lltextbox.h"
+#include "llresizehandle.h"
+#include "llscrollcontainer.h"
+#include "lltoolmgr.h"
+#include "lltoolpipette.h"
+
+#include "lltool.h"
+#include "llviewerwindow.h"
+#include "llviewerobject.h"
+#include "llviewercontrol.h"
+#include "llglheaders.h"
+#include "llvieweruictrlfactory.h"
+
+
+static const S32 CLOSE_BTN_WIDTH = 100;
+const S32 PIPETTE_BTN_WIDTH = 32;
+static const S32 HPAD = 4;
+static const S32 VPAD = 4;
+static const S32 LINE = 16;
+static const S32 SMALL_BTN_WIDTH = 64;
+static const S32 TEX_PICKER_MIN_WIDTH =
+ (HPAD +
+ CLOSE_BTN_WIDTH +
+ HPAD +
+ CLOSE_BTN_WIDTH +
+ HPAD +
+ SMALL_BTN_WIDTH +
+ HPAD +
+ SMALL_BTN_WIDTH +
+ HPAD +
+ 30 +
+ RESIZE_HANDLE_WIDTH * 2);
+static const S32 CLEAR_BTN_WIDTH = 50;
+static const S32 TEX_PICKER_MIN_HEIGHT = 290;
+static const S32 FOOTER_HEIGHT = 100;
+static const S32 BORDER_PAD = HPAD;
+static const S32 TEXTURE_INVENTORY_PADDING = 30;
+static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
+static const F32 CONTEXT_CONE_OUT_ALPHA = 0.35f;
+
+
+//static const char CURRENT_IMAGE_NAME[] = "Current Texture";
+//static const char WHITE_IMAGE_NAME[] = "Blank Texture";
+//static const char NO_IMAGE_NAME[] = "None";
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// LLFloaterTexturePicker
+
+class LLFloaterTexturePicker : public LLFloater
+{
+public:
+ LLFloaterTexturePicker(
+ LLTextureCtrl* owner,
+ const LLRect& rect,
+ const std::string& label,
+ PermissionMask immediate_filter_perm_mask,
+ PermissionMask non_immediate_filter_perm_mask,
+ BOOL can_apply_immediately);
+ virtual ~LLFloaterTexturePicker();
+
+ // LLView overrides
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg);
+ virtual void draw();
+
+ // LLFloater overrides
+ virtual void onClose(bool app_quitting);
+
+ // New functions
+ void setImageID( const LLUUID& image_asset_id);
+ void updateImageStats();
+ const LLUUID& getAssetID() { return mImageAssetID; }
+ const LLUUID& findItemID(const LLUUID& asset_id, BOOL copyable_only);
+ void setCanApplyImmediately(BOOL b);
+
+ void setDirty( BOOL b ) { mIsDirty = b; }
+ BOOL isDirty() { return mIsDirty; }
+ void setActive( BOOL active );
+
+ LLTextureCtrl* getOwner() const { return mOwner; }
+ void setOwner(LLTextureCtrl* owner) { mOwner = owner; }
+
+ void stopUsingPipette();
+ PermissionMask getFilterPermMask();
+ void updateFilterPermMask();
+ void commitIfImmediateSet();
+
+ static void onBtnSetToDefault( void* userdata );
+ static void onBtnSelect( void* userdata );
+ static void onBtnCancel( void* userdata );
+ static void onBtnPipette( void* userdata );
+ //static void onBtnRevert( void* userdata );
+ static void onBtnWhite( void* userdata );
+ static void onBtnNone( void* userdata );
+ static void onBtnClear( void* userdata );
+ static void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data);
+ static void onShowFolders(LLUICtrl* ctrl, void* userdata);
+ static void onApplyImmediateCheck(LLUICtrl* ctrl, void* userdata);
+ static void onSearchEdit(const LLString& search_string, void* user_data );
+ static void onTextureSelect( const LLTextureEntry& te, void *data );
+
+protected:
+ LLPointer<LLViewerImage> mTexturep;
+ LLTextureCtrl* mOwner;
+
+ LLUUID mImageAssetID; // Currently selected texture
+
+ LLUUID mWhiteImageAssetID;
+ LLUUID mSpecialCurrentImageAssetID; // Used when the asset id has no corresponding texture in the user's inventory.
+ LLUUID mOriginalImageAssetID;
+
+ LLTextBox* mTentativeLabel;
+ LLTextBox* mResolutionLabel;
+
+ LLString mPendingName;
+ BOOL mIsDirty;
+ BOOL mActive;
+
+ LLSearchEditor* mSearchEdit;
+ LLInventoryPanel* mInventoryPanel;
+ PermissionMask mImmediateFilterPermMask;
+ PermissionMask mNonImmediateFilterPermMask;
+ BOOL mCanApplyImmediately;
+ BOOL mNoCopyTextureSelected;
+ F32 mContextConeOpacity;
+};
+
+LLFloaterTexturePicker::LLFloaterTexturePicker(
+ LLTextureCtrl* owner,
+ const LLRect& rect,
+ const std::string& label,
+ PermissionMask immediate_filter_perm_mask,
+ PermissionMask non_immediate_filter_perm_mask,
+ BOOL can_apply_immediately)
+ :
+ LLFloater( "texture picker",
+ rect,
+ LLString( "Pick: " ) + label,
+ TRUE,
+ TEX_PICKER_MIN_WIDTH, TEX_PICKER_MIN_HEIGHT ),
+ mOwner( owner ),
+ mImageAssetID( owner->getImageAssetID() ),
+ mWhiteImageAssetID( gSavedSettings.getString( "UIImgWhiteUUID" ) ),
+ mOriginalImageAssetID(owner->getImageAssetID()),
+ mTentativeLabel(NULL),
+ mResolutionLabel(NULL),
+ mIsDirty( FALSE ),
+ mActive( TRUE ),
+ mSearchEdit(NULL),
+ mImmediateFilterPermMask(immediate_filter_perm_mask),
+ mNonImmediateFilterPermMask(non_immediate_filter_perm_mask),
+ mContextConeOpacity(0.f)
+{
+ gUICtrlFactory->buildFloater(this,"floater_texture_ctrl.xml");
+
+ mTentativeLabel = LLUICtrlFactory::getTextBoxByName(this,"Multiple");
+
+ mResolutionLabel = LLUICtrlFactory::getTextBoxByName(this,"unknown");
+
+
+ childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this);
+ childSetAction("None", LLFloaterTexturePicker::onBtnNone,this);
+ childSetAction("Blank", LLFloaterTexturePicker::onBtnWhite,this);
+
+
+ childSetCommitCallback("show_folders_check", onShowFolders, this);
+ childSetVisible("show_folders_check", FALSE);
+
+ mSearchEdit = (LLSearchEditor*)getCtrlByNameAndType("inventory search editor", WIDGET_TYPE_SEARCH_EDITOR);
+ mSearchEdit->setSearchCallback(onSearchEdit, this);
+
+ mInventoryPanel = (LLInventoryPanel*)this->getCtrlByNameAndType("inventory panel", WIDGET_TYPE_INVENTORY_PANEL);
+
+ if(mInventoryPanel)
+ {
+ U32 filter_types = 0x0;
+ filter_types |= 0x1 << LLInventoryType::IT_TEXTURE;
+ filter_types |= 0x1 << LLInventoryType::IT_SNAPSHOT;
+
+ mInventoryPanel->setAutoSelectOverride(true);
+ mInventoryPanel->setFilterTypes(filter_types);
+ mInventoryPanel->setFilterPermMask(getFilterPermMask());
+ mInventoryPanel->setSelectCallback(onSelectionChange, this);
+ mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mInventoryPanel->setAllowMultiSelect(FALSE);
+ // Commented out to stop opening all folders with textures
+ // mInventoryPanel->openDefaultFolderForType(LLAssetType::AT_TEXTURE);
+
+ // don't put keyboard focus on selected item, because the selection callback
+ // will assume that this was user input
+ mInventoryPanel->setSelection(findItemID(mImageAssetID, FALSE), TAKE_FOCUS_NO);
+ }
+
+ mCanApplyImmediately = can_apply_immediately;
+ mNoCopyTextureSelected = FALSE;
+
+ childSetValue("apply_immediate_check", gSavedSettings.getBOOL("ApplyTextureImmediately"));
+ childSetCommitCallback("apply_immediate_check", onApplyImmediateCheck, this);
+
+ if (!can_apply_immediately)
+ {
+ childSetEnabled("show_folders_check", FALSE);
+ }
+
+ childSetAction("Pipette", LLFloaterTexturePicker::onBtnPipette,this);
+ childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this);
+ childSetAction("Select", LLFloaterTexturePicker::onBtnSelect,this);
+
+ // update permission filter once UI is fully initialized
+ updateFilterPermMask();
+
+ setCanMinimize(FALSE);
+}
+
+LLFloaterTexturePicker::~LLFloaterTexturePicker()
+{
+}
+
+void LLFloaterTexturePicker::setImageID(const LLUUID& image_id)
+{
+ if( mImageAssetID != image_id && mActive)
+ {
+ mNoCopyTextureSelected = FALSE;
+ mIsDirty = TRUE;
+ mImageAssetID = image_id;
+ LLUUID item_id = findItemID(mImageAssetID, FALSE);
+ if (item_id.isNull())
+ {
+ mInventoryPanel->clearSelection();
+ }
+ else
+ {
+ LLInventoryItem* itemp = gInventory.getItem(image_id);
+ if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // no copy texture
+ childSetValue("apply_immediate_check", FALSE);
+ mNoCopyTextureSelected = TRUE;
+ }
+ mInventoryPanel->setSelection(item_id, TAKE_FOCUS_NO);
+ }
+ }
+}
+
+void LLFloaterTexturePicker::setActive( BOOL active )
+{
+ if (!active && childGetValue("Pipette").asBoolean())
+ {
+ stopUsingPipette();
+ }
+ mActive = active;
+}
+
+void LLFloaterTexturePicker::setCanApplyImmediately(BOOL b)
+{
+ mCanApplyImmediately = b;
+ if (!mCanApplyImmediately)
+ {
+ childSetValue("apply_immediate_check", FALSE);
+ }
+ updateFilterPermMask();
+}
+
+void LLFloaterTexturePicker::stopUsingPipette()
+{
+ if (gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette)
+ {
+ gToolMgr->clearTransientTool();
+ }
+}
+
+void LLFloaterTexturePicker::updateImageStats()
+{
+ if (mTexturep.notNull())
+ {
+ //RN: have we received header data for this image?
+ if (mTexturep->getWidth(0) > 0 && mTexturep->getHeight(0) > 0)
+ {
+ std::ostringstream formatted_dims;
+ formatted_dims << llformat("Dimensions: %d x %d", mTexturep->getWidth(0),mTexturep->getHeight(0));
+ mResolutionLabel->setText(formatted_dims.str());
+ }
+ else
+ {
+ mResolutionLabel->setText("Dimensions: unknown");
+ }
+ if (gAgent.isGodlike())
+ {
+ LLString tstring = "Pick: " + mTexturep->getID().getString();
+ setTitle(tstring);
+ }
+ }
+}
+
+// virtual
+BOOL LLFloaterTexturePicker::handleDragAndDrop(
+ S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ if (cargo_type == DAD_TEXTURE)
+ {
+ LLInventoryItem *item = (LLInventoryItem *)cargo_data;
+
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ PermissionMask item_perm_mask = 0;
+ if (copy) item_perm_mask |= PERM_COPY;
+ if (mod) item_perm_mask |= PERM_MODIFY;
+ if (xfer) item_perm_mask |= PERM_TRANSFER;
+
+ PermissionMask filter_perm_mask = getFilterPermMask();
+ if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask )
+ {
+ if (drop)
+ {
+ setImageID( item->getAssetUUID() );
+ commitIfImmediateSet();
+ }
+
+ *accept = ACCEPT_YES_SINGLE;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFloaterTexturePicker " << getName() << llendl;
+
+ return handled;
+}
+
+// virtual
+void LLFloaterTexturePicker::onClose(bool app_quitting)
+{
+ if (mOwner)
+ {
+ mOwner->onFloaterClose();
+ }
+ stopUsingPipette();
+ destroy();
+}
+
+// virtual
+void LLFloaterTexturePicker::draw()
+{
+ if (mOwner)
+ {
+ // draw cone of context pointing back to texture swatch
+ LLRect owner_rect;
+ mOwner->localRectToOtherView(mOwner->getLocalRect(), &owner_rect, this);
+ LLRect local_rect = getLocalRect();
+ if (gFocusMgr.childHasKeyboardFocus(this) && mOwner->isInVisibleChain() && mContextConeOpacity > 0.001f)
+ {
+ LLGLSNoTexture no_texture;
+ LLGLEnable(GL_CULL_FACE);
+ glBegin(GL_QUADS);
+ {
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mLeft, owner_rect.mTop);
+ glVertex2i(owner_rect.mRight, owner_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ glVertex2i(owner_rect.mLeft, owner_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mRight, owner_rect.mTop);
+ glVertex2i(owner_rect.mRight, owner_rect.mBottom);
+
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mRight, owner_rect.mBottom);
+ glVertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ }
+ glEnd();
+ }
+ }
+
+ if (gFocusMgr.childHasMouseCapture(mDragHandle))
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 1.f, LLCriticalDamp::getInterpolant(0.1f));
+ }
+ else
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(0.2f));
+ }
+
+ updateImageStats();
+
+ // if we're inactive, gray out "apply immediate" checkbox
+ childSetEnabled("show_folders_check", mActive && mCanApplyImmediately && !mNoCopyTextureSelected);
+ childSetEnabled("Select", mActive);
+ childSetEnabled("Pipette", gToolMgr != NULL && mActive);
+ childSetValue("Pipette", gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette);
+
+ //RN: reset search bar to reflect actual search query (all caps, for example)
+ mSearchEdit->setText(mInventoryPanel->getFilterSubString());
+
+ //BOOL allow_copy = FALSE;
+ if( getVisible() && mOwner)
+ {
+ mTexturep = NULL;
+ if(mImageAssetID.notNull())
+ {
+ mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO);
+ mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
+ }
+
+ if (mTentativeLabel)
+ {
+ mTentativeLabel->setVisible( FALSE );
+ }
+
+ childSetEnabled("Default", mImageAssetID != mOwner->getDefaultImageAssetID());
+ childSetEnabled("Blank", mImageAssetID != mWhiteImageAssetID );
+ childSetEnabled("None", mOwner->getAllowNoTexture() && !mImageAssetID.isNull() );
+
+ LLFloater::draw();
+
+ if( isMinimized() )
+ {
+ return;
+ }
+
+ // Border
+ LLRect border( BORDER_PAD,
+ mRect.getHeight() - LLFLOATER_HEADER_SIZE - BORDER_PAD,
+ ((TEX_PICKER_MIN_WIDTH / 2) - TEXTURE_INVENTORY_PADDING - HPAD) - BORDER_PAD,
+ BORDER_PAD + FOOTER_HEIGHT + (mRect.getHeight() - TEX_PICKER_MIN_HEIGHT));
+ gl_rect_2d( border, LLColor4::black, FALSE );
+
+
+ // Interior
+ LLRect interior = border;
+ interior.stretch( -1 );
+
+ if( mTexturep )
+ {
+ if( mTexturep->getComponents() == 4 )
+ {
+ gl_rect_2d_checkerboard( interior );
+ }
+
+ gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep );
+
+ // Pump the priority
+ mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
+
+ // Draw Tentative Label over the image
+ if( mOwner->getTentative() && !mIsDirty )
+ {
+ mTentativeLabel->setVisible( TRUE );
+ drawChild(mTentativeLabel);
+ }
+ }
+ else
+ {
+ gl_rect_2d( interior, LLColor4::grey, TRUE );
+
+ // Draw X
+ gl_draw_x(interior, LLColor4::black );
+ }
+ }
+}
+
+// static
+/*
+void LLFloaterTexturePicker::onSaveAnotherCopyDialog( S32 option, void* userdata )
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ if( 0 == option )
+ {
+ self->copyToInventoryFinal();
+ }
+}
+*/
+
+const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL copyable_only)
+{
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLAssetIDMatches asset_id_matches(asset_id);
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cats,
+ items,
+ LLInventoryModel::INCLUDE_TRASH,
+ asset_id_matches);
+
+ if (items.count())
+ {
+ // search for copyable version first
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ LLInventoryItem* itemp = items[i];
+ LLPermissions item_permissions = itemp->getPermissions();
+ if (item_permissions.allowCopyBy(gAgent.getID(), gAgent.getGroupID()))
+ {
+ return itemp->getUUID();
+ }
+ }
+ // otherwise just return first instance, unless copyable requested
+ if (copyable_only)
+ {
+ return LLUUID::null;
+ }
+ else
+ {
+ return items[0]->getUUID();
+ }
+ }
+
+ return LLUUID::null;
+}
+
+PermissionMask LLFloaterTexturePicker::getFilterPermMask()
+{
+ bool apply_immediate = childGetValue("apply_immediate_check").asBoolean();
+ return apply_immediate ? mImmediateFilterPermMask : mNonImmediateFilterPermMask;
+}
+
+void LLFloaterTexturePicker::commitIfImmediateSet()
+{
+ bool apply_immediate = childGetValue("apply_immediate_check").asBoolean();
+ if (!mNoCopyTextureSelected && apply_immediate && mOwner)
+ {
+ mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE);
+ }
+}
+
+// static
+void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ if (self->mOwner)
+ {
+ self->setImageID( self->mOwner->getDefaultImageAssetID() );
+ }
+ self->commitIfImmediateSet();
+}
+
+// static
+void LLFloaterTexturePicker::onBtnWhite(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( self->mWhiteImageAssetID );
+ self->commitIfImmediateSet();
+}
+
+
+// static
+void LLFloaterTexturePicker::onBtnNone(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( LLUUID::null );
+ self->commitIfImmediateSet();
+}
+
+/*
+// static
+void LLFloaterTexturePicker::onBtnRevert(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( self->mOriginalImageAssetID );
+ // TODO: Change this to tell the owner to cancel. It needs to be
+ // smart enough to restore multi-texture selections.
+ self->mOwner->onFloaterCommit();
+ self->mIsDirty = FALSE;
+}*/
+
+// static
+void LLFloaterTexturePicker::onBtnCancel(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( self->mOriginalImageAssetID );
+ if (self->mOwner)
+ {
+ self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CANCEL);
+ }
+ self->mIsDirty = FALSE;
+ self->close();
+}
+
+// static
+void LLFloaterTexturePicker::onBtnSelect(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ if (self->mOwner)
+ {
+ self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT);
+ }
+ self->close();
+}
+
+// static
+void LLFloaterTexturePicker::onBtnPipette( void* userdata )
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+
+ if ( self && gToolMgr)
+ {
+ BOOL pipette_active = self->childGetValue("Pipette").asBoolean();
+ pipette_active = !pipette_active;
+ if (pipette_active)
+ {
+ gToolPipette->setSelectCallback(onTextureSelect, self);
+ gToolMgr->setTransientTool(gToolPipette);
+ }
+ else
+ {
+ gToolMgr->clearTransientTool();
+ }
+ }
+
+}
+
+// static
+void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)data;
+ if (items.size())
+ {
+ LLFolderViewItem* first_item = items.front();
+ LLInventoryItem* itemp = gInventory.getItem(first_item->getListener()->getUUID());
+ self->mNoCopyTextureSelected = FALSE;
+ if (itemp)
+ {
+ if (!itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ self->mNoCopyTextureSelected = TRUE;
+ }
+ self->mImageAssetID = itemp->getAssetUUID();
+ self->mIsDirty = TRUE;
+ if (user_action)
+ {
+ // only commit intentional selections, not implicit ones
+ self->commitIfImmediateSet();
+ }
+ }
+ }
+}
+
+// static
+void LLFloaterTexturePicker::onShowFolders(LLUICtrl* ctrl, void *user_data)
+{
+ LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
+ LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;
+
+ if (check_box->get())
+ {
+ picker->mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ }
+ else
+ {
+ picker->mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NO_FOLDERS);
+ }
+}
+
+// static
+void LLFloaterTexturePicker::onApplyImmediateCheck(LLUICtrl* ctrl, void *user_data)
+{
+ LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;
+
+ LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
+ gSavedSettings.setBOOL("ApplyTextureImmediately", check_box->get());
+
+ picker->updateFilterPermMask();
+ picker->commitIfImmediateSet();
+}
+
+void LLFloaterTexturePicker::updateFilterPermMask()
+{
+ mInventoryPanel->setFilterPermMask( getFilterPermMask() );
+}
+
+void LLFloaterTexturePicker::onSearchEdit(const LLString& search_string, void* user_data )
+{
+ LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;
+
+ std::string filter_text = search_string;
+
+ if (filter_text.empty()&& picker->mInventoryPanel->getFilterSubString().empty())
+ {
+ // current filter and new filter empty, do nothing
+ return;
+ }
+ std::string upper_case_search_string = filter_text;
+ LLString::toUpper(upper_case_search_string);
+
+ picker->mInventoryPanel->setFilterSubString(upper_case_search_string);
+
+ LLFolderView* root_folder = picker->mInventoryPanel->getRootFolder();
+
+ //if (search_string.size())
+ //{
+ // LLSelectFirstFilteredItem filter;
+ // root_folder->applyFunctorRecursively(filter);
+ // //...and scroll to show it
+ // root_folder->scrollToShowSelection();
+ //}
+
+ KEY key = gKeyboard->currentKey();
+
+ if ((key == KEY_RETURN || key == KEY_DOWN) && gKeyboard->currentMask(FALSE) == MASK_NONE)
+ {
+ if (search_string.size())
+ {
+ LLSelectFirstFilteredItem filter;
+ root_folder->applyFunctorRecursively(filter);
+ //...and scroll to show it
+ root_folder->scrollToShowSelection();
+ }
+
+ if (!root_folder->getCurSelectedItem())
+ {
+ LLFolderViewItem* itemp = root_folder->getItemByID(gAgent.getInventoryRootID());
+ if (itemp)
+ {
+ root_folder->setSelection(itemp, FALSE, FALSE);
+ }
+ }
+
+ // move focus to inventory proper
+ root_folder->setFocus(TRUE);
+ root_folder->scrollToShowSelection();
+ }
+}
+
+//static
+void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te, void *data )
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)data;
+
+ LLUUID inventory_item_id = self->findItemID(te.getID(), TRUE);
+ if (self && inventory_item_id.notNull())
+ {
+ gToolPipette->setResult(TRUE, "");
+ self->setImageID(te.getID());
+
+ self->mNoCopyTextureSelected = FALSE;
+ LLInventoryItem* itemp = gInventory.getItem(inventory_item_id);
+
+ if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // no copy texture
+ self->mNoCopyTextureSelected = TRUE;
+ }
+
+ self->commitIfImmediateSet();
+ }
+ else
+ {
+ gToolPipette->setResult(FALSE, "You do not have a copy this \nof texture in your inventory");
+ }
+}
+
+///////////////////////////////////////////////////////////////////////
+// LLTextureCtrl
+
+LLTextureCtrl::LLTextureCtrl(
+ const std::string& name,
+ const LLRect &rect,
+ const std::string& label,
+ const LLUUID &image_id,
+ const LLUUID &default_image_id,
+ const std::string& default_image_name )
+ :
+ LLUICtrl(name, rect, TRUE, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mDragCallback(NULL),
+ mDropCallback(NULL),
+ mOnCancelCallback(NULL),
+ mOnSelectCallback(NULL),
+ mBorderColor( gColors.getColor("DefaultHighlightLight") ),
+ mImageAssetID( image_id ),
+ mDefaultImageAssetID( default_image_id ),
+ mDefaultImageName( default_image_name ),
+ mLabel( label ),
+ mAllowNoTexture( FALSE ),
+ mImmediateFilterPermMask( PERM_NONE ),
+ mNonImmediateFilterPermMask( PERM_NONE ),
+ mCanApplyImmediately( FALSE ),
+ mNeedsRawImageData( FALSE ),
+ mValid( TRUE )
+{
+ mCaption = new LLTextBox( label,
+ LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
+ NULL,
+ LLFontGL::sSansSerifSmall );
+ mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCaption );
+
+ S32 image_top = mRect.getHeight();
+ S32 image_bottom = BTN_HEIGHT_SMALL;
+ S32 image_middle = (image_top + image_bottom) / 2;
+ S32 line_height = llround(LLFontGL::sSansSerifSmall->getLineHeight());
+
+ mTentativeLabel = new LLTextBox( "Multiple",
+ LLRect(
+ 0, image_middle + line_height / 2,
+ mRect.getWidth(), image_middle - line_height / 2 ),
+ NULL,
+ LLFontGL::sSansSerifSmall );
+ mTentativeLabel->setHAlign( LLFontGL::HCENTER );
+ mTentativeLabel->setFollowsAll();
+ addChild( mTentativeLabel );
+
+ LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ border_rect.mBottom += BTN_HEIGHT_SMALL;
+ mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
+ addChild(mBorder);
+
+ setEnabled(TRUE); // for the tooltip
+}
+
+
+LLTextureCtrl::~LLTextureCtrl()
+{
+ closeFloater();
+}
+
+// virtual
+LLXMLNodePtr LLTextureCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("label", TRUE)->setStringValue(getLabel());
+
+ node->createChild("default_image_name", TRUE)->setStringValue(getDefaultImageName());
+
+ node->createChild("allow_no_texture", TRUE)->setBoolValue(mAllowNoTexture);
+
+ node->createChild("can_apply_immediately", TRUE)->setBoolValue(mCanApplyImmediately );
+
+ return node;
+}
+
+LLView* LLTextureCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("texture_picker");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent);
+
+ LLString label;
+ node->getAttributeString("label", label);
+
+ LLString image_id("");
+ node->getAttributeString("image", image_id);
+
+ LLString default_image_id("");
+ node->getAttributeString("default_image", default_image_id);
+
+ LLString default_image_name("Default");
+ node->getAttributeString("default_image_name", default_image_name);
+
+ BOOL allow_no_texture = FALSE;
+ node->getAttributeBOOL("allow_no_texture", allow_no_texture);
+
+ BOOL can_apply_immediately = FALSE;
+ node->getAttributeBOOL("can_apply_immediately", can_apply_immediately);
+
+ if (label.empty())
+ {
+ label.assign(node->getValue());
+ }
+
+ LLTextureCtrl* texture_picker = new LLTextureCtrl(
+ name,
+ rect,
+ label,
+ LLUUID(image_id),
+ LLUUID(default_image_id),
+ default_image_name );
+ texture_picker->setAllowNoTexture(allow_no_texture);
+ texture_picker->setCanApplyImmediately(can_apply_immediately);
+
+ texture_picker->initFromXML(node, parent);
+
+ return texture_picker;
+}
+
+void LLTextureCtrl::setCaption(const LLString& caption)
+{
+ mCaption->setText( caption );
+}
+
+void LLTextureCtrl::setCanApplyImmediately(BOOL b)
+{
+ mCanApplyImmediately = b;
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp )
+ {
+ floaterp->setCanApplyImmediately(b);
+ }
+}
+
+void LLTextureCtrl::setVisible( BOOL visible )
+{
+ if( !visible )
+ {
+ closeFloater();
+ }
+ LLUICtrl::setVisible( visible );
+}
+
+void LLTextureCtrl::setEnabled( BOOL enabled )
+{
+ if( enabled )
+ {
+ setToolTip( "Click to choose a picture" );
+ }
+ else
+ {
+ setToolTip( "" );
+ //FIXME: would be better to keep floater open
+ // and show disabled state
+ closeFloater();
+ }
+
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp )
+ {
+ floaterp->setActive(enabled);
+ }
+
+ mCaption->setEnabled( enabled );
+
+ LLUICtrl::setEnabled( enabled );
+}
+
+void LLTextureCtrl::setValid(BOOL valid )
+{
+ mValid = valid;
+ if (!valid)
+ {
+ LLFloaterTexturePicker* pickerp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if (pickerp)
+ {
+ pickerp->setActive(FALSE);
+ }
+ }
+}
+
+
+// virtual
+void LLTextureCtrl::clear()
+{
+ setImageAssetID(LLUUID::null);
+}
+
+void LLTextureCtrl::setLabel(const LLString& label)
+{
+ mLabel = label;
+ mCaption->setText(label);
+}
+
+void LLTextureCtrl::showPicker(BOOL take_focus)
+{
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(mFloaterHandle);
+
+ // Show the dialog
+ if( floaterp )
+ {
+ floaterp->open( );
+ }
+ else
+ {
+ if( !mLastFloaterLeftTop.mX && !mLastFloaterLeftTop.mY )
+ {
+ gFloaterView->getNewFloaterPosition(&mLastFloaterLeftTop.mX, &mLastFloaterLeftTop.mY);
+ }
+ LLRect rect = gSavedSettings.getRect("TexturePickerRect");
+ rect.translate( mLastFloaterLeftTop.mX - rect.mLeft, mLastFloaterLeftTop.mY - rect.mTop );
+
+ floaterp = new LLFloaterTexturePicker(
+ this,
+ rect,
+ mLabel,
+ mImmediateFilterPermMask,
+ mNonImmediateFilterPermMask,
+ mCanApplyImmediately);
+ mFloaterHandle = floaterp->getHandle();
+
+ gFloaterView->getParentFloater(this)->addDependentFloater(floaterp);
+ floaterp->open();
+ }
+
+ if (take_focus)
+ {
+ floaterp->setFocus(TRUE);
+ }
+}
+
+
+void LLTextureCtrl::closeFloater()
+{
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp )
+ {
+ floaterp->setOwner(NULL);
+ floaterp->close();
+ }
+}
+
+// Allow us to download textures quickly when floater is shown
+class LLTextureFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ virtual void done()
+ {
+ // We need to find textures in all folders, so get the main
+ // background download going.
+ gInventory.startBackgroundFetch();
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+BOOL LLTextureCtrl::handleHover(S32 x, S32 y, MASK mask)
+{
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+}
+
+
+BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLUICtrl::handleMouseDown( x, y , mask );
+ if( handled )
+ {
+ showPicker(FALSE);
+
+ //grab textures first...
+ gInventory.startBackgroundFetch(gInventory.findCategoryUUIDForType(LLAssetType::AT_TEXTURE));
+ //...then start full inventory fetch.
+ gInventory.startBackgroundFetch();
+ }
+ return handled;
+}
+
+void LLTextureCtrl::onFloaterClose()
+{
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+
+ if (floaterp)
+ {
+ floaterp->setOwner(NULL);
+ mLastFloaterLeftTop.set( floaterp->getRect().mLeft, floaterp->getRect().mTop );
+ }
+
+ mFloaterHandle.markDead();
+}
+
+void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
+{
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+
+ if( floaterp && mEnabled)
+ {
+ if( floaterp->isDirty() )
+ {
+ setTentative( FALSE );
+ mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
+ lldebugs << "mImageItemID: " << mImageItemID << llendl;
+ mImageAssetID = floaterp->getAssetID();
+ lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
+ if (op == TEXTURE_SELECT && mOnSelectCallback)
+ {
+ mOnSelectCallback(this, mCallbackUserData);
+ }
+ else if (op == TEXTURE_CANCEL && mOnCancelCallback)
+ {
+ mOnCancelCallback(this, mCallbackUserData);
+ }
+ else
+ {
+ onCommit();
+ }
+ }
+ }
+}
+
+void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id )
+{
+ if( mImageAssetID != asset_id )
+ {
+ mImageItemID.setNull();
+ mImageAssetID = asset_id;
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp && getEnabled() )
+ {
+ floaterp->setImageID( asset_id );
+ floaterp->setDirty( FALSE );
+ }
+ }
+}
+
+BOOL LLTextureCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ // this downcast may be invalid - but if the second test below
+ // returns true, then the cast was valid, and we can perform
+ // the third test without problems.
+ LLInventoryItem* item = (LLInventoryItem*)cargo_data;
+ if (mEnabled && (cargo_type == DAD_TEXTURE) && allowDrop(item))
+ {
+ if (drop)
+ {
+ if(doDrop(item))
+ {
+ // This removes the 'Multiple' overlay, since
+ // there is now only one texture selected.
+ setTentative( FALSE );
+ onCommit();
+ }
+ }
+
+ *accept = ACCEPT_YES_SINGLE;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLTextureCtrl " << getName() << llendl;
+
+ return handled;
+}
+
+void LLTextureCtrl::draw()
+{
+ if( getVisible() )
+ {
+ mBorder->setKeyboardFocusHighlight(hasFocus());
+
+ if (mImageAssetID.isNull() || !mValid)
+ {
+ mTexturep = NULL;
+ }
+ else
+ {
+ mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO);
+ mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
+ }
+
+ // Border
+ LLRect border( 0, mRect.getHeight(), mRect.getWidth(), BTN_HEIGHT_SMALL );
+ gl_rect_2d( border, mBorderColor, FALSE );
+
+ // Interior
+ LLRect interior = border;
+ interior.stretch( -1 );
+
+ if( mTexturep )
+ {
+ if( mTexturep->getComponents() == 4 )
+ {
+ gl_rect_2d_checkerboard( interior );
+ }
+
+ gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep);
+ mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
+ }
+ else
+ {
+ gl_rect_2d( interior, LLColor4::grey, TRUE );
+
+ // Draw X
+ gl_draw_x( interior, LLColor4::black );
+ }
+
+ mTentativeLabel->setVisible( !mTexturep.isNull() && mTentative );
+
+ LLUICtrl::draw();
+ }
+}
+
+BOOL LLTextureCtrl::allowDrop(LLInventoryItem* item)
+{
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ PermissionMask item_perm_mask = 0;
+ if (copy) item_perm_mask |= PERM_COPY;
+ if (mod) item_perm_mask |= PERM_MODIFY;
+ if (xfer) item_perm_mask |= PERM_TRANSFER;
+
+ PermissionMask filter_perm_mask = mCanApplyImmediately ?
+ mImmediateFilterPermMask : mNonImmediateFilterPermMask;
+ if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask )
+ {
+ if(mDragCallback)
+ {
+ return mDragCallback(this, item, mCallbackUserData);
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLTextureCtrl::doDrop(LLInventoryItem* item)
+{
+ // call the callback if it exists.
+ if(mDropCallback)
+ {
+ // if it returns TRUE, we return TRUE, and therefore the
+ // commit is called above.
+ return mDropCallback(this, item, mCallbackUserData);
+ }
+
+ // no callback installed, so just set the image ids and carry on.
+ setImageAssetID( item->getAssetUUID() );
+ mImageItemID = item->getUUID();
+ return TRUE;
+}
+
+BOOL LLTextureCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char )
+ {
+ showPicker(TRUE);
+ return TRUE;
+ }
+ return LLUICtrl::handleUnicodeCharHere(uni_char, called_from_parent);
+}
+
+void LLTextureCtrl::setValue( LLSD value )
+{
+ setImageAssetID(value.asUUID());
+}
+
+LLSD LLTextureCtrl::getValue() const
+{
+ return LLSD(getImageAssetID());
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLToolTexEyedropper
+
+class LLToolTexEyedropper : public LLTool
+{
+public:
+ LLToolTexEyedropper( void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ), void* userdata );
+ virtual ~LLToolTexEyedropper();
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+protected:
+ void (*mCallback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata );
+ void* mCallbackUserData;
+};
+
+
+LLToolTexEyedropper::LLToolTexEyedropper(
+ void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ),
+ void* userdata )
+ : LLTool("texeyedropper"),
+ mCallback( callback ),
+ mCallbackUserData( userdata )
+{
+}
+
+LLToolTexEyedropper::~LLToolTexEyedropper()
+{
+}
+
+
+BOOL LLToolTexEyedropper::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if (hit_obj &&
+ !hit_obj->isAvatar())
+ {
+ if( (0 <= gLastHitObjectFace) && (gLastHitObjectFace < hit_obj->getNumTEs()) )
+ {
+ LLViewerImage* image = hit_obj->getTEImage( gLastHitObjectFace );
+ if( image )
+ {
+ if( mCallback )
+ {
+ mCallback( hit_obj->getID(), image->getID(), mCallbackUserData );
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLToolTexEyedropper::handleHover(S32 x, S32 y, MASK mask)
+{
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolTexEyedropper" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_CROSS); // TODO: better cursor
+ return TRUE;
+}
+
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
new file mode 100644
index 0000000000..fa4e71d266
--- /dev/null
+++ b/indra/newview/lltexturectrl.h
@@ -0,0 +1,158 @@
+/**
+ * @file lltexturectrl.h
+ * @author Richard Nelson, James Cook
+ * @brief LLTextureCtrl class header file including related functions
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTURECTRL_H
+#define LL_LLTEXTURECTRL_H
+
+#include "llcoord.h"
+#include "llfloater.h"
+#include "llstring.h"
+#include "lluictrl.h"
+#include "llpermissionsflags.h"
+
+class LLButton;
+class LLFloaterTexturePicker;
+class LLInventoryItem;
+class LLTextBox;
+class LLViewBorder;
+
+// used for setting drag & drop callbacks.
+typedef BOOL (*drag_n_drop_callback)(LLUICtrl*, LLInventoryItem*, void*);
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// LLTextureCtrl
+
+
+class LLTextureCtrl
+: public LLUICtrl
+{
+public:
+ typedef enum e_texture_pick_op
+ {
+ TEXTURE_CHANGE,
+ TEXTURE_SELECT,
+ TEXTURE_CANCEL
+ } ETexturePickOp;
+
+public:
+ LLTextureCtrl(
+ const std::string& name, const LLRect& rect,
+ const std::string& label,
+ const LLUUID& image_id,
+ const LLUUID& default_image_id,
+ const std::string& default_image_name );
+ virtual ~LLTextureCtrl();
+
+ // LLView interface
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TEXTURE_PICKER; }
+ virtual LLString getWidgetTag() const { return LL_TEXTURE_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+
+ virtual void draw();
+ virtual void setVisible( BOOL visible );
+ virtual void setEnabled( BOOL enabled );
+
+ void setValid(BOOL valid);
+
+ // LLUICtrl interface
+ virtual void clear();
+
+ // Takes a UUID, wraps get/setImageAssetID
+ virtual void setValue( LLSD value );
+ virtual LLSD getValue() const;
+
+ // LLTextureCtrl interface
+ void showPicker(BOOL take_focus);
+ void setLabel(const LLString& label);
+ const LLString& getLabel() const { return mLabel; }
+
+ void setAllowNoTexture( BOOL b ) { mAllowNoTexture = b; }
+ bool getAllowNoTexture() const { return mAllowNoTexture; }
+
+ const LLUUID& getImageItemID() { return mImageItemID; }
+
+ void setImageAssetID(const LLUUID &image_asset_id);
+ const LLUUID& getImageAssetID() const { return mImageAssetID; }
+
+ void setDefaultImageAssetID( const LLUUID& id ) { mDefaultImageAssetID = id; }
+
+ const LLString& getDefaultImageName() const { return mDefaultImageName; }
+ const LLUUID& getDefaultImageAssetID() const { return mDefaultImageAssetID; }
+
+ void setCaption(const LLString& caption);
+ void setCanApplyImmediately(BOOL b);
+
+ void setImmediateFilterPermMask(PermissionMask mask)
+ { mImmediateFilterPermMask = mask; }
+ void setNonImmediateFilterPermMask(PermissionMask mask)
+ { mNonImmediateFilterPermMask = mask; }
+ PermissionMask getImmediateFilterPermMask() { return mImmediateFilterPermMask; }
+ PermissionMask getNonImmediateFilterPermMask() { return mNonImmediateFilterPermMask; }
+
+ void closeFloater();
+
+ void onFloaterClose();
+ void onFloaterCommit(ETexturePickOp op);
+
+ // This call is returned when a drag is detected. Your callback
+ // should return TRUE if the drag is acceptable.
+ void setDragCallback(drag_n_drop_callback cb) { mDragCallback = cb; }
+
+ // This callback is called when the drop happens. Return TRUE if
+ // the drop happened - resulting in an on commit callback, but not
+ // necessariliy any other change.
+ void setDropCallback(drag_n_drop_callback cb) { mDropCallback = cb; }
+
+ void setOnCancelCallback(LLUICtrlCallback cb) { mOnCancelCallback = cb; }
+
+ void setOnSelectCallback(LLUICtrlCallback cb) { mOnSelectCallback = cb; }
+
+private:
+ BOOL allowDrop(LLInventoryItem* item);
+ BOOL doDrop(LLInventoryItem* item);
+
+private:
+ drag_n_drop_callback mDragCallback;
+ drag_n_drop_callback mDropCallback;
+ LLUICtrlCallback mOnCancelCallback;
+ LLUICtrlCallback mOnSelectCallback;
+ LLPointer<LLViewerImage> mTexturep;
+ LLColor4 mBorderColor;
+ LLUUID mImageItemID;
+ LLUUID mImageAssetID;
+ LLUUID mDefaultImageAssetID;
+ LLString mDefaultImageName;
+ LLViewHandle mFloaterHandle;
+ LLTextBox* mTentativeLabel;
+ LLTextBox* mCaption;
+ LLString mLabel;
+ BOOL mAllowNoTexture; // If true, the user can select "none" as an option
+ LLCoordGL mLastFloaterLeftTop;
+ PermissionMask mImmediateFilterPermMask;
+ PermissionMask mNonImmediateFilterPermMask;
+ BOOL mCanApplyImmediately;
+ BOOL mNeedsRawImageData;
+ LLViewBorder* mBorder;
+ BOOL mValid;
+};
+
+// XUI HACK: When floaters converted, switch this file to lltexturepicker.h/cpp
+// and class to LLTexturePicker
+#define LLTexturePicker LLTextureCtrl
+
+#endif // LL_LLTEXTURECTRL_H
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
new file mode 100644
index 0000000000..cc27fd4b9e
--- /dev/null
+++ b/indra/newview/lltexturefetch.cpp
@@ -0,0 +1,744 @@
+/**
+ * @file llviewerimage.cpp
+ * @brief Object which handles a received image (and associated texture(s))
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llstl.h"
+
+#include "lltexturefetch.h"
+
+#include "llworkerthread.h"
+#include "llimage.h"
+#include "llvfs.h"
+
+#include "llviewerimage.h"
+#include "viewer.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+class LLTextureFetchWorker : public LLWorkerClass
+{
+ friend class LLTextureFetchImpl;
+
+public:
+ // From LLWorkerClass
+ static void initClass(bool threaded, bool runalways);
+ static void updateClass();
+ static void cleanupClass();
+
+ // New methods
+ static LLTextureFetchWorker* getWorker(const LLUUID& id, const LLHost& host,
+ F32 mPriority, S32 discard,
+ BOOL needs_aux = FALSE);
+ static LLTextureFetchWorker* getActiveWorker(const LLUUID& id);
+
+private:
+ static void sendRequestListToSimulators();
+
+public:
+ virtual bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
+
+ ~LLTextureFetchWorker();
+ void relese() { --mActiveCount; }
+
+
+protected:
+ LLTextureFetchWorker(const LLUUID& id, const LLHost& host, F32 mPriority, S32 discard);
+
+private:
+ virtual void startWork(S32 param); // called from addWork() (MAIN THREAD)
+ virtual void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
+
+ void setImagePriority(F32 priority);
+ void setDesiredDiscard(S32 discard);
+ void insertPacket(S32 index, U8* data, S32 size);
+ void clearPackets();
+ U32 calcWorkPriority();
+ bool startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type);
+ bool loadFromVFS();
+ bool processSimulatorPackets();
+ void startDecode();
+ bool decodeImage();
+
+ void lockWorkData() { mWorkMutex.lock(); }
+ void unlockWorkData() { mWorkMutex.unlock(); }
+
+ static void lockQueue() { sDataMutex->lock(); }
+ static void unlockQueue() { sDataMutex->unlock(); }
+
+private:
+ enum e_state
+ {
+ INIT = 1,
+ LOAD_FROM_CACHE,
+ LOAD_FROM_SIMULATOR,
+ WRITE_TO_VFS,
+ DECODE_IMAGE,
+ DECODE_IMAGE_UPDATE,
+ DONE
+ };
+ e_state mState;
+ LLPointer<LLImageFormatted> mFormattedImage;
+ LLPointer<LLImageRaw> mRawImage;
+ LLPointer<LLImageRaw> mAuxImage;
+ LLUUID mID;
+ LLHost mHost;
+ F32 mPriority;
+ S32 mDesiredDiscard;
+ S32 mRequestedDiscard;
+ S32 mDecodedDiscard;
+ LLFrameTimer mRequestedTimer;
+ LLFrameTimer mIdleTimer;
+ LLVFSThread::handle_t mFileHandle;
+ U8* mBuffer;
+ S32 mBufferSize;
+ S32 mRequestedSize;
+ BOOL mLoaded;
+ BOOL mRequested;
+ BOOL mDecoded;
+ BOOL mNeedsAux;
+ S32 mActiveCount;
+
+ // Work Data
+ LLMutex mWorkMutex;
+ struct PacketData
+ {
+ PacketData(U8* data, S32 size) { mData = data; mSize = size; }
+ ~PacketData() { clearData(); }
+ void clearData() { delete mData; mData = NULL; }
+ U8* mData;
+ U32 mSize;
+ };
+ std::vector<PacketData*> mPackets;
+ S32 mLastPacket;
+ S32 mTotalPackets;
+ S32 mTotalBytes;
+
+ // Class variables (statics)
+
+ static LLWorkerThread* sWorkerThread;
+ static LLMutex* sDataMutex;
+
+ // Map of all requests by UUID
+ typedef std::map<LLUUID,LLTextureFetchWorker*> map_t;
+ static map_t sRequests;
+
+ // Set of requests that require network data
+ typedef std::set<LLTextureFetchWorker*> queue_t ;
+ static queue_t sNetworkQueue;
+
+ static LLFrameTimer sNetworkTimer;
+};
+
+//statics
+LLTextureFetchWorker::map_t LLTextureFetchWorker::sRequests;
+LLTextureFetchWorker::queue_t LLTextureFetchWorker::sNetworkQueue;
+LLFrameTimer LLTextureFetchWorker::sNetworkTimer;
+LLWorkerThread* LLTextureFetchWorker::sWorkerThread = NULL;
+LLMutex* LLTextureFetchWorker::sDataMutex = NULL;
+
+// called from MAIN THREAD
+//static
+void LLTextureFetchWorker::initClass(bool threaded, bool runalways)
+{
+ sWorkerThread = new LLWorkerThread(threaded, runalways);
+ sDataMutex = new LLMutex(sWorkerThread->getAPRPool());
+}
+
+// called from MAIN THREAD
+//static
+void LLTextureFetchWorker::updateClass()
+{
+ const F32 REQUEST_TIME = 1.f;
+ const F32 MIN_IDLE_TIME = 1.f * 60.f; // 1 minute
+ const F32 MAX_IDLE_TIME = 5.f * 60.f; // 5 minutes
+ const S32 MIN_IDLE_COUNT = 16; // always keep last 16 idle requests
+ const F32 MAX_IDLE_COUNT = 1024; // max number of idle requests
+
+ // Periodically, gather the list of textures that need data from the network
+ // And send the requests out to the simulators
+ if (sNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME)
+ {
+ sNetworkTimer.reset();
+ sendRequestListToSimulators();
+ }
+ // Remove any old requests (releasing their raw data)
+ typedef std::pair<F32, LLTextureFetchWorker*> idle_pair;
+ typedef std::set<idle_pair, compare_pair_greater<F32,LLTextureFetchWorker*> > idle_set;
+ idle_set remove_set;
+ for (map_t::iterator iter = sRequests.begin(); iter != sRequests.end(); ++iter)
+ {
+ LLTextureFetchWorker* worker = iter->second;
+ if (worker->mActiveCount > 0)
+ continue;
+ if (worker->haveWork())
+ continue;
+ F32 idletime = worker->mIdleTimer.getElapsedTimeF32();
+ if (idletime < MIN_IDLE_TIME)
+ continue;
+ remove_set.insert(std::make_pair(idletime, worker));
+ }
+ S32 num_left = remove_set.size();
+ for (idle_set::iterator iter = remove_set.begin(); iter != remove_set.end(); ++iter)
+ {
+ if (num_left <= MIN_IDLE_COUNT)
+ break;
+ if (iter->first < MAX_IDLE_TIME &&
+ num_left < MAX_IDLE_COUNT)
+ break;
+ num_left--;
+ }
+}
+
+// called from MAIN THREAD
+//static
+void LLTextureFetchWorker::sendRequestListToSimulators()
+{
+ const F32 LAZY_FLUSH_TIMEOUT = 60.f;
+ const S32 IMAGES_PER_REQUEST = 50;
+ // Send requests
+ typedef std::map< LLHost, std::vector<LLTextureFetchWorker*> > work_request_map_t;
+ work_request_map_t requests;
+ for (queue_t::iterator iter = sNetworkQueue.begin(); iter != sNetworkQueue.end(); ++iter)
+ {
+ LLTextureFetchWorker* req = *iter;
+ if (req->haveWork())
+ {
+ continue; // busy
+ }
+ if ((req->mRequestedDiscard == req->mDesiredDiscard) &&
+ (req->mRequestedTimer.getElapsedTimeF32() < LAZY_FLUSH_TIMEOUT))
+ {
+ continue;
+ }
+ req->mRequestedDiscard = req->mDesiredDiscard;
+ req->mRequestedTimer.reset();
+ requests[req->mHost].push_back(req);
+ }
+ for (work_request_map_t::iterator iter1 = requests.begin();
+ iter1 != requests.end(); ++iter1)
+ {
+ LLHost host = iter1->first;
+ // invalid host = load from static VFS
+ if (host != LLHost::invalid)
+ {
+ S32 request_count = 0;
+ for (std::vector<LLTextureFetchWorker*>::iterator iter2 = iter1->second.begin();
+ iter2 != iter1->second.end(); ++iter2)
+ {
+ LLTextureFetchWorker* req = *iter2;
+ if (0 == request_count)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+ }
+ S32 packet = req->mLastPacket + 1;
+ gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+ gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
+ gMessageSystem->addS32Fast(_PREHASH_DiscardLevel, req->mDesiredDiscard);
+ gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mPriority);
+ gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
+
+ request_count++;
+ if (request_count >= IMAGES_PER_REQUEST)
+ {
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ request_count = 0;
+ }
+ }
+ if (request_count >= IMAGES_PER_REQUEST)
+ {
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ }
+ }
+ }
+}
+
+//static
+LLTextureFetchWorker* LLTextureFetchWorker::getWorker(const LLUUID& id,
+ const LLHost& host,
+ F32 priority,
+ S32 discard,
+ BOOL needs_aux)
+{
+ LLTextureFetchWorker* res;
+ lockQueue();
+ map_t::iterator iter = sRequests.find(id);
+ if (iter != sRequests.end())
+ {
+ res = iter->second;
+ if (res->mHost != host)
+ {
+ llerrs << "LLTextureFetchWorker::getWorker called with multiple hosts" << llendl;
+ }
+ res->setImagePriority(priority);
+ res->setDesiredDiscard(discard);
+
+ }
+ else
+ {
+ res = new LLTextureFetchWorker(id, host, priority, discard);
+ }
+ unlockQueue();
+ res->mActiveCount++;
+ res->mNeedsAux = needs_aux;
+ return res;
+}
+
+LLTextureFetchWorker* LLTextureFetchWorker::getActiveWorker(const LLUUID& id)
+{
+ LLTextureFetchWorker* res = NULL;
+ lockQueue();
+ map_t::iterator iter = sRequests.find(id);
+ if (iter != sRequests.end())
+ {
+ res = iter->second;
+ }
+ unlockQueue();
+ return res;
+}
+
+LLTextureFetchWorker::LLTextureFetchWorker(const LLUUID& id, // Image UUID
+ const LLHost& host, // Simulator host
+ F32 priority, // Priority
+ S32 discard) // Desired discard level
+ : LLWorkerClass(sWorkerThread, "TextureFetch"),
+ mState(INIT),
+ mID(id),
+ mHost(host),
+ mPriority(priority),
+ mDesiredDiscard(discard),
+ mRequestedDiscard(-1),
+ mDecodedDiscard(-1),
+ mFileHandle(LLVFSThread::nullHandle()),
+ mBuffer(NULL),
+ mBufferSize(0),
+ mRequestedSize(0),
+ mLoaded(FALSE),
+ mRequested(FALSE),
+ mDecoded(FALSE),
+ mActiveCount(0),
+ mWorkMutex(sWorkerThread->getAPRPool()),
+ mLastPacket(-1),
+ mTotalPackets(0),
+ mTotalBytes(0)
+{
+ lockQueue();
+ sRequests[mID] = this;
+ unlockQueue();
+ addWork(0, calcWorkPriority());
+}
+
+LLTextureFetchWorker::~LLTextureFetchWorker()
+{
+ lockQueue();
+ mFormattedImage = NULL;
+ map_t::iterator iter = sRequests.find(mID);
+ if (iter != sRequests.end() && iter->second == this)
+ {
+ sRequests.erase(iter);
+ }
+ sNetworkQueue.erase(this);
+ unlockQueue();
+ clearPackets();
+}
+
+void LLTextureFetchWorker::clearPackets()
+{
+ lockWorkData();
+ for_each(mPackets.begin(), mPackets.end(), DeletePointer());
+ mPackets.clear();
+ unlockWorkData();
+}
+
+U32 LLTextureFetchWorker::calcWorkPriority()
+{
+ F32 priority_scale = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerImage::maxDecodePriority();
+ U32 priority = (U32)(mPriority * priority_scale);
+ return LLWorkerThread::PRIORITY_NORMAL | priority;
+}
+
+void LLTextureFetchWorker::setDesiredDiscard(S32 discard)
+{
+ if (mDesiredDiscard != discard)
+ {
+ mDesiredDiscard = discard;
+ if (!haveWork())
+ {
+ addWork(0, calcWorkPriority());
+ }
+ }
+}
+
+void LLTextureFetchWorker::setImagePriority(F32 priority)
+{
+ if (priority != mPriority)
+ {
+ mPriority = priority;
+ setPriority(calcWorkPriority());
+ }
+}
+
+void LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
+{
+ PacketData* packet = new PacketData(data, size);
+
+ lockWorkData();
+ if (index >= (S32)mPackets.size())
+ {
+ mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
+ }
+ if (mPackets[index] != NULL)
+ {
+ llwarns << "LLTextureFetchWorker::insertPacket called for duplicate packet: " << index << llendl;
+ }
+ mPackets[index] = packet;
+ while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
+ {
+ ++mLastPacket;
+ }
+ unlockWorkData();
+}
+
+// Called from LLWorkerThread::processRequest()
+bool LLTextureFetchWorker::doWork(S32 param)
+{
+ switch(mState)
+ {
+ case INIT:
+ {
+ // fall through
+ }
+ case LOAD_FROM_CACHE:
+ {
+ // Load any existing data from the cache
+ if (mFileHandle == LLVFSThread::nullHandle())
+ {
+ bool res = startVFSLoad(gVFS, LLAssetType::AT_TEXTURE);
+ if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE_TGA);
+ if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE);
+ if (!res)
+ {
+ // Didn't load from VFS
+ mFormattedImage = new LLImageJ2C;
+ mState = LOAD_FROM_SIMULATOR;
+ }
+ }
+ if (mFileHandle != LLVFSThread::nullHandle())
+ {
+ if (!loadFromVFS())
+ {
+ return false; // not done
+ }
+ if (!mLoaded)
+ {
+ llwarns << "Load from VFS failed on: " << mID << llendl;
+ return true;
+ }
+ bool res = mFormattedImage->setData(mBuffer, mBufferSize);
+ if (!res)
+ {
+ llwarns << "loadLocalImage() - setData() failed" << llendl;
+ mFormattedImage->deleteData();
+ return true;
+ }
+ // Successfully loaded
+ if (mFormattedImage->getDiscardLevel() <= mRequestedDiscard)
+ {
+ // we have enough data, decode it
+ mState = DECODE_IMAGE;
+ mRequestedSize = mBufferSize;
+ }
+ else
+ {
+ // need more data
+ mState = LOAD_FROM_SIMULATOR;
+ mRequestedSize = mFormattedImage->calcDataSize(mRequestedDiscard);
+ }
+ }
+ return false;
+ }
+ case LOAD_FROM_SIMULATOR:
+ {
+ if (!mRequested)
+ {
+ lockQueue();
+ sNetworkQueue.insert(this);
+ unlockQueue();
+ mRequested = TRUE;
+ }
+ if (processSimulatorPackets())
+ {
+ mState = WRITE_TO_VFS;
+ }
+ return false;
+ }
+ case WRITE_TO_VFS:
+ {
+ mState = DECODE_IMAGE;
+ // fall through
+ }
+ case DECODE_IMAGE:
+ {
+ startDecode();
+ mState = DECODE_IMAGE_UPDATE;
+ // fall through
+ }
+ case DECODE_IMAGE_UPDATE:
+ {
+ if (decodeImage())
+ {
+ mState = DONE;
+ }
+ return false;
+ }
+ case DONE:
+ {
+ // Do any cleanup here
+ // Destroy the formatted image, we don't need it any more (raw image is still valid)
+ mFormattedImage = NULL;
+ mIdleTimer.reset();
+ return true;
+ }
+ default:
+ {
+ llerrs << "LLTextureFetchWorker::doWork() has illegal state" << llendl;
+ return true;
+ }
+ }
+}
+
+// Called from MAIN thread
+void LLTextureFetchWorker::startWork(S32 param)
+{
+}
+
+void LLTextureFetchWorker::endWork(S32 param, bool aborted)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type)
+{
+ // Start load from VFS if it's there
+ if (vfs->getExists(mID, asset_type))
+ {
+ mBufferSize = vfs->getSize(mID, asset_type);
+ mBuffer = new U8[mBufferSize];
+ mFileHandle = LLVFSThread::sLocal->read(vfs, mID, asset_type, mBuffer, 0, mBufferSize);
+ if (mFileHandle == LLVFSThread::nullHandle())
+ {
+ llwarns << "loadLocalImage() - vfs read failed in static VFS: " << mID << llendl;
+ delete mBuffer;
+ mBuffer = NULL;
+ return false;
+ }
+ if (asset_type == LLAssetType::AT_TEXTURE_TGA)
+ {
+ mFormattedImage = new LLImageTGA;
+ }
+ else if (asset_type == LLAssetType::AT_TEXTURE)
+ {
+ mFormattedImage = new LLImageJ2C;
+ }
+ else
+ {
+ llerrs << "LLTextureFetchWorker::startVFSLoad called with bad asset type: " << asset_type << llendl;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool LLTextureFetchWorker::loadFromVFS()
+{
+ LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
+
+ llassert(mLoaded == FALSE);
+
+ // Check loading status
+ LLVFSThread::status_t status = LLVFSThread::sLocal->getRequestStatus(mFileHandle);
+ if (status == LLVFSThread::STATUS_QUEUED || status == LLVFSThread::STATUS_INPROGRESS)
+ {
+ return false;
+ }
+ else if (status == LLVFSThread::STATUS_COMPLETE)
+ {
+ mLoaded = TRUE;
+ return true;
+ }
+ else
+ {
+ llwarns << "loadLocalImage() - vfs read failed" << llendl;
+ LLVFSThread::Request* req = (LLVFSThread::Request*)LLVFSThread::sLocal->getRequest(mFileHandle);
+ if (req && mFormattedImage.notNull())
+ {
+ LLVFS* vfs = req->getVFS();
+ LLAssetType::EType asset_type = mFormattedImage->getCodec() == IMG_CODEC_TGA ? LLAssetType::AT_TEXTURE_TGA : LLAssetType::AT_TEXTURE;
+ vfs->removeFile(mID, asset_type);
+ }
+ return true;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::processSimulatorPackets()
+{
+ bool res = false;
+ lockWorkData();
+ if (mLastPacket >= 0)
+ {
+ S32 data_size = 0;
+ for (S32 i = 0; i<=mLastPacket; i++)
+ {
+ data_size += mPackets[i]->mSize;
+ }
+ if (data_size >= mRequestedSize || mLastPacket == mTotalPackets)
+ {
+ /// We have enough (or all) data, copy it into mBuffer
+ if (mBufferSize < data_size)
+ {
+ delete mBuffer;
+ mBuffer = new U8[data_size];
+ mBufferSize = data_size;
+ }
+ S32 offset = 0;
+ for (S32 i = 0; i<=mLastPacket; i++)
+ {
+ memcpy(mBuffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
+ offset += mPackets[i]->mSize;
+ }
+ res = true;
+ }
+ }
+ unlockWorkData();
+ return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::startDecode()
+{
+ mRawImage = NULL;
+ mAuxImage = NULL;
+}
+
+bool LLTextureFetchWorker::decodeImage()
+{
+ const F32 MAX_DECODE_TIME = .001f; // 1 ms
+ if (mRawImage->getDataSize() == 0)
+ {
+ if (!mFormattedImage->requestDecodedData(mRawImage, -1, MAX_DECODE_TIME))
+ {
+ return false;
+ }
+ mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image
+ }
+ if (mNeedsAux && mAuxImage->getDataSize() == 0)
+ {
+ if (!mFormattedImage->requestDecodedAuxData(mAuxImage, 4, -1, MAX_DECODE_TIME ))
+ {
+ return false;
+ }
+ mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image
+ }
+ mDecodedDiscard = mFormattedImage->getDiscardLevel();
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#if 0
+// static
+void LLTextureFetchWorker::receiveImageHeader(LLMessageSystem *msg, void **user_data)
+{
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
+
+ // Receive image header, copy into image object and decompresses
+ // if this is a one-packet image.
+
+ gImageList.sTextureBits += msg->getReceiveBytes();
+ gImageList.sTexturePackets++;
+
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
+// LLString ip_string(u32_to_ip_string(msg->getSenderIP()));
+
+ LLTextureFetchWorker* worker = getActiveWorker(id);
+ if (!worker)
+ {
+ llwarns << "receiveImageHeader for non active worker: " << id << llendl;
+ return;
+ }
+ worker->mRequestedTimer.reset();
+
+ // check to see if we've gotten this packet before
+ if (worker->mLastPacket != -1)
+ {
+ llwarns << "Img: " << id << ":" << " Duplicate Image Header" << llendl;
+ return;
+ }
+
+ // Copy header data into image object
+ worker->lockWorkData();
+ msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, image->mDataCodec);
+ msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, image->mTotalPackets);
+ msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, image->mTotalBytes);
+ if (0 == image->mTotalPackets)
+ {
+ llwarns << "Img: " << id << ":" << " Number of packets is 0" << llendl;
+ }
+ worker->unlockWorkData();
+
+ U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data);
+ if (data_size)
+ {
+ U8 *data = new U8[data_size];
+ msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
+ worker->insertPacket(0, data, data_size)
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// static
+void LLTextureFetchWorker::receiveImagePacket(LLMessageSystem *msg, void **user_data)
+{
+ LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
+
+ gImageList.sTextureBits += msg->getReceiveBytes();
+ gImageList.sTexturePackets++;
+
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
+// LLString ip_string(u32_to_ip_string(msg->getSenderIP()));
+
+ U16 packet_num;
+ msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num);
+
+ LLTextureFetchWorker* worker = getActiveWorker(id);
+ if (!worker)
+ {
+ llwarns << "receiveImageHeader for non active worker: " << id << llendl;
+ return;
+ }
+ worker->mRequestedTimer.reset();
+
+ U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data);
+ if (data_size)
+ {
+ U8 *data = new U8[data_size];
+ msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
+ worker->insertPacket(0, data, data_size)
+ }
+}
+#endif
+
+ //////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
new file mode 100644
index 0000000000..9d841ae23a
--- /dev/null
+++ b/indra/newview/lltexturefetch.h
@@ -0,0 +1,28 @@
+/**
+ * @file lltexturefetch.h
+ * @brief Object for managing texture fetches.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTUREFETCH_H
+#define LL_LLTEXTUREFETCH_H
+
+#include "llworkerthread.h"
+
+class LLViewerImage;
+
+// Interface class
+class LLTextureFetch
+{
+public:
+ static void initClass();
+ static void updateClass();
+ static void cleanupClass();
+
+ static LLWorkerClass::handle_t addRequest(LLImageFormatted* image, S32 discard);
+ static bool getRequestFinished(LLWorkerClass::handle_t handle);
+};
+
+#endif LL_LLTEXTUREFETCH_H
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
new file mode 100644
index 0000000000..01bc0bd5fd
--- /dev/null
+++ b/indra/newview/lltextureview.cpp
@@ -0,0 +1,304 @@
+/**
+ * @file lltextureview.cpp
+ * @brief LLTextureView class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <set>
+
+#include "lltextureview.h"
+
+#include "llrect.h"
+#include "llerror.h"
+
+#include "viewer.h"
+#include "llui.h"
+
+#include "llviewerimagelist.h"
+#include "llselectmgr.h"
+#include "llviewerobject.h"
+#include "llviewerimage.h"
+#include "llhoverview.h"
+
+LLTextureView *gTextureView = NULL;
+
+//static
+std::set<LLViewerImage*> LLTextureView::sDebugImages;
+
+// Used for sorting
+struct SortTextureBars
+{
+ bool operator()(const LLView* i1, const LLView* i2)
+ {
+ LLTextureBar* bar1p = (LLTextureBar*)i1;
+ LLTextureBar* bar2p = (LLTextureBar*)i2;
+ LLViewerImage *i1p = bar1p->mImagep;
+ LLViewerImage *i2p = bar2p->mImagep;
+ F32 pri1 = i1p->getDecodePriority(); // i1p->mRequestedDownloadPriority
+ F32 pri2 = i2p->getDecodePriority(); // i2p->mRequestedDownloadPriority
+ if (pri1 > pri2)
+ return true;
+ else if (pri2 > pri1)
+ return false;
+ else
+ return i1p->getID() < i2p->getID();
+ }
+};
+
+LLTextureView::LLTextureView(const std::string& name, const LLRect& rect)
+: LLContainerView(name, rect)
+{
+ setVisible(FALSE);
+ mFreezeView = FALSE;
+
+ mNumTextureBars = 0;
+ setDisplayChildren(TRUE);
+ mGLTexMemBar = 0;
+}
+
+LLTextureView::~LLTextureView()
+{
+ // Children all cleaned up by default view destructor.
+ delete mGLTexMemBar;
+ mGLTexMemBar = 0;
+}
+
+EWidgetType LLTextureView::getWidgetType() const
+{
+ return WIDGET_TYPE_TEXTURE_VIEW;
+}
+
+LLString LLTextureView::getWidgetTag() const
+{
+ return LL_TEXTURE_VIEW_TAG;
+}
+
+
+typedef std::pair<F32,LLViewerImage*> decode_pair_t;
+struct compare_decode_pair
+{
+ bool operator()(const decode_pair_t& a, const decode_pair_t& b)
+ {
+ return a.first > b.first;
+ }
+};
+
+void LLTextureView::draw()
+{
+ if (!mFreezeView)
+ {
+// LLViewerObject *objectp;
+// S32 te;
+
+ for_each(mTextureBars.begin(), mTextureBars.end(), DeletePointer());
+ mTextureBars.clear();
+
+ delete mGLTexMemBar;
+ mGLTexMemBar = 0;
+
+ typedef std::multiset<decode_pair_t, compare_decode_pair > display_list_t;
+ display_list_t display_image_list;
+
+ for (LLViewerImageList::image_list_t::iterator iter = gImageList.mImageList.begin();
+ iter != gImageList.mImageList.end(); )
+ {
+ LLPointer<LLViewerImage> imagep = *iter++;
+#if 1
+ if (imagep->getDontDiscard())
+ {
+ continue;
+ }
+#endif
+ if (imagep->isMissingAsset())
+ {
+ continue;
+ }
+
+#define HIGH_PRIORITY 100000000.f
+ F32 pri = imagep->getDecodePriority();
+
+ if (sDebugImages.find(imagep) != sDebugImages.end())
+ {
+ pri += 3*HIGH_PRIORITY;
+ }
+
+#if 1
+ if (pri < HIGH_PRIORITY && gSelectMgr)
+ {
+ S32 te;
+ LLViewerObject *objectp;
+ for (gSelectMgr->getFirstTE(&objectp, &te); objectp; gSelectMgr->getNextTE(&objectp, &te))
+ {
+ if (imagep == objectp->getTEImage(te))
+ {
+ pri += 2*HIGH_PRIORITY;
+ break;
+ }
+ }
+ }
+#endif
+#if 1
+ if (pri < HIGH_PRIORITY)
+ {
+ LLViewerObject *objectp = gHoverView->getLastHoverObject();
+ if (objectp)
+ {
+ S32 tex_count = objectp->getNumTEs();
+ for (S32 i = 0; i < tex_count; i++)
+ {
+ if (imagep == objectp->getTEImage(i))
+ {
+ pri += 2*HIGH_PRIORITY;
+ break;
+ }
+ }
+ }
+ }
+#endif
+#if 0
+ if (pri < HIGH_PRIORITY)
+ {
+ if (imagep->mBoostPriority)
+ {
+ pri += 4*HIGH_PRIORITY;
+ }
+ }
+#endif
+#if 1
+ if (pri > 0.f && pri < HIGH_PRIORITY)
+ {
+ if (imagep->mLastPacketTimer.getElapsedTimeF32() < 1.f ||
+ imagep->mLastDecodeTime.getElapsedTimeF32() < 1.f)
+ {
+ pri += 1*HIGH_PRIORITY;
+ }
+ }
+#endif
+// if (pri > 0.0f)
+ {
+ display_image_list.insert(std::make_pair(pri, imagep));
+ }
+ }
+
+ static S32 max_count = 50;
+ S32 count = 0;
+ for (display_list_t::iterator iter = display_image_list.begin();
+ iter != display_image_list.end(); iter++)
+ {
+ LLViewerImage* imagep = iter->second;
+ S32 hilite = 0;
+ F32 pri = iter->first;
+ if (pri >= 1 * HIGH_PRIORITY)
+ {
+ hilite = (S32)((pri+1) / HIGH_PRIORITY) - 1;
+ }
+ if ((hilite || count < max_count-10) && (count < max_count))
+ {
+ if (addBar(imagep, hilite))
+ {
+ count++;
+ }
+ }
+ }
+
+ sortChildren(SortTextureBars());
+
+ mGLTexMemBar = new LLGLTexMemBar("gl texmem bar");
+ addChild(mGLTexMemBar);
+
+ reshape(mRect.getWidth(), mRect.getHeight(), TRUE);
+
+ /*
+ count = gImageList.getNumImages();
+ char info_string[512];
+ sprintf(info_string, "Global Info:\nTexture Count: %d", count);
+ mInfoTextp->setText(info_string);
+ */
+
+
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *viewp = *child_iter;
+ if (viewp->getRect().mBottom < 0)
+ {
+ viewp->setVisible(FALSE);
+ }
+ }
+ }
+
+ LLContainerView::draw();
+
+}
+
+BOOL LLTextureView::addBar(LLViewerImage *imagep, S32 hilite)
+{
+ if (!imagep)
+ {
+ return FALSE;
+ }
+
+ LLTextureBar *barp;
+ LLRect r;
+
+ mNumTextureBars++;
+
+ for (std::vector<LLTextureBar*>::iterator iter = mTextureBars.begin();
+ iter != mTextureBars.end(); iter++)
+ {
+ LLTextureBar* barp = *iter;
+ if (barp->mImagep == imagep)
+ {
+ barp->mHilite = hilite;
+ return FALSE;
+ }
+ }
+
+ barp = new LLTextureBar("texture bar", r);
+ barp->mImagep = imagep;
+ barp->mHilite = hilite;
+
+ addChild(barp);
+ mTextureBars.push_back(barp);
+
+ // Rearrange all child bars.
+ reshape(mRect.getWidth(), mRect.getHeight());
+ return TRUE;
+}
+
+BOOL LLTextureView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mask & MASK_SHIFT)
+ {
+ mFreezeView = !mFreezeView;
+ return TRUE;
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLTextureView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLTextureView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (key == ' ')
+ {
+ mFreezeView = !mFreezeView;
+ return TRUE;
+ }
+ return FALSE;
+}
+
diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h
new file mode 100644
index 0000000000..e832a55211
--- /dev/null
+++ b/indra/newview/lltextureview.h
@@ -0,0 +1,55 @@
+/**
+ * @file lltextureview.h
+ * @brief LLTextureView class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTUREVIEW_H
+#define LL_LLTEXTUREVIEW_H
+
+#include "lltexturebar.h"
+#include "llcontainerview.h"
+#include "linked_lists.h"
+
+class LLViewerImage;
+
+class LLTextureView : public LLContainerView
+{
+public:
+ LLTextureView(const std::string& name, const LLRect& rect);
+ ~LLTextureView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+
+ static void addDebugImage(LLViewerImage* image) { sDebugImages.insert(image); }
+ static void removeDebugImage(LLViewerImage* image) { sDebugImages.insert(image); }
+ static void clearDebugImages() { sDebugImages.clear(); }
+
+private:
+ BOOL addBar(LLViewerImage *image, BOOL hilight = FALSE);
+ void removeAllBars();
+
+private:
+ LLTextBox *mInfoTextp;
+
+ std::vector<LLTextureBar*> mTextureBars;
+ U32 mNumTextureBars;
+
+ LLGLTexMemBar* mGLTexMemBar;
+
+ BOOL mFreezeView;
+
+public:
+ static std::set<LLViewerImage*> sDebugImages;
+};
+
+extern LLTextureView *gTextureView;
+#endif // LL_TEXTURE_VIEW_H
diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp
new file mode 100644
index 0000000000..f9ff070289
--- /dev/null
+++ b/indra/newview/lltool.cpp
@@ -0,0 +1,151 @@
+/**
+ * @file lltool.cpp
+ * @brief LLTool class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltool.h"
+
+#include "indra_constants.h"
+#include "llerror.h"
+#include "llview.h"
+
+#include "llviewerwindow.h"
+#include "lltoolcomp.h"
+#include "llfocusmgr.h"
+#include "llagent.h"
+#include "llviewborder.h"
+
+extern BOOL gDebugClicks;
+
+//static
+const LLString LLTool::sNameNull("null");
+
+LLTool::LLTool( const LLString& name, LLToolComposite* composite ) :
+ mComposite( composite ),
+ mName(name)
+{
+}
+
+LLTool::~LLTool()
+{
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ llwarns << "Tool deleted holding mouse capture. Mouse capture removed." << llendl;
+ gFocusMgr.removeMouseCaptureWithoutCallback( this );
+ }
+}
+
+
+BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLTool left mouse down" << llendl;
+ }
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleMouseDown" << llendl;
+ gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN);
+ return TRUE;
+}
+
+BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLTool left mouse up" << llendl;
+ }
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleMouseUp" << llendl;
+ gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP);
+ return TRUE;
+}
+
+BOOL LLTool::handleHover(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by a tool" << llendl;
+ // by default, do nothing, say we handled it
+ return TRUE;
+}
+
+BOOL LLTool::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleScrollWheel" << llendl;
+ return FALSE;
+}
+
+BOOL LLTool::handleDoubleClick(S32 x,S32 y,MASK mask)
+{
+ // llinfos << "LLTool::handleDoubleClick" << llendl;
+ // by default, pretend it's a left click
+ return FALSE;
+}
+
+BOOL LLTool::handleRightMouseDown(S32 x,S32 y,MASK mask)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleRightMouseDown" << llendl;
+ return FALSE;
+}
+
+BOOL LLTool::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleRightMouseDown" << llendl;
+ return FALSE;
+}
+
+BOOL LLTool::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleToolTip" << llendl;
+ return FALSE;
+}
+
+void LLTool::setMouseCapture( BOOL b )
+{
+ if( b )
+ {
+ gViewerWindow->setMouseCapture(mComposite ? mComposite : this, &LLTool::onMouseCaptureLost );
+ }
+ else
+ if( hasMouseCapture() )
+ {
+ gViewerWindow->setMouseCapture( NULL, NULL );
+ }
+}
+
+// virtual
+void LLTool::draw()
+{ }
+
+BOOL LLTool::hasMouseCapture()
+{
+ return gViewerWindow->hasMouseCapture(mComposite ? mComposite : this);
+}
+
+BOOL LLTool::handleKey(KEY key, MASK mask)
+{
+ return FALSE;
+}
+
+
+// static
+void LLTool::onMouseCaptureLost( LLMouseHandler* old_captor )
+{
+ LLTool* self = (LLTool*) old_captor;
+ if( self->mComposite )
+ {
+ self->mComposite->onMouseCaptureLost();
+ }
+ else
+ {
+ self->onMouseCaptureLost();
+ }
+}
diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h
new file mode 100644
index 0000000000..f0e1b170bd
--- /dev/null
+++ b/indra/newview/lltool.h
@@ -0,0 +1,84 @@
+/**
+ * @file lltool.h
+ * @brief LLTool class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOL_H
+#define LL_LLTOOL_H
+
+#include "llkeyboard.h"
+#include "llmousehandler.h"
+#include "llcoord.h"
+#include "v3math.h"
+#include "v3dmath.h"
+
+class LLViewerObject;
+class LLToolComposite;
+class LLView;
+class LLPanel;
+
+class LLTool
+: public LLMouseHandler
+{
+public:
+ LLTool( const LLString& name, LLToolComposite* composite = NULL );
+ virtual ~LLTool();
+
+ // Hack to support LLFocusMgr
+ virtual BOOL isView() { return FALSE; }
+
+ // Virtual functions inherited from LLMouseHandler
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen);
+ // Return FALSE to allow context menu to be shown.
+ virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
+ { *local_x = screen_x; *local_y = screen_y; }
+ virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
+ { *screen_x = local_x; *screen_y = local_y; }
+
+ virtual const LLString& getName() const { return mName; }
+
+ // New virtual functions
+ virtual LLViewerObject* getEditingObject() { return NULL; }
+ virtual LLVector3d getEditingPointGlobal() { return LLVector3d(); }
+ virtual BOOL isEditing() { return (getEditingObject() != NULL); }
+ virtual void stopEditing() {}
+
+ virtual BOOL clipMouseWhenDown() { return TRUE; }
+
+ virtual void handleSelect() { } // do stuff when your tool is selected
+ virtual void handleDeselect() { } // clean up when your tool is deselected
+
+ // isAlwaysRendered() - return true if this is a tool that should
+ // always be rendered regardless of selection.
+ virtual BOOL isAlwaysRendered() { return FALSE; }
+
+ virtual void render() {} // draw tool specific 3D content in world
+ virtual void draw(); // draw tool specific 2D overlay
+
+ virtual BOOL handleKey(KEY key, MASK mask);
+
+ // Note: NOT virtual. Subclasses should call this version.
+ void setMouseCapture(BOOL b);
+ BOOL hasMouseCapture();
+ static void onMouseCaptureLost(LLMouseHandler* old_captor);
+ virtual void onMouseCaptureLost() {} // override this one as needed.
+
+protected:
+ LLToolComposite* mComposite; // Composite will handle mouse captures.
+ LLString mName;
+
+public:
+ static const LLString sNameNull;
+};
+
+#endif
diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp
new file mode 100644
index 0000000000..e6cfefa62d
--- /dev/null
+++ b/indra/newview/lltoolbar.cpp
@@ -0,0 +1,436 @@
+/**
+ * @file lltoolbar.cpp
+ * @author James Cook, Richard Nelson
+ * @brief Large friendly buttons at bottom of screen.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolbar.h"
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llviewercontrol.h"
+#include "llmenucommands.h"
+#include "llimview.h"
+#include "lluiconstants.h"
+#include "llvoavatar.h"
+#include "lltooldraganddrop.h"
+#include "llinventoryview.h"
+#include "llfloaterfriends.h"
+#include "llfloatersnapshot.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llfirstuse.h"
+#include "llviewerparcelmgr.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "lltoolgrab.h"
+
+#if LL_DARWIN
+
+ #include "llresizehandle.h"
+
+ // This class draws like an LLResizeHandle but has no interactivity.
+ // It's just there to provide a cue to the user that the lower right corner of the window functions as a resize handle.
+ class LLFakeResizeHandle : public LLResizeHandle
+ {
+ public:
+ LLFakeResizeHandle(const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, ECorner corner = RIGHT_BOTTOM )
+ : LLResizeHandle(name, rect, min_width, min_height, corner )
+ {
+
+ }
+
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask) { return FALSE; };
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; };
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; };
+
+ };
+
+#endif // LL_DARWIN
+
+//
+// Globals
+//
+
+LLToolBar *gToolBar = NULL;
+S32 TOOL_BAR_HEIGHT = 20;
+
+//
+// Statics
+//
+F32 LLToolBar::sInventoryAutoOpenTime = 1.f;
+
+//
+// Functions
+//
+
+LLToolBar::LLToolBar(const std::string& name, const LLRect& r)
+: LLPanel(name, r, BORDER_NO)
+#if LL_DARWIN
+ , mResizeHandle(NULL)
+#endif // LL_DARWIN
+{
+ setIsChrome(TRUE);
+ setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+
+ gUICtrlFactory->buildPanel(this, "panel_toolbar.xml");
+ mIsFocusRoot = TRUE;
+
+}
+
+
+BOOL LLToolBar::postBuild()
+{
+ childSetAction("im_btn", onClickIM, this);
+ childSetControlName("im_btn", "ShowIM");
+
+ childSetAction("chat_btn", onClickChat, this);
+ childSetControlName("chat_btn", "ChatVisible");
+
+ childSetAction("friends_btn", onClickFriends, this);
+ childSetControlName("friends_btn", "ShowFriends");
+
+ childSetAction("appearance_btn", onClickAppearance, this);
+ childSetControlName("appearance_btn", "");
+
+ childSetAction("clothing_btn", onClickClothing, this);
+ childSetControlName("clothing_btn", "ClothingBtnState");
+
+ childSetAction("fly_btn", onClickFly, this);
+ childSetControlName("fly_btn", "FlyBtnState");
+
+ childSetAction("sit_btn", onClickSit, this);
+ childSetControlName("sit_btn", "SitBtnState");
+
+ childSetAction("snapshot_btn", onClickSnapshot, this);
+ childSetControlName("snapshot_btn", "");
+
+ childSetAction("directory_btn", onClickDirectory, this);
+ childSetControlName("directory_btn", "ShowDirectory");
+
+ childSetAction("build_btn", onClickBuild, this);
+ childSetControlName("build_btn", "BuildBtnState");
+
+ childSetAction("radar_btn", onClickRadar, this);
+ childSetControlName("radar_btn", "ShowMiniMap");
+
+ childSetAction("map_btn", onClickMap, this);
+ childSetControlName("map_btn", "ShowWorldMap");
+
+ childSetAction("inventory_btn", onClickInventory, this);
+ childSetControlName("inventory_btn", "ShowInventory");
+
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if(view->getWidgetType() == WIDGET_TYPE_BUTTON)
+ {
+ LLButton* btn = (LLButton*)view;
+ btn->setSoundFlags(LLView::SILENT);
+ }
+ }
+
+#if LL_DARWIN
+ if(mResizeHandle == NULL)
+ {
+ LLRect rect(0, 0, RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT);
+ mResizeHandle = new LLFakeResizeHandle(LLString(""), rect, RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT);
+ this->addChildAtEnd(mResizeHandle);
+ }
+#endif // LL_DARWIN
+
+ layoutButtons();
+
+ return TRUE;
+}
+
+LLToolBar::~LLToolBar()
+{
+ // LLView destructor cleans up children
+}
+
+
+BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ LLButton* inventory_btn = LLUICtrlFactory::getButtonByName(this, "inventory_btn");
+ if (!inventory_btn) return FALSE;
+
+ LLInventoryView* active_inventory = LLInventoryView::getActiveInventory();
+
+ if(active_inventory && active_inventory->getVisible())
+ {
+ mInventoryAutoOpen = FALSE;
+ }
+ else if (inventory_btn->getRect().pointInRect(x, y))
+ {
+ if (mInventoryAutoOpen)
+ {
+ if (!(active_inventory && active_inventory->getVisible()) &&
+ mInventoryAutoOpenTimer.getElapsedTimeF32() > sInventoryAutoOpenTime)
+ {
+ LLInventoryView::showAgentInventory();
+ }
+ }
+ else
+ {
+ mInventoryAutoOpen = TRUE;
+ mInventoryAutoOpenTimer.reset();
+ }
+ }
+
+ return LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+}
+
+// static
+void LLToolBar::toggle(void*)
+{
+ BOOL show = gSavedSettings.getBOOL("ShowToolBar");
+ gSavedSettings.setBOOL("ShowToolBar", !show);
+ gToolBar->setVisible(!show);
+}
+
+
+// static
+BOOL LLToolBar::visible(void*)
+{
+ return gToolBar->getVisible();
+}
+
+
+void LLToolBar::layoutButtons()
+{
+ // Always spans whole window. JC
+ const S32 FUDGE_WIDTH_OF_SCREEN = 4;
+ S32 width = gViewerWindow->getWindowWidth() + FUDGE_WIDTH_OF_SCREEN;
+ S32 count = getChildCount();
+ S32 pad = 2;
+
+#if LL_DARWIN
+ // this function may be called before postBuild(), in which case mResizeHandle won't have been set up yet.
+ if(mResizeHandle != NULL)
+ {
+ // a resize handle has been added as a child, increasing the count by one.
+ count--;
+
+ if(!gViewerWindow->getWindow()->getFullscreen())
+ {
+ // Only when running in windowed mode on the Mac, leave room for a resize widget on the right edge of the bar.
+ width -= RESIZE_HANDLE_WIDTH;
+
+ LLRect r;
+ r.mLeft = width - pad;
+ r.mBottom = 0;
+ r.mRight = r.mLeft + RESIZE_HANDLE_WIDTH;
+ r.mTop = r.mBottom + RESIZE_HANDLE_HEIGHT;
+ mResizeHandle->setRect(r);
+ mResizeHandle->setVisible(TRUE);
+ }
+ else
+ {
+ mResizeHandle->setVisible(FALSE);
+ }
+ }
+#endif // LL_DARWIN
+
+ // We actually want to extend "pad" pixels off the right edge of the
+ // screen, such that the rightmost button is aligned.
+ F32 segment_width = (F32)(width + pad) / (F32)count;
+ S32 btn_width = lltrunc(segment_width - pad);
+
+ // Evenly space all views
+ S32 height = -1;
+ S32 i = count - 1;
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *btn_view = *child_iter;
+ if(btn_view->getWidgetType() == WIDGET_TYPE_BUTTON)
+ {
+ if (height < 0)
+ {
+ height = btn_view->getRect().getHeight();
+ }
+ S32 x = llround(i*segment_width);
+ S32 y = 0;
+ LLRect r;
+ r.setOriginAndSize(x, y, btn_width, height);
+ btn_view->setRect(r);
+ i--;
+ }
+ }
+}
+
+
+// virtual
+void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPanel::reshape(width, height, called_from_parent);
+
+ layoutButtons();
+}
+
+
+// Per-frame updates of visibility
+void LLToolBar::refresh()
+{
+ BOOL show = gSavedSettings.getBOOL("ShowToolBar");
+ BOOL mouselook = gAgent.cameraMouselook();
+ setVisible(show && !mouselook);
+
+ // Clothing button updated inside LLFloaterClothing
+
+ childSetEnabled("fly_btn", gAgent.canFly() || gAgent.getFlying() );
+
+ childSetEnabled("build_btn", gParcelMgr->agentCanBuild() );
+
+ // Check to see if we're in build mode
+ BOOL build_mode = gToolMgr->inEdit();
+ // And not just clicking on a scripted object
+ if (gToolGrab->getHideBuildHighlight())
+ {
+ build_mode = FALSE;
+ }
+ gSavedSettings.setBOOL("BuildBtnState", build_mode);
+}
+
+
+// static
+void LLToolBar::onClickIM(void* user_data)
+{
+ if(gIMView->getFloaterOpen())
+ {
+ // this is if we want Ctrl-T to be simply a toggle
+ // gIMView->setFloaterOpen( FALSE );
+ // three-state behavior follows
+ if(gFocusMgr.childHasKeyboardFocus(gIMView->getFloater()))
+ {
+ gIMView->setFloaterOpen( FALSE );
+ } else {
+ gIMView->getFloater()->setFocus( TRUE );
+ }
+ }
+ else
+ {
+ gIMView->setFloaterOpen( TRUE );
+ }
+}
+
+
+// static
+void LLToolBar::onClickChat(void* user_data)
+{
+ handle_chat(NULL);
+}
+
+
+// static
+void LLToolBar::onClickFriends(void*)
+{
+ LLFloaterFriends::toggle();
+}
+
+
+// static
+void LLToolBar::onClickAppearance(void*)
+{
+ if (gAgent.getWearablesLoaded())
+ {
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+}
+
+
+// static
+void LLToolBar::onClickClothing(void*)
+{
+ handle_clothing(NULL);
+}
+
+
+// static
+void LLToolBar::onClickFly(void*)
+{
+ gAgent.toggleFlying();
+}
+
+
+// static
+void LLToolBar::onClickSit(void*)
+{
+ if (!(gAgent.getControlFlags() & AGENT_CONTROL_SIT_ON_GROUND))
+ {
+ // sit down
+ gAgent.setFlying(FALSE);
+ gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+
+ // Might be first sit
+ LLFirstUse::useSit();
+ }
+ else
+ {
+ // stand up
+ gAgent.setFlying(FALSE);
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ }
+}
+
+
+// static
+void LLToolBar::onClickSnapshot(void*)
+{
+ LLFloaterSnapshot::show (0);
+}
+
+
+// static
+void LLToolBar::onClickDirectory(void*)
+{
+ handle_find(NULL);
+}
+
+
+// static
+void LLToolBar::onClickBuild(void*)
+{
+ toggle_build_mode();
+}
+
+
+// static
+void LLToolBar::onClickRadar(void*)
+{
+ handle_mini_map(NULL);
+}
+
+
+// static
+void LLToolBar::onClickMap(void*)
+{
+ handle_map(NULL);
+}
+
+
+// static
+void LLToolBar::onClickInventory(void*)
+{
+ handle_inventory(NULL);
+}
+
diff --git a/indra/newview/lltoolbar.h b/indra/newview/lltoolbar.h
new file mode 100644
index 0000000000..f9eee1d4fb
--- /dev/null
+++ b/indra/newview/lltoolbar.h
@@ -0,0 +1,76 @@
+/**
+ * @file lltoolbar.h
+ * @brief Large friendly buttons at bottom of screen.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLBAR_H
+#define LL_LLTOOLBAR_H
+
+#include "llpanel.h"
+
+#include "llframetimer.h"
+
+// "Constants" loaded from settings.xml at start time
+extern S32 TOOL_BAR_HEIGHT;
+
+#if LL_DARWIN
+ class LLFakeResizeHandle;
+#endif // LL_DARWIN
+
+class LLToolBar
+: public LLPanel
+{
+public:
+ LLToolBar(const std::string& name, const LLRect& rect );
+ ~LLToolBar();
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ static void toggle(void*);
+ static BOOL visible(void*);
+
+ // Move buttons to appropriate locations based on rect.
+ void layoutButtons();
+
+ // Per-frame refresh call
+ void refresh();
+
+ // callbacks
+ static void onClickIM(void*);
+ static void onClickChat(void* data);
+ static void onClickFriends(void* data);
+ static void onClickAppearance(void* data);
+ static void onClickClothing(void* data);
+ static void onClickFly(void*);
+ static void onClickSit(void*);
+ static void onClickSnapshot(void* data);
+ static void onClickDirectory(void* data);
+ static void onClickBuild(void* data);
+ static void onClickRadar(void* data);
+ static void onClickMap(void* data);
+ static void onClickInventory(void* data);
+
+ static F32 sInventoryAutoOpenTime;
+
+private:
+ BOOL mInventoryAutoOpen;
+ LLFrameTimer mInventoryAutoOpenTimer;
+#if LL_DARWIN
+ LLFakeResizeHandle *mResizeHandle;
+#endif // LL_DARWIN
+};
+
+extern LLToolBar *gToolBar;
+
+#endif
diff --git a/indra/newview/lltoolbrush.cpp b/indra/newview/lltoolbrush.cpp
new file mode 100644
index 0000000000..8255008aa0
--- /dev/null
+++ b/indra/newview/lltoolbrush.cpp
@@ -0,0 +1,585 @@
+/**
+ * @file lltoolbrush.cpp
+ * @brief Implementation of the toolbrushes
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolbrush.h"
+#include "lltoolselectland.h"
+
+#include "llgl.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "llviewercontrol.h"
+#include "llfloatertools.h"
+#include "llregionposition.h"
+#include "llstatusbar.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llparcel.h"
+
+#include "llglheaders.h"
+
+const std::string REGION_BLOCKS_TERRAFORM_MSG = "This region does not allow terraforming.\n"
+ "You will need to buy land in another part of the world to terraform it.";
+
+// Globals
+LLToolBrushLand *gToolLand = NULL;
+
+///============================================================================
+/// Local function declarations, constants, enums, and typedefs
+///============================================================================
+
+const S32 LAND_BRUSH_SIZE_COUNT = 3;
+const F32 LAND_BRUSH_SIZE[LAND_BRUSH_SIZE_COUNT] = {1.0f, 2.0f, 4.0f};
+const S32 LAND_STEPS = 3;
+const F32 LAND_METERS_PER_SECOND = 1.0f;
+enum
+{
+ E_LAND_LEVEL = 0,
+ E_LAND_RAISE = 1,
+ E_LAND_LOWER = 2,
+ E_LAND_SMOOTH = 3,
+ E_LAND_NOISE = 4,
+ E_LAND_REVERT = 5,
+ E_LAND_INVALID = 6,
+};
+const LLColor4 OVERLAY_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
+
+///============================================================================
+/// Class LLToolBrushLand
+///============================================================================
+
+// constructor
+LLToolBrushLand::LLToolBrushLand()
+: LLTool("Land"),
+ mStartingZ( 0.0f ),
+ mMouseX( 0 ),
+ mMouseY(0),
+ mGotHover(FALSE),
+ mLastShowParcelOwners(FALSE),
+ mBrushSelected(FALSE)
+{
+ mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
+}
+
+void LLToolBrushLand::modifyLandAtPointGlobal(const LLVector3d &pos_global,
+ MASK mask)
+{
+ S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
+
+ determineAffectedRegions(mLastAffectedRegions, pos_global);
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ //BOOL is_changed = FALSE;
+ LLVector3 pos_region = regionp->getPosRegionFromGlobal(pos_global);
+ LLSurface &land = regionp->getLand();
+ char action = E_LAND_LEVEL;
+ switch (radioAction)
+ {
+ case 0:
+ // // average toward mStartingZ
+ action = E_LAND_LEVEL;
+ break;
+ case 1:
+ action = E_LAND_RAISE;
+ break;
+ case 2:
+ action = E_LAND_LOWER;
+ break;
+ case 3:
+ action = E_LAND_SMOOTH;
+ break;
+ case 4:
+ action = E_LAND_NOISE;
+ break;
+ case 5:
+ action = E_LAND_REVERT;
+ break;
+ default:
+ action = E_LAND_INVALID;
+ break;
+ }
+
+ // Don't send a message to the region if nothing changed.
+ //if(!is_changed) continue;
+
+ // Now to update the patch information so it will redraw correctly.
+ LLSurfacePatch *patchp= land.resolvePatchRegion(pos_region);
+ if (patchp)
+ {
+ patchp->dirtyZ();
+ }
+
+ // Also force the property lines to update, normals to recompute, etc.
+ regionp->forceUpdate();
+
+ // tell the simulator what we've done
+ F32 seconds = 1.0f / gFPSClamped;
+ F32 x_pos = (F32)pos_region.mV[VX];
+ F32 y_pos = (F32)pos_region.mV[VY];
+ U8 brush_size = (U8)mBrushIndex;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ModifyLand);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ModifyBlock);
+ msg->addU8Fast(_PREHASH_Action, (U8)action);
+ msg->addU8Fast(_PREHASH_BrushSize, brush_size);
+ msg->addF32Fast(_PREHASH_Seconds, seconds);
+ msg->addF32Fast(_PREHASH_Height, mStartingZ);
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1);
+ msg->addF32Fast(_PREHASH_West, x_pos );
+ msg->addF32Fast(_PREHASH_South, y_pos );
+ msg->addF32Fast(_PREHASH_East, x_pos );
+ msg->addF32Fast(_PREHASH_North, y_pos );
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+void LLToolBrushLand::modifyLandInSelectionGlobal()
+{
+ if (gParcelMgr->selectionEmpty())
+ {
+ return;
+ }
+
+ if (gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolParcel)
+ {
+ // selecting land, don't do anything
+ return;
+ }
+
+ LLVector3d min;
+ LLVector3d max;
+
+ gParcelMgr->getSelection(min, max);
+
+ S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
+
+ mLastAffectedRegions.removeAllNodes();
+
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], min.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], max.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], min.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], max.mdV[VY], 0));
+
+ LLRegionPosition mid_point_region((min + max) * 0.5);
+ LLViewerRegion* center_region = mid_point_region.getRegion();
+ if (center_region)
+ {
+ LLVector3 pos_region = mid_point_region.getPositionRegion();
+ U32 grids = center_region->getLand().mGridsPerEdge;
+ S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
+ S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
+ mStartingZ = center_region->getLand().getZ(i+j*grids);
+ }
+ else
+ {
+ mStartingZ = 0.f;
+ }
+
+ // Stop if our selection include a no-terraform region
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ if (!canTerraform(regionp))
+ {
+ alertNoTerraform(regionp);
+ return;
+ }
+ }
+
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ //BOOL is_changed = FALSE;
+ LLVector3 min_region = regionp->getPosRegionFromGlobal(min);
+ LLVector3 max_region = regionp->getPosRegionFromGlobal(max);
+
+ min_region.clamp(0.f, regionp->getWidth());
+ max_region.clamp(0.f, regionp->getWidth());
+ F32 seconds = 1.0f;
+
+ LLSurface &land = regionp->getLand();
+ char action = E_LAND_LEVEL;
+ switch (radioAction)
+ {
+ case 0:
+ // // average toward mStartingZ
+ action = E_LAND_LEVEL;
+ seconds = 1.f;
+ break;
+ case 1:
+ action = E_LAND_RAISE;
+ break;
+ case 2:
+ action = E_LAND_LOWER;
+ break;
+ case 3:
+ action = E_LAND_SMOOTH;
+ seconds = 10.f;
+ break;
+ case 4:
+ action = E_LAND_NOISE;
+ seconds = 0.5f;
+ break;
+ case 5:
+ action = E_LAND_REVERT;
+ seconds = 0.5f;
+ break;
+ default:
+ //action = E_LAND_INVALID;
+ //seconds = 0.0f;
+ return;
+ break;
+ }
+
+ // Don't send a message to the region if nothing changed.
+ //if(!is_changed) continue;
+
+ // Now to update the patch information so it will redraw correctly.
+ LLSurfacePatch *patchp= land.resolvePatchRegion(min_region);
+ if (patchp)
+ {
+ patchp->dirtyZ();
+ }
+
+ // Also force the property lines to update, normals to recompute, etc.
+ regionp->forceUpdate();
+
+ // tell the simulator what we've done
+ U8 brush_size = (U8)mBrushIndex;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ModifyLand);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ModifyBlock);
+ msg->addU8Fast(_PREHASH_Action, (U8)action);
+ msg->addU8Fast(_PREHASH_BrushSize, brush_size);
+ msg->addF32Fast(_PREHASH_Seconds, seconds);
+ msg->addF32Fast(_PREHASH_Height, mStartingZ);
+
+ BOOL parcel_selected = gParcelMgr->getWholeParcelSelected();
+ LLParcel* selected_parcel = gParcelMgr->getSelectedParcel();
+
+ if (parcel_selected && selected_parcel)
+ {
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, selected_parcel->getLocalID());
+ msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1);
+ msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
+ }
+
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+void LLToolBrushLand::brush( void )
+{
+ LLVector3d spot;
+ if( gViewerWindow->mousePointOnLandGlobal( mMouseX, mMouseY, &spot ) )
+ {
+ // Round to nearest X,Y grid
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ modifyLandAtPointGlobal(spot, gKeyboard->currentMask(TRUE));
+ }
+}
+
+BOOL LLToolBrushLand::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Find the z value of the initial click.
+ LLVector3d spot;
+ if( gViewerWindow->mousePointOnLandGlobal( x, y, &spot ) )
+ {
+ // Round to nearest X,Y grid
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ LLRegionPosition region_position( spot );
+ LLViewerRegion* regionp = region_position.getRegion();
+
+ if (!canTerraform(regionp))
+ {
+ alertNoTerraform(regionp);
+ return TRUE;
+ }
+
+ LLVector3 pos_region = region_position.getPositionRegion();
+ U32 grids = regionp->getLand().mGridsPerEdge;
+ S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
+ S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
+ mStartingZ = regionp->getLand().getZ(i+j*grids);
+ mMouseX = x;
+ mMouseY = y;
+ gIdleCallbacks.addFunction( &LLToolBrushLand::onIdle, (void*)this );
+ setMouseCapture( TRUE );
+
+ gParcelMgr->setSelectionVisible(FALSE);
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+BOOL LLToolBrushLand::handleHover( S32 x, S32 y, MASK mask )
+{
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolBrushLand ("
+ << (hasMouseCapture() ? "active":"inactive")
+ << ")" << llendl;
+ mMouseX = x;
+ mMouseY = y;
+ mGotHover = TRUE;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLLAND);
+ return TRUE;
+}
+
+BOOL LLToolBrushLand::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ mLastAffectedRegions.removeAllNodes();
+ if( hasMouseCapture() )
+ {
+ // Release the mouse
+ setMouseCapture( FALSE );
+
+ gParcelMgr->setSelectionVisible(TRUE);
+
+ gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, (void*)this );
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+void LLToolBrushLand::handleSelect()
+{
+ gEditMenuHandler = this;
+
+ gFloaterTools->setStatusText("Click and hold to modify land");
+// if (!mBrushSelected)
+ {
+ mLastShowParcelOwners = gSavedSettings.getBOOL("ShowParcelOwners");
+ gSavedSettings.setBOOL("ShowParcelOwners", TRUE);
+ mBrushSelected = TRUE;
+ }
+}
+
+
+void LLToolBrushLand::handleDeselect()
+{
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+ gFloaterTools->setStatusText("");
+ gSavedSettings.setBOOL("ShowParcelOwners", mLastShowParcelOwners);
+ gParcelMgr->setSelectionVisible(TRUE);
+ mBrushSelected = FALSE;
+}
+
+// Draw the area that will be affected.
+void LLToolBrushLand::render()
+{
+ if(mGotHover)
+ {
+ //llinfos << "LLToolBrushLand::render()" << llendl;
+ LLVector3d spot;
+ if(gViewerWindow->mousePointOnLandGlobal(mMouseX, mMouseY, &spot))
+ {
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
+ LLLinkedList<LLViewerRegion> regions;
+ determineAffectedRegions(regions, spot);
+
+ // Now, for each region, render the overlay
+ LLVector3 pos_world = gAgent.getRegion()->getPosRegionFromGlobal(spot);
+ for(LLViewerRegion* region = regions.getFirstData();
+ region != NULL;
+ region = regions.getNextData())
+ {
+ renderOverlay(region->getLand(),
+ region->getPosRegionFromGlobal(spot),
+ pos_world);
+ }
+ }
+ mGotHover = FALSE;
+ }
+}
+
+void LLToolBrushLand::renderOverlay(LLSurface& land, const LLVector3& pos_region,
+ const LLVector3& pos_world)
+{
+ glMatrixMode(GL_MODELVIEW);
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest mDepthTest(GL_TRUE);
+ glPushMatrix();
+ glColor4fv(OVERLAY_COLOR.mV);
+ glTranslatef(0.0f, 0.0f, 1.0f);
+ //glPushMatrix();
+ //glTranslatef(spot.mV[VX], spot.mV[VY], 100.0f);
+ //gl_rect_2d(0, 10, 10, 0);
+ //glPopMatrix();
+ S32 i = (S32) pos_region.mV[VX];
+ S32 j = (S32) pos_region.mV[VY];
+ S32 half_edge = llfloor(LAND_BRUSH_SIZE[mBrushIndex]);
+ //F32 dz = 0.0f;
+ //S32 dist = 0;
+ glBegin(GL_POINTS);
+ for(S32 di = -half_edge; di <= half_edge; di++)
+ {
+ if((i+di) < 0 || (i+di) >= (S32)land.mGridsPerEdge) continue;
+ for(S32 dj = -half_edge; dj <= half_edge; dj++)
+ {
+ if( (j+dj) < 0 || (j+dj) >= (S32)land.mGridsPerEdge ) continue;
+ glVertex3f(pos_world.mV[VX] + di, pos_world.mV[VY] + dj,
+ land.getZ((i+di)+(j+dj)*land.mGridsPerEdge));
+ }
+ }
+ glEnd();
+ glPopMatrix();
+}
+
+void LLToolBrushLand::determineAffectedRegions(LLLinkedList<LLViewerRegion>& regions,
+ const LLVector3d& spot ) const
+{
+ LLVector3d corner(spot);
+ corner.mdV[VX] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
+ corner.mdV[VY] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
+ LLViewerRegion* region = NULL;
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VY] += LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VX] += LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VY] -= LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+}
+
+// static
+void LLToolBrushLand::onIdle( void* brush_tool )
+{
+ LLToolBrushLand* self = reinterpret_cast<LLToolBrushLand*>(brush_tool);
+
+ if( gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ) == self )
+ {
+ self->brush();
+ }
+ else
+ {
+ gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, self );
+ }
+}
+
+void LLToolBrushLand::onMouseCaptureLost()
+{
+ gIdleCallbacks.deleteFunction(&LLToolBrushLand::onIdle, this);
+}
+
+// static
+void LLToolBrushLand::undo()
+{
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_UndoLand);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->sendMessage(regionp->getHost());
+ }
+}
+
+// static
+void LLToolBrushLand::redo()
+{
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_RedoLand);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->sendMessage(regionp->getHost());
+ }
+}
+
+// static
+bool LLToolBrushLand::canTerraform(LLViewerRegion* regionp) const
+{
+ if (!regionp) return false;
+ if (regionp->canManageEstate()) return true;
+ return !(regionp->getRegionFlags() & REGION_FLAGS_BLOCK_TERRAFORM);
+}
+
+// static
+void LLToolBrushLand::alertNoTerraform(LLViewerRegion* regionp)
+{
+ if (!regionp) return;
+
+ LLStringBase<char>::format_map_t args;
+ args["[REGION]"] = regionp->getName();
+ gViewerWindow->alertXml("RegionNoTerraforming", args);
+
+}
+
+///============================================================================
+/// Local function definitions
+///============================================================================
diff --git a/indra/newview/lltoolbrush.h b/indra/newview/lltoolbrush.h
new file mode 100644
index 0000000000..3cae7559a3
--- /dev/null
+++ b/indra/newview/lltoolbrush.h
@@ -0,0 +1,88 @@
+/**
+ * @file lltoolbrush.h
+ * @brief toolbrush class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLBRUSH_H
+#define LL_LLTOOLBRUSH_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "lleditmenuhandler.h"
+
+class LLSurface;
+class LLVector3d;
+class LLViewerRegion;
+
+template<class DATA_TYPE> class LLLinkedList;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLToolBrushLand
+//
+// A toolbrush that modifies the land.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLToolBrushLand : public LLTool, public LLEditMenuHandler
+{
+public:
+ LLToolBrushLand();
+
+ // x,y in window coords, 0,0 = left,bot
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ // isAlwaysRendered() - return true if this is a tool that should
+ // always be rendered regardless of selection.
+ virtual BOOL isAlwaysRendered() { return TRUE; }
+
+ // Draw the area that will be affected.
+ virtual void render();
+
+ // on Idle is where the land modification actually occurs
+ static void onIdle(void* brush_tool);
+
+ void onMouseCaptureLost();
+
+ void modifyLandInSelectionGlobal();
+ virtual void undo();
+ virtual BOOL canUndo() { return TRUE; }
+
+ virtual void redo();
+ virtual BOOL canRedo() { return FALSE; }
+
+
+protected:
+ void brush( void );
+ void modifyLandAtPointGlobal( const LLVector3d &spot, MASK mask );
+
+ void determineAffectedRegions(LLLinkedList<LLViewerRegion>& regions,
+ const LLVector3d& spot) const;
+ void renderOverlay(LLSurface& land, const LLVector3& pos_region,
+ const LLVector3& pos_world);
+
+ // Does region allow terraform, or are we a god?
+ bool canTerraform(LLViewerRegion* regionp) const;
+
+ // Modal dialog that you can't terraform the region
+ void alertNoTerraform(LLViewerRegion* regionp);
+
+protected:
+ F32 mStartingZ;
+ S32 mMouseX;
+ S32 mMouseY;
+ S32 mBrushIndex;
+ BOOL mGotHover;
+ BOOL mLastShowParcelOwners;
+ BOOL mBrushSelected;
+ LLLinkedList<LLViewerRegion> mLastAffectedRegions;
+};
+
+extern LLToolBrushLand *gToolLand;
+
+#endif // LL_LLTOOLBRUSH_H
diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp
new file mode 100644
index 0000000000..2b8d4ed73c
--- /dev/null
+++ b/indra/newview/lltoolcomp.cpp
@@ -0,0 +1,673 @@
+/**
+ * @file lltoolcomp.cpp
+ * @brief Composite tools
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolcomp.h"
+
+#include "llgl.h"
+#include "indra_constants.h"
+
+#include "llmanip.h"
+#include "llmaniprotate.h"
+#include "llmanipscale.h"
+#include "llmaniptranslate.h"
+#include "llmenugl.h" // for right-click menu hack
+#include "llselectmgr.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolgun.h"
+#include "lltoolmgr.h"
+#include "lltoolselect.h"
+#include "lltoolselectrect.h"
+#include "lltoolplacer.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llagent.h"
+#include "llfloatertools.h"
+#include "llviewercontrol.h"
+
+const S32 BUTTON_HEIGHT = 16;
+const S32 BUTTON_WIDTH_SMALL = 32;
+const S32 BUTTON_WIDTH_BIG = 48;
+const S32 HPAD = 4;
+
+// Globals
+LLToolCompTranslate *gToolTranslate = NULL;
+LLToolCompScale *gToolStretch = NULL;
+LLToolCompRotate *gToolRotate = NULL;
+LLToolCompCreate *gToolCreate = NULL;
+LLToolCompGun *gToolGun = NULL;
+
+extern LLControlGroup gSavedSettings;
+
+//-----------------------------------------------------------------------
+// LLToolComposite
+
+//static
+void LLToolComposite::setCurrentTool( LLTool* new_tool )
+{
+ if( mCur != new_tool )
+ {
+ if( mSelected )
+ {
+ mCur->handleDeselect();
+ mCur = new_tool;
+ mCur->handleSelect();
+ }
+ else
+ {
+ mCur = new_tool;
+ }
+ }
+}
+
+LLToolComposite::LLToolComposite(const LLString& name)
+ : LLTool(name),
+ mCur(NULL), mDefault(NULL), mSelected(FALSE),
+ mMouseDown(FALSE), mManip(NULL), mSelectRect(NULL)
+{
+}
+
+// Returns to the default tool
+BOOL LLToolComposite::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = mCur->handleMouseUp( x, y, mask );
+ if( handled )
+ {
+ setCurrentTool( mDefault );
+ }
+ return handled;
+}
+
+void LLToolComposite::onMouseCaptureLost()
+{
+ mCur->onMouseCaptureLost();
+ setCurrentTool( mDefault );
+}
+
+BOOL LLToolComposite::isSelecting()
+{
+ return mCur == mSelectRect;
+}
+
+void LLToolComposite::handleSelect()
+{
+ if (gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ gSelectMgr->promoteSelectionToRoot();
+ }
+ mCur = mDefault;
+ mCur->handleSelect();
+ mSelected = TRUE;
+}
+
+//----------------------------------------------------------------------------
+// LLToolCompTranslate
+//----------------------------------------------------------------------------
+
+LLToolCompTranslate::LLToolCompTranslate()
+ : LLToolComposite("Move")
+{
+ mManip = new LLManipTranslate(this);
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mManip;
+ mDefault = mManip;
+}
+
+LLToolCompTranslate::~LLToolCompTranslate()
+{
+ delete mManip;
+ mManip = NULL;
+
+ delete mSelectRect;
+ mSelectRect = NULL;
+}
+
+BOOL LLToolCompTranslate::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( !mCur->hasMouseCapture() )
+ {
+ setCurrentTool( mManip );
+ }
+ return mCur->handleHover( x, y, mask );
+}
+
+
+BOOL LLToolCompTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolCompTranslate::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ gToolTranslate->mManip->highlightManipulators(x, y);
+ if (!gToolTranslate->mMouseDown)
+ {
+ // fast click on object, but mouse is already up...just do select
+ gToolTranslate->mSelectRect->handleObjectSelection(hit_obj, mask, !gSavedSettings.getBOOL("SelectLinkedSet"), FALSE);
+ return;
+ }
+
+ if( hit_obj || gToolTranslate->mManip->getHighlightedPart() != LLManip::LL_NO_PART )
+ {
+ if (gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+ if( LLManip::LL_NO_PART != gToolTranslate->mManip->getHighlightedPart() )
+ {
+ gToolTranslate->setCurrentTool( gToolTranslate->mManip );
+ gToolTranslate->mManip->handleMouseDownOnPart( x, y, mask );
+ }
+ else
+ {
+ gToolTranslate->setCurrentTool( gToolTranslate->mSelectRect );
+ gToolTranslate->mSelectRect->handleMouseDown( x, y, mask );
+
+ //FIXME: add toggle to trigger old click-drag functionality
+ // gToolTranslate->mManip->handleMouseDownOnPart( XY_part, x, y, mask);
+ }
+ }
+ else
+ {
+ gToolTranslate->setCurrentTool( gToolTranslate->mSelectRect );
+ gToolTranslate->mSelectRect->handleMouseDown( x, y, mask);
+ }
+}
+
+BOOL LLToolCompTranslate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = FALSE;
+ return LLToolComposite::handleMouseUp(x, y, mask);
+}
+
+BOOL LLToolCompTranslate::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty() && mManip->getHighlightedPart() == LLManip::LL_NO_PART)
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return FALSE;
+ }
+}
+
+
+void LLToolCompTranslate::render()
+{
+ mCur->render();
+
+ if( mCur != mManip )
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ mManip->renderGuidelines();
+ }
+}
+
+
+//-----------------------------------------------------------------------
+// LLToolCompScale
+
+LLToolCompScale::LLToolCompScale()
+ : LLToolComposite("Stretch")
+{
+ mManip = new LLManipScale(this);
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mManip;
+ mDefault = mManip;
+}
+
+LLToolCompScale::~LLToolCompScale()
+{
+ delete mManip;
+ delete mSelectRect;
+}
+
+BOOL LLToolCompScale::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( !mCur->hasMouseCapture() )
+ {
+ setCurrentTool(mManip );
+ }
+ return mCur->handleHover( x, y, mask );
+}
+
+
+BOOL LLToolCompScale::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolCompScale::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ gToolStretch->mManip->highlightManipulators(x, y);
+ if (!gToolStretch->mMouseDown)
+ {
+ // fast click on object, but mouse is already up...just do select
+ gToolStretch->mSelectRect->handleObjectSelection(hit_obj, mask, !gSavedSettings.getBOOL("SelectLinkedSet"), FALSE);
+
+ return;
+ }
+
+ if( hit_obj || gToolStretch->mManip->getHighlightedPart() != LLManip::LL_NO_PART)
+ {
+ if (gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+ if( LLManip::LL_NO_PART != gToolStretch->mManip->getHighlightedPart() )
+ {
+ gToolStretch->setCurrentTool( gToolStretch->mManip );
+ gToolStretch->mManip->handleMouseDownOnPart( x, y, mask );
+ }
+ else
+ {
+ gToolStretch->setCurrentTool( gToolStretch->mSelectRect );
+ gToolStretch->mSelectRect->handleMouseDown( x, y, mask );
+ }
+ }
+ else
+ {
+ gToolStretch->setCurrentTool( gToolStretch->mSelectRect );
+ gToolStretch->mCur->handleMouseDown( x, y, mask );
+ }
+}
+
+BOOL LLToolCompScale::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = FALSE;
+ return LLToolComposite::handleMouseUp(x, y, mask);
+}
+
+BOOL LLToolCompScale::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty() && mManip->getHighlightedPart() == LLManip::LL_NO_PART)
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ //gBuildView->setPropertiesPanelOpen(TRUE);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return handleMouseDown(x, y, mask);
+ }
+}
+
+
+void LLToolCompScale::render()
+{
+ mCur->render();
+
+ if( mCur != mManip )
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ mManip->renderGuidelines();
+ }
+}
+
+//-----------------------------------------------------------------------
+// LLToolCompCreate
+
+LLToolCompCreate::LLToolCompCreate()
+ : LLToolComposite("Create")
+{
+ mPlacer = new LLToolPlacer();
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mPlacer;
+ mDefault = mPlacer;
+ mObjectPlacedOnMouseDown = FALSE;
+}
+
+
+LLToolCompCreate::~LLToolCompCreate()
+{
+ delete mPlacer;
+ delete mSelectRect;
+}
+
+
+BOOL LLToolCompCreate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ mMouseDown = TRUE;
+
+ if ( !(mask == MASK_SHIFT) && !(mask == MASK_CONTROL) )
+ {
+ setCurrentTool( mPlacer );
+ handled = mPlacer->placeObject( x, y, mask );
+ }
+ else
+ {
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ handled = TRUE;
+ }
+
+ mObjectPlacedOnMouseDown = TRUE;
+
+ return TRUE;
+}
+
+void LLToolCompCreate::pickCallback(S32 x, S32 y, MASK mask)
+{
+ // HACK: Mask off shift and control, so you can't multi-select
+ // multiple objects with the create tool.
+ mask = (mask & ~MASK_SHIFT);
+ mask = (mask & ~MASK_CONTROL);
+
+ gToolCreate->setCurrentTool( gToolCreate->mSelectRect );
+ gToolCreate->mSelectRect->handleMouseDown( x, y, mask);
+}
+
+BOOL LLToolCompCreate::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return handleMouseDown(x, y, mask);
+}
+
+BOOL LLToolCompCreate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if ( mMouseDown && !mObjectPlacedOnMouseDown && !(mask == MASK_SHIFT) && !(mask == MASK_CONTROL) )
+ {
+ setCurrentTool( mPlacer );
+ handled = mPlacer->placeObject( x, y, mask );
+ }
+
+ mObjectPlacedOnMouseDown = FALSE;
+ mMouseDown = FALSE;
+
+ return handled;
+}
+
+//-----------------------------------------------------------------------
+// LLToolCompRotate
+
+LLToolCompRotate::LLToolCompRotate()
+ : LLToolComposite("Rotate")
+{
+ mManip = new LLManipRotate(this);
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mManip;
+ mDefault = mManip;
+}
+
+
+LLToolCompRotate::~LLToolCompRotate()
+{
+ delete mManip;
+ delete mSelectRect;
+}
+
+BOOL LLToolCompRotate::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( !mCur->hasMouseCapture() )
+ {
+ setCurrentTool( mManip );
+ }
+ return mCur->handleHover( x, y, mask );
+}
+
+
+BOOL LLToolCompRotate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolCompRotate::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ gToolRotate->mManip->highlightManipulators(x, y);
+ if (!gToolRotate->mMouseDown)
+ {
+ // fast click on object, but mouse is already up...just do select
+ gToolRotate->mSelectRect->handleObjectSelection(hit_obj, mask, !gSavedSettings.getBOOL("SelectLinkedSet"), FALSE);
+ return;
+ }
+
+ if( hit_obj || gToolRotate->mManip->getHighlightedPart() != LLManip::LL_NO_PART)
+ {
+ if (gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+ if( LLManip::LL_NO_PART != gToolRotate->mManip->getHighlightedPart() )
+ {
+ gToolRotate->setCurrentTool( gToolRotate->mManip );
+ gToolRotate->mManip->handleMouseDownOnPart( x, y, mask );
+ }
+ else
+ {
+ gToolRotate->setCurrentTool( gToolRotate->mSelectRect );
+ gToolRotate->mSelectRect->handleMouseDown( x, y, mask );
+ }
+ }
+ else
+ {
+ gToolRotate->setCurrentTool( gToolRotate->mSelectRect );
+ gToolRotate->mCur->handleMouseDown( x, y, mask );
+ }
+}
+
+BOOL LLToolCompRotate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = FALSE;
+ return LLToolComposite::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLToolCompRotate::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty() && mManip->getHighlightedPart() == LLManip::LL_NO_PART)
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ //gBuildView->setPropertiesPanelOpen(TRUE);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return handleMouseDown(x, y, mask);
+ }
+}
+
+
+void LLToolCompRotate::render()
+{
+ mCur->render();
+
+ if( mCur != mManip )
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ mManip->renderGuidelines();
+ }
+}
+
+
+//-----------------------------------------------------------------------
+// LLToolCompGun
+
+LLToolCompGun::LLToolCompGun()
+ : LLToolComposite("Mouselook")
+{
+ mGun = new LLToolGun(this);
+ mGrab = new LLToolGrab(this);
+ mNull = new LLTool("null", this);
+
+ setCurrentTool(mGun);
+ mDefault = mGun;
+}
+
+
+LLToolCompGun::~LLToolCompGun()
+{
+ delete mGun;
+ mGun = NULL;
+
+ delete mGrab;
+ mGrab = NULL;
+
+ delete mNull;
+ mNull = NULL;
+}
+
+BOOL LLToolCompGun::handleHover(S32 x, S32 y, MASK mask)
+{
+ // HACK to make mouselook kick in again after item selected
+ // from context menu
+ if ( mCur == mNull && !gPopupMenuView->getVisible() )
+ {
+ gSelectMgr->deselectAll();
+ setCurrentTool( (LLTool*) mGrab );
+ }
+
+ // Note: if the tool changed, we can't delegate the current mouse event
+ // after the change because tools can modify the mouse during selection and deselection.
+ // Instead we let the current tool handle the event and then make the change.
+ // The new tool will take effect on the next frame.
+
+ mCur->handleHover( x, y, mask );
+
+ // If mouse button not down...
+ if( !gViewerWindow->getLeftMouseDown())
+ {
+ // let ALT switch from gun to grab
+ if ( mCur == mGun && (mask & MASK_ALT) )
+ {
+ setCurrentTool( (LLTool*) mGrab );
+ }
+ else if ( mCur == mGrab && !(mask & MASK_ALT) )
+ {
+ setCurrentTool( (LLTool*) mGun );
+ setMouseCapture(TRUE);
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLToolCompGun::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // if the left button is grabbed, don't put up the pie menu
+ if (gAgent.leftButtonGrabbed())
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN);
+ return FALSE;
+ }
+
+ // On mousedown, start grabbing
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( (LLTool*) mGrab );
+
+ return gToolGrab->handleMouseDown(x, y, mask);
+}
+
+
+BOOL LLToolCompGun::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // if the left button is grabbed, don't put up the pie menu
+ if (gAgent.leftButtonGrabbed())
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN);
+ return FALSE;
+ }
+
+ // On mousedown, start grabbing
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( (LLTool*) mGrab );
+
+ return gToolGrab->handleDoubleClick(x, y, mask);
+}
+
+
+BOOL LLToolCompGun::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ /* JC - suppress context menu 8/29/2002
+
+ // On right mouse, go through some convoluted steps to
+ // make the build menu appear.
+ setCurrentTool( (LLTool*) mNull );
+
+ // This should return FALSE, meaning the context menu will
+ // be shown.
+ return FALSE;
+ */
+
+ // Returning true will suppress the context menu
+ return TRUE;
+}
+
+
+BOOL LLToolCompGun::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_UP);
+ setCurrentTool( (LLTool*) mGun );
+ return TRUE;
+}
+
+void LLToolCompGun::onMouseCaptureLost()
+{
+ mCur->onMouseCaptureLost();
+
+ // JC - I don't know if this is necessary. Maybe we could lose capture
+ // if someone ALT-Tab's out when in mouselook.
+ setCurrentTool( (LLTool*) mGun );
+}
+
+void LLToolCompGun::handleSelect()
+{
+ LLToolComposite::handleSelect();
+ setMouseCapture(TRUE);
+}
+
+void LLToolCompGun::handleDeselect()
+{
+ LLToolComposite::handleDeselect();
+ setMouseCapture(FALSE);
+}
+
+
+BOOL LLToolCompGun::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (clicks > 0)
+ {
+ gAgent.changeCameraToDefault();
+
+ }
+ return TRUE;
+}
diff --git a/indra/newview/lltoolcomp.h b/indra/newview/lltoolcomp.h
new file mode 100644
index 0000000000..cb1ec33f1b
--- /dev/null
+++ b/indra/newview/lltoolcomp.h
@@ -0,0 +1,200 @@
+/**
+ * @file lltoolcomp.h
+ * @brief Composite tools
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLCOMP_H
+#define LL_TOOLCOMP_H
+
+#include "lltool.h"
+
+class LLManip;
+class LLToolSelectRect;
+class LLToolPlacer;
+
+class LLView;
+class LLTextBox;
+
+//-----------------------------------------------------------------------
+// LLToolComposite
+
+class LLToolComposite : public LLTool
+{
+public:
+ LLToolComposite(const LLString& name);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0; // Sets the current tool
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); // Returns to the default tool
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask) = 0;
+
+ // Map virtual functions to the currently active internal tool
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask) { return mCur->handleHover( x, y, mask ); }
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) { return mCur->handleScrollWheel( x, y, clicks ); }
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) { return mCur->handleRightMouseDown( x, y, mask ); }
+
+ virtual LLViewerObject* getEditingObject() { return mCur->getEditingObject(); }
+ virtual LLVector3d getEditingPointGlobal() { return mCur->getEditingPointGlobal(); }
+ virtual BOOL isEditing() { return mCur->isEditing(); }
+ virtual void stopEditing() { mCur->stopEditing(); mCur = mDefault; }
+
+ virtual BOOL clipMouseWhenDown() { return mCur->clipMouseWhenDown(); }
+
+ virtual void handleSelect();
+ virtual void handleDeselect() { mCur->handleDeselect(); mCur = mDefault; mSelected = FALSE; }
+
+ virtual void render() { mCur->render(); }
+ virtual void draw() { mCur->draw(); }
+
+ virtual BOOL handleKey(KEY key, MASK mask) { return mCur->handleKey( key, mask ); }
+
+ virtual void onMouseCaptureLost();
+
+ virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
+ { mCur->screenPointToLocal(screen_x, screen_y, local_x, local_y); }
+
+ virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
+ { mCur->localPointToScreen(local_x, local_y, screen_x, screen_y); }
+
+ BOOL isSelecting();
+protected:
+ void setCurrentTool( LLTool* new_tool );
+ LLTool* getCurrentTool() { return mCur; }
+ // In hover handler, call this to auto-switch tools
+ void setToolFromMask( MASK mask, LLTool *normal );
+
+protected:
+ LLTool* mCur; // The tool to which we're delegating.
+ LLTool* mDefault;
+ BOOL mSelected;
+ BOOL mMouseDown;
+ LLManip* mManip;
+ LLToolSelectRect* mSelectRect;
+
+public:
+ static const LLString sNameComp;
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompTranslate
+
+class LLToolCompTranslate : public LLToolComposite
+{
+public:
+ LLToolCompTranslate();
+ virtual ~LLToolCompTranslate();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); // Returns to the default tool
+ virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompScale
+
+class LLToolCompScale : public LLToolComposite
+{
+public:
+ LLToolCompScale();
+ virtual ~LLToolCompScale();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); // Returns to the default tool
+ virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompRotate
+
+class LLToolCompRotate : public LLToolComposite
+{
+public:
+ LLToolCompRotate();
+ virtual ~LLToolCompRotate();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+};
+
+//-----------------------------------------------------------------------
+// LLToolCompCreate
+
+class LLToolCompCreate : public LLToolComposite
+{
+public:
+ LLToolCompCreate();
+ virtual ~LLToolCompCreate();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+protected:
+ LLToolPlacer* mPlacer;
+ BOOL mObjectPlacedOnMouseDown;
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompGun
+
+class LLToolGun;
+class LLToolGrab;
+class LLToolSelect;
+
+class LLToolCompGun : public LLToolComposite
+{
+public:
+ LLToolCompGun();
+ virtual ~LLToolCompGun();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void onMouseCaptureLost();
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+protected:
+ LLToolGun* mGun;
+ LLToolGrab* mGrab;
+ LLTool* mNull;
+};
+
+extern LLToolCompTranslate *gToolTranslate;
+extern LLToolCompScale *gToolStretch;
+extern LLToolCompRotate *gToolRotate;
+extern LLToolCompCreate *gToolCreate;
+extern LLToolCompGun *gToolGun;
+
+#endif // LL_TOOLCOMP_H
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
new file mode 100644
index 0000000000..dcfcd03a68
--- /dev/null
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -0,0 +1,2969 @@
+/**
+ * @file lltooldraganddrop.cpp
+ * @brief LLToolDragAndDrop class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "message.h"
+#include "lltooldraganddrop.h"
+
+#include "llinstantmessage.h"
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloatertools.h"
+#include "llgesturemgr.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llnotify.h"
+#include "llpreviewnotecard.h"
+#include "llselectmgr.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvolume.h"
+#include "llworld.h"
+#include "object_flags.h"
+#include "viewer.h"
+
+LLToolDragAndDrop *gToolDragAndDrop = NULL;
+
+// MAX ITEMS is based on (sizeof(uuid)+2) * count must be < MTUBYTES
+// or 18 * count < 1200 => count < 1200/18 => 66. I've cut it down a
+// bit from there to give some pad.
+const S32 MAX_ITEMS = 42;
+const char* FOLDER_INCLUDES_ATTACHMENTS_BEING_WORN =
+ "Cannot give folders that contain objects that are attached to you.\n"
+ "Detach the object(s) and then try again.";
+
+
+// syntactic sugar
+#define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember))
+
+/*
+const LLUUID MULTI_CONTAINER_TEXTURE("b2181ea2-1937-2ee1-78b8-bf05c43536b7");
+LLUUID CONTAINER_TEXTURES[LLAssetType::AT_COUNT];
+
+const char* CONTAINER_TEXTURE_NAMES[LLAssetType::AT_COUNT] =
+{
+ "container_texture.tga",
+ "container_sound.tga",
+ "container_many_things.tga",
+ "container_landmark.tga",
+ "container_script.tga",
+ "container_clothing.tga",
+ "container_object.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_script.tga",
+ "container_script.tga",
+ "container_texture.tga",
+ "container_bodypart.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_sound.tga",
+ "container_texture.tga",
+ "container_texture.tga",
+ "container_animation.tga",
+ "container_gesture.tga"
+};
+*/
+
+class LLNoPreferredType : public LLInventoryCollectFunctor
+{
+public:
+ LLNoPreferredType() {}
+ virtual ~LLNoPreferredType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ if(cat && (cat->getPreferredType() == LLAssetType::AT_NONE))
+ {
+ return true;
+ }
+ return false;
+ }
+};
+
+class LLNoPreferredTypeOrItem : public LLInventoryCollectFunctor
+{
+public:
+ LLNoPreferredTypeOrItem() {}
+ virtual ~LLNoPreferredTypeOrItem() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ if(item) return true;
+ if(cat && (cat->getPreferredType() == LLAssetType::AT_NONE))
+ {
+ return true;
+ }
+ return false;
+ }
+};
+
+class LLDroppableItem : public LLInventoryCollectFunctor
+{
+public:
+ LLDroppableItem(BOOL is_transfer) :
+ mCountLosing(0), mIsTransfer(is_transfer) {}
+ virtual ~LLDroppableItem() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+ S32 countNoCopy() const { return mCountLosing; }
+
+protected:
+ S32 mCountLosing;
+ BOOL mIsTransfer;
+};
+
+bool LLDroppableItem::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ bool allowed = false;
+ if(item)
+ {
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = true;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = true;
+ }
+ break;
+
+ default:
+ allowed = true;
+ break;
+ }
+ if(mIsTransfer
+ && !item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID()))
+ {
+ allowed = false;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ ++mCountLosing;
+ }
+ }
+ return allowed;
+}
+
+class LLUncopyableItems : public LLInventoryCollectFunctor
+{
+public:
+ LLUncopyableItems() {}
+ virtual ~LLUncopyableItems() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+};
+
+bool LLUncopyableItems::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ BOOL uncopyable = FALSE;
+ if(item)
+ {
+ BOOL allowed = FALSE;
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ default:
+ allowed = TRUE;
+ break;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ uncopyable = TRUE;
+ }
+ }
+ return (uncopyable ? true : false);
+}
+
+class LLDropCopyableItems : public LLInventoryCollectFunctor
+{
+public:
+ LLDropCopyableItems() {}
+ virtual ~LLDropCopyableItems() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+};
+
+
+bool LLDropCopyableItems::operator()(
+ LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ BOOL allowed = FALSE;
+ if(item)
+ {
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ default:
+ allowed = TRUE;
+ break;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // whoops, can't copy it - don't allow it.
+ allowed = FALSE;
+ }
+ }
+ return (allowed ? true : false);
+}
+
+class LLGiveable : public LLInventoryCollectFunctor
+{
+public:
+ LLGiveable() : mCountLosing(0) {}
+ virtual ~LLGiveable() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+
+ S32 countNoCopy() const { return mCountLosing; }
+protected:
+ S32 mCountLosing;
+};
+
+bool LLGiveable::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ // All categories can be given.
+ if(cat) return TRUE;
+ BOOL allowed = FALSE;
+ if(item)
+ {
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ default:
+ allowed = TRUE;
+ break;
+ }
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID()))
+ {
+ allowed = FALSE;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ ++mCountLosing;
+ }
+ }
+ return (allowed ? true : false);
+}
+
+class LLCategoryFireAndForget : public LLInventoryFetchComboObserver
+{
+public:
+ LLCategoryFireAndForget() {}
+ ~LLCategoryFireAndForget() {}
+ virtual void done()
+ {
+ /* no-op: it's fire n forget right? */
+ lldebugs << "LLCategoryFireAndForget::done()" << llendl;
+ }
+};
+
+class LLCategoryDropObserver : public LLInventoryFetchObserver
+{
+public:
+ LLCategoryDropObserver(
+ const LLUUID& obj_id, LLToolDragAndDrop::ESource src) :
+ mObjectID(obj_id),
+ mSource(src)
+ {}
+ ~LLCategoryDropObserver() {}
+ virtual void done();
+
+protected:
+ LLUUID mObjectID;
+ LLToolDragAndDrop::ESource mSource;
+};
+
+void LLCategoryDropObserver::done()
+{
+ gInventory.removeObserver(this);
+ LLViewerObject* dst_obj = gObjectList.findObject(mObjectID);
+ if(dst_obj)
+ {
+ // *FIX: coalesce these...
+ LLInventoryItem* item = NULL;
+ item_ref_t::iterator it = mComplete.begin();
+ item_ref_t::iterator end = mComplete.end();
+ for(; it < end; ++it)
+ {
+ item = gInventory.getItem(*it);
+ if(item)
+ {
+ LLToolDragAndDrop::dropInventory(
+ dst_obj,
+ item,
+ mSource,
+ LLUUID::null);
+ }
+ }
+ }
+ delete this;
+}
+
+class LLCategoryDropDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLCategoryDropDescendentsObserver(
+ const LLUUID& obj_id, LLToolDragAndDrop::ESource src) :
+ mObjectID(obj_id),
+ mSource(src)
+ {}
+ ~LLCategoryDropDescendentsObserver() {}
+ virtual void done();
+
+protected:
+ LLUUID mObjectID;
+ LLToolDragAndDrop::ESource mSource;
+};
+
+void LLCategoryDropDescendentsObserver::done()
+{
+
+ gInventory.removeObserver(this);
+ folder_ref_t::iterator it = mCompleteFolders.begin();
+ folder_ref_t::iterator end = mCompleteFolders.end();
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ for(; it != end; ++it)
+ {
+ gInventory.collectDescendents(
+ (*it),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH);
+ }
+
+ S32 count = items.count();
+ if(count)
+ {
+ std::set<LLUUID> unique_ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ unique_ids.insert(items.get(i)->getUUID());
+ }
+ LLInventoryFetchObserver::item_ref_t ids;
+ std::back_insert_iterator<LLInventoryFetchObserver::item_ref_t> copier(ids);
+ std::copy(unique_ids.begin(), unique_ids.end(), copier);
+ LLCategoryDropObserver* dropper;
+ dropper = new LLCategoryDropObserver(mObjectID, mSource);
+ dropper->fetchItems(ids);
+ if(dropper->isEverythingComplete())
+ {
+ dropper->done();
+ }
+ else
+ {
+ gInventory.addObserver(dropper);
+ }
+ }
+ delete this;
+}
+
+// This array is used to more easily control what happens when a 3d
+// drag and drop event occurs. Since there's an array of drop target
+// and cargo type, it's implemented as an array of pointers to member
+// functions which correctly carry out the actual drop.
+LLToolDragAndDrop::dragOrDrop3dImpl LLToolDragAndDrop::sDragAndDrop3d[DAD_COUNT][LLToolDragAndDrop::DT_COUNT] =
+{
+ // Source: DAD_NONE
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_LAND
+ },
+ // Source: DAD_TEXTURE
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dTextureObject, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_SOUND
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_CALLINGCARD
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_LAND
+ },
+ // Source: DAD_LANDMARK
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_SCRIPT
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dRezScript, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_CLOTHING
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dWearItem, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_OBJECT
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dRezAttachmentFromInv, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventoryObject, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dRezObjectOnObject, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dRezObjectOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_NOTECARD
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_CATEGORY
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dWearCategory, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventoryCategory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventoryCategory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dCategoryOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_ROOT
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_LAND
+ },
+ // Source: DAD_BODYPART
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dWearItem, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_ANIMATION
+ // TODO: animation on self could play it? edit it?
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_GESTURE
+ // TODO: gesture on self could play it? edit it?
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dActivateGesture, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+};
+
+LLToolDragAndDrop::LLToolDragAndDrop()
+ :
+ LLTool("draganddrop", NULL),
+ mDragStartX(0),
+ mDragStartY(0),
+ mCursor(UI_CURSOR_NO),
+ mLastAccept(ACCEPT_NO),
+ mDrop(FALSE),
+ mCurItemIndex(0)
+{
+ // setup container texture ids
+ //for (S32 i = 0; i < LLAssetType::AT_COUNT; i++)
+ //{
+ // CONTAINER_TEXTURES[i].set(gViewerArt.getString(CONTAINER_TEXTURE_NAMES[i]));
+ //}
+}
+
+void LLToolDragAndDrop::setDragStart(S32 x, S32 y)
+{
+ mDragStartX = x;
+ mDragStartY = y;
+}
+
+BOOL LLToolDragAndDrop::isOverThreshold(S32 x,S32 y)
+{
+ const S32 MIN_MANHATTAN_DIST = 3;
+ S32 manhattan_dist = llabs( x - mDragStartX ) + llabs( y - mDragStartY );
+ return manhattan_dist >= MIN_MANHATTAN_DIST;
+}
+
+void LLToolDragAndDrop::beginDrag(EDragAndDropType type,
+ const LLUUID& cargo_id,
+ ESource source,
+ const LLUUID& source_id,
+ const LLUUID& object_id)
+{
+ if(type == DAD_NONE)
+ {
+ llwarns << "Attempted to start drag without a cargo type" << llendl;
+ return;
+ }
+ mCargoTypes.clear();
+ mCargoTypes.push_back(type);
+ mCargoIDs.clear();
+ mCargoIDs.push_back(cargo_id);
+ mSource = source;
+ mSourceID = source_id;
+ mObjectID = object_id;
+
+ setMouseCapture( TRUE );
+ gToolMgr->setTransientTool( this );
+ mCursor = UI_CURSOR_NO;
+ if((mCargoTypes[0] == DAD_CATEGORY)
+ && ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)))
+ {
+ LLInventoryCategory* cat = gInventory.getCategory(cargo_id);
+ // go ahead and fire & forget the descendents if we are not
+ // dragging a protected folder.
+ if(cat)
+ {
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLNoPreferredTypeOrItem is_not_preferred;
+ LLInventoryFetchComboObserver::folder_ref_t folder_ids;
+ LLInventoryFetchComboObserver::item_ref_t item_ids;
+ if(is_not_preferred(cat, NULL))
+ {
+ folder_ids.push_back(cargo_id);
+ }
+ gInventory.collectDescendentsIf(
+ cargo_id,
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_not_preferred);
+ S32 count = cats.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ folder_ids.push_back(cats.get(i)->getUUID());
+ }
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ item_ids.push_back(items.get(i)->getUUID());
+ }
+ if(!folder_ids.empty() || !item_ids.empty())
+ {
+ LLCategoryFireAndForget fetcher;
+ fetcher.fetch(folder_ids, item_ids);
+ }
+ }
+ }
+}
+
+void LLToolDragAndDrop::beginMultiDrag(
+ const std::vector<EDragAndDropType> types,
+ const std::vector<LLUUID>& cargo_ids,
+ ESource source,
+ const LLUUID& source_id)
+{
+ // assert on public api is evil
+ //llassert( type != DAD_NONE );
+
+ std::vector<EDragAndDropType>::const_iterator types_it;
+ for (types_it = types.begin(); types_it != types.end(); ++types_it)
+ {
+ if(DAD_NONE == *types_it)
+ {
+ llwarns << "Attempted to start drag without a cargo type" << llendl;
+ return;
+ }
+ }
+ mCargoTypes = types;
+ mCargoIDs = cargo_ids;
+ mSource = source;
+ mSourceID = source_id;
+
+ setMouseCapture( TRUE );
+ gToolMgr->setTransientTool( this );
+ mCursor = UI_CURSOR_NO;
+ if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+ {
+ // find cats in the cargo.
+ LLInventoryCategory* cat = NULL;
+ S32 count = llmin(cargo_ids.size(), types.size());
+ std::set<LLUUID> cat_ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ cat = gInventory.getCategory(cargo_ids[i]);
+ if(cat)
+ {
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLNoPreferredType is_not_preferred;
+ if(is_not_preferred(cat, NULL))
+ {
+ cat_ids.insert(cat->getUUID());
+ }
+ gInventory.collectDescendentsIf(
+ cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_not_preferred);
+ S32 cat_count = cats.count();
+ for(S32 i = 0; i < cat_count; ++i)
+ {
+ cat_ids.insert(cat->getUUID());
+ }
+ }
+ }
+ if(!cat_ids.empty())
+ {
+ LLInventoryFetchComboObserver::folder_ref_t folder_ids;
+ LLInventoryFetchComboObserver::item_ref_t item_ids;
+ std::back_insert_iterator<LLInventoryFetchDescendentsObserver::folder_ref_t> copier(folder_ids);
+ std::copy(cat_ids.begin(), cat_ids.end(), copier);
+ LLCategoryFireAndForget fetcher;
+ fetcher.fetch(folder_ids, item_ids);
+ }
+ }
+}
+
+void LLToolDragAndDrop::endDrag()
+{
+ gSelectMgr->unhighlightAll();
+ setMouseCapture(FALSE);
+}
+
+void LLToolDragAndDrop::onMouseCaptureLost()
+{
+ // Called whenever the drag ends or if mouse captue is simply lost
+ gToolMgr->clearTransientTool();
+ mCargoTypes.clear();
+ mCargoIDs.clear();
+ mSource = SOURCE_AGENT;
+ mSourceID.setNull();
+ mObjectID.setNull();
+}
+
+BOOL LLToolDragAndDrop::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ if( hasMouseCapture() )
+ {
+ EAcceptance acceptance = ACCEPT_NO;
+ dragOrDrop( x, y, mask, TRUE, &acceptance );
+ endDrag();
+ }
+ return TRUE;
+}
+
+BOOL LLToolDragAndDrop::handleHover( S32 x, S32 y, MASK mask )
+{
+ EAcceptance acceptance = ACCEPT_NO;
+ dragOrDrop( x, y, mask, FALSE, &acceptance );
+
+ switch( acceptance )
+ {
+ case ACCEPT_YES_MULTI:
+ if (mCargoIDs.size() > 1)
+ {
+ mCursor = UI_CURSOR_ARROWDRAGMULTI;
+ }
+ else
+ {
+ mCursor = UI_CURSOR_ARROWDRAG;
+ }
+ break;
+ case ACCEPT_YES_SINGLE:
+ mCursor = UI_CURSOR_ARROWDRAG;
+ break;
+
+ case ACCEPT_NO_LOCKED:
+ mCursor = UI_CURSOR_NOLOCKED;
+ break;
+
+ case ACCEPT_NO:
+ mCursor = UI_CURSOR_NO;
+ break;
+
+ case ACCEPT_YES_COPY_MULTI:
+ if (mCargoIDs.size() > 1)
+ {
+ mCursor = UI_CURSOR_ARROWCOPYMULTI;
+ }
+ else
+ {
+ mCursor = UI_CURSOR_ARROWCOPY;
+ }
+ break;
+ case ACCEPT_YES_COPY_SINGLE:
+ mCursor = UI_CURSOR_ARROWCOPY;
+ break;
+ case ACCEPT_POSTPONED:
+ break;
+ default:
+ llassert( FALSE );
+ }
+
+ gViewerWindow->getWindow()->setCursor( mCursor );
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolDragAndDrop" << llendl;
+ return TRUE;
+}
+
+BOOL LLToolDragAndDrop::handleKey(KEY key, MASK mask)
+{
+ if (key == KEY_ESCAPE)
+ {
+ // cancel drag and drop operation
+ endDrag();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLToolDragAndDrop::handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen)
+{
+ if (!mToolTipMsg.empty())
+ {
+ msg = mToolTipMsg;
+ //*stick_rect_screen = gViewerWindow->getWindowRect();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLToolDragAndDrop::handleDeselect()
+{
+ mToolTipMsg.clear();
+}
+
+// protected
+void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop,
+ EAcceptance* acceptance)
+{
+ *acceptance = ACCEPT_YES_MULTI;
+
+ BOOL handled = FALSE;
+
+ LLView* top_view = gViewerWindow->getTopView();
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+
+ mToolTipMsg.assign("");
+
+ if(top_view)
+ {
+ handled = TRUE;
+
+ for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ if (cargo)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ EAcceptance item_acceptance = ACCEPT_NO;
+ handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ if (handled)
+ {
+ // use sort order to determine priority of acceptance
+ *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // all objects passed, go ahead and perform drop if necessary
+ if (handled && drop && (U32)*acceptance >= ACCEPT_YES_COPY_SINGLE)
+ {
+ // drop all items
+ if ((U32)*acceptance >= ACCEPT_YES_COPY_MULTI)
+ {
+ mCurItemIndex = 0;
+ }
+ // drop just last item
+ else
+ {
+ mCurItemIndex = mCargoIDs.size() - 1;
+ }
+ for (; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ if (cargo)
+ {
+ S32 local_x, local_y;
+
+ EAcceptance item_acceptance;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ }
+ }
+ }
+ if (handled)
+ {
+ mLastAccept = (EAcceptance)*acceptance;
+ }
+ }
+
+ if(!handled)
+ {
+ handled = TRUE;
+
+ LLView* root_view = gViewerWindow->getRootView();
+
+ for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ EAcceptance item_acceptance = ACCEPT_NO;
+ handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ if (handled)
+ {
+ // use sort order to determine priority of acceptance
+ *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
+ }
+ }
+ // all objects passed, go ahead and perform drop if necessary
+ if (handled && drop && (U32)*acceptance > ACCEPT_NO_LOCKED)
+ {
+ // drop all items
+ if ((U32)*acceptance >= ACCEPT_YES_COPY_MULTI)
+ {
+ mCurItemIndex = 0;
+ }
+ // drop just last item
+ else
+ {
+ mCurItemIndex = mCargoIDs.size() - 1;
+ }
+ for (; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ if (cargo)
+ {
+ //S32 local_x, local_y;
+
+ EAcceptance item_acceptance;
+ handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ }
+ }
+ }
+
+ if (handled)
+ {
+ mLastAccept = (EAcceptance)*acceptance;
+ }
+ }
+
+ if ( !handled )
+ {
+ dragOrDrop3D( x, y, mask, drop, acceptance );
+ }
+}
+
+void LLToolDragAndDrop::dragOrDrop3D( S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* acceptance )
+{
+ mDrop = drop;
+ if (mDrop)
+ {
+ gPickFaces = TRUE;
+ // don't allow drag and drop onto transparent objects
+ gViewerWindow->hitObjectOrLandGlobalImmediate(x, y, pickCallback, FALSE);
+ }
+ else
+ {
+ // Don't pick faces during hover. Nothing currently requires per-face
+ // data.
+ // don't allow drag and drop onto transparent objects
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, FALSE);
+ }
+
+ *acceptance = mLastAccept;
+}
+
+void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
+{
+ EDropTarget target = DT_NONE;
+ S32 hit_face = -1;
+
+ LLViewerObject* hit_obj = gViewerWindow->lastNonFloraObjectHit();
+ gSelectMgr->unhighlightAll();
+
+ // Treat attachments as part of the avatar they are attached to.
+ if (hit_obj)
+ {
+ if(hit_obj->isAttachment() && !hit_obj->isHUDAttachment())
+ {
+ LLVOAvatar* avatar = LLVOAvatar::findAvatarFromAttachment( hit_obj );
+ if( !avatar )
+ {
+ gToolDragAndDrop->mLastAccept = ACCEPT_NO;
+ gToolDragAndDrop->mCursor = UI_CURSOR_NO;
+ gViewerWindow->getWindow()->setCursor( gToolDragAndDrop->mCursor );
+ return;
+ }
+
+ hit_obj = avatar;
+ }
+
+ if(hit_obj->isAvatar())
+ {
+ if(((LLVOAvatar*) hit_obj)->mIsSelf)
+ {
+ target = DT_SELF;
+ hit_face = -1;
+ }
+ else
+ {
+ target = DT_AVATAR;
+ hit_face = -1;
+ }
+ }
+ else
+ {
+ target = DT_OBJECT;
+ hit_face = gLastHitNonFloraObjectFace;
+ // if any item being dragged will be applied to the object under our cursor
+ // highlight that object
+ for (S32 i = 0; i < (S32)gToolDragAndDrop->mCargoIDs.size(); i++)
+ {
+ if (gToolDragAndDrop->mCargoTypes[i] != DAD_OBJECT || (mask & MASK_CONTROL))
+ {
+ gSelectMgr->highlightObjectAndFamily(hit_obj);
+ break;
+ }
+ }
+ }
+ }
+ else if(gLastHitLand)
+ {
+ target = DT_LAND;
+ hit_face = -1;
+ }
+
+ gToolDragAndDrop->mLastAccept = ACCEPT_YES_MULTI;
+
+ for (gToolDragAndDrop->mCurItemIndex = 0; gToolDragAndDrop->mCurItemIndex < (S32)gToolDragAndDrop->mCargoIDs.size();
+ gToolDragAndDrop->mCurItemIndex++)
+ {
+ // Call the right implementation function
+ gToolDragAndDrop->mLastAccept = (EAcceptance)llmin(
+ (U32)gToolDragAndDrop->mLastAccept,
+ (U32)callMemberFunction((*gToolDragAndDrop),
+ gToolDragAndDrop->sDragAndDrop3d[gToolDragAndDrop->mCargoTypes[gToolDragAndDrop->mCurItemIndex]][target])
+ (hit_obj, hit_face, mask, FALSE));
+ }
+
+ if (gToolDragAndDrop->mDrop && (U32)gToolDragAndDrop->mLastAccept >= ACCEPT_YES_COPY_SINGLE)
+ {
+ // if target allows multi-drop, go ahead and start iteration at beginning of cargo list
+ if (gToolDragAndDrop->mLastAccept >= ACCEPT_YES_COPY_MULTI)
+ {
+ gToolDragAndDrop->mCurItemIndex = 0;
+ }
+ // otherwise start at end, to follow selection rules (last selected item is most current)
+ else
+ {
+ gToolDragAndDrop->mCurItemIndex = gToolDragAndDrop->mCargoIDs.size() - 1;
+ }
+
+ for (; gToolDragAndDrop->mCurItemIndex < (S32)gToolDragAndDrop->mCargoIDs.size();
+ gToolDragAndDrop->mCurItemIndex++)
+ {
+ // Call the right implementation function
+ (U32)callMemberFunction((*gToolDragAndDrop),
+ gToolDragAndDrop->sDragAndDrop3d[gToolDragAndDrop->mCargoTypes[gToolDragAndDrop->mCurItemIndex]][target])
+ (hit_obj, hit_face, mask, TRUE);
+ }
+ }
+
+ switch( gToolDragAndDrop->mLastAccept )
+ {
+ case ACCEPT_YES_MULTI:
+ if (gToolDragAndDrop->mCargoIDs.size() > 1)
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWDRAGMULTI;
+ }
+ else
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWDRAG;
+ }
+ break;
+ case ACCEPT_YES_SINGLE:
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWDRAG;
+ break;
+
+ case ACCEPT_NO_LOCKED:
+ gToolDragAndDrop->mCursor = UI_CURSOR_NOLOCKED;
+ break;
+
+ case ACCEPT_NO:
+ gToolDragAndDrop->mCursor = UI_CURSOR_NO;
+ break;
+
+ case ACCEPT_YES_COPY_MULTI:
+ if (gToolDragAndDrop->mCargoIDs.size() > 1)
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWCOPYMULTI;
+ }
+ else
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWCOPY;
+ }
+ break;
+ case ACCEPT_YES_COPY_SINGLE:
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWCOPY;
+ break;
+ case ACCEPT_POSTPONED:
+ break;
+ default:
+ llassert( FALSE );
+ }
+
+ gToolDragAndDrop->mLastHitPos = gLastHitPosGlobal + gLastHitObjectOffset;
+ gToolDragAndDrop->mLastCameraPos = gAgent.getCameraPositionGlobal();
+
+ gViewerWindow->getWindow()->setCursor( gToolDragAndDrop->mCursor );
+}
+
+// static
+BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ // Always succeed if....
+ // texture is from the library
+ // or already in the contents of the object
+ if(SOURCE_LIBRARY == source)
+ {
+ // dropping a texture from the library always just works.
+ return TRUE;
+ }
+
+ if (hit_obj->getInventoryItemByAsset(item->getAssetUUID()))
+ {
+ // if the asset is already in the object's inventory
+ // then it can always be added to a side.
+ // This saves some work if the task's inventory is already loaded
+ return TRUE;
+ }
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ if(!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))
+ {
+ // Check that we can add the texture as inventory to the object
+ if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
+ {
+ return FALSE;
+ }
+ // make sure the object has the texture in it's inventory.
+ if(SOURCE_AGENT == source)
+ {
+ // Remove the texture from local inventory. The server
+ // will actually remove the item from agent inventory.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+ else if(SOURCE_WORLD == source)
+ {
+ // *FIX: if the objects are in different regions, and the
+ // source region has crashed, you can bypass these
+ // permissions.
+ LLViewerObject* src_obj = gObjectList.findObject(src_id);
+ if(src_obj)
+ {
+ src_obj->removeInventory(item->getUUID());
+ }
+ else
+ {
+ llwarns << "Unable to find source object." << llendl;
+ return FALSE;
+ }
+ }
+ hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
+ }
+ else if(!item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID()))
+ {
+ // Check that we can add the testure as inventory to the object
+ if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
+ {
+ return FALSE;
+ }
+ // *FIX: may want to make sure agent can paint hit_obj.
+
+ // make sure the object has the texture in it's inventory.
+ hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
+ }
+ return TRUE;
+}
+
+void LLToolDragAndDrop::dropTextureAllFaces(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ LLUUID asset_id = item->getAssetUUID();
+ BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
+ if(!success)
+ {
+ return;
+ }
+ LLViewerImage* image = gImageList.getImage(asset_id);
+ gViewerStats->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
+ S32 num_faces = hit_obj->getNumTEs();
+ for( S32 face = 0; face < num_faces; face++ )
+ {
+
+ // update viewer side image in anticipation of update from simulator
+ hit_obj->setTEImage(face, image);
+ dialog_refresh_all();
+
+ // send the update to the simulator
+ hit_obj->sendTEUpdate();
+ }
+
+}
+
+/*
+void LLToolDragAndDrop::dropTextureOneFaceAvatar(LLVOAvatar* avatar, S32 hit_face, LLInventoryItem* item)
+{
+ if (hit_face == -1) return;
+ LLViewerImage* image = gImageList.getImage(item->getAssetUUID());
+
+ avatar->userSetOptionalTE( hit_face, image);
+}
+*/
+
+void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,
+ S32 hit_face,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ if (hit_face == -1) return;
+ if (!item)
+ {
+ llwarns << "LLToolDragAndDrop::dropTextureOneFace no texture item." << llendl;
+ return;
+ }
+ LLUUID asset_id = item->getAssetUUID();
+ BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
+ if(!success)
+ {
+ return;
+ }
+ // update viewer side image in anticipation of update from simulator
+ LLViewerImage* image = gImageList.getImage(asset_id);
+ gViewerStats->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
+ hit_obj->setTEImage(hit_face, image);
+ dialog_refresh_all();
+
+ // send the update to the simulator
+ hit_obj->sendTEUpdate();
+}
+
+
+void LLToolDragAndDrop::dropScript(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ BOOL active,
+ ESource source,
+ const LLUUID& src_id)
+{
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == gToolDragAndDrop->mSource)
+ || (SOURCE_NOTECARD == gToolDragAndDrop->mSource))
+ {
+ llwarns << "Call to LLToolDragAndDrop::dropScript() from world"
+ << " or notecard." << llendl;
+ return;
+ }
+ if(hit_obj && item)
+ {
+ LLPointer<LLViewerInventoryItem> new_script = new LLViewerInventoryItem(item);
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ if(SOURCE_AGENT == source)
+ {
+ // Remove the script from local inventory. The server
+ // will actually remove the item from agent inventory.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+ else if(SOURCE_WORLD == source)
+ {
+ // *FIX: if the objects are in different regions, and
+ // the source region has crashed, you can bypass
+ // these permissions.
+ LLViewerObject* src_obj = gObjectList.findObject(src_id);
+ if(src_obj)
+ {
+ src_obj->removeInventory(item->getUUID());
+ }
+ else
+ {
+ llwarns << "Unable to find source object." << llendl;
+ return;
+ }
+ }
+ }
+ hit_obj->saveScript(new_script, active, true);
+ gFloaterTools->dirty();
+
+ // VEFFECT: SetScript
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(hit_obj);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+}
+
+void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target,
+ BOOL bypass_sim_raycast,
+ BOOL from_task_inventory,
+ BOOL remove_from_inventory)
+{
+ LLViewerRegion* regionp = gWorldp->getRegionFromPosGlobal(mLastHitPos);
+ if (!regionp)
+ {
+ llwarns << "Couldn't find region to rez object" << llendl;
+ return;
+ }
+
+ //llinfos << "Rezzing object" << llendl;
+ make_ui_sound("UISndObjectRezIn");
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return;
+
+ if (regionp
+ && (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX))
+ {
+ LLFirstUse::useSandbox();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ if (mSource == SOURCE_NOTECARD)
+ {
+ msg->newMessageFast(_PREHASH_RezObjectFromNotecard);
+ }
+ else
+ {
+ msg->newMessageFast(_PREHASH_RezObject);
+ }
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+
+ msg->nextBlock("RezData");
+ // if it's being rezzed from task inventory, we need to enable
+ // saving it back into the task inventory.
+ // *FIX: We can probably compress this to a single byte, since I
+ // think folderid == mSourceID. This will be a later
+ // optimization.
+ if(from_task_inventory)
+ {
+ msg->addUUIDFast(_PREHASH_FromTaskID, mSourceID);
+ }
+ else
+ {
+ msg->addUUIDFast(_PREHASH_FromTaskID, LLUUID::null);
+ }
+ msg->addU8Fast(_PREHASH_BypassRaycast, (U8) bypass_sim_raycast);
+ msg->addVector3Fast(_PREHASH_RayStart, regionp->getPosRegionFromGlobal(mLastCameraPos));
+ msg->addVector3Fast(_PREHASH_RayEnd, regionp->getPosRegionFromGlobal(mLastHitPos));
+ // Limit raycast to a single object.
+ // Speeds up server raycast + avoid problems with server ray
+ // hitting objects that were clipped by the near plane or culled
+ // on the viewer.
+ LLUUID ray_target_id;
+ if( raycast_target )
+ {
+ ray_target_id = raycast_target->getID();
+ }
+ else
+ {
+ ray_target_id.setNull();
+ }
+ msg->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
+ msg->addBOOLFast(_PREHASH_RayEndIsIntersection, FALSE);
+ // Select the object only if we're editing.
+ BOOL rez_selected = gToolMgr->inEdit();
+ msg->addBOOLFast(_PREHASH_RezSelected, rez_selected);
+
+ // check if it cannot be copied, and mark as remove if it is -
+ // this will remove the object from inventory after rez. Only
+ // bother with this check if we would not normally remove from
+ // inventory.
+ if(!remove_from_inventory
+ && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ remove_from_inventory = TRUE;
+ }
+
+ // Check if it's in the trash.
+ bool is_in_trash = false;
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+ {
+ is_in_trash = true;
+ remove_from_inventory = TRUE;
+ }
+ msg->addBOOLFast(_PREHASH_RemoveItem, remove_from_inventory);
+
+ // deal with permissions slam logic
+ pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
+
+ LLUUID folder_id = item->getParentUUID();
+ if((SOURCE_LIBRARY == mSource) || (is_in_trash))
+ {
+ // since it's coming from the library or trash, we want to not
+ // 'take' it back to the same place.
+ item->setParent(LLUUID::null);
+ }
+ if (mSource == SOURCE_NOTECARD)
+ {
+ msg->nextBlockFast(_PREHASH_NotecardData);
+ msg->addUUIDFast(_PREHASH_NotecardItemID, mSourceID);
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ item->packMessage(msg);
+ }
+ msg->sendReliable(regionp->getHost());
+ // back out the change. no actual internal changes take place.
+ item->setParent(folder_id);
+
+ // If we're going to select it, get ready for the incoming
+ // selected object.
+ if (rez_selected)
+ {
+ gSelectMgr->deselectAll();
+ gViewerWindow->getWindow()->incBusyCount();
+ }
+
+ if(remove_from_inventory)
+ {
+ // Delete it from inventory immediately so that users cannot
+ // easily bypass copy protection in laggy situations. If the
+ // rez fails, we will put it back on the server.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+
+ // VEFFECT: DropObject
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setPositionGlobal(mLastHitPos);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+
+ gViewerStats->incStat(LLViewerStats::ST_REZ_COUNT);
+}
+
+void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == gToolDragAndDrop->mSource)
+ || (SOURCE_NOTECARD == gToolDragAndDrop->mSource))
+ {
+ llwarns << "Call to LLToolDragAndDrop::dropInventory() from world"
+ << " or notecard." << llendl;
+ return;
+ }
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ S32 creation_date = time_corrected();
+ new_item->setCreationDate(creation_date);
+
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ if(SOURCE_AGENT == source)
+ {
+ // Remove the inventory item from local inventory. The
+ // server will actually remove the item from agent
+ // inventory.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+ else if(SOURCE_WORLD == source)
+ {
+ // *FIX: if the objects are in different regions, and the
+ // source region has crashed, you can bypass these
+ // permissions.
+ LLViewerObject* src_obj = gObjectList.findObject(src_id);
+ if(src_obj)
+ {
+ src_obj->removeInventory(item->getUUID());
+ }
+ else
+ {
+ llwarns << "Unable to find source object." << llendl;
+ return;
+ }
+ }
+ }
+ hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
+ if (gFloaterTools->getVisible())
+ {
+ //FIXME: only show this if panel not expanded?
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ }
+
+ // VEFFECT: AddToInventory
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(hit_obj);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gFloaterTools->dirty();
+}
+
+struct LLGiveInventoryInfo
+{
+ LLUUID mToAgentID;
+ LLUUID mInventoryObjectID;
+ LLGiveInventoryInfo(const LLUUID& to_agent, const LLUUID& obj_id) :
+ mToAgentID(to_agent), mInventoryObjectID(obj_id) {}
+};
+
+void LLToolDragAndDrop::giveInventory(const LLUUID& to_agent,
+ LLInventoryItem* item)
+{
+ llinfos << "LLToolDragAndDrop::giveInventory()" << llendl;
+ if(!isInventoryGiveAcceptable(item))
+ {
+ return;
+ }
+ if(item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // just give it away.
+ LLToolDragAndDrop::commitGiveInventoryItem(to_agent, item);
+ }
+ else
+ {
+ // ask if the agent is sure.
+ LLGiveInventoryInfo* info = new LLGiveInventoryInfo(to_agent,
+ item->getUUID());
+
+ gViewerWindow->alertXml("CannotCopyWarning",
+ &LLToolDragAndDrop::handleCopyProtectedItem,
+ (void*)info);
+ }
+}
+
+// static
+void LLToolDragAndDrop::handleCopyProtectedItem(S32 option, void* data)
+{
+ LLGiveInventoryInfo* info = (LLGiveInventoryInfo*)data;
+ LLInventoryItem* item = NULL;
+ switch(option)
+ {
+ case 0: // "Yes"
+ item = gInventory.getItem(info->mInventoryObjectID);
+ if(item)
+ {
+ LLToolDragAndDrop::commitGiveInventoryItem(info->mToAgentID,
+ item);
+ // delete it for now - it will be deleted on the server
+ // quickly enough.
+ gInventory.deleteObject(info->mInventoryObjectID);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ gViewerWindow->alertXml("CannotGiveItem");
+ }
+ break;
+
+ default: // no, cancel, whatever, who cares, not yes.
+ gViewerWindow->alertXml("TransactionCancelled");
+ break;
+ }
+}
+
+// static
+void LLToolDragAndDrop::commitGiveInventoryItem(const LLUUID& to_agent,
+ LLInventoryItem* item)
+{
+ if(!item) return;
+ std::string name;
+ gAgent.buildFullname(name);
+ LLUUID transaction_id;
+ transaction_id.generate();
+ const S32 BUCKET_SIZE = sizeof(U8) + UUID_BYTES;
+ U8 bucket[BUCKET_SIZE];
+ bucket[0] = (U8)item->getType();
+ memcpy(&bucket[1], &(item->getUUID().mData), UUID_BYTES);
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ to_agent,
+ name.c_str(),
+ item->getName().c_str(),
+ IM_ONLINE,
+ IM_INVENTORY_OFFERED,
+ transaction_id,
+ 0,
+ LLUUID::null,
+ gAgent.getPositionAgent(),
+ NO_TIMESTAMP,
+ bucket,
+ BUCKET_SIZE);
+ gAgent.sendReliableMessage();
+
+ // VEFFECT: giveInventory
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(gObjectList.findObject(to_agent));
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gFloaterTools->dirty();
+}
+
+void LLToolDragAndDrop::giveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* cat)
+{
+ if(!cat) return;
+ llinfos << "LLToolDragAndDrop::giveInventoryCategory() - "
+ << cat->getUUID() << llendl;
+
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if( !my_avatar )
+ {
+ return;
+ }
+
+ // Test out how many items are being given.
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLGiveable giveable;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ giveable);
+ S32 count = cats.count();
+ bool complete = true;
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(!gInventory.isCategoryComplete(cats.get(i)->getUUID()))
+ {
+ complete = false;
+ break;
+ }
+ }
+ if(!complete)
+ {
+ LLNotifyBox::showXml("IncompleteInventory");
+ return;
+ }
+ count = items.count() + cats.count();
+ if(count > MAX_ITEMS)
+ {
+ gViewerWindow->alertXml("TooManyItems");
+ return;
+ }
+ else if(count == 0)
+ {
+ gViewerWindow->alertXml("NoItems");
+ return;
+ }
+ else
+ {
+ if(0 == giveable.countNoCopy())
+ {
+ LLToolDragAndDrop::commitGiveInventoryCategory(to_agent, cat);
+ }
+ else
+ {
+ LLGiveInventoryInfo* info = NULL;
+ info = new LLGiveInventoryInfo(to_agent, cat->getUUID());
+ LLStringBase<char>::format_map_t args;
+ args["[COUNT]"] = llformat("%d",giveable.countNoCopy());
+ gViewerWindow->alertXml("CannotCopyCountItems", args,
+ &LLToolDragAndDrop::handleCopyProtectedCategory,
+ (void*)info);
+
+ }
+ }
+}
+
+
+// static
+void LLToolDragAndDrop::handleCopyProtectedCategory(S32 option, void* data)
+{
+ LLGiveInventoryInfo* info = (LLGiveInventoryInfo*)data;
+ LLInventoryCategory* cat = NULL;
+ switch(option)
+ {
+ case 0: // "Yes"
+ cat = gInventory.getCategory(info->mInventoryObjectID);
+ if(cat)
+ {
+ LLToolDragAndDrop::commitGiveInventoryCategory(info->mToAgentID,
+ cat);
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLUncopyableItems remove;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ remove);
+ S32 count = items.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ gInventory.deleteObject(items.get(i)->getUUID());
+ }
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ gViewerWindow->alertXml("CannotGiveCategory");
+ }
+ break;
+
+ default: // no, cancel, whatever, who cares, not yes.
+ gViewerWindow->alertXml("TransactionCancelled");
+ break;
+ }
+}
+
+// static
+void LLToolDragAndDrop::commitGiveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* cat)
+{
+ if(!cat) return;
+ llinfos << "LLToolDragAndDrop::commitGiveInventoryCategory() - "
+ << cat->getUUID() << llendl;
+
+ // Test out how many items are being given.
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLGiveable giveable;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ giveable);
+
+ // MAX ITEMS is based on (sizeof(uuid)+2) * count must be <
+ // MTUBYTES or 18 * count < 1200 => count < 1200/18 =>
+ // 66. I've cut it down a bit from there to give some pad.
+ S32 count = items.count() + cats.count();
+ if(count > MAX_ITEMS)
+ {
+ gViewerWindow->alertXml("TooManyItems");
+ return;
+ }
+ else if(count == 0)
+ {
+ gViewerWindow->alertXml("NoItems");
+ return;
+ }
+ else
+ {
+ std::string name;
+ gAgent.buildFullname(name);
+ LLUUID transaction_id;
+ transaction_id.generate();
+ S32 bucket_size = (sizeof(U8) + UUID_BYTES) * (count + 1);
+ U8* bucket = new U8[bucket_size];
+ U8* pos = bucket;
+ U8 type = (U8)cat->getType();
+ memcpy(pos, &type, sizeof(U8));
+ pos += sizeof(U8);
+ memcpy(pos, &(cat->getUUID()), UUID_BYTES);
+ pos += UUID_BYTES;
+ S32 i;
+ count = cats.count();
+ for(i = 0; i < count; ++i)
+ {
+ memcpy(pos, &type, sizeof(U8));
+ pos += sizeof(U8);
+ memcpy(pos, &(cats.get(i)->getUUID()), UUID_BYTES);
+ pos += UUID_BYTES;
+ }
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ type = (U8)items.get(i)->getType();
+ memcpy(pos, &type, sizeof(U8));
+ pos += sizeof(U8);
+ memcpy(pos, &(items.get(i)->getUUID()), UUID_BYTES);
+ pos += UUID_BYTES;
+ }
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ to_agent,
+ name.c_str(),
+ cat->getName().c_str(),
+ IM_ONLINE,
+ IM_INVENTORY_OFFERED,
+ transaction_id,
+ 0,
+ LLUUID::null,
+ gAgent.getPositionAgent(),
+ NO_TIMESTAMP,
+ bucket,
+ bucket_size);
+ gAgent.sendReliableMessage();
+ delete[] bucket;
+
+ // VEFFECT: giveInventoryCategory
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(gObjectList.findObject(to_agent));
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gFloaterTools->dirty();
+ }
+}
+
+// static
+BOOL LLToolDragAndDrop::isInventoryGiveAcceptable(LLInventoryItem* item)
+{
+ if(!item)
+ {
+ return FALSE;
+ }
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ return FALSE;
+ }
+ BOOL copyable = FALSE;
+ if(item->getPermissions().allowCopyBy(gAgent.getID())) copyable = TRUE;
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if(!my_avatar)
+ {
+ return FALSE;
+ }
+ BOOL acceptable = TRUE;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ acceptable = FALSE;
+ break;
+ case LLAssetType::AT_OBJECT:
+ if(my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ acceptable = FALSE;
+ }
+ break;
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!copyable && gAgent.isWearingItem(item->getUUID()))
+ {
+ acceptable = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ return acceptable;
+}
+
+// Static
+BOOL LLToolDragAndDrop::isInventoryGroupGiveAcceptable(LLInventoryItem* item)
+{
+ if(!item)
+ {
+ return FALSE;
+ }
+
+ // These permissions are double checked in the simulator in
+ // LLGroupNoticeInventoryItemFetch::result().
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ return FALSE;
+ }
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ return FALSE;
+ }
+
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if(!my_avatar)
+ {
+ return FALSE;
+ }
+
+ BOOL acceptable = TRUE;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ acceptable = FALSE;
+ break;
+ case LLAssetType::AT_OBJECT:
+ if(my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ acceptable = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ return acceptable;
+}
+
+// accessor that looks at permissions, copyability, and names of
+// inventory items to determine if a drop would be ok.
+EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item)
+{
+ // check the basics
+ if(!item || !obj) return ACCEPT_NO;
+ // HACK: downcast
+ LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item;
+ if(!vitem->isComplete()) return ACCEPT_NO;
+
+ // deny attempts to drop from an object onto itself. This is to
+ // help make sure that drops that are from an object to an object
+ // don't have to worry about order of evaluation. Think of this
+ // like check for self in assignment.
+ if(obj->getID() == item->getParentUUID())
+ {
+ return ACCEPT_NO;
+ }
+
+ //BOOL copy = (perm.allowCopyBy(gAgent.getID(),
+ // gAgent.getGroupID())
+ // && (obj->mPermModify || obj->mFlagAllowInventoryAdd));
+ BOOL worn = FALSE;
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ worn = TRUE;
+ }
+ break;
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(gAgent.isWearingItem(item->getUUID()))
+ {
+ worn = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ const LLPermissions& perm = item->getPermissions();
+ BOOL modify = (obj->permModify() || obj->flagAllowInventoryAdd());
+ BOOL transfer = FALSE;
+ if((obj->permYouOwner() && (perm.getOwner() == gAgent.getID()))
+ || perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ transfer = TRUE;
+ }
+ BOOL volume = (LL_PCODE_VOLUME == obj->getPCode());
+ BOOL attached = obj->isAttachment();
+ BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE;
+ if(attached && !unrestricted)
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ else if(modify && transfer && volume && !worn)
+ {
+ return ACCEPT_YES_MULTI;
+ }
+ else if(!modify)
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ return ACCEPT_NO;
+}
+
+///
+/// Methods called in the drag & drop array
+///
+
+EAcceptance LLToolDragAndDrop::dad3dNULL(
+ LLViewerObject*, S32, MASK, BOOL)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dNULL()" << llendl;
+ return ACCEPT_NO;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezAttachmentFromInv()" << llendl;
+ // must be in the user's inventory
+ if(mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ // must not be in the trash
+ LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH));
+ if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ // must not be already wearing it
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar || avatar->isWearingAttachment(item->getUUID()) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if( drop )
+ {
+ if(mSource == SOURCE_LIBRARY)
+ {
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ rez_attachment(item, 0);
+ }
+ }
+ return ACCEPT_YES_SINGLE;
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ if (mSource == SOURCE_WORLD)
+ {
+ return dad3dRezFromObjectOnLand(obj, face, mask, drop);
+ }
+
+ lldebugs << "LLToolDragAndDrop::dad3dRezObjectOnLand()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ EAcceptance accept;
+ BOOL remove_inventory;
+
+ // Get initial settings based on shift key
+ if (mask & MASK_SHIFT)
+ {
+ // For now, always make copy
+ //accept = ACCEPT_YES_SINGLE;
+ //remove_inventory = TRUE;
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+ else
+ {
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+
+ // check if the item can be copied. If not, send that to the sim
+ // which will remove the inventory item.
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ // Check if it's in the trash.
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ if(drop)
+ {
+ dropObject(obj, TRUE, FALSE, remove_inventory);
+ }
+
+ return accept;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ // handle objects coming from object inventory
+ if (mSource == SOURCE_WORLD)
+ {
+ return dad3dRezFromObjectOnObject(obj, face, mask, drop);
+ }
+
+ lldebugs << "LLToolDragAndDrop::dad3dRezObjectOnObject()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if((mask & MASK_CONTROL))
+ {
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if(mSource == SOURCE_NOTECARD)
+ {
+ return ACCEPT_NO;
+ }
+
+ EAcceptance rv = willObjectAcceptInventory(obj, item);
+ if(drop && (ACCEPT_YES_SINGLE <= rv))
+ {
+ dropInventory(obj, item, mSource, mSourceID);
+ }
+ return rv;
+ }
+
+ EAcceptance accept;
+ BOOL remove_inventory;
+
+ if (mask & MASK_SHIFT)
+ {
+ // For now, always make copy
+ //accept = ACCEPT_YES_SINGLE;
+ //remove_inventory = TRUE;
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+ else
+ {
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+
+ // check if the item can be copied. If not, send that to the sim
+ // which will remove the inventory item.
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ // Check if it's in the trash.
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ if(drop)
+ {
+ dropObject(obj, FALSE, FALSE, remove_inventory);
+ }
+
+ return accept;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezScript(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezScript()" << llendl;
+
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ EAcceptance rv = willObjectAcceptInventory(obj, item);
+ if(drop && (ACCEPT_YES_SINGLE <= rv))
+ {
+ // rez in the script active by default, rez in inactive if the
+ // control key is being held down.
+ BOOL active = ((mask & MASK_CONTROL) == 0);
+
+ LLViewerObject* root_object = obj;
+ if (obj && obj->getParent())
+ {
+ LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
+ if (!parent_obj->isAvatar())
+ {
+ root_object = parent_obj;
+ }
+ }
+
+ dropScript(root_object, item, active, mSource, mSourceID);
+ }
+ return rv;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dTextureObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dTextureObject()" << llendl;
+
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ EAcceptance rv = willObjectAcceptInventory(obj, item);
+ if((mask & MASK_CONTROL))
+ {
+ if((ACCEPT_YES_SINGLE <= rv) && drop)
+ {
+ dropInventory(obj, item, mSource, mSourceID);
+ }
+ return rv;
+ }
+ if(!obj->permModify())
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ if(drop && (ACCEPT_YES_SINGLE <= rv))
+ {
+ if((mask & MASK_SHIFT))
+ {
+ dropTextureAllFaces(obj, item, mSource, mSourceID);
+ }
+ else
+ {
+ dropTextureOneFace(obj, face, item, mSource, mSourceID);
+ }
+
+ // VEFFECT: SetTexture
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(obj);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+
+ // enable multi-drop, although last texture will win
+ return ACCEPT_YES_MULTI;
+}
+/*
+EAcceptance LLToolDragAndDrop::dad3dTextureSelf(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dTextureAvatar()" << llendl;
+ if(drop)
+ {
+ if( !(mask & MASK_SHIFT) )
+ {
+ dropTextureOneFaceAvatar( (LLVOAvatar*)obj, face, (LLInventoryItem*)mCargoData);
+ }
+ }
+ return (mask & MASK_SHIFT) ? ACCEPT_NO : ACCEPT_YES_SINGLE;
+}
+*/
+
+EAcceptance LLToolDragAndDrop::dad3dWearItem(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dWearItem()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
+ {
+ // it's in the agent inventory
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if( drop )
+ {
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgent.areWearablesLoaded())
+ {
+ gViewerWindow->alertXml("CanNotChangeAppearanceUntilLoaded");
+ return ACCEPT_NO;
+ }
+
+ if(mSource == SOURCE_LIBRARY)
+ {
+ // create item based on that one, and put it on if that
+ // was a success.
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar( item );
+ }
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ // TODO: copy/move item to avatar's inventory and then wear it.
+ return ACCEPT_NO;
+ }
+}
+
+EAcceptance LLToolDragAndDrop::dad3dActivateGesture(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dActivateGesture()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
+ {
+ // it's in the agent inventory
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if( drop )
+ {
+ LLUUID item_id;
+ if(mSource == SOURCE_LIBRARY)
+ {
+ // create item based on that one, and put it on if that
+ // was a success.
+ LLPointer<LLInventoryCallback> cb = new ActivateGestureCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ gGestureManager.activateGesture(item->getUUID());
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ return ACCEPT_NO;
+ }
+}
+
+EAcceptance LLToolDragAndDrop::dad3dWearCategory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dWearCategory()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* category;
+ locateInventory(item, category);
+ if(!category) return ACCEPT_NO;
+
+ if (drop)
+ {
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgent.areWearablesLoaded())
+ {
+ gViewerWindow->alertXml("CanNotChangeAppearanceUntilLoaded");
+ return ACCEPT_NO;
+ }
+ }
+
+ if(mSource == SOURCE_AGENT)
+ {
+ LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH));
+ if( gInventory.isObjectDescendentOf( category->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if(drop)
+ {
+ BOOL append = ( (mask & MASK_SHIFT) ? TRUE : FALSE );
+ wear_inventory_category(category, false, append);
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else if(mSource == SOURCE_LIBRARY)
+ {
+ if(drop)
+ {
+ wear_inventory_category(category, true, false);
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ // TODO: copy/move category to avatar's inventory and then wear it.
+ return ACCEPT_NO;
+ }
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dUpdateInventory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dadUpdateInventory()" << llendl;
+
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ LLViewerObject* root_object = obj;
+ if (obj && obj->getParent())
+ {
+ LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
+ if (!parent_obj->isAvatar())
+ {
+ root_object = parent_obj;
+ }
+ }
+
+ EAcceptance rv = willObjectAcceptInventory(root_object, item);
+ if(root_object && drop && (ACCEPT_YES_COPY_SINGLE <= rv))
+ {
+ dropInventory(root_object, item, mSource, mSourceID);
+ }
+ return rv;
+}
+
+BOOL LLToolDragAndDrop::dadUpdateInventory(LLViewerObject* obj, BOOL drop)
+{
+ EAcceptance rv = dad3dUpdateInventory(obj, -1, MASK_NONE, drop);
+ return (rv >= ACCEPT_YES_COPY_SINGLE);
+}
+
+EAcceptance LLToolDragAndDrop::dad3dUpdateInventoryCategory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dUpdateInventoryCategory()" << llendl;
+ if(mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
+ {
+ return ACCEPT_NO;
+ }
+ if(obj->isAttachment()) return ACCEPT_NO_LOCKED;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!cat) return ACCEPT_NO;
+ EAcceptance rv = ACCEPT_NO;
+
+ // find all the items in the category
+ LLDroppableItem droppable(!obj->permYouOwner());
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ droppable);
+ cats.put(cat);
+ if(droppable.countNoCopy() > 0)
+ {
+ llwarns << "*** Need to confirm this step" << llendl;
+ }
+ LLViewerObject* root_object = obj;
+ if (obj && obj->getParent())
+ {
+ LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
+ if (!parent_obj->isAvatar())
+ {
+ root_object = parent_obj;
+ }
+ }
+
+ // Check for accept
+ S32 i;
+ S32 count = cats.count();
+ for(i = 0; i < count; ++i)
+ {
+ rv = gInventory.isCategoryComplete(cats.get(i)->getUUID()) ? ACCEPT_YES_MULTI : ACCEPT_NO;
+ if(rv < ACCEPT_YES_SINGLE)
+ {
+ lldebugs << "Category " << cats.get(i)->getUUID()
+ << "is not complete." << llendl;
+ break;
+ }
+ }
+ if(ACCEPT_YES_COPY_SINGLE <= rv)
+ {
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ rv = willObjectAcceptInventory(root_object, items.get(i));
+ if(rv < ACCEPT_YES_COPY_SINGLE)
+ {
+ lldebugs << "Object will not accept "
+ << items.get(i)->getUUID() << llendl;
+ break;
+ }
+ }
+ }
+
+ // if every item is accepted, go ahead and send it on.
+ if(drop && (ACCEPT_YES_COPY_SINGLE <= rv))
+ {
+ S32 count = items.count();
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(i = 0; i < count; ++i)
+ {
+ //dropInventory(root_object, items.get(i), mSource, mSourceID);
+ ids.push_back(items.get(i)->getUUID());
+ }
+ LLCategoryDropObserver* dropper;
+ dropper = new LLCategoryDropObserver(obj->getID(), mSource);
+ dropper->fetchItems(ids);
+ if(dropper->isEverythingComplete())
+ {
+ dropper->done();
+ }
+ else
+ {
+ gInventory.addObserver(dropper);
+ }
+ }
+ return rv;
+}
+
+BOOL LLToolDragAndDrop::dadUpdateInventoryCategory(LLViewerObject* obj,
+ BOOL drop)
+{
+ EAcceptance rv = dad3dUpdateInventoryCategory(obj, -1, MASK_NONE, drop);
+ return (rv >= ACCEPT_YES_COPY_SINGLE);
+}
+
+EAcceptance LLToolDragAndDrop::dad3dGiveInventoryObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dGiveInventoryObject()" << llendl;
+
+ // item has to be in agent inventory.
+ if(mSource != SOURCE_AGENT) return ACCEPT_NO;
+
+ // find the item now.
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ // cannot give away no-transfer objects
+ return ACCEPT_NO;
+ }
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(avatar && avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ // You can't give objects that are attached to you
+ return ACCEPT_NO;
+ }
+ if( obj && avatar )
+ {
+ if(drop)
+ {
+ giveInventory(obj->getID(), item );
+ }
+ //FIXME: deal with all the issues surrounding multi-object inventory transfers
+ return ACCEPT_YES_SINGLE;
+ }
+ return ACCEPT_NO;
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dGiveInventory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dGiveInventory()" << llendl;
+ // item has to be in agent inventory.
+ if(mSource != SOURCE_AGENT) return ACCEPT_NO;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if(!isInventoryGiveAcceptable(item))
+ {
+ return ACCEPT_NO;
+ }
+ if(drop && obj)
+ {
+ giveInventory(obj->getID(), item);
+ }
+ //FIXME: deal with all the issues surrounding multi-object inventory transfers
+ return ACCEPT_YES_SINGLE;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dGiveInventoryCategory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dGiveInventoryCategory()" << llendl;
+ if(drop && obj)
+ {
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!cat) return ACCEPT_NO;
+ giveInventoryCategory(obj->getID(), cat);
+ }
+ //FIXME: deal with all the issues surrounding multi-object inventory transfers
+ return ACCEPT_YES_SINGLE;
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnLand(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezFromObjectOnLand()" << llendl;
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if(!item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID())
+ || !item->getPermissions().allowTransferTo(LLUUID::null))
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ if(drop)
+ {
+ dropObject(obj, TRUE, TRUE, FALSE);
+ }
+ return ACCEPT_YES_SINGLE;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezFromObjectOnObject()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if((mask & MASK_CONTROL))
+ {
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ return ACCEPT_NO;
+
+ // *HACK: uncomment this when appropriate
+ //EAcceptance rv = willObjectAcceptInventory(obj, item);
+ //if(drop && (ACCEPT_YES_SINGLE <= rv))
+ //{
+ // dropInventory(obj, item, mSource, mSourceID);
+ //}
+ //return rv;
+ }
+ if(!item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID())
+ || !item->getPermissions().allowTransferTo(LLUUID::null))
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ if(drop)
+ {
+ dropObject(obj, FALSE, TRUE, FALSE);
+ }
+ return ACCEPT_YES_SINGLE;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dCategoryOnLand(
+ LLViewerObject *obj, S32 face, MASK mask, BOOL drop)
+{
+ return ACCEPT_NO;
+ /*
+ lldebugs << "LLToolDragAndDrop::dad3dCategoryOnLand()" << llendl;
+ LLInventoryItem* item;
+ LLInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!cat) return ACCEPT_NO;
+ EAcceptance rv = ACCEPT_NO;
+
+ // find all the items in the category
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLDropCopyableItems droppable;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ droppable);
+ if(items.count() > 0)
+ {
+ rv = ACCEPT_YES_SINGLE;
+ }
+ if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
+ {
+ createContainer(items, cat->getName());
+ return ACCEPT_NO;
+ }
+ return rv;
+ */
+}
+
+
+// This is based on ALOT of copied, special-cased code
+// This shortcuts alot of steps to make a basic object
+// w/ an inventory and a special permissions set
+EAcceptance LLToolDragAndDrop::dad3dAssetOnLand(
+ LLViewerObject *obj, S32 face, MASK mask, BOOL drop)
+{
+ return ACCEPT_NO;
+ /*
+ lldebugs << "LLToolDragAndDrop::dad3dAssetOnLand()" << llendl;
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLViewerInventoryItem::item_array_t copyable_items;
+ locateMultipleInventory(items, cats);
+ if(!items.count()) return ACCEPT_NO;
+ EAcceptance rv = ACCEPT_NO;
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ LLInventoryItem* item = items[i];
+ if(item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ copyable_items.put(item);
+ rv = ACCEPT_YES_SINGLE;
+ }
+ }
+
+ if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
+ {
+ createContainer(copyable_items, NULL);
+ }
+
+ return rv;
+ */
+}
+
+LLInventoryObject* LLToolDragAndDrop::locateInventory(
+ LLViewerInventoryItem*& item,
+ LLViewerInventoryCategory*& cat)
+{
+ item = NULL;
+ cat = NULL;
+ if(mCargoIDs.empty()) return NULL;
+ if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+ {
+ // The object should be in user inventory.
+ item = (LLViewerInventoryItem*)gInventory.getItem(mCargoIDs[mCurItemIndex]);
+ cat = (LLViewerInventoryCategory*)gInventory.getCategory(mCargoIDs[mCurItemIndex]);
+ }
+ else if(mSource == SOURCE_WORLD)
+ {
+ // This object is in some task inventory somewhere.
+ LLViewerObject* obj = gObjectList.findObject(mSourceID);
+ if(obj)
+ {
+ if((mCargoTypes[mCurItemIndex] == DAD_CATEGORY)
+ || (mCargoTypes[mCurItemIndex] == DAD_ROOT_CATEGORY))
+ {
+ cat = (LLViewerInventoryCategory*)obj->getInventoryObject(mCargoIDs[mCurItemIndex]);
+ }
+ else
+ {
+ item = (LLViewerInventoryItem*)obj->getInventoryObject(mCargoIDs[mCurItemIndex]);
+ }
+ }
+ }
+ else if(mSource == SOURCE_NOTECARD)
+ {
+ LLPreviewNotecard* card;
+ card = (LLPreviewNotecard*)LLPreview::find(mSourceID);
+ if(card)
+ {
+ item = (LLViewerInventoryItem*)card->getDragItem();
+ }
+ }
+ if(item) return item;
+ if(cat) return cat;
+ return NULL;
+}
+
+/*
+LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryCategory::cat_array_t& cats,
+ LLViewerInventoryItem::item_array_t& items)
+{
+ if(mCargoIDs.count() == 0) return NULL;
+ if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+ {
+ // The object should be in user inventory.
+ for (S32 i = 0; i < mCargoIDs.count(); i++)
+ {
+ LLInventoryItem* item = gInventory.getItem(mCargoIDs[i]);
+ if (item)
+ {
+ items.put(item);
+ }
+ LLInventoryCategory* category = gInventory.getCategory(mCargoIDs[i]);
+ if (category)
+ {
+ cats.put(category);
+ }
+ }
+ }
+ else if(mSource == SOURCE_WORLD)
+ {
+ // This object is in some task inventory somewhere.
+ LLViewerObject* obj = gObjectList.findObject(mSourceID);
+ if(obj)
+ {
+ if((mCargoType == DAD_CATEGORY)
+ || (mCargoType == DAD_ROOT_CATEGORY))
+ {
+ // The object should be in user inventory.
+ for (S32 i = 0; i < mCargoIDs.count(); i++)
+ {
+ LLInventoryCategory* category = (LLInventoryCategory*)obj->getInventoryObject(mCargoIDs[i]);
+ if (category)
+ {
+ cats.put(category);
+ }
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < mCargoIDs.count(); i++)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)obj->getInventoryObject(mCargoIDs[i]);
+ if (item)
+ {
+ items.put(item);
+ }
+ }
+ }
+ }
+ }
+ else if(mSource == SOURCE_NOTECARD)
+ {
+ LLPreviewNotecard* card;
+ card = (LLPreviewNotecard*)LLPreview::find(mSourceID);
+ if(card)
+ {
+ items.put((LLInventoryItem*)card->getDragItem());
+ }
+ }
+ if(items.count()) return items[0];
+ if(cats.count()) return cats[0];
+ return NULL;
+}
+*/
+
+void LLToolDragAndDrop::createContainer(LLViewerInventoryItem::item_array_t &items, const char* preferred_name )
+{
+ llwarns << "LLToolDragAndDrop::createContainer()" << llendl;
+ return;
+}
+
+
+// utility functions
+
+void pack_permissions_slam(LLMessageSystem* msg, U32 flags, const LLPermissions& perms)
+{
+ U32 group_mask = perms.getMaskGroup();
+ U32 everyone_mask = perms.getMaskEveryone();
+ U32 next_owner_mask = perms.getMaskNextOwner();
+
+ msg->addU32Fast(_PREHASH_ItemFlags, flags);
+ msg->addU32Fast(_PREHASH_GroupMask, group_mask);
+ msg->addU32Fast(_PREHASH_EveryoneMask, everyone_mask);
+ msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_mask);
+}
diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h
new file mode 100644
index 0000000000..8f1c7c8ddb
--- /dev/null
+++ b/indra/newview/lltooldraganddrop.h
@@ -0,0 +1,239 @@
+/**
+ * @file lltooldraganddrop.h
+ * @brief LLToolDragAndDrop class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLDRAGANDDROP_H
+#define LL_TOOLDRAGANDDROP_H
+
+#include "lltool.h"
+#include "llview.h"
+#include "lluuid.h"
+#include "stdenums.h"
+#include "llassetstorage.h"
+#include "lldarray.h"
+#include "llpermissions.h"
+#include "llwindow.h"
+#include "llviewerinventory.h"
+
+class LLToolDragAndDrop;
+class LLViewerRegion;
+class LLVOAvatar;
+
+class LLToolDragAndDrop : public LLTool
+{
+public:
+ LLToolDragAndDrop();
+
+ // overridden from LLTool
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleKey(KEY key, MASK mask);
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen);
+ virtual void onMouseCaptureLost();
+ virtual void handleDeselect();
+
+ void setDragStart( S32 x, S32 y ); // In screen space
+ BOOL isOverThreshold( S32 x, S32 y ); // In screen space
+
+ enum ESource
+ {
+ SOURCE_AGENT,
+ SOURCE_WORLD,
+ SOURCE_NOTECARD,
+ SOURCE_LIBRARY
+ };
+
+ void beginDrag(EDragAndDropType type,
+ const LLUUID& cargo_id,
+ ESource source,
+ const LLUUID& source_id = LLUUID::null,
+ const LLUUID& object_id = LLUUID::null);
+ void beginMultiDrag(const std::vector<EDragAndDropType> types,
+ const std::vector<LLUUID>& cargo_ids,
+ ESource source,
+ const LLUUID& source_id = LLUUID::null);
+ void endDrag();
+ ESource getSource() const { return mSource; }
+ const LLUUID& getSourceID() const { return mSourceID; }
+ const LLUUID& getObjectID() const { return mObjectID; }
+ EAcceptance getLastAccept() { return mLastAccept; }
+
+protected:
+ enum EDropTarget
+ {
+ DT_NONE = 0,
+ DT_SELF = 1,
+ DT_AVATAR = 2,
+ DT_OBJECT = 3,
+ DT_LAND = 4,
+ DT_COUNT = 5
+ };
+
+ // dragOrDrop3dImpl points to a member of LLToolDragAndDrop that
+ // takes parameters (LLViewerObject* obj, S32 face, MASK, BOOL
+ // drop) and returns a BOOL if drop is ok
+ typedef EAcceptance (LLToolDragAndDrop::*dragOrDrop3dImpl)
+ (LLViewerObject*, S32, MASK, BOOL);
+
+ void dragOrDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EAcceptance* acceptance);
+ void dragOrDrop3D(S32 x, S32 y, MASK mask, BOOL drop,
+ EAcceptance* acceptance);
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+
+ S32 mDragStartX;
+ S32 mDragStartY;
+
+ std::vector<EDragAndDropType> mCargoTypes;
+ //void* mCargoData;
+ std::vector<LLUUID> mCargoIDs;
+ ESource mSource;
+ LLUUID mSourceID;
+ LLUUID mObjectID;
+
+ LLVector3d mLastCameraPos;
+ LLVector3d mLastHitPos;
+
+ ECursorType mCursor;
+ EAcceptance mLastAccept;
+ BOOL mDrop;
+ S32 mCurItemIndex;
+ LLString mToolTipMsg;
+
+ // array of pointers to functions that implement the logic to
+ // dragging and dropping into the simulator.
+ static dragOrDrop3dImpl sDragAndDrop3d[DAD_COUNT][DT_COUNT];
+
+protected:
+ // 3d drop functions. these call down into the static functions
+ // named drop<ThingToDrop> if drop is TRUE and permissions allow
+ // that behavior.
+ EAcceptance dad3dNULL(LLViewerObject*, S32, MASK, BOOL);
+ EAcceptance dad3dRezObjectOnLand(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezObjectOnObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezScript(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dTextureObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+// EAcceptance dad3dTextureSelf(LLViewerObject* obj, S32 face,
+// MASK mask, BOOL drop);
+ EAcceptance dad3dWearItem(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dWearCategory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dUpdateInventory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dUpdateInventoryCategory(LLViewerObject* obj,
+ S32 face,
+ MASK mask,
+ BOOL drop);
+ EAcceptance dad3dGiveInventoryObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dGiveInventory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dGiveInventoryCategory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezFromObjectOnLand(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezFromObjectOnObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezAttachmentFromInv(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dCategoryOnLand(LLViewerObject *obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dAssetOnLand(LLViewerObject *obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dActivateGesture(LLViewerObject *obj, S32 face,
+ MASK mask, BOOL drop);
+
+ // This method converts mCargoID to an inventory item or
+ // folder. If no item or category is found, both pointers will be
+ // returned NULL.
+ LLInventoryObject* locateInventory(LLViewerInventoryItem*& item,
+ LLViewerInventoryCategory*& cat);
+
+ //LLInventoryObject* locateMultipleInventory(
+ // LLViewerInventoryCategory::cat_array_t& cats,
+ // LLViewerInventoryItem::item_array_t& items);
+
+ void createContainer(LLViewerInventoryItem::item_array_t &items, const char* preferred_name);
+ void dropObject(LLViewerObject* raycast_target,
+ BOOL bypass_sim_raycast,
+ BOOL from_task_inventory,
+ BOOL remove_from_inventory);
+
+ // accessor that looks at permissions, copyability, and names of
+ // inventory items to determine if a drop would be ok.
+ static EAcceptance willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item);
+
+ // deal with permissions of object, etc. returns TRUE if drop can
+ // proceed, otherwise FALSE.
+ static BOOL handleDropTextureProtections(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id);
+
+
+ // give inventory item functionality
+ static void handleCopyProtectedItem(S32 option, void* data);
+ static void commitGiveInventoryItem(const LLUUID& to_agent,
+ LLInventoryItem* item);
+
+ // give inventory category functionality
+ static void handleCopyProtectedCategory(S32 option, void* data);
+ static void commitGiveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* cat);
+public:
+ // helper functions
+ static BOOL isInventoryDropAcceptable(LLViewerObject* obj, LLInventoryItem* item) { return (ACCEPT_YES_COPY_SINGLE <= willObjectAcceptInventory(obj, item)); }
+
+ // This simple helper function assumes you are attempting to
+ // transfer item. returns true if you can give, otherwise false.
+ static BOOL isInventoryGiveAcceptable(LLInventoryItem* item);
+ static BOOL isInventoryGroupGiveAcceptable(LLInventoryItem* item);
+
+ BOOL dadUpdateInventory(LLViewerObject* obj, BOOL drop);
+ BOOL dadUpdateInventoryCategory(LLViewerObject* obj, BOOL drop);
+
+ // methods that act on the simulator state.
+ static void dropScript(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ BOOL active,
+ ESource source,
+ const LLUUID& src_id);
+ static void dropTextureOneFace(LLViewerObject* hit_obj, S32 hit_face,
+ LLInventoryItem* item,
+ ESource source,
+ const LLUUID& src_id);
+ static void dropTextureAllFaces(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ ESource source,
+ const LLUUID& src_id);
+ //static void dropTextureOneFaceAvatar(LLVOAvatar* avatar,S32 hit_face,
+ // LLInventoryItem* item)
+
+ static void dropInventory(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ ESource source,
+ const LLUUID& src_id);
+
+ static void giveInventory(const LLUUID& to_agent, LLInventoryItem* item);
+ static void giveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* item);
+};
+
+// Singleton
+extern LLToolDragAndDrop *gToolDragAndDrop;
+
+// utility functions
+void pack_permissions_slam(LLMessageSystem* msg, U32 flags, const LLPermissions& perms);
+
+#endif // LL_TOOLDRAGANDDROP_H
diff --git a/indra/newview/lltoolface.cpp b/indra/newview/lltoolface.cpp
new file mode 100644
index 0000000000..c35fcb434c
--- /dev/null
+++ b/indra/newview/lltoolface.cpp
@@ -0,0 +1,141 @@
+/**
+ * @file lltoolface.cpp
+ * @brief A tool to manipulate faces
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolface.h"
+
+// Library includes
+#include "v3math.h"
+
+// Viewer includes
+#include "llagent.h"
+//#include "llbuildview.h"
+#include "llviewercontrol.h"
+#include "llselectmgr.h"
+#include "lltoolview.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llfloatertools.h"
+
+// Globals
+LLToolFace *gToolFace = NULL;
+
+//
+// Member functions
+//
+
+LLToolFace::LLToolFace()
+: LLTool("Texture")
+{ }
+
+
+LLToolFace::~LLToolFace()
+{ }
+
+
+BOOL LLToolFace::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty())
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ //gBuildView->showFacePanel();
+ gFloaterTools->showPanel( LLFloaterTools::PANEL_FACE );
+ //gBuildView->showMore(LLBuildView::PANEL_FACE);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return FALSE;
+ }
+}
+
+
+BOOL LLToolFace::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ gPickFaces = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolFace::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if (hit_obj)
+ {
+ S32 hit_face = gLastHitObjectFace;
+
+ if (hit_obj->isAvatar())
+ {
+ // ...clicked on an avatar, so don't do anything
+ return;
+ }
+
+ // ...clicked on a world object, try to pick the appropriate face
+
+ if (mask & MASK_SHIFT)
+ {
+ // If object not selected, need to inform sim
+ if ( !hit_obj->isSelected() )
+ {
+ // object wasn't selected so add the object and face
+ gSelectMgr->selectObjectOnly(hit_obj, hit_face);
+ }
+ else if (!gSelectMgr->contains(hit_obj, hit_face) )
+ {
+ // object is selected, but not this face, so add it.
+ gSelectMgr->addAsIndividual(hit_obj, hit_face);
+ }
+ else
+ {
+ // object is selected, as is this face, so remove the face.
+ gSelectMgr->remove(hit_obj, hit_face);
+
+ // BUG: If you remove the last face, the simulator won't know about it.
+ }
+ }
+ else
+ {
+ // clicked without modifiers, select only
+ // this face
+ gSelectMgr->deselectAll();
+ gSelectMgr->selectObjectOnly(hit_obj, hit_face);
+ }
+ }
+ else
+ {
+ if (!(mask == MASK_SHIFT))
+ {
+ gSelectMgr->deselectAll();
+ }
+ }
+}
+
+
+void LLToolFace::handleSelect()
+{
+ // From now on, draw faces
+ gSelectMgr->setTEMode(TRUE);
+}
+
+
+void LLToolFace::handleDeselect()
+{
+ // Stop drawing faces
+ gSelectMgr->setTEMode(FALSE);
+}
+
+
+void LLToolFace::render()
+{
+ // for now, do nothing
+}
diff --git a/indra/newview/lltoolface.h b/indra/newview/lltoolface.h
new file mode 100644
index 0000000000..d2b4be5deb
--- /dev/null
+++ b/indra/newview/lltoolface.h
@@ -0,0 +1,34 @@
+/**
+ * @file lltoolface.h
+ * @brief A tool to select object faces
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLFACE_H
+#define LL_LLTOOLFACE_H
+
+#include "lltool.h"
+
+class LLViewerObject;
+
+class LLToolFace
+: public LLTool
+{
+public:
+ LLToolFace();
+ virtual ~LLToolFace();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual void handleSelect();
+ virtual void handleDeselect();
+ virtual void render(); // draw face highlights
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+};
+
+extern LLToolFace *gToolFace;
+
+#endif
diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp
new file mode 100644
index 0000000000..627a49a6cc
--- /dev/null
+++ b/indra/newview/lltoolfocus.cpp
@@ -0,0 +1,442 @@
+/**
+ * @file lltoolfocus.cpp
+ * @brief A tool to set the build focus point.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolfocus.h"
+
+// Library includes
+#include "v3math.h"
+#include "llfontgl.h"
+#include "llui.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llhoverview.h"
+#include "llhudmanager.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolmgr.h"
+#include "lltoolselect.h"
+#include "llviewborder.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llvoavatar.h"
+#include "llmorphview.h"
+
+// Globals
+LLToolCamera *gToolCamera = NULL;
+BOOL gCameraBtnOrbit = FALSE;
+BOOL gCameraBtnPan = FALSE;
+const S32 SLOP_RANGE = 4;
+const F32 FOCUS_OFFSET_FACTOR = 1.f;
+
+extern void handle_first_tool(void*);
+
+
+//
+// Camera - shared functionality
+//
+
+LLToolCamera::LLToolCamera()
+: LLTool("Camera"),
+ mAccumX(0),
+ mAccumY(0),
+ mMouseDownX(0),
+ mMouseDownY(0),
+ mOutsideSlopX(FALSE),
+ mOutsideSlopY(FALSE),
+ mValidClickPoint(FALSE),
+ mMouseSteering(FALSE),
+ mMouseUpX(0),
+ mMouseUpY(0),
+ mMouseUpMask(MASK_NONE)
+{ }
+
+
+LLToolCamera::~LLToolCamera()
+{ }
+
+// virtual
+void LLToolCamera::handleSelect()
+{
+ gFloaterTools->setStatusText("Click and drag to change view");
+}
+
+// virtual
+void LLToolCamera::handleDeselect()
+{
+ gFloaterTools->setStatusText("");
+// gAgent.setLookingAtAvatar(FALSE);
+}
+
+BOOL LLToolCamera::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Ensure a mouseup
+ setMouseCapture(TRUE);
+
+ // call the base class to propogate info to sim
+ LLTool::handleMouseDown(x, y, mask);
+
+ mAccumX = 0;
+ mAccumY = 0;
+
+ mOutsideSlopX = FALSE;
+ mOutsideSlopY = FALSE;
+
+ mValidClickPoint = FALSE;
+
+ // If mouse capture gets ripped away, claim we moused up
+ // at the point we moused down. JC
+ mMouseUpX = x;
+ mMouseUpY = y;
+ mMouseUpMask = mask;
+
+ gViewerWindow->hideCursor();
+
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ // don't steal focus from UI
+ return FALSE;
+}
+
+void LLToolCamera::pickCallback(S32 x, S32 y, MASK mask)
+{
+ if (!gToolCamera->hasMouseCapture())
+ {
+ return;
+ }
+
+ gToolCamera->mMouseDownX = x;
+ gToolCamera->mMouseDownY = y;
+
+ gViewerWindow->moveCursorToCenter();
+
+ // Potentially recenter if click outside rectangle
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ // Check for hit the sky, or some other invalid point
+ if (!hit_obj && gLastHitPosGlobal.isExactlyZero())
+ {
+ gToolCamera->mValidClickPoint = FALSE;
+ return;
+ }
+
+ // check for hud attachments
+ if (hit_obj && hit_obj->isHUDAttachment())
+ {
+ if (!gSelectMgr->getObjectCount() || gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ gToolCamera->mValidClickPoint = FALSE;
+ return;
+ }
+ }
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() )
+ {
+ BOOL good_customize_avatar_hit = FALSE;
+ if( hit_obj )
+ {
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( hit_obj == avatar)
+ {
+ // It's you
+ good_customize_avatar_hit = TRUE;
+ }
+ else
+ if( hit_obj->isAttachment() && hit_obj->permYouOwner() )
+ {
+ // It's an attachment that you're wearing
+ good_customize_avatar_hit = TRUE;
+ }
+ }
+
+ if( !good_customize_avatar_hit )
+ {
+ gToolCamera->mValidClickPoint = FALSE;
+ return;
+ }
+
+ if( gMorphView )
+ {
+ gMorphView->setCameraDrivenByKeys( FALSE );
+ }
+ }
+ //RN: check to see if this is mouse-driving as opposed to ALT-zoom or Focus tool
+ else if (mask & MASK_ALT ||
+ (gToolMgr->getCurrentTool(mask)->getName() == "Camera"))
+ {
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if (hit_obj)
+ {
+ // ...clicked on a world object, so focus at its position
+ // Use "gLastHitPosGlobal" because it's correct for avatar heads,
+ // not pelvis.
+ if (!hit_obj->isHUDAttachment())
+ {
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal( gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID);
+ }
+ }
+ else if (!gLastHitPosGlobal.isExactlyZero())
+ {
+ // Hit the ground
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal( gLastHitPosGlobal, gLastHitObjectID);
+ }
+
+ if (!(mask & MASK_ALT) &&
+ gAgent.cameraThirdPerson() &&
+ gViewerWindow->getLeftMouseDown() &&
+ !gSavedSettings.getBOOL("FreezeTime") &&
+ (hit_obj == gAgent.getAvatarObject() ||
+ (hit_obj && hit_obj->isAttachment() && LLVOAvatar::findAvatarFromAttachment(hit_obj)->mIsSelf)))
+ {
+ gToolCamera->mMouseSteering = TRUE;
+ }
+
+ }
+
+ gToolCamera->mValidClickPoint = TRUE;
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() )
+ {
+ gAgent.setFocusOnAvatar(FALSE, FALSE);
+
+ LLVector3d cam_pos = gAgent.getCameraPositionGlobal();
+ cam_pos -= LLVector3d(gCamera->getLeftAxis() * gAgent.calcCustomizeAvatarUIOffset( cam_pos ));
+
+ gAgent.setCameraPosAndFocusGlobal( cam_pos, gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID);
+ }
+}
+
+
+// "Let go" of the mouse, for example on mouse up or when
+// we lose mouse capture. This ensures that cursor becomes visible
+// if a modal dialog pops up during Alt-Zoom. JC
+void LLToolCamera::releaseMouse()
+{
+ // Need to tell the sim that the mouse button is up, since this
+ // tool is no longer working and cursor is visible (despite actual
+ // mouse button status).
+ LLTool::handleMouseUp(mMouseUpX, mMouseUpY, mMouseUpMask);
+
+ gViewerWindow->showCursor();
+
+ gToolMgr->clearTransientTool();
+
+ mMouseSteering = FALSE;
+ mValidClickPoint = FALSE;
+ mOutsideSlopX = FALSE;
+ mOutsideSlopY = FALSE;
+}
+
+
+BOOL LLToolCamera::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // Claim that we're mousing up somewhere
+ mMouseUpX = x;
+ mMouseUpY = y;
+ mMouseUpMask = mask;
+
+ if (hasMouseCapture())
+ {
+ if (mValidClickPoint)
+ {
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() )
+ {
+ LLCoordGL mouse_pos;
+ LLVector3 focus_pos = gAgent.getPosAgentFromGlobal(gAgent.getFocusGlobal());
+ gCamera->projectPosAgentToScreen(focus_pos, mouse_pos);
+
+ LLUI::setCursorPositionScreen(mouse_pos.mX, mouse_pos.mY);
+ }
+ else if (mMouseSteering)
+ {
+ LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY);
+ }
+ else
+ {
+ gViewerWindow->moveCursorToCenter();
+ }
+ }
+ else
+ {
+ // not a valid zoomable object
+ LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY);
+ }
+
+ // calls releaseMouse() internally
+ setMouseCapture(FALSE);
+ }
+ else
+ {
+ releaseMouse();
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLToolCamera::handleHover(S32 x, S32 y, MASK mask)
+{
+ S32 dx = gViewerWindow->getCurrentMouseDX();
+ S32 dy = gViewerWindow->getCurrentMouseDY();
+
+ BOOL moved_outside_slop = FALSE;
+
+ if (hasMouseCapture() && mValidClickPoint)
+ {
+ mAccumX += llabs(dx);
+ mAccumY += llabs(dy);
+
+ if (mAccumX >= SLOP_RANGE)
+ {
+ if (!mOutsideSlopX)
+ {
+ moved_outside_slop = TRUE;
+ }
+ mOutsideSlopX = TRUE;
+ }
+
+ if (mAccumY >= SLOP_RANGE)
+ {
+ if (!mOutsideSlopY)
+ {
+ moved_outside_slop = TRUE;
+ }
+ mOutsideSlopY = TRUE;
+ }
+ }
+
+ if (mOutsideSlopX || mOutsideSlopY)
+ {
+ if (!mValidClickPoint)
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolFocus [invalid point]" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_NO);
+ gViewerWindow->showCursor();
+ return TRUE;
+ }
+
+ if (gCameraBtnOrbit ||
+ mask == MASK_ORBIT ||
+ mask == (MASK_ALT | MASK_ORBIT))
+ {
+ // Orbit tool
+ if (hasMouseCapture())
+ {
+ const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWindowWidth();
+
+ if (dx != 0)
+ {
+ gAgent.cameraOrbitAround( -dx * RADIANS_PER_PIXEL );
+ }
+
+ if (dy != 0)
+ {
+ gAgent.cameraOrbitOver( -dy * RADIANS_PER_PIXEL );
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolFocus [active]" << llendl;
+ }
+ else if ( gCameraBtnPan ||
+ mask == MASK_PAN ||
+ mask == (MASK_PAN | MASK_ALT) )
+ {
+ // Pan tool
+ if (hasMouseCapture())
+ {
+ LLVector3d camera_to_focus = gAgent.getCameraPositionGlobal();
+ camera_to_focus -= gAgent.getFocusGlobal();
+ F32 dist = (F32) camera_to_focus.normVec();
+
+ // Fudge factor for pan
+ F32 meters_per_pixel = 3.f * dist / gViewerWindow->getWindowWidth();
+
+ if (dx != 0)
+ {
+ gAgent.cameraPanLeft( dx * meters_per_pixel );
+ }
+
+ if (dy != 0)
+ {
+ gAgent.cameraPanUp( -dy * meters_per_pixel );
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPan" << llendl;
+ }
+ else
+ {
+ // Zoom tool
+ if (hasMouseCapture())
+ {
+
+ const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWindowWidth();
+
+ if (dx != 0)
+ {
+ gAgent.cameraOrbitAround( -dx * RADIANS_PER_PIXEL );
+ }
+
+ const F32 IN_FACTOR = 0.99f;
+
+ if (dy != 0 && mOutsideSlopY )
+ {
+ if (mMouseSteering)
+ {
+ gAgent.cameraOrbitOver( -dy * RADIANS_PER_PIXEL );
+ }
+ else
+ {
+ gAgent.cameraZoomIn( pow( IN_FACTOR, dy ) );
+ }
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolZoom" << llendl;
+ }
+ }
+
+ if (gCameraBtnOrbit ||
+ mask == MASK_ORBIT ||
+ mask == (MASK_ALT | MASK_ORBIT))
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+ }
+ else if ( gCameraBtnPan ||
+ mask == MASK_PAN ||
+ mask == (MASK_PAN | MASK_ALT) )
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+ }
+
+ return TRUE;
+}
+
+
+void LLToolCamera::onMouseCaptureLost()
+{
+ releaseMouse();
+}
diff --git a/indra/newview/lltoolfocus.h b/indra/newview/lltoolfocus.h
new file mode 100644
index 0000000000..1d3d4a728b
--- /dev/null
+++ b/indra/newview/lltoolfocus.h
@@ -0,0 +1,58 @@
+/**
+ * @file lltoolfocus.h
+ * @brief A tool to set the build focus point.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLFOCUS_H
+#define LL_LLTOOLFOCUS_H
+
+#include "lltool.h"
+
+class LLToolCamera
+: public LLTool
+{
+public:
+ LLToolCamera();
+ virtual ~LLToolCamera();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual void onMouseCaptureLost();
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+ BOOL mouseSteerMode() { return mMouseSteering; }
+
+protected:
+ // called from handleMouseUp and onMouseCaptureLost to "let go"
+ // of the mouse and make it visible JC
+ void releaseMouse();
+
+protected:
+ S32 mAccumX;
+ S32 mAccumY;
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+ BOOL mOutsideSlopX;
+ BOOL mOutsideSlopY;
+ BOOL mValidClickPoint;
+ BOOL mMouseSteering;
+ S32 mMouseUpX; // needed for releaseMouse()
+ S32 mMouseUpY;
+ MASK mMouseUpMask;
+};
+
+
+extern LLToolCamera *gToolCamera;
+
+extern BOOL gCameraBtnOrbit;
+extern BOOL gCameraBtnPan;
+
+#endif
diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp
new file mode 100644
index 0000000000..898322d5ca
--- /dev/null
+++ b/indra/newview/lltoolgrab.cpp
@@ -0,0 +1,875 @@
+/**
+ * @file lltoolgrab.cpp
+ * @brief LLToolGrab class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolgrab.h"
+
+// library headers
+#include "indra_constants.h" // for agent control flags
+#include "llviewercontrol.h"
+#include "llquaternion.h"
+#include "llbox.h"
+#include "message.h"
+#include "llview.h"
+#include "llfontgl.h"
+#include "llui.h"
+
+// newview headers
+#include "llagent.h"
+//#include "llfloateravatarinfo.h"
+#include "lldrawable.h"
+#include "llfloatertools.h"
+#include "llhudeffect.h"
+#include "llhudmanager.h"
+#include "llregionposition.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "viewer.h"
+
+const S32 SLOP_DIST_SQ = 4;
+
+// Override modifier key behavior with these buttons
+BOOL gGrabBtnVertical = FALSE;
+BOOL gGrabBtnSpin = FALSE;
+LLTool* gGrabTransientTool = NULL;
+LLToolGrab *gToolGrab = NULL;
+extern BOOL gDebugClicks;
+
+//
+// Methods
+//
+LLToolGrab::LLToolGrab( LLToolComposite* composite )
+: LLTool( "Grab", composite ),
+ mMode( GRAB_INACTIVE ),
+ mVerticalDragging( FALSE ),
+ mHitLand(FALSE),
+ mHitObjectID(),
+ mGrabObject( NULL ),
+ mMouseDownX( -1 ),
+ mMouseDownY( -1 ),
+ mHasMoved( FALSE ),
+ mSpinGrabbing( FALSE ),
+ mSpinRotation(),
+ mHideBuildHighlight(FALSE)
+{ }
+
+LLToolGrab::~LLToolGrab()
+{ }
+
+
+// virtual
+void LLToolGrab::handleSelect()
+{
+ gFloaterTools->setStatusText("Drag to move objects, Ctrl to lift, Ctrl-Shift to spin");
+ gGrabBtnVertical = FALSE;
+ gGrabBtnSpin = FALSE;
+}
+
+void LLToolGrab::handleDeselect()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+ }
+
+ gFloaterTools->setStatusText("");
+}
+
+BOOL LLToolGrab::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolGrab handleDoubleClick (becoming mouseDown)" << llendl;
+ }
+
+ return FALSE;
+}
+
+BOOL LLToolGrab::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolGrab handleMouseDown" << llendl;
+ }
+
+ mHitLand = FALSE;
+
+ // call the base class to propogate info to sim
+ LLTool::handleMouseDown(x, y, mask);
+
+ if (!gAgent.leftButtonGrabbed())
+ {
+ // can grab transparent objects (how touch event propagates, scripters rely on this)
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, TRUE);
+ }
+ return TRUE;
+}
+
+void LLToolGrab::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject *objectp = gObjectList.findObject( gLastHitObjectID );
+
+ BOOL extend_select = (mask & MASK_SHIFT);
+
+ if (!extend_select && !gSelectMgr->isEmpty())
+ {
+ gSelectMgr->deselectAll();
+ gToolGrab->mDeselectedThisClick = TRUE;
+ }
+ else
+ {
+ gToolGrab->mDeselectedThisClick = FALSE;
+ }
+
+ // if not over object, do nothing
+ if (!objectp)
+ {
+ gToolGrab->setMouseCapture(TRUE);
+ gToolGrab->mMode = GRAB_NOOBJECT;
+ gToolGrab->mHitObjectID.setNull();
+ }
+ else
+ {
+ gToolGrab->handleObjectHit(objectp, x, y, mask);
+ }
+}
+
+BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mask)
+{
+ mMouseDownX = x;
+ mMouseDownY = y;
+ mMouseMask = mask;
+
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolGrab handleObjectHit " << mMouseDownX << "," << mMouseDownY << llendl;
+ }
+
+ if (objectp->isAvatar())
+ {
+ if (gGrabTransientTool)
+ {
+ gBasicToolset->selectTool( gGrabTransientTool );
+ gGrabTransientTool = NULL;
+ }
+ return TRUE;
+ }
+
+ setMouseCapture( TRUE );
+
+ mHitObjectID = objectp->getID();
+
+ // Grabs always start from the root
+ // objectp = (LLViewerObject *)objectp->getRoot();
+
+ LLViewerObject* parent = objectp->getRootEdit();
+ BOOL script_touch = (objectp && objectp->flagHandleTouch()) || (parent && parent->flagHandleTouch());
+
+ // Clicks on scripted or physical objects are temporary grabs, so
+ // not "Build mode"
+ mHideBuildHighlight = script_touch || objectp->usePhysics();
+
+ if (!objectp->usePhysics())
+ {
+ // In mouselook, we shouldn't be able to grab non-physical,
+ // non-touchable objects. If it has a touch handler, we
+ // do grab it (so llDetectedGrab works), but movement is
+ // blocked on the server side. JC
+ if (gAgent.cameraMouselook() && !script_touch)
+ {
+ mMode = GRAB_LOCKED;
+ }
+ else
+ {
+ mMode = GRAB_NONPHYSICAL;
+ }
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+ // Don't bail out here, go on and grab so buttons can get
+ // their "touched" event.
+ }
+ else if( !objectp->permMove() )
+ {
+ // if mouse is over a physical object without move permission, show feedback if user tries to move it.
+ mMode = GRAB_LOCKED;
+
+ // Don't bail out here, go on and grab so buttons can get
+ // their "touched" event.
+ }
+ else
+ {
+ // if mouse is over a physical object with move permission,
+ // select it and enter "grab" mode (hiding cursor, etc.)
+
+ mMode = GRAB_ACTIVE_CENTER;
+
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ // Always send "touched" message
+
+ mAccumDeltaX = 0;
+ mAccumDeltaY = 0;
+ mHasMoved = FALSE;
+ mOutsideSlop = FALSE;
+
+ mGrabObject = objectp;
+
+ mGrabOffset.clearVec();
+
+ mVerticalDragging = (mask == MASK_VERTICAL) || gGrabBtnVertical;
+
+ startGrab(x, y);
+
+ if ((mask == MASK_SPIN) || gGrabBtnSpin)
+ {
+ startSpin();
+ }
+
+ gSelectMgr->updateSelectionCenter(); // update selection beam
+
+ // update point at
+ LLViewerObject *edit_object = gObjectList.findObject(mHitObjectID);
+ if (edit_object)
+ {
+ LLVector3 local_edit_point = gAgent.getPosAgentFromGlobal(gLastHitNonFloraPosGlobal);
+ local_edit_point -= edit_object->getPositionAgent();
+ local_edit_point = local_edit_point * ~edit_object->getRenderRotation();
+ gAgent.setPointAt(POINTAT_TARGET_GRAB, edit_object, local_edit_point );
+ gAgent.setLookAt(LOOKAT_TARGET_SELECT, edit_object, local_edit_point );
+ }
+
+ // on transient grabs (clicks on world objects), kill the grab immediately
+ if (!gViewerWindow->getLeftMouseDown()
+ && gGrabTransientTool
+ && (mMode == GRAB_NONPHYSICAL || mMode == GRAB_LOCKED))
+ {
+ gBasicToolset->selectTool( gGrabTransientTool );
+ gGrabTransientTool = NULL;
+ }
+
+ return TRUE;
+}
+
+
+void LLToolGrab::startSpin()
+{
+ mSpinGrabbing = TRUE;
+
+ // Was saveSelectedObjectTransform()
+ LLViewerObject *root = (LLViewerObject *)mGrabObject->getRoot();
+ mSpinRotation = root->getRotation();
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectSpinStart);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+}
+
+
+void LLToolGrab::stopSpin()
+{
+ mSpinGrabbing = FALSE;
+
+ LLMessageSystem *msg = gMessageSystem;
+ switch(mMode)
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ case GRAB_LOCKED:
+ msg->newMessageFast(_PREHASH_ObjectSpinStop);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+ break;
+
+ case GRAB_NOOBJECT:
+ case GRAB_INACTIVE:
+ default:
+ // do nothing
+ break;
+ }
+}
+
+
+void LLToolGrab::startGrab(S32 x, S32 y)
+{
+ // Compute grab_offset in the OBJECT's root's coordinate frame
+ // (sometimes root == object)
+ LLViewerObject *root = (LLViewerObject *)mGrabObject->getRoot();
+
+ // drag from center
+ LLVector3d grab_start_global = root->getPositionGlobal();
+
+ // Where the grab starts, relative to the center of the root object of the set.
+ // JC - This code looks wonky, but I believe it does the right thing.
+ // Otherwise, when you grab a linked object set, it "pops" on the start
+ // of the drag.
+ LLVector3d grab_offsetd = root->getPositionGlobal() - mGrabObject->getPositionGlobal();
+
+ LLVector3 grab_offset;
+ grab_offset.setVec(grab_offsetd);
+
+ LLQuaternion rotation = root->getRotation();
+ rotation.conjQuat();
+ grab_offset = grab_offset * rotation;
+
+ // This planar drag starts at the grab point
+ mDragStartPointGlobal = grab_start_global;
+ mDragStartFromCamera = grab_start_global - gAgent.getCameraPositionGlobal();
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectGrab);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_LocalID, mGrabObject->mLocalID);
+ msg->addVector3Fast(_PREHASH_GrabOffset, grab_offset );
+ msg->sendMessage( mGrabObject->getRegion()->getHost());
+
+ mGrabOffsetFromCenterInitial = grab_offset;
+ mGrabHiddenOffsetFromCamera = mDragStartFromCamera;
+
+ mGrabTimer.reset();
+}
+
+
+BOOL LLToolGrab::handleHover(S32 x, S32 y, MASK mask)
+{
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ if (!gViewerWindow->getLeftMouseDown())
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB);
+ setMouseCapture(FALSE);
+ return TRUE;
+ }
+
+ // Do the right hover based on mode
+ switch( mMode )
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ handleHoverActive( x, y, mask ); // cursor hidden
+ break;
+
+ case GRAB_INACTIVE:
+ handleHoverInactive( x, y, mask ); // cursor set here
+ break;
+
+ case GRAB_NOOBJECT:
+ case GRAB_LOCKED:
+ handleHoverFailed( x, y, mask );
+ break;
+
+ }
+
+ return TRUE;
+}
+
+
+
+
+// Dragging.
+void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
+{
+ llassert( hasMouseCapture() );
+ llassert( mGrabObject );
+ if (mGrabObject->isDead())
+ {
+ // Bail out of drag because object has been killed
+ setMouseCapture(FALSE);
+ return;
+ }
+
+ //--------------------------------------------------
+ // Toggle spinning
+ //--------------------------------------------------
+ if (mSpinGrabbing && !(mask == MASK_SPIN) && !gGrabBtnSpin)
+ {
+ // user released ALT key, stop spinning
+ stopSpin();
+ }
+ else if (!mSpinGrabbing && (mask == MASK_SPIN) )
+ {
+ // user pressed ALT key, start spinning
+ startSpin();
+ }
+
+ //--------------------------------------------------
+ // Toggle vertical dragging
+ //--------------------------------------------------
+ if (mVerticalDragging && !(mask == MASK_VERTICAL) && !gGrabBtnVertical)
+ {
+ // ...switch to horizontal dragging
+ mVerticalDragging = FALSE;
+
+ mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, mGrabObject);
+ mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal();
+ }
+ else if (!mVerticalDragging && (mask == MASK_VERTICAL) )
+ {
+ // ...switch to vertical dragging
+ mVerticalDragging = TRUE;
+
+ mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, mGrabObject);
+ mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal();
+ }
+
+ const F32 RADIANS_PER_PIXEL_X = 0.01f;
+ const F32 RADIANS_PER_PIXEL_Y = 0.01f;
+
+ const F32 SENSITIVITY_X = 0.0075f;
+ const F32 SENSITIVITY_Y = 0.0075f;
+
+ S32 dx = x - (gViewerWindow->getWindowWidth() / 2);
+ S32 dy = y - (gViewerWindow->getWindowHeight() / 2);
+
+ if (dx != 0 || dy != 0)
+ {
+ mAccumDeltaX += dx;
+ mAccumDeltaY += dy;
+ S32 dist_sq = mAccumDeltaX * mAccumDeltaX + mAccumDeltaY * mAccumDeltaY;
+ if (dist_sq > SLOP_DIST_SQ)
+ {
+ mOutsideSlop = TRUE;
+ }
+
+ // mouse has moved outside center
+ mHasMoved = TRUE;
+
+ if (mSpinGrabbing)
+ {
+ //------------------------------------------------------
+ // Handle spinning
+ //------------------------------------------------------
+
+ // x motion maps to rotation around vertical axis
+ LLVector3 up(0.f, 0.f, 1.f);
+ LLQuaternion rotation_around_vertical( dx*RADIANS_PER_PIXEL_X, up );
+
+ // y motion maps to rotation around left axis
+ const LLVector3 &agent_left = gCamera->getLeftAxis();
+ LLQuaternion rotation_around_left( dy*RADIANS_PER_PIXEL_Y, agent_left );
+
+ // compose with current rotation
+ mSpinRotation = mSpinRotation * rotation_around_vertical;
+ mSpinRotation = mSpinRotation * rotation_around_left;
+
+ // TODO: Throttle these
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectSpinUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->addQuatFast(_PREHASH_Rotation, mSpinRotation );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+ }
+ else
+ {
+ //------------------------------------------------------
+ // Handle grabbing
+ //------------------------------------------------------
+
+ LLVector3d x_part;
+ x_part.setVec(gCamera->getLeftAxis());
+ x_part.mdV[VZ] = 0.0;
+ x_part.normVec();
+
+ LLVector3d y_part;
+ if( mVerticalDragging )
+ {
+ y_part.setVec(gCamera->getUpAxis());
+ // y_part.setVec(0.f, 0.f, 1.f);
+ }
+ else
+ {
+ // drag toward camera
+ y_part = x_part % LLVector3d::z_axis;
+ y_part.mdV[VZ] = 0.0;
+ y_part.normVec();
+ }
+
+ mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera
+ + (x_part * (-dx * SENSITIVITY_X))
+ + (y_part * ( dy * SENSITIVITY_Y));
+
+
+ // Send the message to the viewer.
+ F32 dt = mGrabTimer.getElapsedTimeAndResetF32();
+ U32 dt_milliseconds = (U32) (1000.f * dt);
+
+ // need to return offset from mGrabStartPoint
+ LLVector3d grab_point_global;
+
+ grab_point_global = gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
+
+ /* Snap to grid disabled for grab tool - very confusing
+ // Handle snapping to grid, but only when the tool is formally selected.
+ BOOL snap_on = gSavedSettings.getBOOL("SnapEnabled");
+ if (snap_on && !gGrabTransientTool)
+ {
+ F64 snap_size = gSavedSettings.getF32("GridResolution");
+ U8 snap_dimensions = (mVerticalDragging ? 3 : 2);
+
+ for (U8 i = 0; i < snap_dimensions; i++)
+ {
+ grab_point_global.mdV[i] += snap_size / 2;
+ grab_point_global.mdV[i] -= fmod(grab_point_global.mdV[i], snap_size);
+ }
+ }
+ */
+
+ // Don't let object centers go underground.
+ F32 land_height = gWorldPointer->resolveLandHeightGlobal(grab_point_global);
+
+ if (grab_point_global.mdV[VZ] < land_height)
+ {
+ grab_point_global.mdV[VZ] = land_height;
+ }
+
+ // For safety, cap heights where objects can be dragged
+ if (grab_point_global.mdV[VZ] > MAX_OBJECT_Z)
+ {
+ grab_point_global.mdV[VZ] = MAX_OBJECT_Z;
+ }
+
+ grab_point_global = gWorldp->clipToVisibleRegions(mDragStartPointGlobal, grab_point_global);
+ // propagate constrained grab point back to grab offset
+ mGrabHiddenOffsetFromCamera = grab_point_global - gAgent.getCameraPositionGlobal();
+
+ // Handle auto-rotation at screen edge.
+ LLVector3 grab_pos_agent = gAgent.getPosAgentFromGlobal( grab_point_global );
+
+ LLCoordGL grab_center_gl( gViewerWindow->getWindowWidth() / 2, gViewerWindow->getWindowHeight() / 2);
+ gCamera->projectPosAgentToScreen(grab_pos_agent, grab_center_gl);
+
+ const S32 ROTATE_H_MARGIN = gViewerWindow->getWindowWidth() / 20;
+ const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+ // ...build mode moves camera about focus point
+ if (grab_center_gl.mX < ROTATE_H_MARGIN)
+ {
+ if (gAgent.getFocusOnAvatar())
+ {
+ gAgent.yaw(rotate_angle);
+ }
+ else
+ {
+ gAgent.cameraOrbitAround(rotate_angle);
+ }
+ }
+ else if (grab_center_gl.mX > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN)
+ {
+ if (gAgent.getFocusOnAvatar())
+ {
+ gAgent.yaw(-rotate_angle);
+ }
+ else
+ {
+ gAgent.cameraOrbitAround(-rotate_angle);
+ }
+ }
+
+ // Don't move above top of screen or below bottom
+ if ((grab_center_gl.mY < gViewerWindow->getWindowHeight() - 6)
+ && (grab_center_gl.mY > 24))
+ {
+ // Transmit update to simulator
+ LLVector3 grab_pos_region = mGrabObject->getRegion()->getPosRegionFromGlobal( grab_point_global );
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial );
+ msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region );
+ msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+ }
+ }
+
+ gViewerWindow->moveCursorToCenter();
+
+ gSelectMgr->updateSelectionCenter();
+
+ }
+
+ // once we've initiated a drag, lock the camera down
+ if (mHasMoved)
+ {
+ if (!gAgent.cameraMouselook() &&
+ !mGrabObject->isHUDAttachment() &&
+ mGrabObject->getRoot() == gAgent.getAvatarObject()->getRoot())
+ {
+ // force focus to point in space where we were looking previously
+ gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null);
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ }
+ else
+ {
+ gAgent.clearFocusObject();
+ }
+ }
+
+ // HACK to avoid assert: error checking system makes sure that the cursor is set during every handleHover. This is actually a no-op since the cursor is hidden.
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (active) [cursor hidden]" << llendl;
+}
+
+
+// Not dragging. Just showing affordances
+void LLToolGrab::handleHoverInactive(S32 x, S32 y, MASK mask)
+{
+ const F32 ROTATE_ANGLE_PER_SECOND = 40.f * DEG_TO_RAD;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+
+ // Look for cursor against the edge of the screen
+ // Only works in fullscreen
+ if (gSavedSettings.getBOOL("FullScreen"))
+ {
+ if (gAgent.cameraThirdPerson() )
+ {
+ if (x == 0)
+ {
+ gAgent.yaw(rotate_angle);
+ //gAgent.setControlFlags(AGENT_CONTROL_YAW_POS);
+ }
+ else if (x == (gViewerWindow->getWindowWidth() - 1) )
+ {
+ gAgent.yaw(-rotate_angle);
+ //gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG);
+ }
+ }
+ }
+
+ // JC - TODO - change cursor based on gGrabBtnVertical, gGrabBtnSpin
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (inactive-not over editable object)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB);
+}
+
+// User is trying to do something that's not allowed.
+void LLToolGrab::handleHoverFailed(S32 x, S32 y, MASK mask)
+{
+ if( GRAB_NOOBJECT == mMode )
+ {
+ gViewerWindow->setCursor(UI_CURSOR_NO);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (not on object)" << llendl;
+ }
+ else
+ {
+ S32 dist_sq = (x-mMouseDownX) * (x-mMouseDownX) + (y-mMouseDownY) * (y-mMouseDownY);
+ if( mOutsideSlop || dist_sq > SLOP_DIST_SQ )
+ {
+ mOutsideSlop = TRUE;
+
+ switch( mMode )
+ {
+ case GRAB_LOCKED:
+ gViewerWindow->setCursor(UI_CURSOR_GRABLOCKED);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, no move permission)" << llendl;
+ break;
+
+// Non physical now handled by handleHoverActive - CRO
+// case GRAB_NONPHYSICAL:
+// gViewerWindow->setCursor(UI_CURSOR_ARROW);
+// lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, nonphysical)" << llendl;
+// break;
+ default:
+ llassert(0);
+ }
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed but within slop)" << llendl;
+ }
+ }
+}
+
+
+
+
+BOOL LLToolGrab::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // call the base class to propogate info to sim
+ LLTool::handleMouseUp(x, y, mask);
+
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+ }
+ mMode = GRAB_INACTIVE;
+
+ // HACK: Make some grabs temporary
+ if (gGrabTransientTool)
+ {
+ gBasicToolset->selectTool( gGrabTransientTool );
+ gGrabTransientTool = NULL;
+ }
+
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+
+ return TRUE;
+}
+
+void LLToolGrab::stopEditing()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+ }
+}
+
+void LLToolGrab::onMouseCaptureLost()
+{
+ // First, fix cursor placement
+ if( !gAgent.cameraMouselook()
+ && (GRAB_ACTIVE_CENTER == mMode || GRAB_NONPHYSICAL == mMode))
+ {
+ llassert( mGrabObject );
+
+ if (mGrabObject->isHUDAttachment())
+ {
+ // ...move cursor "naturally", as if it had moved when hidden
+ S32 x = mMouseDownX + mAccumDeltaX;
+ S32 y = mMouseDownY + mAccumDeltaY;
+ LLUI::setCursorPositionScreen(x, y);
+ }
+ else if (mHasMoved)
+ {
+ // ...move cursor back to the center of the object
+ LLVector3 grab_point_agent = mGrabObject->getRenderPosition();
+
+ LLCoordGL gl_point;
+ gCamera->projectPosAgentToScreen(grab_point_agent, gl_point);
+
+ LLUI::setCursorPositionScreen(gl_point.mX, gl_point.mY);
+ }
+ else
+ {
+ // ...move cursor back to click position
+ LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY);
+ }
+
+ gViewerWindow->showCursor();
+ }
+
+ stopGrab();
+ stopSpin();
+ mMode = GRAB_INACTIVE;
+
+ mHideBuildHighlight = FALSE;
+
+ mGrabObject = NULL;
+
+ gSelectMgr->updateSelectionCenter();
+ gAgent.setPointAt(POINTAT_TARGET_CLEAR);
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+
+ dialog_refresh_all();
+}
+
+
+void LLToolGrab::stopGrab()
+{
+ // Next, send messages to simulator
+ LLMessageSystem *msg = gMessageSystem;
+ switch(mMode)
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ case GRAB_LOCKED:
+ msg->newMessageFast(_PREHASH_ObjectDeGrab);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_LocalID, mGrabObject->mLocalID);
+ msg->sendMessage(mGrabObject->getRegion()->getHost());
+
+ mVerticalDragging = FALSE;
+ mGrabOffset.clearVec();
+ break;
+
+ case GRAB_NOOBJECT:
+ case GRAB_INACTIVE:
+ default:
+ // do nothing
+ break;
+ }
+
+ mHideBuildHighlight = FALSE;
+}
+
+
+void LLToolGrab::draw()
+{ }
+
+void LLToolGrab::render()
+{ }
+
+BOOL LLToolGrab::isEditing()
+{
+ // Can't just compare to null directly due to "smart" pointer.
+ LLViewerObject *obj = mGrabObject;
+ return (obj != NULL);
+}
+
+LLViewerObject* LLToolGrab::getEditingObject()
+{
+ return mGrabObject;
+}
+
+
+LLVector3d LLToolGrab::getEditingPointGlobal()
+{
+ return getGrabPointGlobal();
+}
+
+LLVector3d LLToolGrab::getGrabPointGlobal()
+{
+ switch(mMode)
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ case GRAB_LOCKED:
+ return gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
+
+ case GRAB_NOOBJECT:
+ case GRAB_INACTIVE:
+ default:
+ return gAgent.getPositionGlobal();
+ }
+}
diff --git a/indra/newview/lltoolgrab.h b/indra/newview/lltoolgrab.h
new file mode 100644
index 0000000000..5073d0e211
--- /dev/null
+++ b/indra/newview/lltoolgrab.h
@@ -0,0 +1,112 @@
+/**
+ * @file lltoolgrab.h
+ * @brief LLToolGrab class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLGRAB_H
+#define LL_TOOLGRAB_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llmemory.h"
+#include "lluuid.h"
+
+class LLView;
+class LLTextBox;
+class LLViewerObject;
+
+class LLToolGrab : public LLTool
+{
+public:
+ LLToolGrab( LLToolComposite* composite = NULL );
+ ~LLToolGrab();
+
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ void render(); // 3D elements
+ /*virtual*/ void draw(); // 2D elements
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ virtual LLViewerObject* getEditingObject();
+ virtual LLVector3d getEditingPointGlobal();
+ virtual BOOL isEditing();
+ virtual void stopEditing();
+
+ virtual void onMouseCaptureLost();
+
+ BOOL hasGrabOffset() { return TRUE; } // HACK
+ LLVector3 getGrabOffset(S32 x, S32 y); // HACK
+
+ // Capture the mouse and start grabbing.
+ BOOL handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mask);
+
+ // Certain grabs should not highlight the "Build" toolbar button
+ BOOL getHideBuildHighlight() { return mHideBuildHighlight; }
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+private:
+ LLVector3d getGrabPointGlobal();
+ void startGrab(S32 x, S32 y);
+ void stopGrab();
+
+ void startSpin();
+ void stopSpin();
+
+ void handleHoverSpin(S32 x, S32 y, MASK mask);
+ void handleHoverActive(S32 x, S32 y, MASK mask);
+ void handleHoverInactive(S32 x, S32 y, MASK mask);
+ void handleHoverFailed(S32 x, S32 y, MASK mask);
+
+private:
+ enum EGrabMode { GRAB_INACTIVE, GRAB_ACTIVE_CENTER, GRAB_NONPHYSICAL, GRAB_LOCKED, GRAB_NOOBJECT };
+
+ EGrabMode mMode;
+
+ BOOL mVerticalDragging;
+
+ BOOL mHitLand;
+ LLUUID mHitObjectID; // if hit something, its ID
+
+ LLPointer<LLViewerObject> mGrabObject; // the object currently being grabbed
+ LLTimer mGrabTimer; // send simulator time between hover movements
+
+ LLVector3 mGrabOffsetFromCenterInitial; // meters from CG of object
+ LLVector3 mGrabOffset; // how far cursor currently is from grab start point, meters
+ LLVector3d mGrabHiddenOffsetFromCamera; // in cursor hidden drag, how far is grab offset from camera
+
+ LLVector3d mDragStartPointGlobal; // projected into world
+ LLVector3d mDragStartFromCamera; // drag start relative to camera
+
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+ MASK mMouseMask;
+ S32 mAccumDeltaX; // since cursor hidden, how far have you moved?
+ S32 mAccumDeltaY;
+ BOOL mHasMoved; // has mouse moved off center at all?
+ BOOL mOutsideSlop; // has mouse moved outside center 5 pixels?
+ BOOL mDeselectedThisClick;
+
+ BOOL mSpinGrabbing;
+ LLQuaternion mSpinRotation;
+
+ BOOL mHideBuildHighlight;
+};
+
+extern LLToolGrab *gToolGrab;
+
+extern BOOL gGrabBtnVertical;
+extern BOOL gGrabBtnSpin;
+extern LLTool* gGrabTransientTool;
+
+#endif // LL_TOOLGRAB_H
+
diff --git a/indra/newview/lltoolgun.cpp b/indra/newview/lltoolgun.cpp
new file mode 100644
index 0000000000..4c01b86153
--- /dev/null
+++ b/indra/newview/lltoolgun.cpp
@@ -0,0 +1,122 @@
+/**
+ * @file lltoolgun.cpp
+ * @brief LLToolGun class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolgun.h"
+
+#include "llviewerwindow.h"
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llsky.h"
+#include "viewer.h"
+#include "llresmgr.h"
+#include "llfontgl.h"
+#include "llui.h"
+#include "llviewerimagelist.h"
+#include "llviewercamera.h"
+#include "llhudmanager.h"
+#include "lltoolmgr.h"
+#include "lltoolgrab.h"
+
+LLToolGun::LLToolGun( LLToolComposite* composite )
+ :
+ LLTool( "gun", composite )
+{
+ mCrosshairImg = gImageList.getImage( LLUUID( gSavedSettings.getString("UIImgCrosshairsUUID") ), MIPMAP_FALSE, TRUE );
+}
+
+void LLToolGun::handleSelect()
+{
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+ gViewerWindow->mWindow->setMouseClipping(TRUE);
+}
+
+void LLToolGun::handleDeselect()
+{
+ gViewerWindow->moveCursorToCenter();
+ gViewerWindow->showCursor();
+ gViewerWindow->mWindow->setMouseClipping(FALSE);
+}
+
+BOOL LLToolGun::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( gToolGrab );
+
+ return gToolGrab->handleMouseDown(x, y, mask);
+}
+
+BOOL LLToolGun::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( gAgent.cameraMouselook() )
+ {
+ #if 1 //LL_WINDOWS || LL_DARWIN
+ const F32 NOMINAL_MOUSE_SENSITIVITY = 0.0025f;
+ #else
+ const F32 NOMINAL_MOUSE_SENSITIVITY = 0.025f;
+ #endif
+
+
+ F32 mouse_sensitivity = clamp_rescale(gMouseSensitivity, 0.f, 15.f, 0.5f, 2.75f) * NOMINAL_MOUSE_SENSITIVITY;
+
+ // ...move the view with the mouse
+
+ // get mouse movement delta
+ S32 dx = -gViewerWindow->getCurrentMouseDX();
+ S32 dy = -gViewerWindow->getCurrentMouseDY();
+
+ if (dx != 0 || dy != 0)
+ {
+ // ...actually moved off center
+ if (gInvertMouse)
+ {
+ gAgent.pitch(mouse_sensitivity * -dy);
+ }
+ else
+ {
+ gAgent.pitch(mouse_sensitivity * dy);
+ }
+ LLVector3 skyward = gAgent.getReferenceUpVector();
+ gAgent.rotate(mouse_sensitivity * dx, skyward.mV[VX], skyward.mV[VY], skyward.mV[VZ]);
+
+ if (gSavedSettings.getBOOL("MouseSun"))
+ {
+ gSky.setSunDirection(gCamera->getAtAxis(), LLVector3(0.f, 0.f, 0.f));
+ gSky.setOverrideSun(TRUE);
+ gSavedSettings.setVector3("SkySunDefaultPosition", gCamera->getAtAxis());
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ gViewerWindow->hideCursor();
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGun (mouselook)" << llendl;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGun (not mouselook)" << llendl;
+ }
+
+ // HACK to avoid assert: error checking system makes sure that the cursor is set during every handleHover. This is actually a no-op since the cursor is hidden.
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+
+ return TRUE;
+}
+
+void LLToolGun::draw()
+{
+ if( gSavedSettings.getBOOL("ShowCrosshairs") )
+ {
+ gl_draw_image(
+ ( gViewerWindow->getWindowWidth() - mCrosshairImg->getWidth() ) / 2,
+ ( gViewerWindow->getWindowHeight() - mCrosshairImg->getHeight() ) / 2,
+ mCrosshairImg );
+ }
+}
diff --git a/indra/newview/lltoolgun.h b/indra/newview/lltoolgun.h
new file mode 100644
index 0000000000..d567db30ca
--- /dev/null
+++ b/indra/newview/lltoolgun.h
@@ -0,0 +1,35 @@
+/**
+ * @file lltoolgun.h
+ * @brief LLToolGun class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLGUN_H
+#define LL_TOOLGUN_H
+
+#include "lltool.h"
+#include "llviewerimage.h"
+
+
+class LLToolGun : public LLTool
+{
+public:
+ LLToolGun( LLToolComposite* composite=NULL );
+
+ virtual void draw();
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual BOOL clipMouseWhenDown() { return FALSE; }
+
+private:
+ LLPointer<LLViewerImage> mCrosshairImg;
+};
+
+#endif
diff --git a/indra/newview/lltoolindividual.cpp b/indra/newview/lltoolindividual.cpp
new file mode 100644
index 0000000000..a5afd379ed
--- /dev/null
+++ b/indra/newview/lltoolindividual.cpp
@@ -0,0 +1,102 @@
+/**
+ * @file lltoolindividual.cpp
+ * @brief LLToolIndividual class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+//
+// This is a tool for selecting individual object from the
+// toolbox. Handy for when you want to deal with child object
+// inventory...
+//
+//*****************************************************************************
+
+#include "llviewerprecompiledheaders.h"
+#include "lltoolindividual.h"
+
+#include "llselectmgr.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llfloatertools.h"
+
+///----------------------------------------------------------------------------
+/// Globals
+///----------------------------------------------------------------------------
+
+LLToolIndividual* gToolIndividual = NULL;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+
+///----------------------------------------------------------------------------
+/// Class LLToolIndividual
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLToolIndividual::LLToolIndividual()
+ : LLTool("Individual")
+{
+}
+
+// Destroys the object
+LLToolIndividual::~LLToolIndividual()
+{
+}
+
+BOOL LLToolIndividual::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolIndividual::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* obj = gViewerWindow->lastObjectHit();
+ gSelectMgr->deselectAll();
+ if(obj)
+ {
+ gSelectMgr->selectObjectOnly(obj);
+ }
+}
+
+BOOL LLToolIndividual::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if(!gSelectMgr->isEmpty())
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its inventory.
+ //gBuildView->showInventoryPanel();
+ //gBuildView->showPanel(LLBuildView::PANEL_CONTENTS);
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return FALSE;
+ }
+}
+
+void LLToolIndividual::handleSelect()
+{
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ if(!obj)
+ {
+ obj = gSelectMgr->getFirstObject();
+ }
+ gSelectMgr->deselectAll();
+ if(obj)
+ {
+ gSelectMgr->selectObjectOnly(obj);
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/lltoolindividual.h b/indra/newview/lltoolindividual.h
new file mode 100644
index 0000000000..b5fe789d50
--- /dev/null
+++ b/indra/newview/lltoolindividual.h
@@ -0,0 +1,41 @@
+/**
+ * @file lltoolindividual.h
+ * @brief LLToolIndividual class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLINDIVIDUAL_H
+#define LL_LLTOOLINDIVIDUAL_H
+
+#include "lltool.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class lltoolindividual
+//
+// A tool to select individual objects rather than linked sets.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLToolIndividual : public LLTool
+{
+public:
+ LLToolIndividual();
+ virtual ~LLToolIndividual();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual void handleSelect();
+ //virtual void handleDeselect();
+ //virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+
+};
+
+extern LLToolIndividual* gToolIndividual;
+
+
+#endif // LL_LLTOOLINDIVIDUAL_H
diff --git a/indra/newview/lltoolmgr.cpp b/indra/newview/lltoolmgr.cpp
new file mode 100644
index 0000000000..99271e18c5
--- /dev/null
+++ b/indra/newview/lltoolmgr.cpp
@@ -0,0 +1,532 @@
+/**
+ * @file lltoolmgr.cpp
+ * @brief LLToolMgr class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolmgr.h"
+
+#include "lltool.h"
+// tools and manipulators
+#include "llmanipscale.h"
+#include "lltoolbrush.h"
+#include "lltoolcomp.h"
+#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolindividual.h"
+#include "lltoolmorph.h"
+#include "lltoolpie.h"
+#include "lltoolplacer.h"
+#include "lltoolselect.h"
+#include "lltoolselectland.h"
+#include "lltoolobjpicker.h"
+#include "lltoolpipette.h"
+
+// Globals (created and destroyed by LLAgent)
+LLToolMgr* gToolMgr = NULL;
+
+// Used when app not active to avoid processing hover.
+LLTool* gToolNull = NULL;
+
+LLToolset* gCurrentToolset = NULL;
+LLToolset* gBasicToolset = NULL;
+LLToolset* gCameraToolset = NULL;
+//LLToolset* gLandToolset = NULL;
+LLToolset* gMouselookToolset = NULL;
+LLToolset* gFaceEditToolset = NULL;
+
+/////////////////////////////////////////////////////
+// LLToolMgr
+
+LLToolMgr::LLToolMgr()
+ :
+ mCurrentTool(NULL),
+ mSavedTool(NULL),
+ mTransientTool( NULL ),
+ mOverrideTool( NULL )
+{
+ gToolNull = new LLTool(NULL); // Does nothing
+ setCurrentTool(gToolNull);
+
+ gBasicToolset = new LLToolset();
+ gCameraToolset = new LLToolset();
+// gLandToolset = new LLToolset();
+ gMouselookToolset = new LLToolset();
+ gFaceEditToolset = new LLToolset();
+
+ gCurrentToolset = gBasicToolset;
+}
+
+void LLToolMgr::initTools()
+{
+ // Initialize all the tools
+ // Variables that are reused for each tool
+ LLTool* tool = NULL;
+
+ //
+ // Pie tool (not visible in UI, implicit)
+ //
+ gToolPie = new LLToolPie();
+
+ gBasicToolset->addTool( gToolPie );
+// gCameraToolset->addTool( gToolPie );
+// gLandToolset->addTool( gToolPie );
+
+ // Camera tool
+ gToolCamera = new LLToolCamera();
+ gBasicToolset ->addTool( gToolCamera );
+ gCameraToolset->addTool( gToolCamera );
+
+ //
+ // Grab tool
+ //
+ gToolGrab = new LLToolGrab();
+ tool = gToolGrab;
+
+ gBasicToolset->addTool( tool );
+
+ //
+ // Translation tool
+ //
+ gToolTranslate = new LLToolCompTranslate();
+ tool = gToolTranslate;
+
+ gBasicToolset->addTool( tool );
+
+ //
+ // Scale ("Stretch") tool
+ //
+ gToolStretch = new LLToolCompScale();
+ tool = gToolStretch;
+
+
+ //
+ // Rotation tool
+ //
+ gToolRotate = new LLToolCompRotate();
+ tool = gToolRotate;
+
+
+ //
+ // Face tool
+ //
+ gToolFace = new LLToolFace();
+ tool = gToolFace;
+
+ //
+ // Pipette tool
+ //
+ gToolPipette = new LLToolPipette();
+
+ //
+ // Individual object selector
+ //
+ gToolIndividual = new LLToolIndividual();
+
+
+ //
+ // Create object tool
+ //
+ gToolCreate = new LLToolCompCreate();
+ tool = gToolCreate;
+
+ gBasicToolset->addTool( tool );
+
+ //
+ // Land brush tool
+ //
+ gToolLand = new LLToolBrushLand();
+ tool = gToolLand;
+
+ gBasicToolset->addTool( tool );
+
+
+ //
+ // Land select tool
+ //
+ gToolParcel = new LLToolSelectLand();
+ tool = gToolParcel;
+
+ //
+ // Gun tool
+ //
+ gToolGun = new LLToolCompGun();
+ gMouselookToolset->addTool( gToolGun );
+
+ //
+ // Face edit tool
+ //
+// gToolMorph = new LLToolMorph();
+// gFaceEditToolset->addTool( gToolMorph );
+ gFaceEditToolset->addTool( gToolCamera );
+
+ // Drag and drop tool
+ gToolDragAndDrop = new LLToolDragAndDrop();
+
+ gToolObjPicker = new LLToolObjPicker();
+
+ // On startup, use "select" tool
+ gBasicToolset->selectTool( gToolPie );
+ useSelectedTool( gBasicToolset );
+}
+
+LLToolMgr::~LLToolMgr()
+{
+ delete gToolPie;
+ gToolPie = NULL;
+
+ delete gToolGun;
+ gToolGun = NULL;
+
+ delete gToolCamera;
+ gToolCamera = NULL;
+
+// delete gToolMorph;
+// gToolMorph = NULL;
+
+ delete gToolDragAndDrop;
+ gToolDragAndDrop = NULL;
+
+ delete gBasicToolset;
+ gBasicToolset = NULL;
+
+ delete gToolGrab;
+ gToolGrab = NULL;
+
+ delete gToolRotate;
+ gToolRotate = NULL;
+
+ delete gToolTranslate;
+ gToolTranslate = NULL;
+
+ delete gToolStretch;
+ gToolStretch = NULL;
+
+ delete gToolIndividual;
+ gToolIndividual = NULL;
+
+ delete gToolPipette;
+ gToolPipette = NULL;
+
+ delete gToolCreate;
+ gToolCreate = NULL;
+
+ delete gToolFace;
+ gToolFace = NULL;
+
+ delete gToolLand;
+ gToolLand = NULL;
+
+ delete gToolParcel;
+ gToolParcel = NULL;
+
+ delete gToolObjPicker;
+ gToolObjPicker = NULL;
+
+ delete gMouselookToolset;
+ gMouselookToolset = NULL;
+
+ delete gFaceEditToolset;
+ gFaceEditToolset = NULL;
+
+ delete gCameraToolset;
+ gCameraToolset = NULL;
+
+ delete gToolNull;
+ gToolNull = NULL;
+}
+
+
+void LLToolMgr::useSelectedTool( LLToolset* vp )
+{
+ setCurrentTool( vp->getSelectedTool() );
+}
+
+BOOL LLToolMgr::usingTransientTool()
+{
+ return mTransientTool ? TRUE : FALSE;
+}
+
+void LLToolMgr::setCurrentTool( LLTool* tool )
+{
+ if (tool == mCurrentTool)
+ {
+ // didn't change tool, so don't mess with
+ // handleSelect or handleDeselect
+ return;
+ }
+
+ if (mTransientTool)
+ {
+ mTransientTool->handleDeselect();
+ mTransientTool = NULL;
+ }
+ else if( mCurrentTool )
+ {
+ mCurrentTool->handleDeselect();
+ }
+
+ mCurrentTool = tool;
+ if (mCurrentTool)
+ {
+ mCurrentTool->handleSelect();
+ }
+}
+
+LLTool* LLToolMgr::getCurrentTool(MASK override_mask)
+{
+ // In mid-drag, always keep the current tool
+ if (gToolTranslate->hasMouseCapture()
+ || gToolRotate->hasMouseCapture()
+ || gToolStretch->hasMouseCapture())
+ {
+ // might have gotten here by overriding another tool
+ if (mOverrideTool)
+ {
+ return mOverrideTool;
+ }
+ else
+ {
+ return mCurrentTool;
+ }
+ }
+
+ if (mTransientTool)
+ {
+ mOverrideTool = NULL;
+ return mTransientTool;
+ }
+
+ if (mCurrentTool == gToolGun)
+ {
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+
+ // ALT always gets you the camera tool
+ if (override_mask & MASK_ALT)
+ {
+ mOverrideTool = gToolCamera;
+ return mOverrideTool;
+ }
+
+ if (mCurrentTool == gToolCamera)
+ {
+ // ...can't switch out of camera
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+ else if (mCurrentTool == gToolGrab)
+ {
+ // ...can't switch out of grab
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+ else
+ {
+ // ...can switch between editing tools
+ if (override_mask == MASK_CONTROL)
+ {
+ // Control lifts when in the pie tool, otherwise switches to rotate
+ if (mCurrentTool == gToolPie)
+ {
+ mOverrideTool = gToolGrab;
+ }
+ else
+ {
+ mOverrideTool = gToolRotate;
+ }
+ return mOverrideTool;
+ }
+ else if (override_mask == (MASK_CONTROL | MASK_SHIFT))
+ {
+ // Shift-Control spins when in the pie tool, otherwise switches to scale
+ if (mCurrentTool == gToolPie)
+ {
+ mOverrideTool = gToolGrab;
+ }
+ else
+ {
+ mOverrideTool = gToolStretch;
+ }
+ return mOverrideTool;
+ }
+ else
+ {
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+ }
+}
+
+BOOL LLToolMgr::inEdit()
+{
+ return mCurrentTool != gToolPie && mCurrentTool != gToolNull;
+}
+
+void LLToolMgr::setTransientTool(LLTool* tool)
+{
+ if (!tool)
+ {
+ clearTransientTool();
+ }
+ else
+ {
+ if (mTransientTool)
+ {
+ mTransientTool->handleDeselect();
+ mTransientTool = NULL;
+ }
+ else if (mCurrentTool)
+ {
+ mCurrentTool->handleDeselect();
+ }
+
+ mTransientTool = tool;
+ mTransientTool->handleSelect();
+ }
+}
+
+void LLToolMgr::clearTransientTool()
+{
+ if (mTransientTool)
+ {
+ mTransientTool->handleDeselect();
+ mTransientTool = NULL;
+ if (mCurrentTool)
+ {
+ mCurrentTool->handleSelect();
+ }
+ else
+ {
+ llwarns << "mCurrentTool is NULL" << llendl;
+ }
+ }
+}
+
+
+// The "gun tool", used for handling mouselook, captures the mouse and
+// locks it within the window. When the app loses focus we need to
+// release this locking.
+void LLToolMgr::onAppFocusLost()
+{
+ if (mCurrentTool
+ && mCurrentTool == gToolGun)
+ {
+ mCurrentTool->handleDeselect();
+ }
+ mSavedTool = mCurrentTool;
+ mCurrentTool = gToolNull;
+}
+
+void LLToolMgr::onAppFocusGained()
+{
+ if (mSavedTool)
+ {
+ if (mSavedTool == gToolGun)
+ {
+ mCurrentTool->handleSelect();
+ }
+ mCurrentTool = mSavedTool;
+ mSavedTool = NULL;
+ }
+}
+
+/////////////////////////////////////////////////////
+// LLToolset
+
+void LLToolset::addTool(LLTool* tool)
+{
+ llassert( !mToolList.checkData( tool ) ); // check for duplicates
+
+ mToolList.addDataAtEnd( tool );
+ if( !mSelectedTool )
+ {
+ mSelectedTool = tool;
+ }
+}
+
+
+void LLToolset::selectTool(LLTool* tool)
+{
+ mSelectedTool = tool;
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+
+void LLToolset::selectToolByIndex( S32 index )
+{
+ LLTool *tool = mToolList.getNthData( index );
+ if (tool)
+ {
+ mSelectedTool = tool;
+ gToolMgr->setCurrentTool( tool );
+ }
+}
+
+BOOL LLToolset::isToolSelected( S32 index )
+{
+ return (mToolList.getNthData( index ) == mSelectedTool);
+}
+
+
+void LLToolset::selectFirstTool()
+{
+ mSelectedTool = mToolList.getFirstData();
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+
+void LLToolset::selectNextTool()
+{
+ LLTool* next = NULL;
+ for( LLTool* cur = mToolList.getFirstData(); cur; cur = mToolList.getNextData() )
+ {
+ if( cur == mSelectedTool )
+ {
+ next = mToolList.getNextData();
+ break;
+ }
+ }
+
+ if( !next )
+ {
+ next = mToolList.getFirstData();
+ }
+
+ mSelectedTool = next;
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+void LLToolset::selectPrevTool()
+{
+ LLTool* prev = NULL;
+ for( LLTool* cur = mToolList.getLastData(); cur; cur = mToolList.getPreviousData() )
+ {
+ if( cur == mSelectedTool )
+ {
+ prev = mToolList.getPreviousData();
+ break;
+ }
+ }
+
+ if( !prev )
+ {
+ prev = mToolList.getLastData();
+ }
+
+ mSelectedTool = prev;
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+void select_tool( void *tool_pointer )
+{
+ LLTool *tool = (LLTool *)tool_pointer;
+ gCurrentToolset->selectTool( tool );
+}
diff --git a/indra/newview/lltoolmgr.h b/indra/newview/lltoolmgr.h
new file mode 100644
index 0000000000..b54a893b0e
--- /dev/null
+++ b/indra/newview/lltoolmgr.h
@@ -0,0 +1,99 @@
+/**
+ * @file lltoolmgr.h
+ * @brief LLToolMgr class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLMGR_H
+#define LL_TOOLMGR_H
+
+#include "doublelinkedlist.h"
+#include "llkeyboard.h"
+
+class LLTool;
+class LLToolset;
+
+// Key bindings for common operations
+const MASK MASK_VERTICAL = MASK_CONTROL;
+const MASK MASK_SPIN = MASK_CONTROL | MASK_SHIFT;
+const MASK MASK_ZOOM = MASK_NONE;
+const MASK MASK_ORBIT = MASK_CONTROL;
+const MASK MASK_PAN = MASK_CONTROL | MASK_SHIFT;
+const MASK MASK_COPY = MASK_SHIFT;
+
+class LLToolMgr
+{
+public:
+ LLToolMgr();
+ ~LLToolMgr();
+
+ // Must be called after gSavedSettings set up.
+ void initTools();
+
+ LLTool* getCurrentTool(MASK override_mask);
+
+ BOOL inEdit();
+ void useSelectedTool( LLToolset* vp );
+
+ void setTransientTool(LLTool* tool);
+ void clearTransientTool();
+ BOOL usingTransientTool();
+
+ void onAppFocusGained();
+ void onAppFocusLost();
+
+protected:
+ friend class LLToolset; // to allow access to setCurrentTool();
+ void setCurrentTool(LLTool* tool);
+
+protected:
+ LLTool* mCurrentTool;
+ LLTool* mSavedTool; // The current tool at the time application focus was lost.
+ LLTool* mTransientTool;
+ LLTool* mOverrideTool; // Tool triggered by keyboard override
+};
+
+// Sets of tools for various modes
+class LLToolset
+{
+public:
+ LLToolset() : mSelectedTool(NULL) {}
+
+ LLTool* getSelectedTool() { return mSelectedTool; }
+
+ void addTool(LLTool* tool);
+
+ void selectTool( LLTool* tool );
+ void selectToolByIndex( S32 index );
+ void selectFirstTool();
+ void selectNextTool();
+ void selectPrevTool();
+
+ void handleScrollWheel(S32 clicks);
+
+ BOOL isToolSelected( S32 index );
+
+protected:
+ LLTool* mSelectedTool;
+ LLDoubleLinkedList<LLTool> mToolList;
+};
+
+// Handy callbacks for switching tools
+void select_tool(void *tool);
+
+
+// Globals (created and destroyed by LLViewerWindow)
+extern LLToolMgr* gToolMgr;
+
+extern LLToolset* gCurrentToolset;
+extern LLToolset* gBasicToolset;
+extern LLToolset *gCameraToolset;
+//extern LLToolset *gLandToolset;
+extern LLToolset* gMouselookToolset;
+extern LLToolset* gFaceEditToolset;
+
+extern LLTool* gToolNull;
+
+#endif
diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp
new file mode 100644
index 0000000000..87e6496588
--- /dev/null
+++ b/indra/newview/lltoolmorph.cpp
@@ -0,0 +1,279 @@
+/**
+ * @file lltoolmorph.cpp
+ * @brief A tool to manipulate faces..
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolmorph.h"
+
+// Library includes
+#include "audioengine.h"
+#include "llviewercontrol.h"
+#include "llfontgl.h"
+#include "sound_ids.h"
+#include "v3math.h"
+#include "v3color.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "llfloatercustomize.h"
+#include "llmorphview.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "lltexlayer.h"
+#include "lltoolmgr.h"
+#include "lltoolview.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+//LLToolMorph *gToolMorph = NULL;
+
+//static
+LLLinkedList<LLVisualParamHint> LLVisualParamHint::sInstances;
+BOOL LLVisualParamReset::sDirty = FALSE;
+
+//-----------------------------------------------------------------------------
+// LLVisualParamHint()
+//-----------------------------------------------------------------------------
+
+// static
+LLVisualParamHint::LLVisualParamHint(
+ S32 pos_x, S32 pos_y,
+ S32 width, S32 height,
+ LLViewerJointMesh *mesh,
+ LLViewerVisualParam *param,
+ F32 param_weight)
+ :
+ LLDynamicTexture(width, height, 3, LLDynamicTexture::ORDER_MIDDLE, TRUE ),
+ mNeedsUpdate( TRUE ),
+ mIsVisible( FALSE ),
+ mJointMesh( mesh ),
+ mVisualParam( param ),
+ mVisualParamWeight( param_weight ),
+ mAllowsUpdates( TRUE ),
+ mDelayFrames( 0 ),
+ mRect( pos_x, pos_y + height, pos_x + width, pos_y ),
+ mLastParamWeight(0.f)
+{
+ LLVisualParamHint::sInstances.addData( this );
+ LLUUID id;
+ id.set( gViewerArt.getString("avatar_thumb_bkgrnd.tga") );
+ mBackgroundp = gImageList.getImage(id, FALSE, TRUE);
+
+
+ llassert(width != 0);
+ llassert(height != 0);
+}
+
+//-----------------------------------------------------------------------------
+// ~LLVisualParamHint()
+//-----------------------------------------------------------------------------
+LLVisualParamHint::~LLVisualParamHint()
+{
+ LLVisualParamHint::sInstances.removeData( this );
+}
+
+//-----------------------------------------------------------------------------
+// static
+// requestHintUpdates()
+// Requests updates for all instances (excluding two possible exceptions) Grungy but efficient.
+//-----------------------------------------------------------------------------
+void LLVisualParamHint::requestHintUpdates( LLVisualParamHint* exception1, LLVisualParamHint* exception2 )
+{
+ S32 delay_frames = 0;
+ for(LLVisualParamHint* instance = sInstances.getFirstData();
+ instance;
+ instance = sInstances.getNextData())
+ {
+ if( (instance != exception1) && (instance != exception2) )
+ {
+ if( instance->mAllowsUpdates )
+ {
+ instance->mNeedsUpdate = TRUE;
+ instance->mDelayFrames = delay_frames;
+ delay_frames++;
+ }
+ else
+ {
+ instance->mNeedsUpdate = TRUE;
+ instance->mDelayFrames = 0;
+ }
+ }
+ }
+}
+
+BOOL LLVisualParamHint::needsRender()
+{
+ return mNeedsUpdate && mDelayFrames-- <= 0 && !gAgent.getAvatarObject()->mAppearanceAnimating && mAllowsUpdates;
+}
+
+void LLVisualParamHint::preRender(BOOL clear_depth)
+{
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+
+ mLastParamWeight = avatarp->getVisualParamWeight(mVisualParam);
+ avatarp->setVisualParamWeight(mVisualParam, mVisualParamWeight);
+ avatarp->setVisualParamWeight("Blink_Left", 0.f);
+ avatarp->setVisualParamWeight("Blink_Right", 0.f);
+ avatarp->updateComposites();
+ avatarp->updateVisualParams();
+ avatarp->updateGeometry(avatarp->mDrawable);
+ LLDynamicTexture::preRender(clear_depth);
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParamHint::render()
+{
+ LLVisualParamReset::sDirty = TRUE;
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSUIDefault gls_ui;
+ //LLGLState::verify(TRUE);
+ LLViewerImage::bindTexture(mBackgroundp);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ gl_rect_2d_simple_tex( mWidth, mHeight );
+ mBackgroundp->unbindTexture(0, GL_TEXTURE_2D);
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ mNeedsUpdate = FALSE;
+ mIsVisible = TRUE;
+
+ LLViewerJointMesh* cam_target_joint = NULL;
+ const std::string& cam_target_mesh_name = mVisualParam->getCameraTargetName();
+ if( !cam_target_mesh_name.empty() )
+ {
+ cam_target_joint = (LLViewerJointMesh*)avatarp->getJoint( cam_target_mesh_name );
+ }
+ if( !cam_target_joint )
+ {
+ cam_target_joint = (LLViewerJointMesh*)gMorphView->getCameraTargetJoint();
+ }
+ if( !cam_target_joint )
+ {
+ cam_target_joint = (LLViewerJointMesh*)avatarp->getJoint("mHead");
+ }
+
+ LLQuaternion avatar_rotation;
+ LLJoint* root_joint = avatarp->getRootJoint();
+ if( root_joint )
+ {
+ avatar_rotation = root_joint->getWorldRotation();
+ }
+
+ LLVector3 target_joint_pos = cam_target_joint->getWorldPosition();
+
+ LLVector3 target_offset( 0, 0, mVisualParam->getCameraElevation() );
+ LLVector3 target_pos = target_joint_pos + (target_offset * avatar_rotation);
+
+ F32 cam_angle_radians = mVisualParam->getCameraAngle() * DEG_TO_RAD;
+ LLVector3 camera_snapshot_offset(
+ mVisualParam->getCameraDistance() * cosf( cam_angle_radians ),
+ mVisualParam->getCameraDistance() * sinf( cam_angle_radians ),
+ mVisualParam->getCameraElevation() );
+ LLVector3 camera_pos = target_joint_pos + (camera_snapshot_offset * avatar_rotation);
+
+ gCamera->setAspect((F32)mWidth / (F32)mHeight);
+ gCamera->setOriginAndLookAt(
+ camera_pos, // camera
+ LLVector3(0.f, 0.f, 1.f), // up
+ target_pos ); // point of interest
+
+ gCamera->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mWidth, mHeight, FALSE);
+
+ if (avatarp->mDrawable.notNull())
+ {
+ LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ gPipeline.unbindAGP();
+ avatarPoolp->syncAGP();
+ gPipeline.bindAGP();
+ avatarPoolp->renderAvatars(avatarp, TRUE); // renders only one avatar (no shaders)
+ }
+ avatarp->setVisualParamWeight(mVisualParam, mLastParamWeight);
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLVisualParamHint::draw()
+{
+ if (!mIsVisible) return;
+
+ bindTexture();
+
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+
+ LLGLSUIDefault gls_ui;
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2i(0, 1);
+ glVertex2i(0, mHeight);
+ glTexCoord2i(0, 0);
+ glVertex2i(0, 0);
+ glTexCoord2i(1, 0);
+ glVertex2i(mWidth, 0);
+ glTexCoord2i(1, 1);
+ glVertex2i(mWidth, mHeight);
+ }
+ glEnd();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+}
+
+//-----------------------------------------------------------------------------
+// LLVisualParamReset()
+//-----------------------------------------------------------------------------
+LLVisualParamReset::LLVisualParamReset() : LLDynamicTexture(1, 1, 1, ORDER_RESET, FALSE)
+{
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParamReset::render()
+{
+ if (sDirty)
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ avatarp->updateComposites();
+ avatarp->updateVisualParams();
+ avatarp->updateGeometry(avatarp->mDrawable);
+ sDirty = FALSE;
+ }
+
+ return FALSE;
+}
diff --git a/indra/newview/lltoolmorph.h b/indra/newview/lltoolmorph.h
new file mode 100644
index 0000000000..0cdb0cb188
--- /dev/null
+++ b/indra/newview/lltoolmorph.h
@@ -0,0 +1,88 @@
+/**
+ * @file lltoolmorph.h
+ * @brief A tool to select object faces.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLMORPH_H
+#define LL_LLTOOLMORPH_H
+
+#include "lltool.h"
+#include "m4math.h"
+#include "v2math.h"
+#include "linked_lists.h"
+#include "lldynamictexture.h"
+#include "llundo.h"
+#include "lltextbox.h"
+#include "llstrider.h"
+#include "llviewervisualparam.h"
+#include "llframetimer.h"
+#include "llviewerimage.h"
+
+class LLViewerJointMesh;
+class LLPolyMesh;
+class LLViewerObject;
+
+//-----------------------------------------------------------------------------
+// LLVisualParamHint
+//-----------------------------------------------------------------------------
+class LLVisualParamHint
+: public LLDynamicTexture
+{
+public:
+ LLVisualParamHint(
+ S32 pos_x, S32 pos_y,
+ S32 width, S32 height,
+ LLViewerJointMesh *mesh,
+ LLViewerVisualParam *param,
+ F32 param_weight);
+ virtual ~LLVisualParamHint();
+
+ BOOL needsRender();
+ void preRender(BOOL clear_depth);
+ BOOL render();
+ void requestUpdate( S32 delay_frames ) {mNeedsUpdate = TRUE; mDelayFrames = delay_frames; }
+ void setUpdateDelayFrames( S32 delay_frames ) { mDelayFrames = delay_frames; }
+ void draw();
+
+ LLViewerVisualParam* getVisualParam() { return mVisualParam; }
+ F32 getVisualParamWeight() { return mVisualParamWeight; }
+ BOOL getVisible() { return mIsVisible; }
+
+ void setAllowsUpdates( BOOL b ) { mAllowsUpdates = b; }
+
+ const LLRect& getRect() { return mRect; }
+
+ // Requests updates for all instances (excluding two possible exceptions) Grungy but efficient.
+ static void requestHintUpdates( LLVisualParamHint* exception1 = NULL, LLVisualParamHint* exception2 = NULL );
+
+protected:
+ BOOL mNeedsUpdate; // does this texture need to be re-rendered?
+ BOOL mIsVisible; // is this distortion hint visible?
+ LLViewerJointMesh* mJointMesh; // mesh that this distortion applies to
+ LLViewerVisualParam* mVisualParam; // visual param applied by this hint
+ F32 mVisualParamWeight; // weight for this visual parameter
+ BOOL mAllowsUpdates; // updates are blocked unless this is true
+ S32 mDelayFrames; // updates are blocked for this many frames
+ LLRect mRect;
+ F32 mLastParamWeight;
+
+ LLPointer<LLViewerImage> mBackgroundp;
+
+ static LLLinkedList<LLVisualParamHint> sInstances;
+};
+
+// this class resets avatar data at the end of an update cycle
+class LLVisualParamReset : public LLDynamicTexture
+{
+public:
+ LLVisualParamReset();
+ /*virtual */ BOOL render();
+
+ static BOOL sDirty;
+};
+
+#endif
+
diff --git a/indra/newview/lltoolobjpicker.cpp b/indra/newview/lltoolobjpicker.cpp
new file mode 100644
index 0000000000..ca74a64bf8
--- /dev/null
+++ b/indra/newview/lltoolobjpicker.cpp
@@ -0,0 +1,162 @@
+/**
+ * @file lltoolobjpicker.cpp
+ * @brief LLToolObjPicker class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// LLToolObjPicker is a transient tool, useful for a single object pick.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolobjpicker.h"
+
+#include "llagent.h"
+#include "llselectmgr.h"
+#include "llworld.h"
+#include "viewer.h" // for gFPSClamped, pie menus
+#include "llviewercontrol.h"
+#include "llmenugl.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewermenu.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "lldrawable.h"
+
+LLToolObjPicker* gToolObjPicker = NULL;
+
+LLToolObjPicker::LLToolObjPicker()
+: LLTool( "ObjPicker", NULL ),
+ mPicked( FALSE ),
+ mHitObjectID( LLUUID::null ),
+ mExitCallback( NULL ),
+ mExitCallbackData( NULL )
+{ }
+
+
+// returns TRUE if an object was selected
+BOOL LLToolObjPicker::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLView* viewp = gViewerWindow->getRootView();
+ BOOL handled = viewp->handleMouseDown(x, y, mask);
+
+ mHitObjectID.setNull();
+
+ if (! handled)
+ {
+ // didn't click in any UI object, so must have clicked in the world
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ handled = TRUE;
+ }
+ else
+ {
+ if (hasMouseCapture())
+ {
+ setMouseCapture(FALSE);
+ }
+ else
+ {
+ llwarns << "PickerTool doesn't have mouse capture on mouseDown" << llendl;
+ }
+ }
+
+ // Pass mousedown to base class
+ LLTool::handleMouseDown(x, y, mask);
+
+ return handled;
+}
+
+void LLToolObjPicker::pickCallback(S32 x, S32 y, MASK mask)
+{
+ // You must hit the body for this tool to think you hit the object.
+ LLViewerObject* objectp = NULL;
+ objectp = gObjectList.findObject( gLastHitObjectID );
+ if (objectp)
+ {
+ gToolObjPicker->mHitObjectID = objectp->mID;
+ gToolObjPicker->mPicked = TRUE;
+ }
+}
+
+
+BOOL LLToolObjPicker::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLView* viewp = gViewerWindow->getRootView();
+ BOOL handled = viewp->handleHover(x, y, mask);
+ if (handled)
+ {
+ // let UI handle this
+ }
+
+ LLTool::handleMouseUp(x, y, mask);
+ if (hasMouseCapture())
+ {
+ setMouseCapture(FALSE);
+ }
+ else
+ {
+ llwarns << "PickerTool doesn't have mouse capture on mouseUp" << llendl;
+ }
+ return handled;
+}
+
+
+BOOL LLToolObjPicker::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLView *viewp = gViewerWindow->getRootView();
+ BOOL handled = viewp->handleHover(x, y, mask);
+ if (!handled)
+ {
+ // Used to do pick on hover. Now we just always display the cursor.
+ ECursorType cursor = UI_CURSOR_ARROWLOCKED;
+
+ cursor = UI_CURSOR_TOOLPICKOBJECT3;
+
+ gViewerWindow->getWindow()->setCursor(cursor);
+ }
+ return handled;
+}
+
+
+void LLToolObjPicker::onMouseCaptureLost()
+{
+ if (mExitCallback)
+ {
+ mExitCallback(mExitCallbackData);
+
+ mExitCallback = NULL;
+ mExitCallbackData = NULL;
+ }
+
+ mPicked = FALSE;
+ mHitObjectID.setNull();
+}
+
+// virtual
+void LLToolObjPicker::setExitCallback(void (*callback)(void *), void *callback_data)
+{
+ mExitCallback = callback;
+ mExitCallbackData = callback_data;
+}
+
+// virtual
+void LLToolObjPicker::handleSelect()
+{
+ LLTool::handleSelect();
+ setMouseCapture(TRUE);
+}
+
+// virtual
+void LLToolObjPicker::handleDeselect()
+{
+ if (hasMouseCapture())
+ {
+ LLTool::handleDeselect();
+ setMouseCapture(FALSE);
+ }
+}
+
+
diff --git a/indra/newview/lltoolobjpicker.h b/indra/newview/lltoolobjpicker.h
new file mode 100644
index 0000000000..cf6e81a5c7
--- /dev/null
+++ b/indra/newview/lltoolobjpicker.h
@@ -0,0 +1,45 @@
+/**
+ * @file lltoolobjpicker.h
+ * @brief LLToolObjPicker class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLOBJPICKER_H
+#define LL_TOOLOBJPICKER_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "lluuid.h"
+
+class LLToolObjPicker : public LLTool
+{
+public:
+ LLToolObjPicker();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ virtual void onMouseCaptureLost();
+
+ virtual void setExitCallback(void (*callback)(void *), void *callback_data);
+
+ LLUUID getObjectID() const { return mHitObjectID; }
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+ BOOL mPicked;
+ LLUUID mHitObjectID;
+ void (*mExitCallback)(void *callback_data);
+ void *mExitCallbackData;
+};
+
+extern LLToolObjPicker* gToolObjPicker;
+
+#endif
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
new file mode 100644
index 0000000000..89df9c5249
--- /dev/null
+++ b/indra/newview/lltoolpie.cpp
@@ -0,0 +1,635 @@
+/**
+ * @file lltoolpie.cpp
+ * @brief LLToolPie class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolpie.h"
+
+#include "indra_constants.h"
+#include "llclickaction.h"
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterland.h"
+#include "llfloaterscriptdebug.h"
+#include "llhoverview.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llmenugl.h"
+#include "llmutelist.h"
+#include "llselectmgr.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolselect.h"
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "llui.h"
+
+LLToolPie *gToolPie = NULL;
+
+LLViewerObject* LLToolPie::sClickActionObject = NULL;
+
+extern void handle_buy(void*);
+
+extern BOOL gDebugClicks;
+
+LLToolPie::LLToolPie()
+: LLTool("Select"),
+ mPieMouseButtonDown( FALSE ),
+ mGrabMouseButtonDown( FALSE ),
+ mHitLand( FALSE ),
+ mHitObjectID(),
+ mMouseOutsideSlop( FALSE )
+{ }
+
+
+BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // if buttons swapped, don't pick transparent so users can't "pay"
+ // transparent objects
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, leftMouseCallback,
+ TRUE, TRUE);
+ mGrabMouseButtonDown = TRUE;
+ return TRUE;
+}
+
+// static
+void LLToolPie::leftMouseCallback(S32 x, S32 y, MASK mask)
+{
+ gToolPie->pickAndShowMenu(x, y, mask, FALSE);
+}
+
+BOOL LLToolPie::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Pick faces in case they select "Copy Texture" and need that info.
+ gPickFaces = TRUE;
+ // don't pick transparent so users can't "pay" transparent objects
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, rightMouseCallback,
+ FALSE, TRUE);
+ mPieMouseButtonDown = TRUE;
+ // don't steal focus from UI
+ return FALSE;
+}
+
+// static
+void LLToolPie::rightMouseCallback(S32 x, S32 y, MASK mask)
+{
+ gToolPie->pickAndShowMenu(x, y, mask, TRUE);
+}
+
+// True if you selected an object.
+BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
+{
+ if (!always_show && gLastHitParcelWall)
+ {
+ LLParcel* parcel = gParcelMgr->getCollisionParcel();
+ if (parcel)
+ {
+ gParcelMgr->selectCollisionParcel();
+ if (parcel->getParcelFlag(PF_USE_PASS_LIST)
+ && !gParcelMgr->isCollisionBanned())
+ {
+ // if selling passes, just buy one
+ void* deselect_when_done = (void*)TRUE;
+ LLPanelLandGeneral::onClickBuyPass(deselect_when_done);
+ }
+ else
+ {
+ // not selling passes, get info
+ LLFloaterLand::show();
+ }
+ }
+
+ return LLTool::handleMouseDown(x, y, mask);
+ }
+
+ // didn't click in any UI object, so must have clicked in the world
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ LLViewerObject *parent = NULL;
+
+ mHitLand = !object && !gLastHitPosGlobal.isExactlyZero();
+ if (!mHitLand)
+ {
+ gParcelMgr->deselectLand();
+ }
+
+ if (object)
+ {
+ mHitObjectID = object->mID;
+
+ parent = object->getRootEdit();
+ }
+ else
+ {
+ mHitObjectID.setNull();
+ }
+
+ BOOL touchable = (object && object->flagHandleTouch())
+ || (parent && parent->flagHandleTouch());
+
+ // If it's a left-click, and we have a special action, do it.
+ if (useClickAction(always_show, mask, object, parent))
+ {
+ U8 click_action = 0;
+ if (object && object->getClickAction())
+ {
+ click_action = object->getClickAction();
+ }
+ else if (parent && parent->getClickAction())
+ {
+ click_action = parent->getClickAction();
+ }
+
+ switch(click_action)
+ {
+ case CLICK_ACTION_TOUCH:
+ default:
+ // nothing
+ break;
+ case CLICK_ACTION_SIT:
+ handle_sit_or_stand();
+ return TRUE;
+ case CLICK_ACTION_PAY:
+ if (object && object->flagTakesMoney()
+ || parent && parent->flagTakesMoney())
+ {
+ sClickActionObject = parent;
+ LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+ return TRUE;
+ }
+ break;
+ case CLICK_ACTION_BUY:
+ sClickActionObject = parent;
+ LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+ return TRUE;
+ case CLICK_ACTION_OPEN:
+ if (parent && parent->allowOpen())
+ {
+ sClickActionObject = parent;
+ LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+ }
+ return TRUE;
+ }
+ }
+
+ // Switch to grab tool if physical or triggerable
+ if (object &&
+ !object->isAvatar() &&
+ ((object->usePhysics() || (parent && !parent->isAvatar() && parent->usePhysics())) || touchable) &&
+ !always_show)
+ {
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( gToolGrab );
+ return gToolGrab->handleObjectHit( object, x, y, mask);
+ }
+
+ if (!object && gLastHitHUDIcon && gLastHitHUDIcon->getSourceObject())
+ {
+ LLFloaterScriptDebug::show(gLastHitHUDIcon->getSourceObject()->getID());
+ }
+
+ // If left-click never selects or spawns a menu
+ // Eat the event.
+ if (!gSavedSettings.getBOOL("LeftClickShowMenu")
+ && !always_show)
+ {
+ // mouse already released
+ if (!mGrabMouseButtonDown)
+ {
+ return TRUE;
+ }
+
+ while( object && object->isAttachment() && !object->flagHandleTouch())
+ {
+ // don't pick avatar through hud attachment
+ if (object->isHUDAttachment())
+ {
+ break;
+ }
+ object = (LLViewerObject*)object->getParent();
+ }
+ if (object && object == gAgent.getAvatarObject())
+ {
+ // we left clicked on avatar, switch to focus mode
+ gToolMgr->setTransientTool(gToolCamera);
+ gViewerWindow->hideCursor();
+ gToolCamera->setMouseCapture(TRUE);
+ gToolCamera->pickCallback(gViewerWindow->getCurrentMouseX(), gViewerWindow->getCurrentMouseY(), mask);
+ gAgent.setFocusOnAvatar(TRUE, TRUE);
+
+ return TRUE;
+ }
+ // Could be first left-click on nothing
+ LLFirstUse::useLeftClickNoHit();
+
+ // Eat the event
+ return LLTool::handleMouseDown(x, y, mask);
+ }
+
+ if (!always_show && gAgent.leftButtonGrabbed())
+ {
+ // if the left button is grabbed, don't put up the pie menu
+ return LLTool::handleMouseDown(x, y, mask);
+ }
+
+ // Can't ignore children here.
+ LLToolSelect::handleObjectSelection(object, mask, FALSE, TRUE);
+
+ // Spawn pie menu
+ if (mHitLand)
+ {
+ gParcelMgr->selectParcelAt( gLastHitPosGlobal );
+
+ gPieLand->show(x, y, mPieMouseButtonDown);
+
+ // VEFFECT: ShowPie
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_SPHERE, TRUE);
+ effectp->setPositionGlobal(gLastHitPosGlobal);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ effectp->setDuration(0.25f);
+ }
+ else if (mHitObjectID == gAgent.getID() )
+ {
+ gPieSelf->show(x, y, mPieMouseButtonDown);
+ }
+ else if (object)
+ {
+ if (object->isAvatar()
+ || (object->isAttachment() && !object->isHUDAttachment() && !object->permYouOwner()))
+ {
+ // Find the attachment's avatar
+ while( object && object->isAttachment())
+ {
+ object = (LLViewerObject*)object->getParent();
+ }
+
+ // Object is an avatar, so check for mute by id.
+ LLVOAvatar* avatar = (LLVOAvatar*)object;
+ LLString name = avatar->getFullname();
+ if (gMuteListp->isMuted(avatar->getID(), name))
+ {
+ gMenuHolder->childSetText("Avatar Mute", "Unmute");
+ //gMutePieMenu->setLabel("Unmute");
+ }
+ else
+ {
+ gMenuHolder->childSetText("Avatar Mute", "Mute");
+ //gMutePieMenu->setLabel("Mute");
+ }
+
+ gPieAvatar->show(x, y, mPieMouseButtonDown);
+ }
+ else if (object->isAttachment())
+ {
+ gPieAttachment->show(x, y, mPieMouseButtonDown);
+ }
+ else
+ {
+ // BUG: What about chatting child objects?
+ LLString name;
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node)
+ {
+ name = node->mName;
+ }
+ if (gMuteListp->isMuted(object->getID(), name))
+ {
+ gMenuHolder->childSetText("Object Mute", "Unmute");
+ //gMuteObjectPieMenu->setLabel("Unmute");
+ }
+ else
+ {
+ gMenuHolder->childSetText("Object Mute", "Mute");
+ //gMuteObjectPieMenu->setLabel("Mute");
+ }
+
+ gPieObject->show(x, y, mPieMouseButtonDown);
+
+ // VEFFECT: ShowPie object
+ // Don't show when you click on someone else, it freaks them
+ // out.
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_SPHERE, TRUE);
+ effectp->setPositionGlobal(gLastHitPosGlobal);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ effectp->setDuration(0.25f);
+ }
+ }
+
+ if (always_show)
+ {
+ // ignore return value
+ LLTool::handleRightMouseDown(x, y, mask);
+ }
+ else
+ {
+ // ignore return value
+ LLTool::handleMouseDown(x, y, mask);
+ }
+
+ // We handled the event.
+ return TRUE;
+}
+
+BOOL LLToolPie::useClickAction(BOOL always_show,
+ MASK mask,
+ LLViewerObject* object,
+ LLViewerObject* parent)
+{
+ return !always_show
+ && mask == MASK_NONE
+ && object
+ && !object->isAttachment()
+ && LLPrimitive::isPrimitive(object->getPCode())
+ && (object->getClickAction()
+ || parent->getClickAction());
+
+}
+
+U8 final_click_action(LLViewerObject* obj)
+{
+ if (!obj) return CLICK_ACTION_NONE;
+ if (obj->isAttachment()) return CLICK_ACTION_NONE;
+
+ U8 click_action = CLICK_ACTION_TOUCH;
+ LLViewerObject* parent = (obj ? obj->getRootEdit() : NULL);
+ if ((obj && obj->getClickAction())
+ || (parent && parent->getClickAction()))
+ {
+ if (obj && obj->getClickAction())
+ {
+ click_action = obj->getClickAction();
+ }
+ else if (parent && parent->getClickAction())
+ {
+ click_action = parent->getClickAction();
+ }
+ }
+ return click_action;
+}
+
+// When we get object properties after left-clicking on an object
+// with left-click = buy, if it's the same object, do the buy.
+// static
+void LLToolPie::selectionPropertiesReceived()
+{
+ // Make sure all data has been received.
+ // This function will be called repeatedly as the data comes in.
+ if (!gSelectMgr->selectGetAllValid())
+ {
+ return;
+ }
+
+ if (sClickActionObject
+ && !sClickActionObject->isDead())
+ {
+ LLViewerObject* root = gSelectMgr->getFirstRootObject();
+ if (root == sClickActionObject)
+ {
+ U8 action = root->getClickAction();
+ switch (action)
+ {
+ case CLICK_ACTION_BUY:
+ handle_buy(NULL);
+ break;
+ case CLICK_ACTION_PAY:
+ handle_give_money_dialog();
+ break;
+ case CLICK_ACTION_OPEN:
+ handle_object_open();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ sClickActionObject = NULL;
+}
+
+BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
+{
+ /*
+ // If auto-rotate occurs, tag mouse-outside-slop to make sure the drag
+ // gets started.
+ const S32 ROTATE_H_MARGIN = (S32) (0.1f * gViewerWindow->getWindowWidth() );
+ const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+ // ...normal modes can only yaw
+ if (x < ROTATE_H_MARGIN)
+ {
+ gAgent.yaw(rotate_angle);
+ mMouseOutsideSlop = TRUE;
+ }
+ else if (x > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN)
+ {
+ gAgent.yaw(-rotate_angle);
+ mMouseOutsideSlop = TRUE;
+ }
+ */
+
+ LLViewerObject *object = NULL;
+ LLViewerObject *parent = NULL;
+ if (gHoverView)
+ {
+ object = gHoverView->getLastHoverObject();
+ }
+
+ if (object)
+ {
+ parent = object->getRootEdit();
+ }
+
+ if (object && useClickAction(FALSE, mask, object, parent))
+ {
+ U8 click_action = final_click_action(object);
+ ECursorType cursor = UI_CURSOR_ARROW;
+ switch(click_action)
+ {
+ default: break;
+ case CLICK_ACTION_SIT: cursor = UI_CURSOR_TOOLSIT; break;
+ case CLICK_ACTION_BUY: cursor = UI_CURSOR_TOOLBUY; break;
+ case CLICK_ACTION_OPEN:
+ // Open always opens the parent.
+ if (parent && parent->allowOpen())
+ {
+ cursor = UI_CURSOR_TOOLOPEN;
+ }
+ break;
+ case CLICK_ACTION_PAY:
+ if ((object && object->flagTakesMoney())
+ || (parent && parent->flagTakesMoney()))
+ {
+ cursor = UI_CURSOR_TOOLPAY;
+ }
+ break;
+ }
+ gViewerWindow->getWindow()->setCursor(cursor);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+ else if ((object && !object->isAvatar() && object->usePhysics())
+ || (parent && !parent->isAvatar() && parent->usePhysics()))
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLGRAB);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+ else if ( (object && object->flagHandleTouch())
+ || (parent && parent->flagHandleTouch()))
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_HAND);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+ else
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+
+ return TRUE;
+}
+
+BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* obj = gViewerWindow->lastObjectHit();
+ U8 click_action = final_click_action(obj);
+ if (click_action != CLICK_ACTION_NONE)
+ {
+ switch(click_action)
+ {
+ case CLICK_ACTION_BUY:
+ case CLICK_ACTION_PAY:
+ case CLICK_ACTION_OPEN:
+ // Because these actions open UI dialogs, we won't change
+ // the cursor again until the next hover and GL pick over
+ // the world. Keep the cursor an arrow, assuming that
+ // after the user moves off the UI, they won't be on the
+ // same object anymore.
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ // Make sure the hover-picked object is ignored.
+ gHoverView->resetLastHoverObject();
+ break;
+ default:
+ break;
+ }
+ }
+
+ mGrabMouseButtonDown = FALSE;
+ gToolMgr->clearTransientTool();
+ return LLTool::handleMouseUp(x, y, mask);
+}
+
+BOOL LLToolPie::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ mPieMouseButtonDown = FALSE;
+ gToolMgr->clearTransientTool();
+ return LLTool::handleRightMouseUp(x, y, mask);
+}
+
+
+BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolPie handleDoubleClick (becoming mouseDown)" << llendl;
+ }
+
+ if (gSavedSettings.getBOOL("DoubleClickAutoPilot"))
+ {
+ if (gLastHitLand
+ && !gLastHitPosGlobal.isExactlyZero())
+ {
+ handle_go_to();
+ return TRUE;
+ }
+ else if (gLastHitObjectID.notNull()
+ && !gLastHitPosGlobal.isExactlyZero())
+ {
+ // Hit an object
+ // HACK: Call the last hit position the point we hit on the object
+ gLastHitPosGlobal += gLastHitObjectOffset;
+ handle_go_to();
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+ /* JC - don't do go-there, because then double-clicking on physical
+ objects gets you into trouble.
+
+ // If double-click on object or land, go there.
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ if (object)
+ {
+ if (object->isAvatar())
+ {
+ LLFloaterAvatarInfo::showFromAvatar(object);
+ }
+ else
+ {
+ handle_go_to(NULL);
+ }
+ }
+ else if (!gLastHitPosGlobal.isExactlyZero())
+ {
+ handle_go_to(NULL);
+ }
+
+ return TRUE;
+ */
+}
+
+
+void LLToolPie::handleDeselect()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+ // remove temporary selection for pie menu
+ gSelectMgr->validateSelection();
+}
+
+
+void LLToolPie::stopEditing()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+}
+
+void LLToolPie::onMouseCaptureLost()
+{
+ mMouseOutsideSlop = FALSE;
+}
+
+
+// true if x,y outside small box around start_x,start_y
+BOOL LLToolPie::outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy);
+}
+
+
+void LLToolPie::render()
+{
+ return;
+}
+
diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h
new file mode 100644
index 0000000000..d440a443e2
--- /dev/null
+++ b/indra/newview/lltoolpie.h
@@ -0,0 +1,59 @@
+/**
+ * @file lltoolpie.h
+ * @brief LLToolPie class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLPIE_H
+#define LL_TOOLPIE_H
+
+#include "lltool.h"
+#include "lluuid.h"
+
+class LLViewerObject;
+
+class LLToolPie
+: public LLTool
+{
+public:
+ LLToolPie( );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ virtual void render();
+
+ virtual void stopEditing();
+
+ virtual void onMouseCaptureLost();
+ virtual void handleDeselect();
+
+ static void leftMouseCallback(S32 x, S32 y, MASK mask);
+ static void rightMouseCallback(S32 x, S32 y, MASK mask);
+
+ static void selectionPropertiesReceived();
+
+protected:
+ BOOL outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
+ BOOL pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL edit_menu);
+ BOOL useClickAction(BOOL always_show, MASK mask, LLViewerObject* object,
+ LLViewerObject* parent);
+
+protected:
+ BOOL mPieMouseButtonDown;
+ BOOL mGrabMouseButtonDown;
+ BOOL mHitLand;
+ LLUUID mHitObjectID;
+ BOOL mMouseOutsideSlop; // for this drag, has mouse moved outside slop region
+ static LLViewerObject* sClickActionObject;
+};
+
+extern LLToolPie *gToolPie;
+
+#endif
diff --git a/indra/newview/lltoolpipette.cpp b/indra/newview/lltoolpipette.cpp
new file mode 100755
index 0000000000..8d4d58f40c
--- /dev/null
+++ b/indra/newview/lltoolpipette.cpp
@@ -0,0 +1,120 @@
+/**
+ * @file lltoolpipette.cpp
+ * @brief LLToolPipette class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * A tool to pick texture entry infro from objects in world (color/texture)
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolpipette.h"
+
+// Library includes
+
+// Viewer includes
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llselectmgr.h"
+#include "lltoolmgr.h"
+
+// Globals
+LLToolPipette *gToolPipette = NULL;
+
+//
+// Member functions
+//
+
+LLToolPipette::LLToolPipette()
+: LLTool("Pipette"),
+mSuccess(TRUE)
+{
+ mSelectCallback = NULL;
+ mUserData = NULL;
+}
+
+
+LLToolPipette::~LLToolPipette()
+{ }
+
+
+BOOL LLToolPipette::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mSuccess = TRUE;
+ mTooltipMsg.clear();
+ gPickFaces = TRUE;
+ setMouseCapture(TRUE);
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+BOOL LLToolPipette::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mSuccess = TRUE;
+ gSelectMgr->unhighlightAll();
+ //FIXME: this assumes the pipette tool is a transient tool
+ gToolMgr->clearTransientTool();
+ setMouseCapture(FALSE);
+ return TRUE;
+}
+
+BOOL LLToolPipette::handleHover(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->setCursor(mSuccess ? UI_CURSOR_PIPETTE : UI_CURSOR_NO);
+ if (hasMouseCapture()) // mouse button is down
+ {
+ gPickFaces = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLToolPipette::handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen)
+{
+ if (mTooltipMsg.empty())
+ {
+ return FALSE;
+ }
+ // keep tooltip message up when mouse in this part of screen
+ sticky_rect_screen->setCenterAndSize(x, y, 20, 20);
+ msg = mTooltipMsg;
+ return TRUE;
+}
+
+void LLToolPipette::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ gSelectMgr->unhighlightAll();
+
+ // if we clicked on a face of a valid prim, save off texture entry data
+ if (hit_obj &&
+ hit_obj->getPCode() == LL_PCODE_VOLUME &&
+ gLastHitObjectFace != -1)
+ {
+ //TODO: this should highlight the selected face only
+ gSelectMgr->highlightObjectOnly(hit_obj);
+ gToolPipette->mTextureEntry = *hit_obj->getTE(gLastHitObjectFace);
+ if (gToolPipette->mSelectCallback)
+ {
+ gToolPipette->mSelectCallback(gToolPipette->mTextureEntry, gToolPipette->mUserData);
+ }
+ }
+}
+
+void LLToolPipette::setSelectCallback(select_callback callback, void* user_data)
+{
+ mSelectCallback = callback;
+ mUserData = user_data;
+}
+
+void LLToolPipette::setResult(BOOL success, const LLString& msg)
+{
+ mTooltipMsg = msg;
+ mSuccess = success;
+}
diff --git a/indra/newview/lltoolpipette.h b/indra/newview/lltoolpipette.h
new file mode 100755
index 0000000000..532f75c3e7
--- /dev/null
+++ b/indra/newview/lltoolpipette.h
@@ -0,0 +1,47 @@
+/**
+ * @file lltoolpipette.h
+ * @brief LLToolPipette class header file
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A tool to pick texture entry infro from objects in world (color/texture)
+
+#ifndef LL_LLTOOLPIPETTE_H
+#define LL_LLTOOLPIPETTE_H
+
+#include "lltool.h"
+#include "lltextureentry.h"
+
+class LLViewerObject;
+
+class LLToolPipette
+: public LLTool
+{
+public:
+ LLToolPipette();
+ virtual ~LLToolPipette();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen);
+
+ typedef void (*select_callback)(const LLTextureEntry& te, void *data);
+ void setSelectCallback(select_callback callback, void* user_data);
+ void setResult(BOOL success, const LLString& msg);
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+ LLTextureEntry mTextureEntry;
+ select_callback mSelectCallback;
+ BOOL mSuccess;
+ LLString mTooltipMsg;
+ void* mUserData;
+};
+
+extern LLToolPipette *gToolPipette;
+
+#endif //LL_LLTOOLPIPETTE_H
diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp
new file mode 100644
index 0000000000..facfda2c85
--- /dev/null
+++ b/indra/newview/lltoolplacer.cpp
@@ -0,0 +1,225 @@
+/**
+ * @file lltoolplacer.cpp
+ * @brief Tool for placing new objects into the world
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// self header
+#include "lltoolplacer.h"
+
+// linden library headers
+#include "llprimitive.h"
+
+// viewer headers
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llui.h"
+
+//static
+LLPCode LLToolPlacer::sObjectType = LL_PCODE_CUBE;
+
+LLToolPlacer::LLToolPlacer()
+: LLTool( "Create" )
+{
+}
+
+// Used by the placer tool to add copies of the current selection.
+// Inspired by add_object(). JC
+BOOL add_duplicate(S32 x, S32 y)
+{
+ LLVector3 ray_start_region;
+ LLVector3 ray_end_region;
+ LLViewerRegion* regionp = NULL;
+ BOOL b_hit_land = FALSE;
+ S32 hit_face = -1;
+ LLViewerObject* hit_obj = NULL;
+ BOOL success = raycast_for_new_obj_pos( x, y, &hit_obj, &hit_face, &b_hit_land, &ray_start_region, &ray_end_region, &regionp );
+ if( !success )
+ {
+ make_ui_sound("UISndInvalidOp");
+ return FALSE;
+ }
+ if( hit_obj && (hit_obj->isAvatar() || hit_obj->isAttachment()) )
+ {
+ // Can't create objects on avatars or attachments
+ make_ui_sound("UISndInvalidOp");
+ return FALSE;
+ }
+
+
+ // Limit raycast to a single object.
+ // Speeds up server raycast + avoid problems with server ray hitting objects
+ // that were clipped by the near plane or culled on the viewer.
+ LLUUID ray_target_id;
+ if( hit_obj )
+ {
+ ray_target_id = hit_obj->getID();
+ }
+ else
+ {
+ ray_target_id.setNull();
+ }
+
+ gSelectMgr->selectDuplicateOnRay(ray_start_region,
+ ray_end_region,
+ b_hit_land, // suppress raycast
+ FALSE, // intersection
+ ray_target_id,
+ gSavedSettings.getBOOL("CreateToolCopyCenters"),
+ gSavedSettings.getBOOL("CreateToolCopyRotates"),
+ FALSE); // select copy
+
+ if (regionp
+ && (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX))
+ {
+ LLFirstUse::useSandbox();
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLToolPlacer::placeObject(S32 x, S32 y, MASK mask)
+{
+ BOOL added = TRUE;
+
+ if (gSavedSettings.getBOOL("CreateToolCopySelection"))
+ {
+ added = add_duplicate(x, y);
+ }
+ else
+ {
+ added = add_object( sObjectType, x, y, NO_PHYSICS );
+ }
+
+ // ...and go back to the default tool
+ if (added && !gSavedSettings.getBOOL("CreateToolKeepSelected"))
+ {
+ gCurrentToolset->selectTool( gToolTranslate );
+ }
+
+ return added;
+}
+
+BOOL LLToolPlacer::handleHover(S32 x, S32 y, MASK mask)
+{
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPlacer" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLCREATE);
+ return TRUE;
+}
+
+void LLToolPlacer::handleSelect()
+{
+ gFloaterTools->setStatusText("Click in the world to create, shift-click to select");
+}
+
+void LLToolPlacer::handleDeselect()
+{
+ gFloaterTools->setStatusText("");
+}
+
+//////////////////////////////////////////////////////
+// LLToolPlacerPanel
+
+// static
+LLPCode LLToolPlacerPanel::sCube = LL_PCODE_CUBE;
+LLPCode LLToolPlacerPanel::sPrism = LL_PCODE_PRISM;
+LLPCode LLToolPlacerPanel::sPyramid = LL_PCODE_PYRAMID;
+LLPCode LLToolPlacerPanel::sTetrahedron = LL_PCODE_TETRAHEDRON;
+LLPCode LLToolPlacerPanel::sCylinder = LL_PCODE_CYLINDER;
+LLPCode LLToolPlacerPanel::sCylinderHemi= LL_PCODE_CYLINDER_HEMI;
+LLPCode LLToolPlacerPanel::sCone = LL_PCODE_CONE;
+LLPCode LLToolPlacerPanel::sConeHemi = LL_PCODE_CONE_HEMI;
+LLPCode LLToolPlacerPanel::sTorus = LL_PCODE_TORUS;
+LLPCode LLToolPlacerPanel::sSquareTorus = LLViewerObject::LL_VO_SQUARE_TORUS;
+LLPCode LLToolPlacerPanel::sTriangleTorus = LLViewerObject::LL_VO_TRIANGLE_TORUS;
+LLPCode LLToolPlacerPanel::sSphere = LL_PCODE_SPHERE;
+LLPCode LLToolPlacerPanel::sSphereHemi = LL_PCODE_SPHERE_HEMI;
+LLPCode LLToolPlacerPanel::sTree = LL_PCODE_LEGACY_TREE;
+LLPCode LLToolPlacerPanel::sGrass = LL_PCODE_LEGACY_GRASS;
+LLPCode LLToolPlacerPanel::sTreeNew = LL_PCODE_TREE_NEW;
+
+S32 LLToolPlacerPanel::sButtonsAdded = 0;
+LLButton* LLToolPlacerPanel::sButtons[ TOOL_PLACER_NUM_BUTTONS ];
+
+LLToolPlacerPanel::LLToolPlacerPanel(const std::string& name, const LLRect& rect)
+ :
+ LLPanel( name, rect )
+{
+ /* DEPRECATED - JC
+ addButton( "UIImgCubeUUID", "UIImgCubeSelectedUUID", &LLToolPlacerPanel::sCube );
+ addButton( "UIImgPrismUUID", "UIImgPrismSelectedUUID", &LLToolPlacerPanel::sPrism );
+ addButton( "UIImgPyramidUUID", "UIImgPyramidSelectedUUID", &LLToolPlacerPanel::sPyramid );
+ addButton( "UIImgTetrahedronUUID", "UIImgTetrahedronSelectedUUID", &LLToolPlacerPanel::sTetrahedron );
+ addButton( "UIImgCylinderUUID", "UIImgCylinderSelectedUUID", &LLToolPlacerPanel::sCylinder );
+ addButton( "UIImgHalfCylinderUUID", "UIImgHalfCylinderSelectedUUID",&LLToolPlacerPanel::sCylinderHemi );
+ addButton( "UIImgConeUUID", "UIImgConeSelectedUUID", &LLToolPlacerPanel::sCone );
+ addButton( "UIImgHalfConeUUID", "UIImgHalfConeSelectedUUID", &LLToolPlacerPanel::sConeHemi );
+ addButton( "UIImgSphereUUID", "UIImgSphereSelectedUUID", &LLToolPlacerPanel::sSphere );
+ addButton( "UIImgHalfSphereUUID", "UIImgHalfSphereSelectedUUID", &LLToolPlacerPanel::sSphereHemi );
+ addButton( "UIImgTreeUUID", "UIImgTreeSelectedUUID", &LLToolPlacerPanel::sTree );
+ addButton( "UIImgGrassUUID", "UIImgGrassSelectedUUID", &LLToolPlacerPanel::sGrass );
+ addButton( "ObjectTorusImageID", "ObjectTorusActiveImageID", &LLToolPlacerPanel::sTorus );
+ addButton( "ObjectTubeImageID", "ObjectTubeActiveImageID", &LLToolPlacerPanel::sSquareTorus );
+ */
+}
+
+void LLToolPlacerPanel::addButton( const LLString& up_state, const LLString& down_state, LLPCode* pcode )
+{
+ const S32 TOOL_SIZE = 32;
+ const S32 HORIZ_SPACING = TOOL_SIZE + 5;
+ const S32 VERT_SPACING = TOOL_SIZE + 5;
+ const S32 VPAD = 10;
+ const S32 HPAD = 7;
+
+ S32 row = sButtonsAdded / 4;
+ S32 column = sButtonsAdded % 4;
+
+ LLRect help_rect = gSavedSettings.getRect("ToolHelpRect");
+
+ // Build the rectangle, recalling the origin is at lower left
+ // and we want the icons to build down from the top.
+ LLRect rect;
+ rect.setLeftTopAndSize(
+ HPAD + (column * HORIZ_SPACING),
+ help_rect.mBottom - VPAD - (row * VERT_SPACING),
+ TOOL_SIZE,
+ TOOL_SIZE );
+
+ LLButton* btn = new LLButton(
+ "ToolPlacerOptBtn",
+ rect,
+ up_state,
+ down_state,
+ "", &LLToolPlacerPanel::setObjectType,
+ pcode,
+ LLFontGL::sSansSerif);
+ btn->setFollowsBottom();
+ btn->setFollowsLeft();
+ addChild(btn);
+
+ sButtons[sButtonsAdded] = btn;
+ sButtonsAdded++;
+}
+
+// static
+void LLToolPlacerPanel::setObjectType( void* data )
+{
+ LLPCode pcode = *(LLPCode*) data;
+ LLToolPlacer::setObjectType( pcode );
+}
diff --git a/indra/newview/lltoolplacer.h b/indra/newview/lltoolplacer.h
new file mode 100644
index 0000000000..650fc03b01
--- /dev/null
+++ b/indra/newview/lltoolplacer.h
@@ -0,0 +1,79 @@
+/**
+ * @file lltoolplacer.h
+ * @brief Tool for placing new objects into the world
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLPLACER_H
+#define LL_TOOLPLACER_H
+
+#include "llprimitive.h"
+#include "llpanel.h"
+#include "lltool.h"
+
+class LLButton;
+
+////////////////////////////////////////////////////
+// LLToolPlacer
+
+class LLToolPlacer
+ : public LLTool
+{
+public:
+ LLToolPlacer();
+
+ virtual BOOL placeObject(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void handleSelect(); // do stuff when your tool is selected
+ virtual void handleDeselect(); // clean up when your tool is deselected
+
+ static void setObjectType( LLPCode type ) { sObjectType = type; }
+ static LLPCode getObjectType() { return sObjectType; }
+
+protected:
+ static LLPCode sObjectType;
+};
+
+////////////////////////////////////////////////////
+// LLToolPlacerPanel
+
+
+const S32 TOOL_PLACER_NUM_BUTTONS = 14;
+
+
+class LLToolPlacerPanel : public LLPanel
+{
+public:
+
+ LLToolPlacerPanel(const std::string& name, const LLRect& rect);
+
+ static void setObjectType( void* data );
+
+ static LLPCode sCube;
+ static LLPCode sPrism;
+ static LLPCode sPyramid;
+ static LLPCode sTetrahedron;
+ static LLPCode sCylinder;
+ static LLPCode sCylinderHemi;
+ static LLPCode sCone;
+ static LLPCode sConeHemi;
+ static LLPCode sTorus;
+ static LLPCode sSquareTorus;
+ static LLPCode sTriangleTorus;
+ static LLPCode sSphere;
+ static LLPCode sSphereHemi;
+ static LLPCode sTree;
+ static LLPCode sGrass;
+ static LLPCode sTreeNew;
+
+private:
+ void addButton( const LLString& up_state, const LLString& down_state, LLPCode* pcode );
+
+private:
+ static S32 sButtonsAdded;
+ static LLButton* sButtons[ TOOL_PLACER_NUM_BUTTONS ];
+};
+
+#endif
diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp
new file mode 100644
index 0000000000..a303776ade
--- /dev/null
+++ b/indra/newview/lltoolselect.cpp
@@ -0,0 +1,244 @@
+/**
+ * @file lltoolselect.cpp
+ * @brief LLToolSelect class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolselect.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llmanip.h"
+#include "llmenugl.h"
+#include "llselectmgr.h"
+#include "lltoolmgr.h"
+#include "llfloaterscriptdebug.h"
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "viewer.h" // for gFPSClamped, pie menus
+
+// Globals
+LLToolSelect *gToolSelect = NULL;
+extern BOOL gAllowSelectAvatar;
+
+const F32 SELECTION_ROTATION_TRESHOLD = 0.1f;
+
+LLToolSelect::LLToolSelect( LLToolComposite* composite )
+: LLTool( "Select", composite ),
+ mIgnoreGroup( FALSE )
+{
+ }
+
+// True if you selected an object.
+BOOL LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // didn't click in any UI object, so must have clicked in the world
+ LLViewerObject* object = NULL;
+
+ // You must hit the body for this tool to think you hit the object.
+ object = gObjectList.findObject( gLastHitObjectID );
+
+ if (object)
+ {
+ mSelectObjectID = object->getID();
+ handled = TRUE;
+ }
+ else
+ {
+ mSelectObjectID.setNull();
+ }
+
+ // Pass mousedown to agent
+ LLTool::handleMouseDown(x, y, mask);
+
+ return handled;
+}
+
+BOOL LLToolSelect::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ //RN: double click to toggle individual/linked picking???
+ return LLTool::handleDoubleClick(x, y, mask);
+}
+
+// static
+void LLToolSelect::handleObjectSelection(LLViewerObject *object, MASK mask, BOOL ignore_group, BOOL temp_select)
+{
+ BOOL select_owned = gSavedSettings.getBOOL("SelectOwnedOnly");
+ BOOL select_movable = gSavedSettings.getBOOL("SelectMovableOnly");
+
+ // *NOTE: These settings must be cleaned up at bottom of function.
+ if (temp_select || gAllowSelectAvatar)
+ {
+ gSavedSettings.setBOOL("SelectOwnedOnly", FALSE);
+ gSavedSettings.setBOOL("SelectMovableOnly", FALSE);
+ gSelectMgr->setForceSelection(TRUE);
+ }
+
+ BOOL extend_select = (mask == MASK_SHIFT) || (mask == MASK_CONTROL);
+
+ // If no object, check for icon, then just deselect
+ if (!object)
+ {
+ if (gLastHitHUDIcon && gLastHitHUDIcon->getSourceObject())
+ {
+ LLFloaterScriptDebug::show(gLastHitHUDIcon->getSourceObject()->getID());
+ }
+ else if (!extend_select)
+ {
+ gSelectMgr->deselectAll();
+ }
+ }
+ else
+ {
+ BOOL already_selected = object->isSelected();
+
+ if ( extend_select )
+ {
+ if ( already_selected )
+ {
+ if ( ignore_group )
+ {
+ gSelectMgr->deselectObjectOnly(object);
+ }
+ else
+ {
+ gSelectMgr->deselectObjectAndFamily(object);
+ }
+ }
+ else
+ {
+ if ( ignore_group )
+ {
+ gSelectMgr->selectObjectOnly(object, SELECT_ALL_TES);
+ }
+ else
+ {
+ gSelectMgr->selectObjectAndFamily(object);
+ }
+ }
+ }
+ else
+ {
+ // JC - Change behavior to make it easier to select children
+ // of linked sets. 9/3/2002
+ if( !already_selected || ignore_group)
+ {
+ // ...lose current selection in favor of just this object
+ gSelectMgr->deselectAll();
+ }
+
+ if ( ignore_group )
+ {
+ gSelectMgr->selectObjectOnly(object, SELECT_ALL_TES);
+ }
+ else
+ {
+ gSelectMgr->selectObjectAndFamily(object);
+ }
+ }
+
+ if (!gAgent.getFocusOnAvatar() && // if camera not glued to avatar
+ LLVOAvatar::findAvatarFromAttachment(object) != gAgent.getAvatarObject() && // and it's not one of your attachments
+ object != gAgent.getAvatarObject()) // and it's not you
+ {
+ // have avatar turn to face the selected object(s)
+ LLVector3d selection_center = gSelectMgr->getSelectionCenterGlobal();
+ selection_center = selection_center - gAgent.getPositionGlobal();
+ LLVector3 selection_dir;
+ selection_dir.setVec(selection_center);
+ selection_dir.mV[VZ] = 0.f;
+ selection_dir.normVec();
+ if (!object->isAvatar() && gAgent.getAtAxis() * selection_dir < 0.6f)
+ {
+ LLQuaternion target_rot;
+ target_rot.shortestArc(LLVector3::x_axis, selection_dir);
+ gAgent.startAutoPilotGlobal(gAgent.getPositionGlobal(), "", &target_rot, NULL, NULL, 1.f, SELECTION_ROTATION_TRESHOLD);
+ }
+ }
+
+ if (temp_select)
+ {
+ if (!already_selected)
+ {
+ LLViewerObject* root_object = (LLViewerObject*)object->getRootEdit();
+
+ // this is just a temporary selection
+ LLSelectNode* select_node = gSelectMgr->findSelectNode(root_object);
+ if (select_node)
+ {
+ select_node->setTransient(TRUE);
+ }
+
+ for (S32 i = 0; i < (S32)root_object->mChildList.size(); i++)
+ {
+ select_node = gSelectMgr->findSelectNode(root_object->mChildList[i]);
+ if (select_node)
+ {
+ select_node->setTransient(TRUE);
+ }
+ }
+
+ }
+ } //if(temp_select)
+ } //if(!object)
+
+ // Cleanup temp select settings above.
+ if (temp_select || gAllowSelectAvatar)
+ {
+ gSavedSettings.setBOOL("SelectOwnedOnly", select_owned);
+ gSavedSettings.setBOOL("SelectMovableOnly", select_movable);
+ gSelectMgr->setForceSelection(FALSE);
+ }
+}
+
+BOOL LLToolSelect::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mIgnoreGroup = !gSavedSettings.getBOOL("SelectLinkedSet");
+
+ LLViewerObject* object = gObjectList.findObject(mSelectObjectID);
+ LLToolSelect::handleObjectSelection(object, mask, mIgnoreGroup, FALSE);
+
+ return LLTool::handleMouseUp(x, y, mask);
+}
+
+void LLToolSelect::handleDeselect()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+}
+
+
+void LLToolSelect::stopEditing()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+}
+
+void LLToolSelect::onMouseCaptureLost()
+{
+ // Finish drag
+
+ gSelectMgr->enableSilhouette(TRUE);
+
+ // Clean up drag-specific variables
+ mIgnoreGroup = FALSE;
+}
+
+
diff --git a/indra/newview/lltoolselect.h b/indra/newview/lltoolselect.h
new file mode 100644
index 0000000000..db31494834
--- /dev/null
+++ b/indra/newview/lltoolselect.h
@@ -0,0 +1,39 @@
+/**
+ * @file lltoolselect.h
+ * @brief LLToolSelect class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLSELECT_H
+#define LL_TOOLSELECT_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "lluuid.h"
+
+class LLToolSelect : public LLTool
+{
+public:
+ LLToolSelect( LLToolComposite* composite );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ virtual void stopEditing();
+
+ static void handleObjectSelection(LLViewerObject *object, MASK mask, BOOL ignore_group, BOOL temp_select);
+
+ virtual void onMouseCaptureLost();
+ virtual void handleDeselect();
+
+protected:
+ BOOL mIgnoreGroup;
+ LLUUID mSelectObjectID;
+};
+
+extern LLToolSelect *gToolSelect;
+
+#endif // LL_TOOLSELECTION_H
diff --git a/indra/newview/lltoolselectland.cpp b/indra/newview/lltoolselectland.cpp
new file mode 100644
index 0000000000..7d51d4a81c
--- /dev/null
+++ b/indra/newview/lltoolselectland.cpp
@@ -0,0 +1,223 @@
+/**
+ * @file lltoolselectland.cpp
+ * @brief LLToolSelectLand class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolselectland.h"
+
+// indra includes
+#include "llparcel.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolview.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+// Globals
+LLToolSelectLand *gToolParcel = NULL;
+
+//
+// Member functions
+//
+
+LLToolSelectLand::LLToolSelectLand( )
+: LLTool( "Parcel" ),
+ mDragStartGlobal(),
+ mDragEndGlobal(),
+ mDragEndValid(FALSE),
+ mDragStartX(0),
+ mDragStartY(0),
+ mDragEndX(0),
+ mDragEndY(0),
+ mMouseOutsideSlop(FALSE),
+ mWestSouthBottom(),
+ mEastNorthTop(),
+ mLastShowParcelOwners(FALSE)
+{ }
+
+
+BOOL LLToolSelectLand::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL hit_land = gViewerWindow->mousePointOnLandGlobal(x, y, &mDragStartGlobal);
+ if (hit_land)
+ {
+ setMouseCapture( TRUE );
+
+ mDragStartX = x;
+ mDragStartY = y;
+ mDragEndX = x;
+ mDragEndY = y;
+
+ mDragEndValid = TRUE;
+ mDragEndGlobal = mDragStartGlobal;
+
+ sanitize_corners(mDragStartGlobal, mDragEndGlobal, mWestSouthBottom, mEastNorthTop);
+
+ mWestSouthBottom -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ mEastNorthTop += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+
+ roundXY(mWestSouthBottom);
+ roundXY(mEastNorthTop);
+
+ mMouseOutsideSlop = TRUE; //FALSE;
+
+ gParcelMgr->deselectLand();
+ }
+
+ return hit_land;
+}
+
+
+BOOL LLToolSelectLand::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ LLVector3d pos_global;
+ BOOL hit_land = gViewerWindow->mousePointOnLandGlobal(x, y, &pos_global);
+ if (hit_land)
+ {
+ // Auto-select this parcel
+ gParcelMgr->selectParcelAt( pos_global );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLToolSelectLand::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+
+ if (mMouseOutsideSlop && mDragEndValid)
+ {
+ // Take the drag start and end locations, then map the southwest
+ // point down to the next grid location, and the northeast point up
+ // to the next grid location.
+
+ sanitize_corners(mDragStartGlobal, mDragEndGlobal, mWestSouthBottom, mEastNorthTop);
+
+ mWestSouthBottom -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ mEastNorthTop += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+
+ roundXY(mWestSouthBottom);
+ roundXY(mEastNorthTop);
+
+ // Don't auto-select entire parcel.
+ gParcelMgr->selectLand( mWestSouthBottom, mEastNorthTop, FALSE );
+ }
+
+ mMouseOutsideSlop = FALSE;
+ mDragEndValid = FALSE;
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLToolSelectLand::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if (mMouseOutsideSlop || outsideSlop(x, y, mDragStartX, mDragStartY))
+ {
+ mMouseOutsideSlop = TRUE;
+
+ // Must do this every frame, in case the camera moved or the land moved
+ // since last frame.
+
+ // If doesn't hit land, doesn't change old value
+ LLVector3d land_global;
+ BOOL hit_land = gViewerWindow->mousePointOnLandGlobal(x, y, &land_global);
+ if (hit_land)
+ {
+ mDragEndValid = TRUE;
+ mDragEndGlobal = land_global;
+
+ sanitize_corners(mDragStartGlobal, mDragEndGlobal, mWestSouthBottom, mEastNorthTop);
+
+ mWestSouthBottom -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ mEastNorthTop += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+
+ roundXY(mWestSouthBottom);
+ roundXY(mEastNorthTop);
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (active, land)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ else
+ {
+ mDragEndValid = FALSE;
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (active, no land)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_NO);
+ }
+
+ mDragEndX = x;
+ mDragEndY = y;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (active, in slop)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (inactive)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+
+ return TRUE;
+}
+
+
+void LLToolSelectLand::render()
+{
+ if( hasMouseCapture() && /*mMouseOutsideSlop &&*/ mDragEndValid)
+ {
+ gParcelMgr->renderRect( mWestSouthBottom, mEastNorthTop );
+ }
+}
+
+void LLToolSelectLand::handleSelect()
+{
+ gFloaterTools->setStatusText("Click and drag to select land");
+ mLastShowParcelOwners = gSavedSettings.getBOOL("ShowParcelOwners");
+ gSavedSettings.setBOOL("ShowParcelOwners", TRUE);
+}
+
+
+void LLToolSelectLand::handleDeselect()
+{
+ gFloaterTools->setStatusText("");
+ //gParcelMgr->deselectLand();
+ gSavedSettings.setBOOL("ShowParcelOwners", mLastShowParcelOwners);
+}
+
+
+void LLToolSelectLand::roundXY(LLVector3d &vec)
+{
+ vec.mdV[VX] = llround( vec.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
+ vec.mdV[VY] = llround( vec.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );
+}
+
+
+// true if x,y outside small box around start_x,start_y
+BOOL LLToolSelectLand::outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy);
+}
diff --git a/indra/newview/lltoolselectland.h b/indra/newview/lltoolselectland.h
new file mode 100644
index 0000000000..9ae2b071b5
--- /dev/null
+++ b/indra/newview/lltoolselectland.h
@@ -0,0 +1,56 @@
+/**
+ * @file lltoolselectland.h
+ * @brief LLToolSelectLand class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLSELECTLAND_H
+#define LL_LLTOOLSELECTLAND_H
+
+#include "lltool.h"
+#include "v3dmath.h"
+
+class LLToolSelectLand
+: public LLTool
+{
+public:
+ LLToolSelectLand( );
+
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ void render(); // draw the select rectangle
+ /*virtual*/ BOOL isAlwaysRendered() { return TRUE; }
+
+ /*virtual*/ void handleSelect();
+ /*virtual*/ void handleDeselect();
+
+protected:
+ BOOL outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
+ void roundXY(LLVector3d& vec);
+
+protected:
+ LLVector3d mDragStartGlobal; // global coords
+ LLVector3d mDragEndGlobal; // global coords
+ BOOL mDragEndValid; // is drag end a valid point in the world?
+
+ S32 mDragStartX; // screen coords, from left
+ S32 mDragStartY; // screen coords, from bottom
+
+ S32 mDragEndX;
+ S32 mDragEndY;
+
+ BOOL mMouseOutsideSlop; // has mouse ever gone outside slop region?
+
+ LLVector3d mWestSouthBottom; // global coords, from drag
+ LLVector3d mEastNorthTop; // global coords, from drag
+
+ BOOL mLastShowParcelOwners; // store last Show Parcel Owners setting
+};
+
+extern LLToolSelectLand *gToolParcel;
+
+#endif
diff --git a/indra/newview/lltoolselectrect.cpp b/indra/newview/lltoolselectrect.cpp
new file mode 100644
index 0000000000..b9c43d4221
--- /dev/null
+++ b/indra/newview/lltoolselectrect.cpp
@@ -0,0 +1,182 @@
+/**
+ * @file lltoolselectrect.cpp
+ * @brief A tool to select multiple objects with a screen-space rectangle.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolselectrect.h"
+
+// Library includes
+#include "llgl.h"
+#include "lldarray.h"
+
+// Viewer includes
+#include "llviewercontrol.h"
+#include "llui.h"
+#include "llselectmgr.h"
+#include "lltoolview.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llviewercamera.h"
+#include "viewer.h"
+
+#include "llglheaders.h"
+
+// Globals
+const S32 SLOP_RADIUS = 5;
+
+
+//
+// Member functions
+//
+
+LLToolSelectRect::LLToolSelectRect( LLToolComposite* composite )
+ :
+ LLToolSelect( composite ),
+ mDragStartX(0),
+ mDragStartY(0),
+ mDragEndX(0),
+ mDragEndY(0),
+ mDragLastWidth(0),
+ mDragLastHeight(0),
+ mMouseOutsideSlop(FALSE)
+
+{ }
+
+
+void dialog_refresh_all(void);
+
+BOOL LLToolSelectRect::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // start dragging rectangle
+ setMouseCapture( TRUE );
+
+ mDragStartX = x;
+ mDragStartY = y;
+ mDragEndX = x;
+ mDragEndY = y;
+
+ mMouseOutsideSlop = FALSE;
+
+ LLToolSelect::handleMouseDown(x, y, mask);
+ return TRUE;
+}
+
+
+BOOL LLToolSelectRect::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ setMouseCapture( FALSE );
+
+ if( mMouseOutsideSlop )
+ {
+ mDragLastWidth = 0;
+ mDragLastHeight = 0;
+
+ mMouseOutsideSlop = FALSE;
+
+ if (mask == MASK_CONTROL)
+ {
+ gSelectMgr->deselectHighlightedObjects();
+ }
+ else
+ {
+ gSelectMgr->selectHighlightedObjects();
+ }
+ return TRUE;
+ }
+ else
+ {
+ return LLToolSelect::handleMouseUp(x, y, mask);
+ }
+}
+
+
+BOOL LLToolSelectRect::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if (mMouseOutsideSlop || outsideSlop(x, y, mDragStartX, mDragStartY))
+ {
+ if (!mMouseOutsideSlop && !(mask & MASK_SHIFT) && !(mask & MASK_CONTROL))
+ {
+ // just started rect select, and not adding to current selection
+ gSelectMgr->deselectAll();
+ }
+ mMouseOutsideSlop = TRUE;
+ mDragEndX = x;
+ mDragEndY = y;
+
+ handleRectangleSelection(x, y, mask);
+ }
+ else
+ {
+ return LLToolSelect::handleHover(x, y, mask);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectRect (active)" << llendl;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectRect (inactive)" << llendl;
+ }
+
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ return TRUE;
+}
+
+
+void LLToolSelectRect::draw()
+{
+ if( hasMouseCapture() && mMouseOutsideSlop)
+ {
+ if (gKeyboard->currentMask(TRUE) == MASK_CONTROL)
+ {
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ glColor4f(1.f, 1.f, 0.f, 1.f);
+ }
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(
+ llmin(mDragStartX, mDragEndX),
+ llmax(mDragStartY, mDragEndY),
+ llmax(mDragStartX, mDragEndX),
+ llmin(mDragStartY, mDragEndY),
+ FALSE);
+ if (gKeyboard->currentMask(TRUE) == MASK_CONTROL)
+ {
+ glColor4f(1.f, 0.f, 0.f, 0.1f);
+ }
+ else
+ {
+ glColor4f(1.f, 1.f, 0.f, 0.1f);
+ }
+ gl_rect_2d(
+ llmin(mDragStartX, mDragEndX),
+ llmax(mDragStartY, mDragEndY),
+ llmax(mDragStartX, mDragEndX),
+ llmin(mDragStartY, mDragEndY));
+ }
+}
+
+// true if x,y outside small box around start_x,start_y
+BOOL LLToolSelectRect::outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -SLOP_RADIUS || SLOP_RADIUS <= dx || dy <= -SLOP_RADIUS || SLOP_RADIUS <= dy);
+}
+
+
+//
+// Static functions
+//
diff --git a/indra/newview/lltoolselectrect.h b/indra/newview/lltoolselectrect.h
new file mode 100644
index 0000000000..fb6355460e
--- /dev/null
+++ b/indra/newview/lltoolselectrect.h
@@ -0,0 +1,44 @@
+/**
+ * @file lltoolselectrect.h
+ * @brief A tool to select multiple objects with a screen-space rectangle.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLSELECTRECT_H
+#define LL_LLTOOLSELECTRECT_H
+
+#include "lltool.h"
+#include "lltoolselect.h"
+
+class LLToolSelectRect
+: public LLToolSelect
+{
+public:
+ LLToolSelectRect( LLToolComposite* composite );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void draw(); // draw the select rectangle
+
+protected:
+ void handleRectangleSelection(S32 x, S32 y, MASK mask); // true if you selected one
+ BOOL outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
+
+protected:
+ S32 mDragStartX; // screen coords, from left
+ S32 mDragStartY; // screen coords, from bottom
+
+ S32 mDragEndX;
+ S32 mDragEndY;
+
+ S32 mDragLastWidth;
+ S32 mDragLastHeight;
+
+ BOOL mMouseOutsideSlop; // has mouse ever gone outside slop region?
+};
+
+
+#endif
diff --git a/indra/newview/lltoolview.cpp b/indra/newview/lltoolview.cpp
new file mode 100644
index 0000000000..49e5896ab7
--- /dev/null
+++ b/indra/newview/lltoolview.cpp
@@ -0,0 +1,175 @@
+/**
+ * @file lltoolview.cpp
+ * @brief A UI contains for tool palette tools
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolview.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llpanel.h"
+#include "lltool.h"
+#include "lltoolmgr.h"
+#include "lltextbox.h"
+#include "llresmgr.h"
+
+
+LLToolContainer::LLToolContainer(LLToolView* parent)
+: mParent(parent),
+ mButton(NULL),
+ mPanel(NULL),
+ mTool(NULL)
+{ }
+
+
+LLToolContainer::~LLToolContainer()
+{
+ // mParent is a pointer to the tool view
+ // mButton is owned by the tool view
+ // mPanel is owned by the tool view
+ delete mTool;
+ mTool = NULL;
+}
+
+
+LLToolView::LLToolView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, MOUSE_OPAQUE),
+ mButtonCount(0)
+{
+}
+
+
+LLToolView::~LLToolView()
+{
+ mContainList.deleteAllData();
+}
+
+//XUI: translate
+void LLToolView::addTool(const LLString& icon_off, const LLString& icon_on, LLPanel* panel, LLTool* tool, LLView* hoverView, const char* label)
+{
+ llassert(tool);
+
+ LLToolContainer* contain = new LLToolContainer(this);
+
+ LLRect btn_rect = getButtonRect(mButtonCount);
+
+ contain->mButton = new LLButton("ToolBtn",
+ btn_rect,
+ icon_off,
+ icon_on,
+ "",
+ &LLToolView::onClickToolButton,
+ contain,
+ LLFontGL::sSansSerif);
+
+ contain->mPanel = panel;
+ contain->mTool = tool;
+
+ addChild(contain->mButton);
+ mButtonCount++;
+
+ const S32 LABEL_TOP_SPACING = 0;
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ S32 label_width = font->getWidth( label );
+ LLRect label_rect;
+ label_rect.setLeftTopAndSize(
+ btn_rect.mLeft + btn_rect.getWidth() / 2 - label_width / 2,
+ btn_rect.mBottom - LABEL_TOP_SPACING,
+ label_width,
+ llfloor(font->getLineHeight()));
+ addChild( new LLTextBox( "tool label", label_rect, label, font ) );
+
+ // Can optionally ignore panel
+ if (contain->mPanel)
+ {
+ contain->mPanel->setBackgroundVisible( FALSE );
+ contain->mPanel->setBorderVisible( FALSE );
+ addChild(contain->mPanel);
+ }
+
+ mContainList.addData(contain);
+}
+
+
+LLRect LLToolView::getButtonRect(S32 button_index)
+{
+ const S32 HPAD = 7;
+ const S32 VPAD = 7;
+ const S32 TOOL_SIZE = 32;
+ const S32 HORIZ_SPACING = TOOL_SIZE + 5;
+ const S32 VERT_SPACING = TOOL_SIZE + 14;
+
+ S32 tools_per_row = mRect.getWidth() / HORIZ_SPACING;
+
+ S32 row = button_index / tools_per_row;
+ S32 column = button_index % tools_per_row;
+
+ // Build the rectangle, recalling the origin is at lower left
+ // and we want the icons to build down from the top.
+ LLRect rect;
+ rect.setLeftTopAndSize( HPAD + (column * HORIZ_SPACING),
+ -VPAD + getRect().getHeight() - (row * VERT_SPACING),
+ TOOL_SIZE,
+ TOOL_SIZE
+ );
+
+ return rect;
+}
+
+
+void LLToolView::draw()
+{
+ // turn off highlighting for all containers
+ // and hide all option panels except for the selected one.
+ LLTool* selected = gCurrentToolset->getSelectedTool();
+ for( LLToolContainer* contain = mContainList.getFirstData();
+ contain != NULL;
+ contain = mContainList.getNextData()
+ )
+ {
+ BOOL state = (contain->mTool == selected);
+ contain->mButton->setToggleState( state );
+ if (contain->mPanel)
+ {
+ contain->mPanel->setVisible( state );
+ }
+ }
+
+ // Draw children normally
+ LLView::draw();
+}
+
+// protected
+LLToolContainer* LLToolView::findToolContainer( LLTool *tool )
+{
+ // Find the container for this tool
+ llassert( tool );
+ for( LLToolContainer* contain = mContainList.getFirstData(); contain; contain = mContainList.getNextData() )
+ {
+ if( contain->mTool == tool )
+ {
+ return contain;
+ }
+ }
+ llerrs << "LLToolView::findToolContainer - tool not found" << llendl;
+ return NULL;
+}
+
+// static
+void LLToolView::onClickToolButton(void* userdata)
+{
+ LLToolContainer* clicked = (LLToolContainer*) userdata;
+
+ // Switch to this one
+ gCurrentToolset->selectTool( clicked->mTool );
+ gToolMgr->useSelectedTool( gCurrentToolset );
+}
+
diff --git a/indra/newview/lltoolview.h b/indra/newview/lltoolview.h
new file mode 100644
index 0000000000..7c7cb581dd
--- /dev/null
+++ b/indra/newview/lltoolview.h
@@ -0,0 +1,69 @@
+/**
+ * @file lltoolview.h
+ * @brief UI container for tools.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLVIEW_H
+#define LL_LLTOOLVIEW_H
+
+// requires stdtypes.h
+#include "linked_lists.h"
+#include "llpanel.h"
+
+// forward declares
+class LLTool;
+class LLButton;
+
+class LLToolView;
+
+
+// Utility class, container for the package of information we need about
+// each tool. The package can either point directly to a tool, or indirectly
+// to another view of tools.
+class LLToolContainer
+{
+public:
+ LLToolContainer(LLToolView* parent);
+ ~LLToolContainer();
+
+public:
+ LLToolView* mParent; // toolview that owns this container
+ LLButton* mButton;
+ LLPanel* mPanel;
+ LLTool* mTool; // if not NULL, this is a tool ref
+};
+
+
+// A view containing automatically arranged button icons representing
+// tools. The icons sit on top of panels containing options for each
+// tool.
+class LLToolView
+: public LLView
+{
+public:
+ LLToolView(const std::string& name, const LLRect& rect);
+ ~LLToolView();
+
+ virtual void draw(); // handle juggling tool button highlights, panel visibility
+
+ static void onClickToolButton(void* container);
+
+ void addTool(const LLString& icon_off, const LLString& icon_on, LLPanel* panel, LLTool* tool, LLView* hoverView, const char* label);
+
+ LLView* getCurrentHoverView();
+
+private:
+ LLRect getButtonRect(S32 button_index); // return rect for button to add, zero-based index
+ LLToolContainer *findToolContainer(LLTool *tool);
+
+
+private:
+ LLLinkedList
+ <LLToolContainer> mContainList;
+ S32 mButtonCount; // used to compute rectangles
+};
+
+#endif
diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp
new file mode 100644
index 0000000000..10641df4c3
--- /dev/null
+++ b/indra/newview/lltracker.cpp
@@ -0,0 +1,801 @@
+/**
+ * @file lltracker.cpp
+ * @brief Container for objects user is tracking.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// library includes
+#include "llcoord.h"
+#include "lldarray.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llinventory.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4color.h"
+
+// viewer includes
+#include "viewer.h"
+#include "lltracker.h"
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llcylinder.h"
+#include "llfloaterworldmap.h"
+#include "llhudtext.h"
+#include "llhudview.h"
+#include "llinventorymodel.h"
+#include "lllandmarklist.h"
+#include "llsky.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerinventory.h"
+#include "llworld.h"
+#include "llworldmapview.h"
+
+const F32 DESTINATION_REACHED_RADIUS = 3.0f;
+const F32 DESTINATION_VISITED_RADIUS = 6.0f;
+
+// this last one is useful for when the landmark is
+// very close to agent when tracking is turned on
+const F32 DESTINATION_UNVISITED_RADIUS = 12.0f;
+
+const S32 ARROW_OFF_RADIUS_SQRD = 100;
+
+const S32 HUD_ARROW_SIZE = 32;
+
+// static
+LLTracker *LLTracker::sTrackerp = NULL;
+BOOL LLTracker::sCheesyBeacon = FALSE;
+
+LLTracker::LLTracker()
+: mTrackingStatus(TRACKING_NOTHING),
+ mTrackingLocationType(LOCATION_NOTHING),
+ mHUDArrowCenterX(0),
+ mHUDArrowCenterY(0),
+ mToolTip( "" ),
+ mTrackedLandmarkName(""),
+ mHasReachedLandmark(FALSE),
+ mHasLandmarkPosition(FALSE),
+ mLandmarkHasBeenVisited(FALSE),
+ mTrackedLocationName( "" ),
+ mIsTrackingLocation(FALSE)
+{ }
+
+
+LLTracker::~LLTracker()
+{
+ purgeBeaconText();
+}
+
+
+// static
+void LLTracker::stopTracking(void* userdata)
+{
+ BOOL clear_ui = ((BOOL)(intptr_t)userdata);
+ instance()->stopTrackingAll(clear_ui);
+}
+
+
+// static virtual
+void LLTracker::drawHUDArrow()
+{
+ /* tracking autopilot destination has been disabled
+ -- 2004.01.09, Leviathan
+ // Draw dot for autopilot target
+ if (gAgent.getAutoPilot())
+ {
+ instance()->drawMarker( gAgent.getAutoPilotTargetGlobal(), gTrackColor );
+ return;
+ }
+ */
+ switch (getTrackingStatus())
+ {
+ case TRACKING_AVATAR:
+ // Tracked avatar
+ if(LLAvatarTracker::instance().haveTrackingInfo())
+ {
+ instance()->drawMarker( LLAvatarTracker::instance().getGlobalPos(), gTrackColor );
+ }
+ break;
+
+ case TRACKING_LANDMARK:
+ instance()->drawMarker( getTrackedPositionGlobal(), gTrackColor );
+ break;
+
+ case TRACKING_LOCATION:
+ if (!gWorldp)
+ {
+ break;
+ }
+ // HACK -- try to keep the location just above the terrain
+#if 0
+ // UNHACKED by CRO - keep location where the location is
+ instance()->mTrackedPositionGlobal.mdV[VZ] =
+ 0.9f * instance()->mTrackedPositionGlobal.mdV[VZ]
+ + 0.1f * (gWorldp->resolveLandHeightGlobal(getTrackedPositionGlobal()) + 1.5f);
+#endif
+ instance()->mTrackedPositionGlobal.mdV[VZ] = llclamp((F32)instance()->mTrackedPositionGlobal.mdV[VZ], gWorldp->resolveLandHeightGlobal(getTrackedPositionGlobal()) + 1.5f, (F32)instance()->getTrackedPositionGlobal().mdV[VZ]);
+ instance()->drawMarker( getTrackedPositionGlobal(), gTrackColor );
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+// static
+void LLTracker::render3D()
+{
+ if (!gFloaterWorldMap)
+ {
+ return;
+ }
+
+ // Arbitary location beacon
+ if( instance()->mIsTrackingLocation )
+ {
+ if (!instance()->mBeaconText)
+ {
+ instance()->mBeaconText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ instance()->mBeaconText->setDoFade(FALSE);
+ }
+
+ LLVector3d pos_global = instance()->mTrackedPositionGlobal;
+ // (z-attenuation < 1) means compute "shorter" distance in z-axis,
+ // so cancel tracking even if avatar is a little above or below.
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(pos_global, 0.5f);
+ if (dist < DESTINATION_REACHED_RADIUS)
+ {
+ instance()->stopTrackingLocation();
+ }
+ else
+ {
+ renderBeacon( instance()->mTrackedPositionGlobal, gTrackColor,
+ instance()->mBeaconText, instance()->mTrackedLocationName );
+ }
+ }
+
+ // Landmark beacon
+ else if( !instance()->mTrackedLandmarkAssetID.isNull() )
+ {
+ if (!instance()->mBeaconText)
+ {
+ instance()->mBeaconText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ instance()->mBeaconText->setDoFade(FALSE);
+ }
+
+ if (instance()->mHasLandmarkPosition)
+ {
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(instance()->mTrackedPositionGlobal, 1.0f);
+
+ if ( !instance()->mLandmarkHasBeenVisited
+ && dist < DESTINATION_VISITED_RADIUS )
+ {
+ // its close enough ==> flag as visited
+ instance()->setLandmarkVisited();
+ }
+
+ if ( !instance()->mHasReachedLandmark
+ && dist < DESTINATION_REACHED_RADIUS )
+ {
+ // its VERY CLOSE ==> automatically stop tracking
+ instance()->stopTrackingLandmark();
+ }
+ else
+ {
+ if ( instance()->mHasReachedLandmark
+ && dist > DESTINATION_UNVISITED_RADIUS )
+ {
+ // this is so that landmark beacons don't immediately
+ // disappear when they're created only a few meters
+ // away, yet disappear when the agent wanders away
+ // and back again
+ instance()->mHasReachedLandmark = FALSE;
+ }
+ renderBeacon( instance()->mTrackedPositionGlobal, gTrackColor,
+ instance()->mBeaconText, instance()->mTrackedLandmarkName );
+ }
+ }
+ else
+ {
+ // probably just finished downloading the asset
+ instance()->cacheLandmarkPosition();
+ }
+ }
+ else
+ {
+ // Avatar beacon
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if(av_tracker.haveTrackingInfo())
+ {
+ if (!instance()->mBeaconText)
+ {
+ instance()->mBeaconText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ instance()->mBeaconText->setDoFade(FALSE);
+ }
+
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(instance()->mTrackedPositionGlobal, 0.0f);
+ if (dist < DESTINATION_REACHED_RADIUS)
+ {
+ instance()->stopTrackingAvatar();
+ }
+ else
+ {
+ renderBeacon( av_tracker.getGlobalPos(), gTrackColor,
+ instance()->mBeaconText, av_tracker.getName() );
+ }
+ }
+ else
+ {
+ BOOL stop_tracking = FALSE;
+ const LLUUID& avatar_id = av_tracker.getAvatarID();
+ if(avatar_id.isNull())
+ {
+ stop_tracking = TRUE;
+ }
+ else
+ {
+ const LLRelationship* buddy = av_tracker.getBuddyInfo(avatar_id);
+ if(buddy && !buddy->isOnline())
+ {
+ stop_tracking = TRUE;
+ }
+ if(!buddy && !gAgent.isGodlike())
+ {
+ stop_tracking = TRUE;
+ }
+ }
+ if(stop_tracking)
+ {
+ instance()->stopTrackingAvatar();
+ }
+ }
+ }
+}
+
+
+// static
+void LLTracker::trackAvatar( const LLUUID& avatar_id, const LLString& name )
+{
+ instance()->stopTrackingLandmark();
+ instance()->stopTrackingLocation();
+
+ LLAvatarTracker::instance().track( avatar_id, name );
+ instance()->mTrackingStatus = TRACKING_AVATAR;
+ instance()->mLabel = name;
+}
+
+
+// static
+void LLTracker::trackLandmark( const LLUUID& asset_id, const LLUUID& item_id, const LLString& name)
+{
+ instance()->stopTrackingAvatar();
+ instance()->stopTrackingLocation();
+
+ instance()->mTrackedLandmarkAssetID = asset_id;
+ instance()->mTrackedLandmarkItemID = item_id;
+ instance()->mTrackedLandmarkName = name;
+ instance()->cacheLandmarkPosition();
+ instance()->mTrackingStatus = TRACKING_LANDMARK;
+ instance()->mLabel = name;
+}
+
+
+// static
+void LLTracker::trackLocation(const LLVector3d& pos_global, const LLString& full_name, const LLString& tooltip, ETrackingLocationType location_type)
+{
+ instance()->stopTrackingAvatar();
+ instance()->stopTrackingLandmark();
+
+ instance()->mTrackedPositionGlobal = pos_global;
+ instance()->mTrackedLocationName = full_name;
+ instance()->mIsTrackingLocation = TRUE;
+ instance()->mTrackingStatus = TRACKING_LOCATION;
+ instance()->mTrackingLocationType = location_type;
+ instance()->mLabel = full_name;
+ instance()->mToolTip = tooltip;
+}
+
+
+// static
+BOOL LLTracker::handleMouseDown(S32 x, S32 y)
+{
+ BOOL eat_mouse_click = FALSE;
+ // fortunately, we can always compute the tracking arrow center
+ S32 dist_sqrd = (x - instance()->mHUDArrowCenterX) * (x - instance()->mHUDArrowCenterX) +
+ (y - instance()->mHUDArrowCenterY) * (y - instance()->mHUDArrowCenterY);
+ if (dist_sqrd < ARROW_OFF_RADIUS_SQRD)
+ {
+ /* tracking autopilot destination has been disabled
+ -- 2004.01.09, Leviathan
+ // turn off tracking
+ if (gAgent.getAutoPilot())
+ {
+ gAgent.stopAutoPilot(TRUE); // TRUE because cancelled by user
+ eat_mouse_click = TRUE;
+ }
+ */
+ if (getTrackingStatus())
+ {
+ instance()->stopTrackingAll();
+ eat_mouse_click = TRUE;
+ }
+ }
+ return eat_mouse_click;
+}
+
+
+// static
+LLVector3d LLTracker::getTrackedPositionGlobal()
+{
+ LLVector3d pos_global;
+ switch (getTrackingStatus())
+ {
+ case TRACKING_AVATAR:
+ {
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if (av_tracker.haveTrackingInfo())
+ {
+ pos_global = av_tracker.getGlobalPos(); }
+ break;
+ }
+ case TRACKING_LANDMARK:
+ if( instance()->mHasLandmarkPosition )
+ {
+ pos_global = instance()->mTrackedPositionGlobal;
+ }
+ break;
+ case TRACKING_LOCATION:
+ pos_global = instance()->mTrackedPositionGlobal;
+ break;
+ default:
+ break;
+ }
+ return pos_global;
+}
+
+
+// static
+BOOL LLTracker::hasLandmarkPosition()
+{
+ if (!instance()->mHasLandmarkPosition)
+ {
+ // maybe we just received the landmark position info
+ instance()->cacheLandmarkPosition();
+ }
+ return instance()->mHasLandmarkPosition;
+}
+
+
+// static
+const LLString& LLTracker::getTrackedLocationName()
+{
+ return instance()->mTrackedLocationName;
+}
+
+F32 pulse_func(F32 t, F32 z)
+{
+ if (!LLTracker::sCheesyBeacon)
+ {
+ return 0.f;
+ }
+
+ t *= 3.14159f;
+ z -= t*64.f - 256.f;
+
+ F32 a = cosf(z*3.14159/512.f)*10.0f;
+ a = llmax(a, 9.9f);
+ a -= 9.9f;
+ a *= 10.f;
+ return a;
+}
+
+void draw_shockwave(F32 center_z, F32 t, S32 steps, LLColor4 color)
+{
+ if (!LLTracker::sCheesyBeacon)
+ {
+ return;
+ }
+
+ t *= 0.6284f/3.14159f;
+
+ t -= (F32) (S32) t;
+
+ t = llmax(t, 0.5f);
+ t -= 0.5f;
+ t *= 2.0f;
+
+ F32 radius = t*16536.f;
+
+ // Inexact, but reasonably fast.
+ F32 delta = F_TWO_PI / steps;
+ F32 sin_delta = sin( delta );
+ F32 cos_delta = cos( delta );
+ F32 x = radius;
+ F32 y = 0.f;
+
+ LLColor4 ccol = LLColor4(1,1,1,(1.f-t)*0.25f);
+ glBegin(GL_TRIANGLE_FAN);
+ glColor4fv(ccol.mV);
+ glVertex3f(0.f, 0.f, center_z);
+ // make sure circle is complete
+ steps += 1;
+
+ color.mV[3] = (1.f-t*t);
+
+ glColor4fv(color.mV);
+ while( steps-- )
+ {
+ // Successive rotations
+ glVertex3f( x, y, center_z );
+ F32 x_new = x * cos_delta - y * sin_delta;
+ y = x * sin_delta + y * cos_delta;
+ x = x_new;
+ }
+ glEnd();
+}
+
+
+// static
+void LLTracker::renderBeacon(LLVector3d pos_global,
+ const LLColor4& color,
+ LLHUDText* hud_textp,
+ const std::string& label )
+{
+ sCheesyBeacon = gSavedSettings.getBOOL("CheesyBeacon");
+ LLVector3d to_vec = pos_global - gAgent.getCameraPositionGlobal();
+
+ F32 dist = (F32)to_vec.magVec();
+ F32 color_frac = 1.f;
+ if (dist > 0.99f * gCamera->getFar())
+ {
+ color_frac = 0.4f;
+ // pos_global = gAgent.getCameraPositionGlobal() + 0.99f*(gCamera->getFar()/dist)*to_vec;
+ }
+ else
+ {
+ color_frac = 1.f - 0.6f*(dist/gCamera->getFar());
+ }
+
+ LLColor4 fogged_color = color_frac * color + (1 - color_frac)*gSky.getFogColor();
+
+ F32 FADE_DIST = 3.f;
+ fogged_color.mV[3] = llmax(0.2f, llmin(0.5f,(dist-FADE_DIST)/FADE_DIST));
+
+ LLVector3 pos_agent = gAgent.getPosAgentFromGlobal(pos_global);
+
+ LLGLSTracker gls_tracker; // default - TEXTURE + CULL_FACE + LIGHTING + GL_BLEND + GL_ALPHA_TEST
+ LLGLDisable cull_face(GL_CULL_FACE);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef(pos_agent.mV[0], pos_agent.mV[1], pos_agent.mV[2]);
+
+ draw_shockwave(1024.f, gRenderStartTime.getElapsedTimeF32(), 32, fogged_color);
+
+ //glScalef(1.f, 1.f, 1000.f);
+ glColor4fv(fogged_color.mV);
+ const U32 BEACON_VERTS = 256;
+ const F32 step = 1024.0f/BEACON_VERTS;
+
+ LLVector3 x_axis = gCamera->getLeftAxis();
+ F32 t = gRenderStartTime.getElapsedTimeF32();
+ F32 dr = dist/gCamera->getFar();
+
+ for (U32 i = 0; i < BEACON_VERTS; i++)
+ {
+ F32 x = x_axis.mV[0];
+ F32 y = x_axis.mV[1];
+
+ F32 z = i * step;
+ F32 z_next = (i+1)*step;
+
+ F32 a = pulse_func(t, z);
+ F32 an = pulse_func(t, z_next);
+
+ LLColor4 c_col = fogged_color + LLColor4(a,a,a,a);
+ LLColor4 col_next = fogged_color + LLColor4(an,an,an,an);
+ LLColor4 col_edge = fogged_color * LLColor4(a,a,a,0.0f);
+ LLColor4 col_edge_next = fogged_color * LLColor4(an,an,an,0.0f);
+
+ a *= 2.f;
+ a += 1.0f+dr;
+
+ an *= 2.f;
+ an += 1.0f+dr;
+
+ glBegin(GL_TRIANGLE_STRIP);
+ glColor4fv(col_edge.mV);
+ glVertex3f(-x*a, -y*a, z);
+ glColor4fv(col_edge_next.mV);
+ glVertex3f(-x*an, -y*an, z_next);
+
+ glColor4fv(c_col.mV);
+ glVertex3f(0, 0, z);
+ glColor4fv(col_next.mV);
+ glVertex3f(0, 0, z_next);
+
+ glColor4fv(col_edge.mV);
+ glVertex3f(x*a,y*a,z);
+ glColor4fv(col_edge_next.mV);
+ glVertex3f(x*an,y*an,z_next);
+
+ glEnd();
+ }
+
+ //gCylinder.render(1000);
+ glPopMatrix();
+
+ char text[1024];
+ sprintf(text, "%.0f m", to_vec.magVec());
+
+ LLWString wstr;
+ wstr += utf8str_to_wstring(label);
+ wstr += '\n';
+ wstr += utf8str_to_wstring(text);
+
+ hud_textp->setFont(LLFontGL::sSansSerif);
+ hud_textp->setZCompare(FALSE);
+ hud_textp->setColor(LLColor4(1.f, 1.f, 1.f, llmax(0.2f, llmin(1.f,(dist-FADE_DIST)/FADE_DIST))));
+
+ hud_textp->setString(wstr);
+ hud_textp->setVertAlignment(LLHUDText::ALIGN_VERT_CENTER);
+ hud_textp->setPositionAgent(pos_agent);
+}
+
+
+void LLTracker::stopTrackingAll(BOOL clear_ui)
+{
+ switch (mTrackingStatus)
+ {
+ case TRACKING_AVATAR :
+ stopTrackingAvatar(clear_ui);
+ break;
+ case TRACKING_LANDMARK :
+ stopTrackingLandmark(clear_ui);
+ break;
+ case TRACKING_LOCATION :
+ stopTrackingLocation(clear_ui);
+ break;
+ default:
+ mTrackingStatus = TRACKING_NOTHING;
+ break;
+ }
+}
+
+
+void LLTracker::stopTrackingAvatar(BOOL clear_ui)
+{
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if( !av_tracker.getAvatarID().isNull() )
+ {
+ av_tracker.untrack( av_tracker.getAvatarID() );
+ }
+
+ purgeBeaconText();
+ gFloaterWorldMap->clearAvatarSelection(clear_ui);
+ mTrackingStatus = TRACKING_NOTHING;
+}
+
+
+void LLTracker::stopTrackingLandmark(BOOL clear_ui)
+{
+ purgeBeaconText();
+ mTrackedLandmarkAssetID.setNull();
+ mTrackedLandmarkItemID.setNull();
+ mTrackedLandmarkName.assign("");
+ mTrackedPositionGlobal.zeroVec();
+ mHasLandmarkPosition = FALSE;
+ mHasReachedLandmark = FALSE;
+ mLandmarkHasBeenVisited = TRUE;
+ gFloaterWorldMap->clearLandmarkSelection(clear_ui);
+ mTrackingStatus = TRACKING_NOTHING;
+}
+
+
+void LLTracker::stopTrackingLocation(BOOL clear_ui)
+{
+ purgeBeaconText();
+ mTrackedLocationName.assign("");
+ mIsTrackingLocation = FALSE;
+ mTrackedPositionGlobal.zeroVec();
+ gFloaterWorldMap->clearLocationSelection(clear_ui);
+ mTrackingStatus = TRACKING_NOTHING;
+ mTrackingLocationType = LOCATION_NOTHING;
+}
+
+
+void LLTracker::drawMarker(const LLVector3d& pos_global, const LLColor4& color)
+{
+ if (!gCamera)
+ {
+ return;
+ }
+
+ // get position
+ LLVector3 pos_local = gAgent.getPosAgentFromGlobal(pos_global);
+
+ // check in frustum
+ LLCoordGL screen;
+ S32 x = 0;
+ S32 y = 0;
+ const BOOL CLAMP = TRUE;
+
+ if (gCamera->projectPosAgentToScreen(pos_local, screen, CLAMP)
+ || gCamera->projectPosAgentToScreenEdge(pos_local, screen) )
+ {
+ gHUDView->screenPointToLocal(screen.mX, screen.mY, &x, &y);
+
+ // the center of the rendered position of the arrow obeys
+ // the following rules:
+ // (1) it lies on an ellipse centered on the target position
+ // (2) it lies on the line between the target and the window center
+ // (3) right now the radii of the ellipse are fixed, but eventually
+ // they will be a function of the target text
+ //
+ // from those rules we can compute the position of the
+ // lower left corner of the image
+ LLRect rect = gHUDView->getRect();
+ S32 x_center = lltrunc(0.5f * (F32)rect.getWidth());
+ S32 y_center = lltrunc(0.5f * (F32)rect.getHeight());
+ x = x - x_center; // x and y relative to center
+ y = y - y_center;
+ F32 dist = sqrt((F32)(x*x + y*y));
+ S32 half_arrow_size = lltrunc(0.5f * HUD_ARROW_SIZE);
+ if (dist > 0.f)
+ {
+ const F32 ARROW_ELLIPSE_RADIUS_X = 2 * HUD_ARROW_SIZE;
+ const F32 ARROW_ELLIPSE_RADIUS_Y = HUD_ARROW_SIZE;
+
+ // compute where the arrow should be
+ F32 x_target = (F32)(x + x_center) - (ARROW_ELLIPSE_RADIUS_X * ((F32)x / dist) );
+ F32 y_target = (F32)(y + y_center) - (ARROW_ELLIPSE_RADIUS_Y * ((F32)y / dist) );
+
+ // keep the arrow within the window
+ F32 x_clamped = llclamp( x_target, (F32)half_arrow_size, (F32)(rect.getWidth() - half_arrow_size));
+ F32 y_clamped = llclamp( y_target, (F32)half_arrow_size, (F32)(rect.getHeight() - half_arrow_size));
+
+ F32 slope = (F32)(y) / (F32)(x);
+ F32 window_ratio = (F32)(rect.getHeight() - HUD_ARROW_SIZE) / (F32)(rect.getWidth() - HUD_ARROW_SIZE);
+
+ // if the arrow has been clamped on one axis
+ // then we need to compute the other axis
+ if (llabs(slope) > window_ratio)
+ {
+ if (y_clamped != (F32)y_target)
+ {
+ // clamp by y
+ x_clamped = (y_clamped - (F32)y_center) / slope + (F32)x_center;
+ }
+ }
+ else if (x_clamped != (F32)x_target)
+ {
+ // clamp by x
+ y_clamped = (x_clamped - (F32)x_center) * slope + (F32)y_center;
+ }
+ mHUDArrowCenterX = lltrunc(x_clamped);
+ mHUDArrowCenterY = lltrunc(y_clamped);
+ }
+ else
+ {
+ // recycle the old values
+ x = mHUDArrowCenterX - x_center;
+ y = mHUDArrowCenterY - y_center;
+ }
+
+ F32 angle = atan2( (F32)y, (F32)x );
+
+ gl_draw_scaled_rotated_image(mHUDArrowCenterX - half_arrow_size,
+ mHUDArrowCenterY - half_arrow_size,
+ HUD_ARROW_SIZE, HUD_ARROW_SIZE,
+ RAD_TO_DEG * angle,
+ LLWorldMapView::sTrackArrowImage,
+ color);
+ }
+}
+
+
+void LLTracker::setLandmarkVisited()
+{
+ // poke the inventory item
+ if (!mTrackedLandmarkItemID.isNull())
+ {
+ LLInventoryItem* i = gInventory.getItem( mTrackedLandmarkItemID );
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)i;
+ if ( item
+ && !(item->getFlags()&LLInventoryItem::II_FLAGS_LANDMARK_VISITED))
+ {
+ U32 flags = item->getFlags();
+ flags |= LLInventoryItem::II_FLAGS_LANDMARK_VISITED;
+ item->setFlags(flags);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ChangeInventoryItemFlags");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("ItemID", mTrackedLandmarkItemID);
+ msg->addU32("Flags", flags);
+ gAgent.sendReliableMessage();
+
+ LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), 0);
+ gInventory.accountForUpdate(up);
+
+ // need to communicate that the icon needs to change...
+ gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item->getUUID());
+ gInventory.notifyObservers();
+ }
+ }
+}
+
+
+void LLTracker::cacheLandmarkPosition()
+{
+ // the landmark asset download may have finished, in which case
+ // we'll now be able to figure out where we're trying to go
+ BOOL found_landmark = FALSE;
+ if( mTrackedLandmarkAssetID == LLFloaterWorldMap::getHomeID())
+ {
+ LLVector3d pos_global;
+ if ( gAgent.getHomePosGlobal( &mTrackedPositionGlobal ))
+ {
+ found_landmark = TRUE;
+ }
+ else
+ {
+ llwarns << "LLTracker couldn't find home pos" << llendl;
+ mTrackedLandmarkAssetID.setNull();
+ mTrackedLandmarkItemID.setNull();
+ }
+ }
+ else
+ {
+ LLLandmark* landmark = gLandmarkList.getAsset(mTrackedLandmarkAssetID);
+ if(landmark && landmark->getGlobalPos(mTrackedPositionGlobal))
+ {
+ found_landmark = TRUE;
+
+ // cache the object's visitation status
+ mLandmarkHasBeenVisited = FALSE;
+ LLInventoryItem* item = gInventory.getItem(mTrackedLandmarkItemID);
+ if ( item
+ && item->getFlags()&LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ mLandmarkHasBeenVisited = TRUE;
+ }
+ }
+ }
+ if ( found_landmark && gFloaterWorldMap )
+ {
+ mHasReachedLandmark = FALSE;
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(mTrackedPositionGlobal, 1.0f);
+ if ( dist < DESTINATION_UNVISITED_RADIUS )
+ {
+ mHasReachedLandmark = TRUE;
+ }
+ mHasLandmarkPosition = TRUE;
+ }
+ mHasLandmarkPosition = found_landmark;
+}
+
+
+void LLTracker::purgeBeaconText()
+{
+ if(!mBeaconText.isNull())
+ {
+ mBeaconText->markDead();
+ mBeaconText = NULL;
+ }
+}
+
diff --git a/indra/newview/lltracker.h b/indra/newview/lltracker.h
new file mode 100644
index 0000000000..ea32b48b8e
--- /dev/null
+++ b/indra/newview/lltracker.h
@@ -0,0 +1,135 @@
+/**
+ * @file lltracker.h
+ * @brief Container for objects user is tracking.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A singleton class for tracking stuff.
+//
+// TODO -- LLAvatarTracker functionality should probably be moved
+// to the LLTracker class.
+
+
+#ifndef LL_LLTRACKER_H
+#define LL_LLTRACKER_H
+
+#include "lldarray.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "v3dmath.h"
+
+class LLHUDText;
+
+
+class LLTracker
+{
+public:
+ enum ETrackingStatus
+ {
+ TRACKING_NOTHING = 0,
+ TRACKING_AVATAR = 1,
+ TRACKING_LANDMARK = 2,
+ TRACKING_LOCATION = 3,
+ };
+
+ enum ETrackingLocationType
+ {
+ LOCATION_NOTHING,
+ LOCATION_EVENT,
+ LOCATION_ITEM,
+ };
+
+ static LLTracker* instance()
+ {
+ if (!sTrackerp)
+ {
+ sTrackerp = new LLTracker();
+ }
+ return sTrackerp;
+ }
+
+ static void cleanupInstance() { delete sTrackerp; sTrackerp = NULL; }
+
+ //static void drawTrackerArrow();
+ // these are static so that they can be used a callbacks
+ static ETrackingStatus getTrackingStatus() { return instance()->mTrackingStatus; }
+ static ETrackingLocationType getTrackedLocationType() { return instance()->mTrackingLocationType; }
+ static BOOL isTracking(void*) { return (BOOL) instance()->mTrackingStatus; }
+ static void stopTracking(void*);
+
+ static const LLUUID& getTrackedLandmarkAssetID() { return instance()->mTrackedLandmarkAssetID; }
+ static const LLUUID& getTrackedLandmarkItemID() { return instance()->mTrackedLandmarkItemID; }
+
+ static void trackAvatar( const LLUUID& avatar_id, const LLString& name );
+ static void trackLandmark( const LLUUID& landmark_asset_id, const LLUUID& landmark_item_id , const LLString& name);
+ static void trackLocation(const LLVector3d& pos, const LLString& full_name, const LLString& tooltip, ETrackingLocationType location_type = LOCATION_NOTHING);
+
+ // returns global pos of tracked thing
+ static LLVector3d getTrackedPositionGlobal();
+
+ static BOOL hasLandmarkPosition();
+ static const LLString& getTrackedLocationName();
+
+ static void drawHUDArrow();
+
+ // Draw in-world 3D tracking stuff
+ static void render3D();
+
+ static BOOL handleMouseDown(S32 x, S32 y);
+
+ static LLTracker* sTrackerp;
+ static BOOL sCheesyBeacon;
+
+ static const LLString& getLabel() { return instance()->mLabel; }
+ static const LLString& getToolTip() { return instance()->mToolTip; }
+protected:
+ LLTracker();
+ ~LLTracker();
+
+ static void renderBeacon( LLVector3d pos_global,
+ const LLColor4& color,
+ LLHUDText* hud_textp,
+ const std::string& label );
+
+ void stopTrackingAll(BOOL clear_ui = FALSE);
+ void stopTrackingAvatar(BOOL clear_ui = FALSE);
+ void stopTrackingLocation(BOOL clear_ui = FALSE);
+ void stopTrackingLandmark(BOOL clear_ui = FALSE);
+
+ void drawMarker(const LLVector3d& pos_global, const LLColor4& color);
+ void setLandmarkVisited();
+ void cacheLandmarkPosition();
+ void purgeBeaconText();
+
+protected:
+ ETrackingStatus mTrackingStatus;
+ ETrackingLocationType mTrackingLocationType;
+ LLPointer<LLHUDText> mBeaconText;
+ S32 mHUDArrowCenterX;
+ S32 mHUDArrowCenterY;
+
+ LLVector3d mTrackedPositionGlobal;
+
+ LLString mLabel;
+ LLString mToolTip;
+
+ LLString mTrackedLandmarkName;
+ LLUUID mTrackedLandmarkAssetID;
+ LLUUID mTrackedLandmarkItemID;
+ LLDynamicArray<LLUUID> mLandmarkAssetIDList;
+ LLDynamicArray<LLUUID> mLandmarkItemIDList;
+ BOOL mHasReachedLandmark;
+ BOOL mHasLandmarkPosition;
+ BOOL mLandmarkHasBeenVisited;
+
+ LLString mTrackedLocationName;
+ BOOL mIsTrackingLocation;
+ BOOL mHasReachedLocation;
+};
+
+
+#endif
+
diff --git a/indra/newview/lluiconstants.h b/indra/newview/lluiconstants.h
new file mode 100644
index 0000000000..01663ed233
--- /dev/null
+++ b/indra/newview/lluiconstants.h
@@ -0,0 +1,32 @@
+/**
+ * @file lluiconstants.h
+ * @brief Compile-time configuration for UI
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUICONSTANTS_H
+#define LL_LLUICONSTANTS_H
+
+// spacing for small font lines of text, like LLTextBoxes
+const S32 LINE = 16;
+
+// spacing for larger lines of text
+const S32 LINE_BIG = 24;
+
+// default vertical padding
+const S32 VPAD = 4;
+
+// default horizontal padding
+const S32 HPAD = 4;
+
+// Account History, how far to look into past
+const S32 SUMMARY_INTERVAL = 7; // one week
+const S32 SUMMARY_MAX = 8; //
+const S32 DETAILS_INTERVAL = 1; // one day
+const S32 DETAILS_MAX = 30; // one month
+const S32 TRANSACTIONS_INTERVAL = 1;// one day
+const S32 TRANSACTIONS_MAX = 30; // one month
+
+#endif
diff --git a/indra/newview/lluploaddialog.cpp b/indra/newview/lluploaddialog.cpp
new file mode 100644
index 0000000000..f91db06ef9
--- /dev/null
+++ b/indra/newview/lluploaddialog.cpp
@@ -0,0 +1,149 @@
+/**
+ * @file lluploaddialog.cpp
+ * @brief LLUploadDialog class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lluploaddialog.h"
+#include "llviewerwindow.h"
+#include "llfontgl.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llkeyboard.h"
+#include "llfocusmgr.h"
+#include "llviewercontrol.h"
+
+// static
+LLUploadDialog* LLUploadDialog::sDialog = NULL;
+
+// static
+LLUploadDialog* LLUploadDialog::modalUploadDialog(const std::string& msg)
+{
+ // Note: object adds, removes, and destroys itself.
+ return new LLUploadDialog(msg);
+}
+
+// static
+void LLUploadDialog::modalUploadFinished()
+{
+ // Note: object adds, removes, and destroys itself.
+ delete LLUploadDialog::sDialog;
+ LLUploadDialog::sDialog = NULL;
+}
+
+////////////////////////////////////////////////////////////
+// Private methods
+
+LLUploadDialog::LLUploadDialog( const std::string& msg)
+ :
+ LLPanel( "Uploading...", LLRect(0,100,100,0) ) // dummy rect. Will reshape below.
+{
+ setBackgroundVisible( TRUE );
+
+ if( LLUploadDialog::sDialog )
+ {
+ delete LLUploadDialog::sDialog;
+ }
+ LLUploadDialog::sDialog = this;
+
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ LLRect msg_rect;
+ for (int line_num=0; line_num<16; ++line_num)
+ {
+ mLabelBox[line_num] = new LLTextBox( "Filename", msg_rect, "", font );
+ addChild(mLabelBox[line_num]);
+ }
+
+ setMessage(msg);
+
+ // The dialog view is a root view
+ gViewerWindow->setTopView( this, NULL );
+}
+
+void LLUploadDialog::setMessage( const std::string& msg)
+{
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ const S32 VPAD = 16;
+ const S32 HPAD = 25;
+
+ // Make the text boxes a little wider than the text
+ const S32 TEXT_PAD = 8;
+
+ // Split message into lines, separated by '\n'
+ S32 max_msg_width = 0;
+ LLDoubleLinkedList<LLString> msg_lines;
+
+ S32 size = msg.size() + 1;// + strlen("Uploading...\n\n");
+ char* temp_msg = new char[size];
+
+ //strcpy(temp_msg,"Uploading...\n\n");
+ strcpy( temp_msg, msg.c_str());
+
+ char* token = strtok( temp_msg, "\n" );
+ while( token )
+ {
+ S32 cur_width = S32(font->getWidth(token) + 0.99f) + TEXT_PAD;
+ max_msg_width = llmax( max_msg_width, cur_width );
+ msg_lines.addDataAtEnd( new LLString( token ) );
+ token = strtok( NULL, "\n" );
+ }
+ delete[] temp_msg;
+
+
+ S32 line_height = S32( font->getLineHeight() + 0.99f );
+ S32 dialog_width = max_msg_width + 2 * HPAD;
+ S32 dialog_height = line_height * msg_lines.getLength() + 2 * VPAD;
+
+ reshape( dialog_width, dialog_height, FALSE );
+
+ // Message
+ S32 msg_x = (mRect.getWidth() - max_msg_width) / 2;
+ S32 msg_y = mRect.getHeight() - VPAD - line_height;
+ int line_num;
+ for (line_num=0; line_num<16; ++line_num)
+ {
+ mLabelBox[line_num]->setVisible(FALSE);
+ }
+ line_num = 0;
+ for( LLString* cur_line = msg_lines.getFirstData(); cur_line; cur_line = msg_lines.getNextData() )
+ {
+ LLRect msg_rect;
+ msg_rect.setOriginAndSize( msg_x, msg_y, max_msg_width, line_height );
+ mLabelBox[line_num]->setRect(msg_rect);
+ mLabelBox[line_num]->setText(*cur_line);
+ mLabelBox[line_num]->setColor( gColors.getColor( "LabelTextColor" ) );
+ mLabelBox[line_num]->setVisible(TRUE);
+ msg_y -= line_height;
+ ++line_num;
+ }
+ msg_lines.deleteAllData();
+
+ centerDialog();
+}
+
+LLUploadDialog::~LLUploadDialog()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+// LLFilePicker::instance().reset();
+
+
+ LLUploadDialog::sDialog = NULL;
+}
+
+void LLUploadDialog::centerDialog()
+{
+ LLRect window_rect = gViewerWindow->getRootView()->getRect();
+
+ S32 dialog_left = window_rect.mLeft + (window_rect.getWidth() - mRect.getWidth()) / 2;
+ S32 dialog_bottom = window_rect.mBottom + (window_rect.getHeight() - mRect.getHeight()) / 2;
+
+ translate( dialog_left - mRect.mLeft, dialog_bottom - mRect.mBottom );
+}
+
diff --git a/indra/newview/lluploaddialog.h b/indra/newview/lluploaddialog.h
new file mode 100644
index 0000000000..4af3dce378
--- /dev/null
+++ b/indra/newview/lluploaddialog.h
@@ -0,0 +1,38 @@
+/**
+ * @file lluploaddialog.h
+ * @brief LLUploadDialog class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_UPLOADDIALOG_H
+#define LL_UPLOADDIALOG_H
+
+#include "llpanel.h"
+#include "lltextbox.h"
+
+class LLUploadDialog : public LLPanel
+{
+public:
+ // Use this function to open a modal dialog and display it until the user presses the "close" button.
+ static LLUploadDialog* modalUploadDialog(const std::string& msg); // Message to display
+ static void modalUploadFinished(); // Message to display
+
+ static bool modalUploadIsFinished() { return (sDialog == NULL); }
+
+ void setMessage( const std::string& msg );
+
+private:
+ LLUploadDialog( const std::string& msg);
+ virtual ~LLUploadDialog(); // No you can't kill it. It can only kill itself.
+
+ void centerDialog();
+
+ LLTextBox* mLabelBox[16];
+
+private:
+ static LLUploadDialog* sDialog; // Hidden singleton instance, created and destroyed as needed.
+};
+
+#endif // LL_UPLOADDIALOG_H
diff --git a/indra/newview/llurl.cpp b/indra/newview/llurl.cpp
new file mode 100644
index 0000000000..2cd05bdcbf
--- /dev/null
+++ b/indra/newview/llurl.cpp
@@ -0,0 +1,256 @@
+/**
+ * @file llurl.cpp
+ * @brief Text url class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include <string.h>
+#include "llurl.h"
+#include "llerror.h"
+
+LLURL::LLURL()
+{
+ init("");
+}
+
+LLURL::LLURL(const LLURL &url)
+{
+ if (this != &url)
+ {
+ init(url.getFQURL());
+ }
+ else
+ {
+ init("");
+ }
+}
+
+LLURL::LLURL(const char * url)
+{
+ init(url);
+}
+
+LLURL::~LLURL()
+{
+ free();
+}
+
+void LLURL::init(const char * url)
+{
+ mURI[0] = '\0';
+ mAuthority[0] = '\0';
+ mPath[0] = '\0';
+ mFilename[0] = '\0';
+ mExtension[0] = '\0';
+ mTag[0] = '\0';
+
+ char url_copy[MAX_STRING];
+
+ strcpy (url_copy,url);
+
+ char *parse;
+ char *leftover_url = url_copy;
+ S32 span = 0;
+
+ // copy and lop off tag
+ if ((parse = strchr(url_copy,'#')))
+ {
+ strcpy(mTag,parse+1);
+ *parse = '\0';
+ }
+
+ // copy out URI if it exists, update leftover_url
+ if ((parse = strchr(url_copy,':')))
+ {
+ *parse = '\0';
+ strcpy(mURI,leftover_url);
+ leftover_url = parse + 1;
+ }
+
+ // copy out authority if it exists, update leftover_url
+ if ((leftover_url[0] == '/') && (leftover_url[1] == '/'))
+ {
+ leftover_url += 2; // skip the "//"
+
+ span = strcspn(leftover_url, "/");
+ strncat(mAuthority,leftover_url,span);
+ leftover_url += span;
+ }
+
+ if ((parse = strrchr(leftover_url,'.')))
+ {
+ // copy and lop off extension
+ strcpy(mExtension,parse+1);
+ *parse = '\0';
+ }
+
+ if ((parse = strrchr(leftover_url,'/')))
+ {
+ parse++;
+ }
+ else
+ {
+ parse = leftover_url;
+ }
+
+ // copy and lop off filename
+ strcpy(mFilename,parse);
+ *parse = '\0';
+
+ // what's left should be the path
+ strcpy(mPath,leftover_url);
+
+// llinfos << url << " decomposed into: " << llendl;
+// llinfos << " URI : <" << mURI << ">" << llendl;
+// llinfos << " Auth: <" << mAuthority << ">" << llendl;
+// llinfos << " Path: <" << mPath << ">" << llendl;
+// llinfos << " File: <" << mFilename << ">" << llendl;
+// llinfos << " Ext : <" << mExtension << ">" << llendl;
+// llinfos << " Tag : <" << mTag << ">" << llendl;
+}
+
+void LLURL::free()
+{
+}
+
+// Copy assignment
+LLURL &LLURL::operator=(const LLURL &rhs)
+{
+ if (this != &rhs)
+ {
+ this->init(rhs.getFQURL());
+ }
+
+ return *this;
+}
+
+// Compare
+bool LLURL::operator==(const LLURL &rhs) const
+{
+ if ((strcmp(mURI, rhs.mURI))
+ || (strcmp(mAuthority, rhs.mAuthority))
+ || (strcmp(mPath, rhs.mPath))
+ || (strcmp(mFilename, rhs.mFilename))
+ || (strcmp(mExtension, rhs.mExtension))
+ || (strcmp(mTag, rhs.mTag))
+ )
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool LLURL::operator!=(const LLURL& rhs) const
+{
+ return !(*this == rhs);
+}
+
+const char * LLURL::getFQURL() const
+{
+ char fqurl[LL_MAX_PATH];
+
+ fqurl[0] = '\0';
+
+ if (mURI[0])
+ {
+ strcat(fqurl,mURI);
+ strcat(fqurl,":");
+ if (mAuthority[0])
+ {
+ strcat(fqurl,"//");
+ }
+ }
+
+ if (mAuthority[0])
+ {
+ strcat(fqurl,mAuthority);
+ }
+
+ strcat(fqurl,mPath);
+
+ strcat(fqurl,mFilename);
+
+ if (mExtension[0])
+ {
+ strcat(fqurl,".");
+ strcat(fqurl,mExtension);
+ }
+
+ if (mTag[0])
+ {
+ strcat(fqurl,"#");
+ strcat(fqurl,mTag);
+ }
+
+ strcpy(LLURL::sReturnString,fqurl);
+
+ return(LLURL::sReturnString);
+}
+
+
+const char* LLURL::updateRelativePath(const LLURL &url)
+{
+ char new_path[LL_MAX_PATH];
+ char tmp_path[LL_MAX_PATH];
+
+ char *parse;
+
+ if (mPath[0] != '/')
+ {
+ //start with existing path
+ strcpy (new_path,url.mPath);
+ strcpy (tmp_path,mPath);
+
+ parse = strtok(tmp_path,"/");
+ while (parse)
+ {
+ if (!strcmp(parse,"."))
+ {
+ // skip this, it's meaningless
+ }
+ else if (!strcmp(parse,".."))
+ {
+ if ((parse = strrchr(new_path, '/')))
+ {
+ *parse = '\0';
+ if ((parse = strrchr(new_path, '/')))
+ {
+ *(parse+1) = '\0';
+ }
+ else
+ {
+ new_path[0] = '\0';
+ }
+ }
+ else
+ {
+ strcat(new_path,"../");
+ }
+
+ }
+ else
+ {
+ strcat(new_path,parse);
+ strcat(new_path,"/");
+ }
+ parse = strtok(NULL,"/");
+ }
+ strcpy(mPath,new_path);
+ }
+ return mPath;
+}
+
+const char * LLURL::getFullPath()
+{
+ strcpy(LLURL::sReturnString,mPath);
+ strcat(LLURL::sReturnString,mFilename);
+ strcat(LLURL::sReturnString,".");
+ strcat(LLURL::sReturnString,mExtension);
+ return(sReturnString);
+}
+
+
+char LLURL::sReturnString[LL_MAX_PATH] = "";
diff --git a/indra/newview/llurl.h b/indra/newview/llurl.h
new file mode 100644
index 0000000000..1e13aa94b8
--- /dev/null
+++ b/indra/newview/llurl.h
@@ -0,0 +1,76 @@
+/**
+ * @file llurl.h
+ * @brief Text url class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURL_H
+#define LL_LLURL_H
+
+// splits a URL into its parts, which are:
+//
+// [URI][AUTHORITY][PATH][FILENAME][EXTENSION][TAG]
+//
+// e.g. http://www.lindenlab.com/early/bite_me.html#where
+//
+// URI= "http"
+// AUTHORITY= "www.lindenlab.com"
+// PATH= "/early/"
+// FILENAME= "bite_me"
+// EXTENSION= "html"
+// TAG= "where"
+//
+//
+// test cases:
+//
+// http://www.lindenlab.com/early/bite_me.html#where
+// http://www.lindenlab.com/
+// http://www.lindenlab.com
+// www.lindenlab.com ?
+// early/bite_me.html#where
+// mailto://test@lindenlab.com
+// mailto:test@lindenlab.com
+//
+//
+
+
+class LLURL
+{
+public:
+ LLURL();
+ LLURL(const LLURL &url);
+ LLURL(const char * url);
+
+ LLURL &operator=(const LLURL &rhs);
+
+ virtual ~LLURL();
+
+ virtual void init (const char * url);
+ virtual void free ();
+
+ bool operator==(const LLURL &rhs) const;
+ bool operator!=(const LLURL &rhs) const;
+
+ virtual const char *getFQURL() const;
+ virtual const char *getFullPath();
+
+ virtual const char *updateRelativePath(const LLURL &url);
+
+ virtual BOOL isExtension(const char *compare) {return (!strcmp(mExtension,compare));};
+
+public:
+
+ char mURI[LL_MAX_PATH];
+ char mAuthority[LL_MAX_PATH];
+ char mPath[LL_MAX_PATH];
+ char mFilename[LL_MAX_PATH];
+ char mExtension[LL_MAX_PATH];
+ char mTag[LL_MAX_PATH];
+
+ static char sReturnString[LL_MAX_PATH];
+};
+
+#endif // LL_LLURL_H
+
diff --git a/indra/newview/llurlwhitelist.cpp b/indra/newview/llurlwhitelist.cpp
new file mode 100644
index 0000000000..d582d2d19f
--- /dev/null
+++ b/indra/newview/llurlwhitelist.cpp
@@ -0,0 +1,221 @@
+/**
+ * @file llurlwhitelist.cpp
+ * @author Callum Prentice
+ * @brief maintains a "white list" of acceptable URLS that are stored on disk
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llurlwhitelist.h"
+
+#include <iostream>
+#include <fstream>
+
+LLUrlWhiteList* LLUrlWhiteList::sInstance = 0;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLUrlWhiteList::LLUrlWhiteList () :
+ mLoaded ( false ),
+ mFilename ( "url_whitelist.ini" ),
+ mUrlList ( 0 ),
+ mUrlListIter ( 0 )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLUrlWhiteList::~LLUrlWhiteList ()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+//static
+void LLUrlWhiteList::initClass ()
+{
+ if ( ! sInstance )
+ {
+ sInstance = new LLUrlWhiteList ();
+ }
+}
+
+//static
+void LLUrlWhiteList::cleanupClass ()
+{
+ delete sInstance;
+ sInstance = NULL;
+}
+
+LLUrlWhiteList* LLUrlWhiteList::getInstance ()
+{
+ return sInstance;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::load ()
+{
+ // don't load if we're already loaded
+ if ( mLoaded )
+ return ( true );
+
+ // remove current entries before we load over them
+ clear ();
+
+ // build filename for each user
+ LLString resolvedFilename = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, mFilename.c_str () );
+
+ // open a file for reading
+ llifstream file ( resolvedFilename.c_str () );
+ if ( file.is_open () )
+ {
+ // add each line in the file to the list
+ std::string line;
+ while ( std::getline ( file, line ) )
+ {
+ addItem ( line, false );
+ };
+
+ file.close ();
+
+ // flag as loaded
+ mLoaded = true;
+
+ return true;
+ };
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::save ()
+{
+ // build filename for each user
+ LLString resolvedFilename = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, mFilename.c_str () );
+
+ // open a file for writing
+ llofstream file ( resolvedFilename.c_str () );
+ if ( file.is_open () )
+ {
+ // for each entry we have
+ for ( LLStringListIter iter = mUrlList.begin (); iter != mUrlList.end (); ++iter )
+ {
+ file << ( *iter ) << std::endl;
+ };
+
+ file.close ();
+
+ return true;
+ };
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::clear ()
+{
+ mUrlList.clear ();
+
+ // invalidate iterator since we changed the contents
+ mUrlListIter = mUrlList.end ();
+
+ return true;
+}
+
+LLString url_cleanup(LLString pattern)
+{
+ LLString::trim(pattern);
+ S32 length = pattern.length();
+ S32 position = 0;
+ std::string::reverse_iterator it = pattern.rbegin();
+ ++it; // skip last char, might be '/'
+ ++position;
+ for (; it < pattern.rend(); ++it)
+ {
+ char c = *it;
+ if (c == '/')
+ {
+ // found second to last '/'
+ S32 desired_length = length - position;
+ LLString::truncate(pattern, desired_length);
+ break;
+ }
+ ++position;
+ }
+ return pattern;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::addItem ( const LLString& itemIn, bool saveAfterAdd )
+{
+ LLString item = url_cleanup(itemIn);
+
+ mUrlList.insert ( mUrlList.end (), item );
+
+ // use this when all you want to do is call addItem ( ... ) where necessary
+ if ( saveAfterAdd )
+ save ();
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::getFirst ( LLString& valueOut )
+{
+ if ( mUrlList.size () == 0 )
+ return false;
+
+ mUrlListIter = mUrlList.begin ();
+
+ valueOut = * ( mUrlListIter );
+
+ ++mUrlListIter;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::getNext ( LLString& valueOut )
+{
+ if ( mUrlListIter == mUrlList.end () )
+ return false;
+
+ valueOut = * ( mUrlListIter );
+
+ ++mUrlListIter;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::containsMatch ( const LLString& patternIn )
+{
+ return false;
+
+ // CP: removed since they're not useful without Mozilla enabled
+ #if LL_MOZILLA_ENABLED
+ LLString pattern = url_cleanup(patternIn);
+
+ if (pattern.empty()) return false;
+
+ LLStringListIter iter = std::find ( mUrlList.begin (), mUrlList.end (), pattern );
+
+ if ( iter != mUrlList.end () )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ };
+ #endif
+}
diff --git a/indra/newview/llurlwhitelist.h b/indra/newview/llurlwhitelist.h
new file mode 100644
index 0000000000..e0d1f64ddb
--- /dev/null
+++ b/indra/newview/llurlwhitelist.h
@@ -0,0 +1,48 @@
+/**
+ * @file llurlwhitelist.h
+ * @author Callum Prentice
+ * @brief maintains a "white list" of acceptable URLS that are stored on disk
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURLWHITELIST_H
+#define LL_LLURLWHITELIST_H
+
+#include <list>
+
+class LLUrlWhiteList
+{
+ public:
+ virtual ~LLUrlWhiteList ();
+
+ static void initClass();
+ static void cleanupClass();
+ static LLUrlWhiteList* getInstance ();
+
+ bool load ();
+ bool save ();
+
+ bool clear ();
+ bool addItem ( const LLString& itemIn, bool saveAfterAdd );
+
+ bool containsMatch ( const LLString& patternIn );
+
+ bool getFirst ( LLString& valueOut );
+ bool getNext ( LLString& valueOut );
+
+ private:
+ LLUrlWhiteList ();
+ static LLUrlWhiteList* sInstance;
+
+ typedef std::list < LLString > LLStringList;
+ typedef std::list < LLString >::iterator LLStringListIter;
+
+ bool mLoaded;
+ const LLString mFilename;
+ LLStringList mUrlList;
+ LLStringListIter mUrlListIter;
+};
+
+#endif // LL_LLURLWHITELIST_H
diff --git a/indra/newview/llviewchildren.cpp b/indra/newview/llviewchildren.cpp
new file mode 100644
index 0000000000..3bf138d103
--- /dev/null
+++ b/indra/newview/llviewchildren.cpp
@@ -0,0 +1,99 @@
+/**
+ * @file llviewchildren.cpp
+ * @brief LLViewChildren class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewchildren.h"
+
+#include "lluictrlfactory.h"
+
+// viewer includes
+#include "llbutton.h"
+#include "lliconctrl.h"
+#include "lltextbox.h"
+#include "lluuid.h"
+#include "llpanel.h"
+#include "llviewercontrol.h"
+
+
+// *NOTE: Do not use mParent reference in the constructor, since it is
+// potentially not fully constructud.
+LLViewChildren::LLViewChildren(LLPanel& parent)
+ : mParent(parent)
+{
+}
+
+
+void LLViewChildren::show(const char* id, bool visible)
+{
+ mParent.childSetVisible(id, visible);
+}
+
+void LLViewChildren::enable(const char* id, bool enabled)
+{
+ mParent.childSetEnabled(id, enabled);
+}
+
+void LLViewChildren::setText(
+ const char* id, const std::string& text, bool visible)
+{
+ LLTextBox* child = LLUICtrlFactory::getTextBoxByName(&mParent, id);
+ if (child)
+ {
+ child->setVisible(visible);
+ child->setText(text);
+ }
+}
+
+void LLViewChildren::setWrappedText(
+ const char* id, const std::string& text, bool visible)
+{
+ LLTextBox* child = LLUICtrlFactory::getTextBoxByName(&mParent, id);
+ if (child)
+ {
+ child->setVisible(visible);
+ child->setWrappedText(text);
+ }
+}
+
+void LLViewChildren::setBadge(const char* id, Badge badge, bool visible)
+{
+ static LLUUID badgeOK(gViewerArt.getString("badge_ok.tga"));
+ static LLUUID badgeNote(gViewerArt.getString("badge_note.tga"));
+ static LLUUID badgeWarn(gViewerArt.getString("badge_warn.tga"));
+ static LLUUID badgeError(gViewerArt.getString("badge_error.tga"));
+
+ LLUUID badgeUUID;
+ switch (badge)
+ {
+ default:
+ case BADGE_OK: badgeUUID = badgeOK; break;
+ case BADGE_NOTE: badgeUUID = badgeNote; break;
+ case BADGE_WARN: badgeUUID = badgeWarn; break;
+ case BADGE_ERROR: badgeUUID = badgeError; break;
+ }
+
+ LLIconCtrl* child = LLUICtrlFactory::getIconByName(&mParent, id);
+ if (child)
+ {
+ child->setVisible(visible);
+ child->setImage(badgeUUID);
+ }
+}
+
+void LLViewChildren::setAction(const char* id,
+ void(*function)(void*), void* value)
+{
+ LLButton* button = LLUICtrlFactory::getButtonByName(&mParent, id);
+ if (button)
+ {
+ button->setClickedCallback(function, value);
+ }
+}
+
+
diff --git a/indra/newview/llviewchildren.h b/indra/newview/llviewchildren.h
new file mode 100644
index 0000000000..1a7238127f
--- /dev/null
+++ b/indra/newview/llviewchildren.h
@@ -0,0 +1,49 @@
+/**
+ * @file llviewchildren.h
+ * @brief LLViewChildren class header file
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWCHILDREN_H
+#define LL_LLVIEWCHILDREN_H
+
+class LLPanel;
+
+class LLViewChildren
+ // makes it easy to manipulate children of a view by id safely
+ // encapsulates common operations into simple, one line calls
+{
+public:
+ LLViewChildren(LLPanel& parent);
+
+ // all views
+ void show(const char* id, bool visible = true);
+ void hide(const char* id) { show(id, false); }
+
+ void enable(const char* id, bool enabled = true);
+ void disable(const char* id) { enable(id, false); };
+
+ //
+ // LLTextBox
+ void setText(const char* id,
+ const std::string& text, bool visible = true);
+ void setWrappedText(const char* id,
+ const std::string& text, bool visible = true);
+
+ // LLIconCtrl
+ enum Badge { BADGE_OK, BADGE_NOTE, BADGE_WARN, BADGE_ERROR };
+
+ void setBadge(const char* id, Badge b, bool visible = true);
+
+
+ // LLButton
+ void setAction(const char* id, void(*function)(void*), void* value);
+
+
+private:
+ LLPanel& mParent;
+};
+
+#endif
diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp
new file mode 100644
index 0000000000..b75f86d76f
--- /dev/null
+++ b/indra/newview/llviewerassetstorage.cpp
@@ -0,0 +1,196 @@
+/**
+ * @file llviewerassetstorage.cpp
+ * @brief Subclass capable of loading asset data to/from an external source.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#include "linden_common.h"
+#include "llviewerprecompiledheaders.h"
+
+#include "llagent.h"
+#include "llviewerassetstorage.h"
+#include "llviewerbuild.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host)
+ : LLAssetStorage(msg, xfer, vfs, upstream_host)
+{
+}
+
+
+LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs)
+ : LLAssetStorage(msg, xfer, vfs)
+{
+}
+
+// virtual
+void LLViewerAssetStorage::storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority,
+ bool store_local)
+{
+ LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+ llinfos << "LLViewerAssetStorage::storeAssetData (legacy) " << tid << ":" << LLAssetType::lookup(asset_type)
+ << " ASSET_ID: " << asset_id << llendl;
+
+ if (mUpstreamHost.isOk())
+ {
+ if (mVFS->getExists(asset_id, asset_type))
+ {
+ // Pack data into this packet if we can fit it.
+ U8 buffer[MTUBYTES];
+ buffer[0] = 0;
+
+ LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ);
+ S32 asset_size = vfile.getSize();
+
+ LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type);
+ req->mUpCallback = callback;
+ req->mUserData = user_data;
+
+ if (asset_size < 1)
+ {
+ // This can happen if there's a bug in our code or if the VFS has been corrupted.
+ llwarns << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << llendl;
+
+ delete req;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED);
+ }
+ return;
+ }
+ else if(is_priority)
+ {
+ mPendingUploads.push_front(req);
+ }
+ else
+ {
+ mPendingUploads.push_back(req);
+ }
+
+ // Read the data from the VFS if it'll fit in this packet.
+ if (asset_size + 100 < MTUBYTES)
+ {
+ BOOL res = vfile.read(buffer, asset_size);
+ S32 bytes_read = res ? vfile.getLastBytesRead() : 0;
+
+ if( bytes_read == asset_size )
+ {
+ req->mDataSentInFirstPacket = TRUE;
+ //llinfos << "LLViewerAssetStorage::createAsset sending data in first packet" << llendl;
+ }
+ else
+ {
+ llwarns << "Probable corruption in VFS file, aborting store asset data" << llendl;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE);
+ }
+ return;
+ }
+ }
+ else
+ {
+ // Too big, do an xfer
+ buffer[0] = 0;
+ asset_size = 0;
+ }
+ mMessageSys->newMessageFast(_PREHASH_AssetUploadRequest);
+ mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
+ mMessageSys->addUUIDFast(_PREHASH_TransactionID, tid);
+ mMessageSys->addS8Fast(_PREHASH_Type, (S8)asset_type);
+ mMessageSys->addBOOLFast(_PREHASH_Tempfile, temp_file);
+ mMessageSys->addBOOLFast(_PREHASH_StoreLocal, store_local);
+ mMessageSys->addBinaryDataFast( _PREHASH_AssetData, buffer, asset_size );
+ mMessageSys->sendReliable(mUpstreamHost);
+ }
+ else
+ {
+ llwarns << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE);
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Attempt to move asset store request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+}
+
+void LLViewerAssetStorage::storeAssetData(
+ const char* filename,
+ const LLTransactionID& tid,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+ llinfos << "LLViewerAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
+
+ LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
+
+ llinfos << "ASSET_ID: " << asset_id << llendl;
+
+ legacy->mUpCallback = callback;
+ legacy->mUserData = user_data;
+
+ FILE *fp = LLFile::fopen(filename, "rb");
+ if (fp)
+ {
+ LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+
+ // if this upload fails, the caller needs to setup a new tempfile for us
+ if (temp_file)
+ {
+ LLFile::remove(filename);
+ }
+
+ LLViewerAssetStorage::storeAssetData(
+ tid,
+ asset_type,
+ legacyStoreDataCallback,
+ (void**)legacy,
+ temp_file,
+ is_priority);
+ }
+ else
+ {
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_CANNOT_OPEN_FILE);
+ }
+ }
+}
diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h
new file mode 100644
index 0000000000..15dc533f09
--- /dev/null
+++ b/indra/newview/llviewerassetstorage.h
@@ -0,0 +1,47 @@
+/**
+ * @file llviewerassetstorage.h
+ * @brief Class for loading asset data to/from an external source.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLVIEWERASSETSTORAGE_H
+#define LLVIEWERASSETSTORAGE_H
+
+#include "llassetstorage.h"
+//#include "curl/curl.h"
+
+class LLVFile;
+
+class LLViewerAssetStorage : public LLAssetStorage
+{
+public:
+ typedef void (*LLStoreAssetCallback)(const LLUUID &transaction_id, void *user_data, S32 status);
+
+ LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host);
+
+ LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs);
+
+ virtual void storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false);
+
+ virtual void storeAssetData(
+ const char* filename,
+ const LLTransactionID& tid,
+ LLAssetType::EType type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false);
+};
+
+#endif
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
new file mode 100644
index 0000000000..c4c25b33f4
--- /dev/null
+++ b/indra/newview/llviewercamera.cpp
@@ -0,0 +1,611 @@
+/**
+ * @file llviewercamera.cpp
+ * @brief LLViewerCamera class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iomanip> // for setprecision
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewercamera.h"
+
+#include "llquaternion.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvovolume.h"
+#include "llworld.h"
+
+BOOL LLViewerCamera::sDisableCameraConstraints = FALSE;
+
+LLViewerCamera *gCamera = NULL;
+
+LLViewerCamera::LLViewerCamera() : LLCamera()
+{
+ calcProjection(getFar());
+ S32 i;
+ for (i = 0; i < 16; i++)
+ {
+ mGLProjectionMatrix[i] = 0.f;
+ }
+ mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW;
+ mPixelMeterRatio = 0.f;
+ mScreenPixelArea = 0;
+ mZoomFactor = 1.f;
+ mZoomSubregion = 1;
+}
+
+void LLViewerCamera::updateCameraLocation(const LLVector3 &center,
+ const LLVector3 &up_direction,
+ const LLVector3 &point_of_interest)
+{
+ LLVector3 last_position;
+ LLVector3 last_axis;
+ last_position = getOrigin();
+ last_axis = getAtAxis();
+
+ mLastPointOfInterest = point_of_interest;
+
+ // constrain to max distance from avatar
+ LLVector3 camera_offset = center - gAgent.getPositionAgent();
+
+ setOriginAndLookAt(center, up_direction, point_of_interest);
+
+ F32 dpos = (center - last_position).magVec();
+ LLQuaternion rotation;
+ rotation.shortestArc(last_axis, getAtAxis());
+
+ F32 x, y, z;
+ F32 drot;
+ rotation.getAngleAxis(&drot, &x, &y, &z);
+ mVelocityStat.addValue(dpos);
+ mAngularVelocityStat.addValue(drot);
+ // update pixel meter ratio using default fov, not modified one
+ mPixelMeterRatio = mViewHeightInPixels / (2.f*tanf(mCameraFOVDefault*0.5));
+ // update screen pixel area
+ mScreenPixelArea =(S32)((F32)mViewHeightInPixels * ((F32)mViewHeightInPixels * mAspect));
+}
+
+// Handy copies of last good GL matrices
+F64 gGLModelView[16];
+S32 gGLViewport[4];
+
+const LLMatrix4 &LLViewerCamera::getProjection() const
+{
+ calcProjection(getFar());
+ return mProjectionMatrix;
+
+}
+
+const LLMatrix4 &LLViewerCamera::getModelview() const
+{
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ getMatrixToLocal(mModelviewMatrix);
+ mModelviewMatrix *= cfr;
+ return mModelviewMatrix;
+}
+
+void LLViewerCamera::calcProjection(const F32 far_distance) const
+{
+ F32 fov_y, z_far, z_near, aspect, f;
+ fov_y = getView();
+ z_far = far_distance;
+ z_near = getNear();
+ aspect = getAspect();
+
+ f = 1/tan(fov_y*0.5f);
+
+ mProjectionMatrix.zero();
+ mProjectionMatrix.mMatrix[0][0] = f/aspect;
+ mProjectionMatrix.mMatrix[1][1] = f;
+ mProjectionMatrix.mMatrix[2][2] = (z_far + z_near)/(z_near - z_far);
+ mProjectionMatrix.mMatrix[3][2] = (2*z_far*z_near)/(z_near - z_far);
+ mProjectionMatrix.mMatrix[2][3] = -1;
+}
+
+// Sets up opengl state for 3D drawing. If for selection, also
+// sets up a pick matrix. x and y are ignored if for_selection is false.
+// The picking region is centered on x,y and has the specified width and
+// height.
+
+LLMatrix4 gProjectionMat;
+
+void LLViewerCamera::updateFrustumPlanes()
+{
+ GLint viewport[4];
+ GLdouble model[16];
+ GLdouble proj[16];
+ GLdouble objX,objY,objZ;
+
+ LLVector3 frust[8];
+
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ glGetDoublev(GL_MODELVIEW_MATRIX, model);
+ glGetDoublev(GL_PROJECTION_MATRIX,proj);
+
+ gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
+ /*gluUnProject(viewport[0],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[4].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[5].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[6].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[7].setVec((F32)objX,(F32)objY,(F32)objZ);*/
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ LLVector3 vec = frust[i] - getOrigin();
+ vec.normVec();
+ frust[i+4] = getOrigin() + vec*getFar()*2.0;
+ }
+
+ calcAgentFrustumPlanes(frust);
+}
+
+void LLViewerCamera::setPerspective(BOOL for_selection,
+ S32 x, S32 y_from_bot, S32 width, S32 height,
+ BOOL limit_select_distance,
+ F32 z_near, F32 z_far)
+{
+ F32 fov_y, aspect;
+ fov_y = RAD_TO_DEG * getView();
+ BOOL z_default_near, z_default_far = FALSE;
+ if (z_far <= 0)
+ {
+ z_default_far = TRUE;
+ z_far = getFar();
+ }
+ if (z_near <= 0)
+ {
+ z_default_near = TRUE;
+ z_near = getNear();
+ }
+ aspect = getAspect();
+
+ // Load camera view matrix
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+
+ if (for_selection)
+ {
+ // make a tiny little viewport
+ // anything drawn into this viewport will be "selected"
+ const U8 VIEWPORT_VECTOR_LEN = 4;
+ GLint viewport[VIEWPORT_VECTOR_LEN];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ gluPickMatrix(x + width / 2, y_from_bot + height / 2, width, height, viewport);
+
+ if (limit_select_distance)
+ {
+ // ...select distance from control
+ z_far = gSavedSettings.getF32("MaxSelectDistance");
+ }
+ else
+ {
+ z_far = gAgent.mDrawDistance;
+ }
+ }
+ else
+ {
+ // Only override the far clip if it's not passed in explicitly.
+ if (z_default_far)
+ {
+ z_far = MAX_FAR_CLIP;
+ }
+ glViewport(x, y_from_bot, width, height);
+ }
+
+ if (mZoomFactor > 1.f)
+ {
+ float offset = mZoomFactor - 1.f;
+ int pos_y = mZoomSubregion / llceil(mZoomFactor);
+ int pos_x = mZoomSubregion - (pos_y*llceil(mZoomFactor));
+ glTranslatef(offset - (F32)pos_x * 2.f, offset - (F32)pos_y * 2.f, 0.f);
+ glScalef(mZoomFactor, mZoomFactor, 1.f);
+ }
+
+ calcProjection(z_far); // Update the projection matrix cache
+
+ gluPerspective(fov_y,
+ aspect,
+ z_near,
+ z_far);
+ glGetFloatv(GL_PROJECTION_MATRIX, (float*)&gProjectionMat);
+
+ glMatrixMode( GL_MODELVIEW );
+
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+
+ GLfloat ogl_matrix[16];
+ getOpenGLTransform(ogl_matrix);
+ glMultMatrixf(ogl_matrix);
+
+ if (for_selection && (width > 1 || height > 1))
+ {
+ calculateFrustumPlanesFromWindow((F32)(x - width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
+ (F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f,
+ (F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
+ (F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f);
+
+ }
+
+ // if not picking and not doing a snapshot, cache various GL matrices
+ if (!for_selection && mZoomFactor == 1.f)
+ {
+ // Save GL matrices for access elsewhere in code, especially project_world_to_screen
+ glGetDoublev(GL_PROJECTION_MATRIX, mGLProjectionMatrix);
+ glGetDoublev(GL_MODELVIEW_MATRIX, gGLModelView);
+ glGetIntegerv(GL_VIEWPORT, (GLint*)gGLViewport);
+ }
+
+ updateFrustumPlanes();
+
+ if (gSavedSettings.getBOOL("CameraOffset"))
+ {
+ glMatrixMode(GL_PROJECTION);
+ glTranslatef(0,0,-50);
+ glRotatef(20.0,1,0,0);
+ glMatrixMode(GL_MODELVIEW);
+ }
+}
+
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// screen coordinates to the agent's region.
+void LLViewerCamera::projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent) const
+{
+
+ GLdouble x, y, z;
+ gluUnProject(
+ GLdouble(screen_x), GLdouble(screen_y), 0.0,
+ gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x,
+ &y,
+ &z );
+ pos_agent->setVec( (F32)x, (F32)y, (F32)z );
+}
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// the agent's region space to screen coordinates. Returns TRUE if point in within
+// the current window.
+BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp) const
+{
+ BOOL in_front = TRUE;
+ GLdouble x, y, z; // object's window coords, GL-style
+
+ LLVector3 dir_to_point = pos_agent - getOrigin();
+ dir_to_point /= dir_to_point.magVec();
+
+ if (dir_to_point * getAtAxis() < 0.f)
+ {
+ if (clamp)
+ {
+ return FALSE;
+ }
+ else
+ {
+ in_front = FALSE;
+ }
+ }
+
+ if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ],
+ gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x, &y, &z))
+ {
+ // convert screen coordinates to virtual UI coordinates
+ x /= gViewerWindow->getDisplayScale().mV[VX];
+ y /= gViewerWindow->getDisplayScale().mV[VY];
+
+ // should now have the x,y coords of grab_point in screen space
+ const LLRect& window_rect = gViewerWindow->getWindowRect();
+
+ // ...sanity check
+ S32 int_x = lltrunc(x);
+ S32 int_y = lltrunc(y);
+
+ BOOL valid = TRUE;
+
+ if (clamp)
+ {
+ if (int_x < window_rect.mLeft)
+ {
+ out_point.mX = window_rect.mLeft;
+ valid = FALSE;
+ }
+ else if (int_x > window_rect.mRight)
+ {
+ out_point.mX = window_rect.mRight;
+ valid = FALSE;
+ }
+ else
+ {
+ out_point.mX = int_x;
+ }
+
+ if (int_y < window_rect.mBottom)
+ {
+ out_point.mY = window_rect.mBottom;
+ valid = FALSE;
+ }
+ else if (int_y > window_rect.mTop)
+ {
+ out_point.mY = window_rect.mTop;
+ valid = FALSE;
+ }
+ else
+ {
+ out_point.mY = int_y;
+ }
+ return valid;
+ }
+ else
+ {
+ out_point.mX = int_x;
+ out_point.mY = int_y;
+
+ if (int_x < window_rect.mLeft)
+ {
+ valid = FALSE;
+ }
+ else if (int_x > window_rect.mRight)
+ {
+ valid = FALSE;
+ }
+ if (int_y < window_rect.mBottom)
+ {
+ valid = FALSE;
+ }
+ else if (int_y > window_rect.mTop)
+ {
+ valid = FALSE;
+ }
+
+ return in_front && valid;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// the agent's region space to the nearest edge in screen coordinates.
+// Returns TRUE if projection succeeds.
+BOOL LLViewerCamera::projectPosAgentToScreenEdge(const LLVector3 &pos_agent,
+ LLCoordGL &out_point) const
+{
+ LLVector3 dir_to_point = pos_agent - getOrigin();
+ dir_to_point /= dir_to_point.magVec();
+
+ BOOL in_front = TRUE;
+ if (dir_to_point * getAtAxis() < 0.f)
+ {
+ in_front = FALSE;
+ }
+
+ GLdouble x, y, z; // object's window coords, GL-style
+ if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY],
+ pos_agent.mV[VZ], gGLModelView,
+ mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x, &y, &z))
+ {
+ x /= gViewerWindow->getDisplayScale().mV[VX];
+ y /= gViewerWindow->getDisplayScale().mV[VY];
+ // should now have the x,y coords of grab_point in screen space
+ const LLRect& window_rect = gViewerWindow->getVirtualWindowRect();
+
+ // ...sanity check
+ S32 int_x = lltrunc(x);
+ S32 int_y = lltrunc(y);
+
+ // find the center
+ GLdouble center_x = (GLdouble)(0.5f * (window_rect.mLeft + window_rect.mRight));
+ GLdouble center_y = (GLdouble)(0.5f * (window_rect.mBottom + window_rect.mTop));
+
+ if (x == center_x && y == center_y)
+ {
+ // can't project to edge from exact center
+ return FALSE;
+ }
+
+ // find the line from center to local
+ GLdouble line_x = x - center_x;
+ GLdouble line_y = y - center_y;
+
+ int_x = lltrunc(center_x);
+ int_y = lltrunc(center_y);
+
+
+ if (0.f == line_x)
+ {
+ // the slope of the line is undefined
+ if (line_y > 0.f)
+ {
+ int_y = window_rect.mTop;
+ }
+ else
+ {
+ int_y = window_rect.mBottom;
+ }
+ }
+ else if (0 == window_rect.getWidth())
+ {
+ // the diagonal slope of the view is undefined
+ if (y < window_rect.mBottom)
+ {
+ int_y = window_rect.mBottom;
+ }
+ else if ( y > window_rect.mTop)
+ {
+ int_y = window_rect.mTop;
+ }
+ }
+ else
+ {
+ F32 line_slope = (F32)(line_y / line_x);
+ F32 rect_slope = ((F32)window_rect.getHeight()) / ((F32)window_rect.getWidth());
+
+ if (fabs(line_slope) > rect_slope)
+ {
+ if (line_y < 0.f)
+ {
+ // bottom
+ int_y = window_rect.mBottom;
+ }
+ else
+ {
+ // top
+ int_y = window_rect.mTop;
+ }
+ int_x = lltrunc(((GLdouble)int_y - center_y) / line_slope + center_x);
+ }
+ else if (fabs(line_slope) < rect_slope)
+ {
+ if (line_x < 0.f)
+ {
+ // left
+ int_x = window_rect.mLeft;
+ }
+ else
+ {
+ // right
+ int_x = window_rect.mRight;
+ }
+ int_y = lltrunc(((GLdouble)int_x - center_x) * line_slope + center_y);
+ }
+ else
+ {
+ // exactly parallel ==> push to the corners
+ if (line_x > 0.f)
+ {
+ int_x = window_rect.mRight;
+ }
+ else
+ {
+ int_x = window_rect.mLeft;
+ }
+ if (line_y > 0.0f)
+ {
+ int_y = window_rect.mTop;
+ }
+ else
+ {
+ int_y = window_rect.mBottom;
+ }
+ }
+ }
+ if (!in_front)
+ {
+ int_x = window_rect.mLeft + window_rect.mRight - int_x;
+ int_y = window_rect.mBottom + window_rect.mTop - int_y;
+ }
+ out_point.mX = int_x;
+ out_point.mY = int_y;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void LLViewerCamera::getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right)
+{
+ LLVector3 to_vec = pos_agent - getOrigin();
+
+ F32 at_dist = to_vec * getAtAxis();
+
+ F32 height_meters = at_dist* (F32)tan(getView()/2.f);
+ F32 height_pixels = getViewHeightInPixels()/2.f;
+
+ F32 pixel_aspect = gViewerWindow->getWindow()->getPixelAspectRatio();
+
+ F32 meters_per_pixel = height_meters / height_pixels;
+ up = getUpAxis() * meters_per_pixel * gViewerWindow->getDisplayScale().mV[VY];
+ right = -1.f * pixel_aspect * meters_per_pixel * getLeftAxis() * gViewerWindow->getDisplayScale().mV[VX];
+}
+
+LLVector3 LLViewerCamera::roundToPixel(const LLVector3 &pos_agent)
+{
+ F32 dist = (pos_agent - getOrigin()).magVec();
+ // Convert to screen space and back, preserving the depth.
+ LLCoordGL screen_point;
+ if (!projectPosAgentToScreen(pos_agent, screen_point, FALSE))
+ {
+ // Off the screen, just return the original position.
+ return pos_agent;
+ }
+
+ LLVector3 ray_dir;
+
+ projectScreenToPosAgent(screen_point.mX, screen_point.mY, &ray_dir);
+ ray_dir -= getOrigin();
+ ray_dir.normVec();
+
+ LLVector3 pos_agent_rounded = getOrigin() + ray_dir*dist;
+
+ /*
+ LLVector3 pixel_x, pixel_y;
+ getPixelVectors(pos_agent_rounded, pixel_y, pixel_x);
+ pos_agent_rounded += 0.5f*pixel_x, 0.5f*pixel_y;
+ */
+ return pos_agent_rounded;
+}
+
+BOOL LLViewerCamera::cameraUnderWater() const
+{
+ return getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight();
+}
+
+BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
+{
+ S32 i, num_faces;
+ LLDrawable* drawablep = volumep->mDrawable;
+
+ if (!drawablep)
+ {
+ return FALSE;
+ }
+
+ num_faces = drawablep->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ LLStrider<LLVector3> vertices;
+ LLFace* face = drawablep->getFace(i);
+ face->getVertices(vertices);
+
+ for (S32 v = 0; v < (S32)drawablep->getFace(i)->getGeomCount(); v++)
+ {
+ LLVector3 vec = vertices[v];
+ if (!face->isState(LLFace::GLOBAL))
+ {
+ vec = vec*face->getRenderMatrix();
+ }
+
+ BOOL in_frustum = pointInFrustum(vec) > 0;
+
+ if ( !in_frustum && all_verts ||
+ in_frustum && !all_verts)
+ {
+ return !all_verts;
+ }
+ }
+ }
+ return all_verts;
+}
diff --git a/indra/newview/llviewercamera.h b/indra/newview/llviewercamera.h
new file mode 100644
index 0000000000..6797103997
--- /dev/null
+++ b/indra/newview/llviewercamera.h
@@ -0,0 +1,95 @@
+/**
+ * @file llviewercamera.h
+ * @brief LLViewerCamera class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERCAMERA_H
+#define LL_LLVIEWERCAMERA_H
+
+#include "llcamera.h"
+#include "lltimer.h"
+#include "llstat.h"
+#include "m4math.h"
+
+class LLCoordGL;
+class LLViewerObject;
+
+// This rotation matrix moves the default OpenGL reference frame
+// (-Z at, Y up) to Cory's favorite reference frame (X at, Z up)
+const F32 OGL_TO_CFR_ROTATION[16] = { 0.f, 0.f, -1.f, 0.f, // -Z becomes X
+ -1.f, 0.f, 0.f, 0.f, // -X becomes Y
+ 0.f, 1.f, 0.f, 0.f, // Y becomes Z
+ 0.f, 0.f, 0.f, 1.f };
+
+class LLViewerCamera : public LLCamera
+{
+public:
+ LLViewerCamera();
+
+// const LLVector3 &getPositionAgent() const;
+// const LLVector3d &getPositionGlobal() const;
+
+ void updateCameraLocation(const LLVector3 &center,
+ const LLVector3 &up_direction,
+ const LLVector3 &point_of_interest);
+
+ void updateFrustumPlanes();
+ void setPerspective(BOOL for_selection, S32 x, S32 y_from_bot, S32 width, S32 height, BOOL limit_select_distance, F32 z_near = 0, F32 z_far = 0);
+
+ const LLMatrix4 &getProjection() const;
+ const LLMatrix4 &getModelview() const;
+
+ // Warning! These assume the current global matrices are correct
+ void projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent ) const;
+ BOOL projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp = TRUE) const;
+ BOOL projectPosAgentToScreenEdge(const LLVector3 &pos_agent, LLCoordGL &out_point) const;
+
+
+ LLStat *getVelocityStat() { return &mVelocityStat; }
+ LLStat *getAngularVelocityStat() { return &mAngularVelocityStat; }
+
+ void getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right);
+ LLVector3 roundToPixel(const LLVector3 &pos_agent);
+
+ void setDefaultFOV(F32 fov) { mCameraFOVDefault = fov; }
+ F32 getDefaultFOV() { return mCameraFOVDefault; }
+
+ BOOL cameraUnderWater() const;
+
+ const LLVector3 &getPointOfInterest() { return mLastPointOfInterest; }
+ BOOL areVertsVisible(LLViewerObject* volumep, BOOL all_verts);
+ F32 getPixelMeterRatio() const { return mPixelMeterRatio; }
+ S32 getScreenPixelArea() const { return mScreenPixelArea; }
+
+ void setZoomParameters(F32 factor, S16 subregion) { mZoomFactor = factor; mZoomSubregion = subregion; }
+ F32 getZoomFactor() { return mZoomFactor; }
+ S16 getZoomSubRegion() { return mZoomSubregion; }
+
+protected:
+ void calcProjection(const F32 far_distance) const;
+
+ LLStat mVelocityStat;
+ LLStat mAngularVelocityStat;
+ mutable LLMatrix4 mProjectionMatrix; // Cache of perspective matrix
+ mutable LLMatrix4 mModelviewMatrix;
+ F32 mCameraFOVDefault;
+ LLVector3 mLastPointOfInterest;
+ F32 mPixelMeterRatio; // Divide by distance from camera to get pixels per meter at that distance.
+ S32 mScreenPixelArea; // Pixel area of entire window
+ F32 mZoomFactor;
+ S16 mZoomSubregion;
+
+public:
+ static BOOL sDisableCameraConstraints;
+ F64 mGLProjectionMatrix[16];
+
+};
+
+extern LLViewerCamera *gCamera;
+extern F64 gGLModelView[16];
+extern S32 gGLViewport[4];
+
+#endif // LL_LLVIEWERCAMERA_H
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
new file mode 100644
index 0000000000..c0de0c872a
--- /dev/null
+++ b/indra/newview/llviewercontrol.cpp
@@ -0,0 +1,465 @@
+/**
+ * @file llviewercontrol.cpp
+ * @brief Viewer configuration
+ * @author Richard Nelson
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewercontrol.h"
+
+#include "indra_constants.h"
+
+#include "v3math.h"
+#include "v3dmath.h"
+#include "llrect.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v3color.h"
+
+#include "llfloater.h"
+#include "llvieweruictrlfactory.h"
+#include "llfirstuse.h"
+#include "llcombobox.h"
+#include "llspinctrl.h"
+#include "llcolorswatch.h"
+
+LLFloaterSettingsDebug* LLFloaterSettingsDebug::sInstance = NULL;
+
+LLControlGroup gSavedSettings; // saved at end of session
+LLControlGroup gSavedPerAccountSettings; // saved at end of session
+LLControlGroup gViewerArt; // read-only
+LLControlGroup gColors; // read-only
+LLControlGroup gCrashSettings; // saved at end of session
+
+LLString gLastRunVersion;
+LLString gCurrentVersion;
+
+char gSettingsFileName[LL_MAX_PATH];
+char gPerAccountSettingsFileName[LL_MAX_PATH] = "";
+
+LLFloaterSettingsDebug::LLFloaterSettingsDebug() : LLFloater("Configuration Editor")
+{
+}
+
+LLFloaterSettingsDebug::~LLFloaterSettingsDebug()
+{
+ sInstance = NULL;
+}
+
+BOOL LLFloaterSettingsDebug::postBuild()
+{
+ LLComboBox* settings_combo = LLUICtrlFactory::getComboBoxByName(this, "settings_combo");
+
+ LLControlGroup::ctrl_name_table_t::iterator name_it;
+ for(name_it = gSavedSettings.mNameTable.begin(); name_it != gSavedSettings.mNameTable.end(); ++name_it)
+ {
+ settings_combo->add(name_it->first, (void*)name_it->second);
+ }
+ for(name_it = gSavedPerAccountSettings.mNameTable.begin(); name_it != gSavedPerAccountSettings.mNameTable.end(); ++name_it)
+ {
+ settings_combo->add(name_it->first, (void*)name_it->second);
+ }
+ for(name_it = gColors.mNameTable.begin(); name_it != gColors.mNameTable.end(); ++name_it)
+ {
+ settings_combo->add(name_it->first, (void*)name_it->second);
+ }
+ settings_combo->sortByName();
+ settings_combo->setCommitCallback(onSettingSelect);
+ settings_combo->setCallbackUserData(this);
+ settings_combo->updateSelection();
+
+ childSetCommitCallback("val_spinner_1", onCommitSettings);
+ childSetUserData("val_spinner_1", this);
+ childSetCommitCallback("val_spinner_2", onCommitSettings);
+ childSetUserData("val_spinner_2", this);
+ childSetCommitCallback("val_spinner_3", onCommitSettings);
+ childSetUserData("val_spinner_3", this);
+ childSetCommitCallback("val_spinner_4", onCommitSettings);
+ childSetUserData("val_spinner_4", this);
+ childSetCommitCallback("val_text", onCommitSettings);
+ childSetUserData("val_text", this);
+ childSetCommitCallback("boolean_combo", onCommitSettings);
+ childSetUserData("boolean_combo", this);
+ childSetCommitCallback("color_swatch", onCommitSettings);
+ childSetUserData("color_swatch", this);
+
+ mComment = (LLTextEditor*)getChildByName("comment_text");
+ return TRUE;
+}
+
+void LLFloaterSettingsDebug::draw()
+{
+ LLComboBox* settings_combo = (LLComboBox*)getChildByName("settings_combo");
+ LLControlBase* controlp = (LLControlBase*)settings_combo->getCurrentUserdata();
+ updateControl(controlp);
+
+ LLFloater::draw();
+}
+
+//static
+void LLFloaterSettingsDebug::show(void*)
+{
+ if (sInstance == NULL)
+ {
+ sInstance = new LLFloaterSettingsDebug();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_settings_debug.xml");
+ }
+
+ sInstance->open();
+}
+
+//static
+void LLFloaterSettingsDebug::onSettingSelect(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterSettingsDebug* floaterp = (LLFloaterSettingsDebug*)user_data;
+ LLComboBox* combo_box = (LLComboBox*)ctrl;
+ LLControlBase* controlp = (LLControlBase*)combo_box->getCurrentUserdata();
+
+ floaterp->updateControl(controlp);
+}
+
+//static
+void LLFloaterSettingsDebug::onCommitSettings(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterSettingsDebug* floaterp = (LLFloaterSettingsDebug*)user_data;
+
+ LLComboBox* settings_combo = (LLComboBox*)floaterp->getChildByName("settings_combo");
+ LLControlBase* controlp = (LLControlBase*)settings_combo->getCurrentUserdata();
+
+ LLVector3 vector;
+ LLVector3d vectord;
+ LLRect rect;
+ LLColor4 col4;
+ LLColor3 col3;
+ LLColor4U col4U;
+ LLColor4 color_with_alpha;
+
+ switch(controlp->type())
+ {
+ case TYPE_U32:
+ controlp->set(floaterp->childGetValue("val_spinner_1"));
+ break;
+ case TYPE_S32:
+ controlp->set(floaterp->childGetValue("val_spinner_1"));
+ break;
+ case TYPE_F32:
+ controlp->set(LLSD(floaterp->childGetValue("val_spinner_1").asReal()));
+ break;
+ case TYPE_BOOLEAN:
+ controlp->set(floaterp->childGetValue("boolean_combo"));
+ break;
+ case TYPE_STRING:
+ controlp->set(LLSD(floaterp->childGetValue("val_text").asString()));
+ break;
+ case TYPE_VEC3:
+ vector.mV[VX] = (F32)floaterp->childGetValue("val_spinner_1").asReal();
+ vector.mV[VY] = (F32)floaterp->childGetValue("val_spinner_2").asReal();
+ vector.mV[VZ] = (F32)floaterp->childGetValue("val_spinner_3").asReal();
+ controlp->set(vector.getValue());
+ break;
+ case TYPE_VEC3D:
+ vectord.mdV[VX] = floaterp->childGetValue("val_spinner_1").asReal();
+ vectord.mdV[VY] = floaterp->childGetValue("val_spinner_2").asReal();
+ vectord.mdV[VZ] = floaterp->childGetValue("val_spinner_3").asReal();
+ controlp->set(vectord.getValue());
+ break;
+ case TYPE_RECT:
+ rect.mLeft = floaterp->childGetValue("val_spinner_1").asInteger();
+ rect.mRight = floaterp->childGetValue("val_spinner_2").asInteger();
+ rect.mBottom = floaterp->childGetValue("val_spinner_3").asInteger();
+ rect.mTop = floaterp->childGetValue("val_spinner_4").asInteger();
+ controlp->set(rect.getValue());
+ break;
+ case TYPE_COL4:
+ col3.setValue(floaterp->childGetValue("color_swatch"));
+ col4 = LLColor4(col3, (F32)floaterp->childGetValue("val_spinner_4").asReal());
+ controlp->set(col4.getValue());
+ break;
+ case TYPE_COL3:
+ controlp->set(floaterp->childGetValue("color_swatch"));
+ //col3.mV[VRED] = (F32)floaterp->childGetValue("val_spinner_1").asC();
+ //col3.mV[VGREEN] = (F32)floaterp->childGetValue("val_spinner_2").asReal();
+ //col3.mV[VBLUE] = (F32)floaterp->childGetValue("val_spinner_3").asReal();
+ //controlp->set(col3.getValue());
+ break;
+ case TYPE_COL4U:
+ col3.setValue(floaterp->childGetValue("color_swatch"));
+ col4U.setVecScaleClamp(col3);
+ col4U.mV[VALPHA] = floaterp->childGetValue("val_spinner_4").asInteger();
+ controlp->set(col4U.getValue());
+ break;
+ default:
+ break;
+ }
+}
+
+// we've switched controls, or doing per-frame update, so update spinners, etc.
+void LLFloaterSettingsDebug::updateControl(LLControlBase* controlp)
+{
+ LLSpinCtrl* spinner1 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_1");
+ LLSpinCtrl* spinner2 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_2");
+ LLSpinCtrl* spinner3 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_3");
+ LLSpinCtrl* spinner4 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_4");
+ LLColorSwatchCtrl* color_swatch = LLUICtrlFactory::getColorSwatchByName(this, "color_swatch");
+ spinner1->setVisible(FALSE);
+ spinner2->setVisible(FALSE);
+ spinner3->setVisible(FALSE);
+ spinner4->setVisible(FALSE);
+ color_swatch->setVisible(FALSE);
+ childSetVisible("val_text", FALSE);
+ childSetVisible("boolean_combo", FALSE);
+ mComment->setText("");
+
+ if (controlp)
+ {
+ eControlType type = controlp->type();
+ mComment->setText(controlp->getComment());
+ spinner1->setMaxValue(F32_MAX);
+ spinner2->setMaxValue(F32_MAX);
+ spinner3->setMaxValue(F32_MAX);
+ spinner4->setMaxValue(F32_MAX);
+ spinner1->setMinValue(-F32_MAX);
+ spinner2->setMinValue(-F32_MAX);
+ spinner3->setMinValue(-F32_MAX);
+ spinner4->setMinValue(-F32_MAX);
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setIncrement(0.1f);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setIncrement(0.1f);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setIncrement(0.1f);
+ }
+ if (!spinner4->hasFocus())
+ {
+ spinner4->setIncrement(0.1f);
+ }
+
+ LLSD sd = controlp->get();
+ switch(type)
+ {
+ case TYPE_U32:
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("value");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setValue(sd);
+ spinner1->setMinValue((F32)U32_MIN);
+ spinner1->setMaxValue((F32)U32_MAX);
+ spinner1->setIncrement(1.f);
+ spinner1->setPrecision(0);
+ }
+ break;
+ case TYPE_S32:
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("value");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setValue(sd);
+ spinner1->setMinValue((F32)S32_MIN);
+ spinner1->setMaxValue((F32)S32_MAX);
+ spinner1->setIncrement(1.f);
+ spinner1->setPrecision(0);
+ }
+ break;
+ case TYPE_F32:
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("value");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(3);
+ spinner1->setValue(sd);
+ }
+ break;
+ case TYPE_BOOLEAN:
+ childSetVisible("boolean_combo", TRUE);
+
+ if (!childHasFocus("boolean_combo"))
+ {
+ if (sd.asBoolean())
+ {
+ childSetValue("boolean_combo", LLSD("true"));
+ }
+ else
+ {
+ childSetValue("boolean_combo", LLSD(""));
+ }
+ }
+ break;
+ case TYPE_STRING:
+ childSetVisible("val_text", TRUE);
+ if (!childHasFocus("val_text"))
+ {
+ childSetValue("val_text", sd);
+ }
+ break;
+ case TYPE_VEC3:
+ {
+ LLVector3 v;
+ v.setValue(sd);
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("X");
+ spinner2->setVisible(TRUE);
+ spinner2->setLabel("Y");
+ spinner3->setVisible(TRUE);
+ spinner3->setLabel("Z");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(3);
+ spinner1->setValue(v[VX]);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setPrecision(3);
+ spinner2->setValue(v[VY]);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setPrecision(3);
+ spinner3->setValue(v[VZ]);
+ }
+ break;
+ }
+ case TYPE_VEC3D:
+ {
+ LLVector3d v;
+ v.setValue(sd);
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("X");
+ spinner2->setVisible(TRUE);
+ spinner2->setLabel("Y");
+ spinner3->setVisible(TRUE);
+ spinner3->setLabel("Z");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(3);
+ spinner1->setValue(v[VX]);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setPrecision(3);
+ spinner2->setValue(v[VY]);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setPrecision(3);
+ spinner3->setValue(v[VZ]);
+ }
+ break;
+ }
+ case TYPE_RECT:
+ {
+ LLRect r;
+ r.setValue(sd);
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("Left");
+ spinner2->setVisible(TRUE);
+ spinner2->setLabel("Right");
+ spinner3->setVisible(TRUE);
+ spinner3->setLabel("Bottom");
+ spinner4->setVisible(TRUE);
+ spinner4->setLabel("Top");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(0);
+ spinner1->setValue(r.mLeft);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setPrecision(0);
+ spinner2->setValue(r.mRight);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setPrecision(0);
+ spinner3->setValue(r.mBottom);
+ }
+ if (!spinner4->hasFocus())
+ {
+ spinner4->setPrecision(0);
+ spinner4->setValue(r.mTop);
+ }
+
+ spinner1->setMinValue((F32)S32_MIN);
+ spinner1->setMaxValue((F32)S32_MAX);
+ spinner1->setIncrement(1.f);
+
+ spinner2->setMinValue((F32)S32_MIN);
+ spinner2->setMaxValue((F32)S32_MAX);
+ spinner2->setIncrement(1.f);
+
+ spinner3->setMinValue((F32)S32_MIN);
+ spinner3->setMaxValue((F32)S32_MAX);
+ spinner3->setIncrement(1.f);
+
+ spinner4->setMinValue((F32)S32_MIN);
+ spinner4->setMaxValue((F32)S32_MAX);
+ spinner4->setIncrement(1.f);
+ break;
+ }
+ case TYPE_COL4:
+ {
+ LLColor4 clr;
+ clr.setValue(sd);
+ color_swatch->setVisible(TRUE);
+ // only set if changed so color picker doesn't update
+ if(clr != LLColor4(color_swatch->getValue()))
+ {
+ color_swatch->set(LLColor4(sd), TRUE, FALSE);
+ }
+ spinner4->setVisible(TRUE);
+ spinner4->setLabel("Alpha");
+ if (!spinner4->hasFocus())
+ {
+ spinner4->setPrecision(3);
+ spinner4->setValue(clr.mV[VALPHA]);
+ }
+ break;
+ }
+ case TYPE_COL3:
+ {
+ LLColor3 clr;
+ clr.setValue(sd);
+ color_swatch->setVisible(TRUE);
+ color_swatch->setValue(sd);
+ break;
+ }
+ case TYPE_COL4U:
+ {
+ LLColor4U clr;
+ clr.setValue(sd);
+ color_swatch->setVisible(TRUE);
+ if(LLColor4(clr) != LLColor4(color_swatch->getValue()))
+ {
+ color_swatch->set(LLColor4(clr), TRUE, FALSE);
+ }
+ spinner4->setVisible(TRUE);
+ spinner4->setLabel("Alpha");
+ if(!spinner4->hasFocus())
+ {
+ spinner4->setPrecision(0);
+ spinner4->setValue(clr.mV[VALPHA]);
+ }
+
+ spinner4->setMinValue(0);
+ spinner4->setMaxValue(255);
+ spinner4->setIncrement(1.f);
+
+ break;
+ }
+ default:
+ mComment->setText("unknown");
+ break;
+ }
+ }
+
+}
diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h
new file mode 100644
index 0000000000..a6692df7fb
--- /dev/null
+++ b/indra/newview/llviewercontrol.h
@@ -0,0 +1,60 @@
+/**
+ * @file llviewercontrol.h
+ * @brief references to viewer-specific control files
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERCONTROL_H
+#define LL_LLVIEWERCONTROL_H
+
+#include "llcontrol.h"
+#include "llfloater.h"
+#include "lltexteditor.h"
+
+class LLFloaterSettingsDebug : public LLFloater
+{
+public:
+ LLFloaterSettingsDebug();
+ virtual ~LLFloaterSettingsDebug();
+
+ virtual BOOL postBuild();
+ virtual void draw();
+
+ void updateControl(LLControlBase* control);
+
+ static void show(void*);
+ static void onSettingSelect(LLUICtrl* ctrl, void* user_data);
+ static void onCommitSettings(LLUICtrl* ctrl, void* user_data);
+
+protected:
+ static LLFloaterSettingsDebug* sInstance;
+ LLTextEditor* mComment;
+};
+
+//setting variables are declared in this function
+void declare_settings();
+
+// saved at end of session
+extern LLControlGroup gSavedSettings;
+extern LLControlGroup gSavedPerAccountSettings;
+
+// Read-only
+extern LLControlGroup gViewerArt;
+
+// Read-only
+extern LLControlGroup gColors;
+
+// Saved at end of session
+extern LLControlGroup gCrashSettings;
+
+// Set after settings loaded
+extern LLString gLastRunVersion;
+extern LLString gCurrentVersion;
+
+extern char gSettingsFileName[LL_MAX_PATH];
+extern char gPerAccountSettingsFileName[LL_MAX_PATH];
+extern const char *DEFAULT_SETTINGS_FILE;
+
+#endif // LL_LLVIEWERCONTROL_H
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
new file mode 100644
index 0000000000..d027f7dd54
--- /dev/null
+++ b/indra/newview/llviewerdisplay.cpp
@@ -0,0 +1,832 @@
+/**
+ * @file llviewerdisplay.cpp
+ * @brief LLViewerDisplay class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llcoord.h"
+#include "llcriticaldamp.h"
+#include "lldir.h"
+#include "lldynamictexture.h"
+#include "lldrawpoolalpha.h"
+#include "llframestats.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llhudmanager.h"
+#include "llimagebmp.h"
+#include "llimagegl.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llstartup.h"
+#include "lltoolfocus.h"
+#include "lltoolmgr.h"
+#include "lltooldraganddrop.h"
+#include "lltoolpie.h"
+#include "lltracker.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvograss.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llstartup.h"
+#include "llfasttimer.h"
+#include "llfloatertools.h"
+#include "llviewerimagelist.h"
+#include "llfocusmgr.h"
+
+extern U32 gFrameCount;
+extern LLPointer<LLImageGL> gStartImageGL;
+extern LLPointer<LLImageGL> gDisconnectedImagep;
+extern BOOL gLogoutRequestSent;
+extern LLTimer gLogoutTimer;
+extern BOOL gHaveSavedSnapshot;
+extern BOOL gDisplaySwapBuffers;
+
+// used to toggle renderer back on after teleport
+const F32 TELEPORT_RENDER_DELAY = 20.f; // Max time a teleport is allowed to take before we raise the curtain
+const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived.
+BOOL gTeleportDisplay = FALSE;
+LLFrameTimer gTeleportDisplayTimer;
+LLFrameTimer gTeleportArrivalTimer;
+const F32 RESTORE_GL_TIME = 5.f; // Wait this long while reloading textures before we raise the curtain
+
+BOOL gForceRenderLandFence = FALSE;
+BOOL gDisplaySwapBuffers = FALSE;
+
+// Rendering stuff
+void pre_show_depth_buffer();
+void post_show_depth_buffer();
+void render_ui_and_swap();
+void render_ui_3d();
+void render_ui_2d();
+void render_disconnected_background();
+
+void process_keystrokes_async(); // in viewer.cpp
+
+void display_startup()
+{
+ if ( !gViewerWindow->getActive()
+ || !gViewerWindow->mWindow->getVisible()
+ || gViewerWindow->mWindow->getMinimized()
+ || gNoRender )
+ {
+ return;
+ }
+
+ LLDynamicTexture::updateAllInstances();
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ LLGLSDefault gls_default;
+ LLGLSUIDefault gls_ui;
+ gPipeline.disableLights();
+
+ gViewerWindow->setup2DRender();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ gViewerWindow->draw();
+ gViewerWindow->mWindow->swapBuffers();
+}
+
+
+void display_update_camera()
+{
+ // TODO: cut draw distance down if customizing avatar?
+ // TODO: cut draw distance on per-parcel basis?
+
+ // Cut draw distance in half when customizing avatar,
+ // but on the viewer only.
+ F32 final_far = gAgent.mDrawDistance;
+ if (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode())
+ {
+ final_far *= 0.5f;
+ }
+ gCamera->setFar(final_far);
+ gViewerWindow->setup3DRender();
+
+ // Update land visibility too
+ if (gWorldp)
+ {
+ gWorldp->setLandFarClip(final_far);
+ }
+}
+
+
+// Paint the display!
+void display(BOOL rebuild, F32 zoom_factor, int subfield)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER);
+
+ LLGLSDefault gls_default;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE, GL_LEQUAL);
+
+ // No clue where this is getting unset, but safe enough to reset it here.
+ for (S32 j = 7; j >=0; j--)
+ {
+ glActiveTextureARB(GL_TEXTURE0_ARB+j);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB+j);
+ j == 0 ? glEnable(GL_TEXTURE_2D) : glDisable(GL_TEXTURE_2D);
+ }
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+#endif
+
+ gPipeline.disableLights();
+
+ // Don't draw if the window is hidden or minimized.
+ // In fact, must explicitly check the minimized state before drawing.
+ // Attempting to draw into a minimized window causes a GL error. JC
+ if ( !gViewerWindow->getActive()
+ || !gViewerWindow->mWindow->getVisible()
+ || gViewerWindow->mWindow->getMinimized() )
+ {
+ // Clean up memory the pools may have allocated
+ if (rebuild)
+ {
+ if (!gViewerWindow->renderingFastFrame())
+ {
+ gFrameStats.start(LLFrameStats::REBUILD);
+ gPipeline.rebuildPools();
+ }
+ }
+ return;
+ }
+
+ gViewerWindow->checkSettings();
+ gViewerWindow->performPick();
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+#endif
+
+ //////////////////////////////////////////////////////////
+ //
+ // Logic for forcing window updates if we're in drone mode.
+ //
+
+ if (gNoRender)
+ {
+#if LL_WINDOWS
+ static F32 last_update_time = 0.f;
+ if ((gFrameTimeSeconds - last_update_time) > 1.f)
+ {
+ InvalidateRect(llwindow_get_hwnd(gViewerWindow->getWindow()), NULL, FALSE);
+ last_update_time = gFrameTimeSeconds;
+ }
+#elif LL_DARWIN
+ // MBW -- Do something clever here.
+#endif
+ // Not actually rendering, don't bother.
+ return;
+ }
+
+
+ //
+ // Bail out if we're in the startup state and don't want to try to
+ // render the world.
+ //
+ if (gStartupState < STATE_STARTED)
+ {
+ display_startup();
+ return;
+ }
+
+ //LLGLState::verify(FALSE);
+
+ /////////////////////////////////////////////////
+ //
+ // Update GL Texture statistics (used for discard logic?)
+ //
+
+ gFrameStats.start(LLFrameStats::UPDATE_TEX_STATS);
+ stop_glerror();
+
+ LLImageGL::updateStats(gFrameTimeSeconds);
+
+ LLVOAvatar::sRenderName = gSavedSettings.getS32("RenderName");
+ gPipeline.mBackfaceCull = TRUE;
+ gFrameCount++;
+
+ //////////////////////////////////////////////////////////
+ //
+ // Display start screen if we're teleporting, and skip render
+ //
+
+ if (gTeleportDisplay)
+ {
+ const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived.
+
+ S32 attach_count = 0;
+ if (gAgent.getAvatarObject())
+ {
+ attach_count = gAgent.getAvatarObject()->getAttachmentCount();
+ }
+ F32 teleport_save_time = TELEPORT_EXPIRY + TELEPORT_EXPIRY_PER_ATTACHMENT * attach_count;
+ F32 teleport_elapsed = gTeleportDisplayTimer.getElapsedTimeF32();
+ F32 teleport_percent = teleport_elapsed * (100.f / teleport_save_time);
+ if( (gAgent.getTeleportState() != LLAgent::TELEPORT_START) && (teleport_percent > 100.f) )
+ {
+ // Give up. Don't keep the UI locked forever.
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ gAgent.setTeleportMessage("");
+ }
+
+ const LLString& message = gAgent.getTeleportMessage();
+ switch( gAgent.getTeleportState() )
+ {
+ case LLAgent::TELEPORT_START:
+ // Transition to REQUESTED. Viewer has sent some kind
+ // of TeleportRequest to the source simulator
+ gTeleportDisplayTimer.reset();
+ gViewerWindow->setShowProgress(TRUE);
+ gViewerWindow->setProgressPercent(0);
+ gAgent.setTeleportState( LLAgent::TELEPORT_REQUESTED );
+ gAgent.setTeleportMessage("Requesting Teleport...");
+ break;
+
+ case LLAgent::TELEPORT_REQUESTED:
+ // Waiting for source simulator to respond
+ gViewerWindow->setProgressPercent( llmin(teleport_percent, 37.5f) );
+ gViewerWindow->setProgressString(message);
+ break;
+
+ case LLAgent::TELEPORT_MOVING:
+ // Viewer has received destination location from source simulator
+ gViewerWindow->setProgressPercent( llmin(teleport_percent, 75.f) );
+ gViewerWindow->setProgressString(message);
+ break;
+
+ case LLAgent::TELEPORT_START_ARRIVAL:
+ // Transition to ARRIVING. Viewer has received avatar update, etc., from destination simulator
+ gTeleportArrivalTimer.reset();
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
+ gViewerWindow->setProgressPercent(75.f);
+ gAgent.setTeleportState( LLAgent::TELEPORT_ARRIVING );
+ gAgent.setTeleportMessage("Arriving...");
+ gImageList.mForceResetTextureStats = TRUE;
+ break;
+
+ case LLAgent::TELEPORT_ARRIVING:
+ // Make the user wait while content "pre-caches"
+ {
+ F32 arrival_fraction = (gTeleportArrivalTimer.getElapsedTimeF32() / TELEPORT_ARRIVAL_DELAY);
+ if( arrival_fraction > 1.f )
+ {
+ arrival_fraction = 1.f;
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
+ gViewerWindow->setProgressPercent( arrival_fraction * 25.f + 75.f);
+ gViewerWindow->setProgressString(message);
+ }
+ break;
+
+ case LLAgent::TELEPORT_CANCELLING:
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
+ gViewerWindow->setProgressPercent( 100.f );
+ gViewerWindow->setProgressString("Canceling...");
+ break;
+
+ case LLAgent::TELEPORT_NONE:
+ // No teleport in progress
+ gViewerWindow->setShowProgress(FALSE);
+ gTeleportDisplay = FALSE;
+ break;
+ }
+ }
+ else if(gLogoutRequestSent)
+ {
+ F32 percent_done = gLogoutTimer.getElapsedTimeF32() * 100.f / gLogoutMaxTime;
+ if (percent_done > 100.f)
+ {
+ percent_done = 100.f;
+ }
+
+ if( gQuit )
+ {
+ percent_done = 100.f;
+ }
+
+ gViewerWindow->setProgressPercent( percent_done );
+ }
+ else
+ if (gRestoreGL)
+ {
+ F32 percent_done = gRestoreGLTimer.getElapsedTimeF32() * 100.f / RESTORE_GL_TIME;
+ if( percent_done > 100.f )
+ {
+ gViewerWindow->setShowProgress(FALSE);
+ gRestoreGL = FALSE;
+ }
+ else
+ {
+
+ if( gQuit )
+ {
+ percent_done = 100.f;
+ }
+
+ gViewerWindow->setProgressPercent( percent_done );
+ }
+ }
+
+ //////////////////////////
+ //
+ // Prepare for the next frame
+ //
+
+ // Hmm... Should this be moved elsewhere? - djs 09/09/02
+ // do render-to-texture stuff here
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES))
+ {
+// LLFastTimer t(LLFastTimer::FTM_UPDATE_TEXTURES);
+ if (LLDynamicTexture::updateAllInstances())
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ }
+
+ if (rebuild)
+ {
+ if (gViewerWindow->renderingFastFrame())
+ {
+ gFrameStats.start(LLFrameStats::STATE_SORT);
+ gFrameStats.start(LLFrameStats::REBUILD);
+ }
+ }
+
+ /////////////////////////////
+ //
+ // Update the camera
+ //
+ //
+
+ gCamera->setZoomParameters(zoom_factor, subfield);
+
+ //////////////////////////
+ //
+ // clear the next buffer
+ // (must follow dynamic texture writing since that uses the frame buffer)
+ //
+
+ if (gDisconnected)
+ {
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ render_disconnected_background();
+ }
+ else if (!gViewerWindow->isPickPending())
+ {
+ glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+ }
+ gViewerWindow->setupViewport();
+
+
+ //////////////////////////
+ //
+ // Set rendering options
+ //
+ //
+ stop_glerror();
+ if (gSavedSettings.getBOOL("ShowDepthBuffer"))
+ {
+ pre_show_depth_buffer();
+ }
+
+ if(gUseWireframe)//gSavedSettings.getBOOL("UseWireframe"))
+ {
+ glClearColor(0.5f, 0.5f, 0.5f, 0.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ }
+ stop_glerror();
+
+ ///////////////////////////////////////
+ //
+ // Slam lighting parameters back to our defaults.
+ // Note that these are not the same as GL defaults...
+
+ stop_glerror();
+ F32 one[4] = {1.f, 1.f, 1.f, 1.f};
+ glLightModelfv (GL_LIGHT_MODEL_AMBIENT,one);
+ stop_glerror();
+
+ //LLGLState::verify();
+
+ /////////////////////////////////////
+ //
+ // Render
+ //
+ // Actually push all of our triangles to the screen.
+ //
+ if (!gDisconnected)
+ {
+ LLFastTimer t(LLFastTimer::FTM_WORLD_UPDATE);
+ stop_glerror();
+ display_update_camera();
+ stop_glerror();
+
+ //FIXME: merge these two methods
+ gHUDManager->updateEffects();
+ LLHUDObject::updateAll();
+ stop_glerror();
+
+ gFrameStats.start(LLFrameStats::UPDATE_GEOM);
+ const F32 max_geom_update_time = 0.005f; // 5 ms update time
+ gPipeline.updateGeom(max_geom_update_time);
+ stop_glerror();
+
+ gFrameStats.start(LLFrameStats::UPDATE_CULL);
+ gPipeline.updateCull();
+ stop_glerror();
+
+ if (rebuild && !gViewerWindow->renderingFastFrame())
+ {
+ LLFastTimer t(LLFastTimer::FTM_REBUILD);
+
+ ///////////////////////////////////
+ //
+ // StateSort
+ //
+ // Responsible for taking visible objects, and adding them to the appropriate draw orders.
+ // In the case of alpha objects, z-sorts them first.
+ // Also creates special lists for outlines and selected face rendering.
+ //
+ gFrameStats.start(LLFrameStats::STATE_SORT);
+ gPipeline.stateSort();
+ stop_glerror();
+
+ //////////////////////////////////////
+ //
+ // rebuildPools
+ //
+ //
+ gFrameStats.start(LLFrameStats::REBUILD);
+ gPipeline.rebuildPools();
+ stop_glerror();
+ }
+ }
+
+ //// render frontmost floater opaque for occlusion culling purposes
+ //LLFloater* frontmost_floaterp = gFloaterView->getFrontmost();
+ //// assumes frontmost floater with focus is opaque
+ //if (frontmost_floaterp && gFocusMgr.childHasKeyboardFocus(frontmost_floaterp))
+ //{
+ // glMatrixMode(GL_MODELVIEW);
+ // glPushMatrix();
+ // {
+ // LLGLSNoTexture gls_no_texture;
+
+ // glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ // glLoadIdentity();
+
+ // LLRect floater_rect = frontmost_floaterp->getScreenRect();
+ // // deflate by one pixel so rounding errors don't occlude outside of floater extents
+ // floater_rect.stretch(-1);
+ // LLRectf floater_3d_rect((F32)floater_rect.mLeft / (F32)gViewerWindow->getWindowWidth(),
+ // (F32)floater_rect.mTop / (F32)gViewerWindow->getWindowHeight(),
+ // (F32)floater_rect.mRight / (F32)gViewerWindow->getWindowWidth(),
+ // (F32)floater_rect.mBottom / (F32)gViewerWindow->getWindowHeight());
+ // floater_3d_rect.translate(-0.5f, -0.5f);
+ // glTranslatef(0.f, 0.f, -gCamera->getNear());
+ // glScalef(gCamera->getNear() * gCamera->getAspect() / sinf(gCamera->getView()), gCamera->getNear() / sinf(gCamera->getView()), 1.f);
+ // glColor4fv(LLColor4::white.mV);
+ // glBegin(GL_QUADS);
+ // {
+ // glVertex3f(floater_3d_rect.mLeft, floater_3d_rect.mBottom, 0.f);
+ // glVertex3f(floater_3d_rect.mLeft, floater_3d_rect.mTop, 0.f);
+ // glVertex3f(floater_3d_rect.mRight, floater_3d_rect.mTop, 0.f);
+ // glVertex3f(floater_3d_rect.mRight, floater_3d_rect.mBottom, 0.f);
+ // }
+ // glEnd();
+ // glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ // }
+ // glPopMatrix();
+ //}
+
+ if (gViewerWindow->renderingFastFrame())
+ {
+ gFrameStats.start(LLFrameStats::RENDER_SYNC);
+ gFrameStats.start(LLFrameStats::RENDER_GEOM);
+ }
+ else if (!(gLogoutRequestSent && gHaveSavedSnapshot)
+ && !gRestoreGL
+ && !gDisconnected)
+ {
+ gPipeline.renderGeom();
+ stop_glerror();
+ }
+
+ gFrameStats.start(LLFrameStats::RENDER_UI);
+
+ if (gHandleKeysAsync)
+ {
+ process_keystrokes_async();
+ stop_glerror();
+ }
+
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+ render_ui_and_swap();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+
+ gFrameStats.start(LLFrameStats::MISC_END);
+ stop_glerror();
+
+}
+
+
+void render_ui_and_swap()
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+
+ LLGLSDefault gls_default;
+ {
+ LLGLSUIDefault gls_ui;
+ gPipeline.disableLights();
+
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
+ {
+ LLFastTimer t(LLFastTimer::FTM_RENDER_UI);
+ if (!gViewerWindow->renderingFastFrame() && !gDisconnected)
+ {
+ render_ui_3d();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+ }
+
+ render_ui_2d();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+ }
+
+ // now do the swap buffer
+ if (gDisplaySwapBuffers)
+ {
+ LLFastTimer t(LLFastTimer::FTM_SWAP);
+ gViewerWindow->mWindow->swapBuffers();
+ }
+ }
+
+ gViewerWindow->finishFirstFastFrame();
+}
+
+void render_ui_3d()
+{
+ LLGLSPipeline gls_pipeline;
+
+ //////////////////////////////////////
+ //
+ // Render 3D UI elements
+ // NOTE: zbuffer is cleared before we get here by LLDrawPoolHUD,
+ // so 3d elements requiring Z buffer are moved to LLDrawPoolHUD
+ //
+
+ // Render selections
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ /////////////////////////////////////////////////////////////
+ //
+ // Render 2.5D elements (2D elements in the world)
+ // Stuff without z writes
+ //
+
+ // Debugging stuff goes before the UI.
+
+ if (gSavedSettings.getBOOL("ShowDepthBuffer"))
+ {
+ post_show_depth_buffer();
+ }
+
+ // Coordinate axes
+ if (gSavedSettings.getBOOL("ShowAxes"))
+ {
+ draw_axes();
+ }
+
+ stop_glerror();
+
+ gViewerWindow->renderSelections(FALSE, FALSE, TRUE); // Non HUD call in render_hud_elements
+ stop_glerror();
+}
+
+void render_ui_2d()
+{
+ LLGLSUIDefault gls_ui;
+
+ /////////////////////////////////////////////////////////////
+ //
+ // Render 2D UI elements that overlay the world (no z compare)
+
+ // Disable wireframe mode below here, as this is HUD/menus
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ // Menu overlays, HUD, etc
+ gViewerWindow->setup2DRender();
+
+ F32 zoom_factor = gCamera->getZoomFactor();
+ S16 sub_region = gCamera->getZoomSubRegion();
+
+ if (zoom_factor > 1.f)
+ {
+ //decompose subregion number to x and y values
+ int pos_y = sub_region / llceil(zoom_factor);
+ int pos_x = sub_region - (pos_y*llceil(zoom_factor));
+ // offset for this tile
+ LLFontGL::sCurOrigin.mX -= llround((F32)gViewerWindow->getWindowWidth() * (F32)pos_x / zoom_factor);
+ LLFontGL::sCurOrigin.mY -= llround((F32)gViewerWindow->getWindowHeight() * (F32)pos_y / zoom_factor);
+ }
+
+
+ stop_glerror();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // render outline for HUD
+ if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->mHUDCurZoom < 0.98f)
+ {
+ glPushMatrix();
+ S32 half_width = (gViewerWindow->getWindowWidth() / 2);
+ S32 half_height = (gViewerWindow->getWindowHeight() / 2);
+ glTranslatef((F32)half_width, (F32)half_height, 0.f);
+ glScalef(gAgent.getAvatarObject()->mHUDCurZoom, gAgent.getAvatarObject()->mHUDCurZoom, gAgent.getAvatarObject()->mHUDCurZoom);
+ glColor4fv(LLColor4::white.mV);
+ gl_rect_2d(-half_width, half_height, half_width, -half_height, FALSE);
+ glPopMatrix();
+ stop_glerror();
+ }
+ gViewerWindow->draw();
+ if (gDebugSelect)
+ {
+ gViewerWindow->drawPickBuffer();
+ }
+
+ // reset current origin for font rendering, in case of tiling render
+ LLFontGL::sCurOrigin.set(0, 0);
+}
+
+void renderCoordinateAxes()
+{
+ LLGLSNoTexture gls_no_texture;
+ glBegin(GL_LINES);
+ glColor3f(1.0f, 0.0f, 0.0f); // i direction = X-Axis = red
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(2.0f, 0.0f, 0.0f);
+ glVertex3f(3.0f, 0.0f, 0.0f);
+ glVertex3f(5.0f, 0.0f, 0.0f);
+ glVertex3f(6.0f, 0.0f, 0.0f);
+ glVertex3f(8.0f, 0.0f, 0.0f);
+ // Make an X
+ glVertex3f(11.0f, 1.0f, 1.0f);
+ glVertex3f(11.0f, -1.0f, -1.0f);
+ glVertex3f(11.0f, 1.0f, -1.0f);
+ glVertex3f(11.0f, -1.0f, 1.0f);
+
+ glColor3f(0.0f, 1.0f, 0.0f); // j direction = Y-Axis = green
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 2.0f, 0.0f);
+ glVertex3f(0.0f, 3.0f, 0.0f);
+ glVertex3f(0.0f, 5.0f, 0.0f);
+ glVertex3f(0.0f, 6.0f, 0.0f);
+ glVertex3f(0.0f, 8.0f, 0.0f);
+ // Make a Y
+ glVertex3f(1.0f, 11.0f, 1.0f);
+ glVertex3f(0.0f, 11.0f, 0.0f);
+ glVertex3f(-1.0f, 11.0f, 1.0f);
+ glVertex3f(0.0f, 11.0f, 0.0f);
+ glVertex3f(0.0f, 11.0f, 0.0f);
+ glVertex3f(0.0f, 11.0f, -1.0f);
+
+ glColor3f(0.0f, 0.0f, 1.0f); // Z-Axis = blue
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.0f, 2.0f);
+ glVertex3f(0.0f, 0.0f, 3.0f);
+ glVertex3f(0.0f, 0.0f, 5.0f);
+ glVertex3f(0.0f, 0.0f, 6.0f);
+ glVertex3f(0.0f, 0.0f, 8.0f);
+ // Make a Z
+ glVertex3f(-1.0f, 1.0f, 11.0f);
+ glVertex3f(1.0f, 1.0f, 11.0f);
+ glVertex3f(1.0f, 1.0f, 11.0f);
+ glVertex3f(-1.0f, -1.0f, 11.0f);
+ glVertex3f(-1.0f, -1.0f, 11.0f);
+ glVertex3f(1.0f, -1.0f, 11.0f);
+ glEnd();
+}
+
+void draw_axes()
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ // A vertical white line at origin
+ LLVector3 v = gAgent.getPositionAgent();
+ glBegin(GL_LINES);
+ glColor3f(1.0f, 1.0f, 1.0f);
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.0f, 40.0f);
+ glEnd();
+ // Some coordinate axes
+ glPushMatrix();
+ glTranslatef( v.mV[VX], v.mV[VY], v.mV[VZ] );
+ renderCoordinateAxes();
+ glPopMatrix();
+}
+
+
+void render_disconnected_background()
+{
+ if (!gDisconnectedImagep && gDisconnected)
+ {
+ llinfos << "Loading last bitmap..." << llendl;
+
+ char temp_str[MAX_PATH];
+ strcpy(temp_str, gDirUtilp->getLindenUserDir().c_str());
+ strcat(temp_str, gDirUtilp->getDirDelimiter().c_str());
+
+ strcat(temp_str, SCREEN_LAST_FILENAME);
+
+ LLPointer<LLImageBMP> image_bmp = new LLImageBMP;
+ if( !image_bmp->load(temp_str) )
+ {
+ //llinfos << "Bitmap load failed" << llendl;
+ return;
+ }
+
+ gDisconnectedImagep = new LLImageGL( FALSE );
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ if (!image_bmp->decode(raw))
+ {
+ llinfos << "Bitmap decode failed" << llendl;
+ gDisconnectedImagep = NULL;
+ return;
+ }
+
+ U8 *rawp = raw->getData();
+ S32 npixels = (S32)image_bmp->getWidth()*(S32)image_bmp->getHeight();
+ for (S32 i = 0; i < npixels; i++)
+ {
+ S32 sum = 0;
+ sum = *rawp + *(rawp+1) + *(rawp+2);
+ sum /= 3;
+ *rawp = ((S32)sum*6 + *rawp)/7;
+ rawp++;
+ *rawp = ((S32)sum*6 + *rawp)/7;
+ rawp++;
+ *rawp = ((S32)sum*6 + *rawp)/7;
+ rawp++;
+ }
+
+
+ raw->expandToPowerOfTwo();
+ gDisconnectedImagep->createGLTexture(0, raw);
+ gStartImageGL = gDisconnectedImagep;
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+
+ // Make sure the progress view always fills the entire window.
+ S32 width = gViewerWindow->getWindowWidth();
+ S32 height = gViewerWindow->getWindowHeight();
+
+ if (gDisconnectedImagep)
+ {
+ LLGLSUIDefault gls_ui;
+ gViewerWindow->setup2DRender();
+ glPushMatrix();
+ {
+ // scale ui to reflect UIScaleFactor
+ // this can't be done in setup2DRender because it requires a
+ // pushMatrix/popMatrix pair
+ const LLVector2& display_scale = gViewerWindow->getDisplayScale();
+ glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+
+ LLViewerImage::bindTexture(gDisconnectedImagep);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ gl_rect_2d_simple_tex(width, height);
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ glPopMatrix();
+ }
+}
diff --git a/indra/newview/llviewerdisplay.h b/indra/newview/llviewerdisplay.h
new file mode 100644
index 0000000000..6dd3e51efd
--- /dev/null
+++ b/indra/newview/llviewerdisplay.h
@@ -0,0 +1,17 @@
+/**
+ * @file llviewerdisplay.h
+ * @brief LLViewerDisplay class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERDISPLAY_H
+#define LL_LLVIEWERDISPLAY_H
+
+void display_startup();
+
+extern BOOL gDisplaySwapBuffers;
+
+
+#endif // LL_LLVIEWERDISPLAY_H
diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp
new file mode 100644
index 0000000000..6710f8c00c
--- /dev/null
+++ b/indra/newview/llviewergesture.cpp
@@ -0,0 +1,260 @@
+/**
+ * @file llviewergesture.cpp
+ * @brief LLViewerGesture class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewergesture.h"
+
+#include "audioengine.h"
+#include "lldir.h"
+#include "llviewerinventory.h"
+#include "sound_ids.h" // for testing
+
+#include "llchatbar.h"
+#include "llkeyboard.h" // for key shortcuts for testing
+#include "llinventorymodel.h"
+#include "llvoavatar.h"
+#include "llxfermanager.h"
+#include "llviewermessage.h" // send_guid_sound_trigger
+#include "llviewernetwork.h"
+#include "llagent.h"
+
+// Globals
+LLViewerGestureList gGestureList;
+
+const F32 LLViewerGesture::SOUND_VOLUME = 1.f;
+
+LLViewerGesture::LLViewerGesture()
+: LLGesture()
+{ }
+
+LLViewerGesture::LLViewerGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id,
+ const std::string &animation,
+ const std::string &output_string)
+: LLGesture(key, mask, trigger, sound_item_id, animation, output_string)
+{
+}
+
+LLViewerGesture::LLViewerGesture(U8 **buffer, S32 max_size)
+: LLGesture(buffer, max_size)
+{
+}
+
+LLViewerGesture::LLViewerGesture(const LLViewerGesture &rhs)
+: LLGesture((LLGesture)rhs)
+{
+}
+
+BOOL LLViewerGesture::trigger(KEY key, MASK mask)
+{
+ if (mKey == key && mMask == mask)
+ {
+ doTrigger( TRUE );
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+BOOL LLViewerGesture::trigger(const std::string &trigger_string)
+{
+ // Assumes trigger_string is lowercase
+ if (mTriggerLower == trigger_string)
+ {
+ doTrigger( FALSE );
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+// private
+void LLViewerGesture::doTrigger( BOOL send_chat )
+{
+ if (mSoundItemID != LLUUID::null)
+ {
+ LLViewerInventoryItem *item;
+ item = gInventory.getItem(mSoundItemID);
+ if (item)
+ {
+ send_sound_trigger(item->getAssetUUID(), SOUND_VOLUME);
+ }
+ }
+
+ if (!mAnimation.empty())
+ {
+ // AFK animations trigger the special "away" state, which
+ // includes agent control settings. JC
+ if (mAnimation == "enter_away_from_keyboard_state" || mAnimation == "away")
+ {
+ gAgent.setAFK();
+ }
+ else
+ {
+ LLUUID anim_id = gAnimLibrary.stringToAnimState(mAnimation.c_str());
+ gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_START);
+ }
+ }
+
+ if ( send_chat && !mOutputString.empty())
+ {
+ // Don't play nodding animation, since that might not blend
+ // with the gesture animation.
+ gChatBar->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE);
+ }
+}
+
+
+LLViewerGestureList::LLViewerGestureList()
+: LLGestureList()
+{
+ mIsLoaded = FALSE;
+}
+
+void LLViewerGestureList::saveToServer()
+{
+ U8 *buffer = new U8[getMaxSerialSize()];
+
+ U8 *end = serialize(buffer);
+
+ if (end - buffer > getMaxSerialSize())
+ {
+ llerrs << "Wrote off end of buffer, serial size computation is wrong" << llendl;
+ }
+
+ //U64 xfer_id = gXferManager->registerXfer(buffer, end - buffer);
+ // write to a file because mem<->mem xfer isn't implemented
+ LLUUID random_uuid;
+ char filename[LL_MAX_PATH];
+ random_uuid.generate();
+ random_uuid.toString(filename);
+ strcat(filename,".tmp");
+
+ char filename_and_path[LL_MAX_PATH];
+ sprintf(filename_and_path, "%s%s%s",
+ gDirUtilp->getTempDir().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ filename);
+
+ FILE *fp = LLFile::fopen(filename_and_path, "wb");
+
+ if (fp)
+ {
+ fwrite(buffer, end - buffer, 1, fp);
+ fclose(fp);
+
+ gMessageSystem->newMessageFast(_PREHASH_GestureUpdate);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addStringFast(_PREHASH_Filename, filename);
+ gMessageSystem->addBOOLFast(_PREHASH_ToViewer, FALSE);
+ gMessageSystem->sendReliable(gUserServer);
+ }
+
+ delete[] buffer;
+}
+
+/*
+void LLViewerGestureList::requestFromServer()
+{
+ gMessageSystem->newMessageFast(_PREHASH_GestureRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, agent_get_id());
+ gMessageSystem->addU8("Reset", 0);
+ gMessageSystem->sendReliable(gUserServer);
+}
+
+void LLViewerGestureList::requestResetFromServer( BOOL is_male )
+{
+ gMessageSystem->newMessageFast(_PREHASH_GestureRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, agent_get_id());
+ gMessageSystem->addU8("Reset", is_male ? 1 : 2);
+ gMessageSystem->sendReliable(gUserServer);
+ mIsLoaded = FALSE;
+}
+*/
+
+// helper for deserialize that creates the right LLGesture subclass
+LLGesture *LLViewerGestureList::create_gesture(U8 **buffer, S32 max_size)
+{
+ return new LLViewerGesture(buffer, max_size);
+}
+
+
+// See if the prefix matches any gesture. If so, return TRUE
+// and place the full text of the gesture trigger into
+// output_str
+BOOL LLViewerGestureList::matchPrefix(const std::string& in_str, std::string* out_str)
+{
+ S32 in_len = in_str.length();
+
+ LLString in_str_lc = in_str;
+ LLString::toLower(in_str_lc);
+
+ for (S32 i = 0; i < count(); i++)
+ {
+ LLGesture* gesture = get(i);
+ const std::string &trigger = gesture->getTrigger();
+
+ if (in_len > (S32)trigger.length())
+ {
+ // too short, bail out
+ continue;
+ }
+
+ std::string trigger_trunc = utf8str_truncate(trigger, in_len);
+ LLString::toLower(trigger_trunc);
+ if (in_str_lc == trigger_trunc)
+ {
+ *out_str = trigger;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+// static
+void LLViewerGestureList::xferCallback(void *data, S32 size, void** /*user_data*/, S32 status)
+{
+ if (LL_ERR_NOERR == status)
+ {
+ U8 *buffer = (U8 *)data;
+ U8 *end = gGestureList.deserialize(buffer, size);
+
+ if (end - buffer > size)
+ {
+ llerrs << "Read off of end of array, error in serialization" << llendl;
+ }
+
+ gGestureList.mIsLoaded = TRUE;
+ }
+ else
+ {
+ llwarns << "Unable to load gesture list!" << llendl;
+ }
+}
+
+// static
+void LLViewerGestureList::processGestureUpdate(LLMessageSystem *msg, void** /*user_data*/)
+{
+ char remote_filename[MAX_STRING];
+ msg->getStringFast(_PREHASH_AgentBlock, _PREHASH_Filename, MAX_STRING, remote_filename);
+
+
+ gXferManager->requestFile(remote_filename, LL_PATH_CACHE, msg->getSender(), TRUE, xferCallback, NULL,
+ LLXferManager::HIGH_PRIORITY);
+}
diff --git a/indra/newview/llviewergesture.h b/indra/newview/llviewergesture.h
new file mode 100644
index 0000000000..ced12a7c34
--- /dev/null
+++ b/indra/newview/llviewergesture.h
@@ -0,0 +1,71 @@
+/**
+ * @file llviewergesture.h
+ * @brief LLViewerGesture class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERGESTURE_H
+#define LL_LLVIEWERGESTURE_H
+
+#include "llanimationstates.h"
+#include "lluuid.h"
+#include "llstring.h"
+#include "lldarray.h"
+#include "llgesture.h"
+
+class LLMessageSystem;
+
+class LLViewerGesture : public LLGesture
+{
+public:
+ LLViewerGesture();
+ LLViewerGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id, const std::string &animation,
+ const std::string &output_string);
+
+ LLViewerGesture(U8 **buffer, S32 max_size); // deserializes, advances buffer
+ LLViewerGesture(const LLViewerGesture &gesture);
+
+ // Triggers if a key/mask matches it
+ virtual BOOL trigger(KEY key, MASK mask);
+
+ // Triggers if case-insensitive substring matches (assumes string is lowercase)
+ virtual BOOL trigger(const std::string &string);
+
+ void doTrigger( BOOL send_chat );
+
+protected:
+ static const F32 SOUND_VOLUME;
+};
+
+class LLViewerGestureList : public LLGestureList
+{
+public:
+ LLViewerGestureList();
+
+ void saveToServer();
+ //void requestFromServer();
+ BOOL getIsLoaded() { return mIsLoaded; }
+
+ //void requestResetFromServer( BOOL is_male );
+
+ // See if the prefix matches any gesture. If so, return TRUE
+ // and place the full text of the gesture trigger into
+ // output_str
+ BOOL matchPrefix(const std::string& in_str, std::string* out_str);
+
+ static void xferCallback(void *data, S32 size, void** /*user_data*/, S32 status);
+ static void processGestureUpdate(LLMessageSystem *msg, void** /*user_data*/);
+
+protected:
+ LLGesture *create_gesture(U8 **buffer, S32 max_size);
+
+protected:
+ BOOL mIsLoaded;
+};
+
+extern LLViewerGestureList gGestureList;
+
+#endif
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
new file mode 100644
index 0000000000..9c2638362e
--- /dev/null
+++ b/indra/newview/llviewerinventory.cpp
@@ -0,0 +1,705 @@
+/**
+ * @file llviewerinventory.cpp
+ * @brief Implementation of the viewer side inventory objects.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llviewerinventory.h"
+
+#include "message.h"
+#include "indra_constants.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llconsole.h"
+#include "llinventorymodel.h"
+#include "llnotify.h"
+#include "llimview.h"
+#include "viewer.h"
+#include "llgesturemgr.h"
+
+#include "llinventoryview.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+///----------------------------------------------------------------------------
+/// Class LLViewerInventoryItem
+///----------------------------------------------------------------------------
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ const LLPermissions& perm,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ LLInventoryType::EType inv_type,
+ const LLString& name,
+ const LLString& desc,
+ const LLSaleInfo& sale_info,
+ U32 flags,
+ S32 creation_date_utc) :
+ LLInventoryItem(uuid, parent_uuid, perm, asset_uuid, type, inv_type,
+ name, desc, sale_info, flags, creation_date_utc),
+ mIsComplete(TRUE)
+{
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const char* name,
+ LLInventoryType::EType inv_type) :
+ LLInventoryItem(),
+ mIsComplete(FALSE)
+{
+ mUUID = item_id;
+ mParentUUID = parent_id;
+ mInventoryType = inv_type;
+ mName.assign(name);
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem() :
+ LLInventoryItem(),
+ mIsComplete(FALSE)
+{
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other) :
+ LLInventoryItem()
+{
+ copy(other);
+ if (!mIsComplete)
+ {
+ llwarns << "LLViewerInventoryItem copy constructor for incomplete item"
+ << mUUID << llendl;
+ }
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLInventoryItem *other) :
+ LLInventoryItem(other)
+{
+ LLInventoryItem::copy(other);
+ if (!mIsComplete)
+ {
+ llwarns << "LLViewerInventoryItem copy constructor for incomplete item"
+ << mUUID << llendl;
+ }
+}
+
+
+LLViewerInventoryItem::~LLViewerInventoryItem()
+{
+}
+
+// virtual
+void LLViewerInventoryItem::copy(const LLViewerInventoryItem* other)
+{
+ LLInventoryItem::copy(other);
+ mIsComplete = other->mIsComplete;
+ mTransactionID = other->mTransactionID;
+}
+
+void LLViewerInventoryItem::copy(const LLInventoryItem *other)
+{
+ LLInventoryItem::copy(other);
+ mIsComplete = true;
+ mTransactionID.setNull();
+}
+
+// virtual
+void LLViewerInventoryItem::clone(LLPointer<LLViewerInventoryItem>& newitem) const
+{
+ newitem = new LLViewerInventoryItem(this);
+ if(newitem.notNull())
+ {
+ LLUUID item_id;
+ item_id.generate();
+ newitem->setUUID(item_id);
+ }
+}
+
+void LLViewerInventoryItem::removeFromServer()
+{
+ llinfos << "Removing inventory item " << mUUID << " from server."
+ << llendl;
+
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, mUUID);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryItem::updateServer(BOOL is_new) const
+{
+ if(!mIsComplete)
+ {
+ // *FIX: deal with this better.
+ // If we're crashing here then the UI is incorrectly enabled.
+ llerrs << "LLViewerInventoryItem::updateServer() - for incomplete item"
+ << llendl;
+ return;
+ }
+ if(gAgent.getID() != mPermissions.getOwner())
+ {
+ // *FIX: deal with this better.
+ llwarns << "LLViewerInventoryItem::updateServer() - for unowned item"
+ << llendl;
+ return;
+ }
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_CallbackID, 0);
+ packMessage(msg);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryItem::fetchFromServer(void) const
+{
+ if(!mIsComplete)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("FetchInventory");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("OwnerID", mPermissions.getOwner());
+ msg->addUUID("ItemID", mUUID);
+ gAgent.sendReliableMessage();
+ }
+ else
+ {
+ // *FIX: this can be removed after a bit.
+ llwarns << "request to fetch complete item" << llendl;
+ }
+}
+
+// virtual
+BOOL LLViewerInventoryItem::unpackMessage(
+ LLMessageSystem* msg, const char* block, S32 block_num)
+{
+ BOOL rv = LLInventoryItem::unpackMessage(msg, block, block_num);
+ mIsComplete = TRUE;
+ return rv;
+}
+
+void LLViewerInventoryItem::setTransactionID(const LLTransactionID& transaction_id)
+{
+ mTransactionID = transaction_id;
+}
+// virtual
+void LLViewerInventoryItem::packMessage(LLMessageSystem* msg) const
+{
+ msg->addUUIDFast(_PREHASH_ItemID, mUUID);
+ msg->addUUIDFast(_PREHASH_FolderID, mParentUUID);
+ mPermissions.packMessage(msg);
+ msg->addUUIDFast(_PREHASH_TransactionID, mTransactionID);
+ S8 type = static_cast<S8>(mType);
+ msg->addS8Fast(_PREHASH_Type, type);
+ type = static_cast<S8>(mInventoryType);
+ msg->addS8Fast(_PREHASH_InvType, type);
+ msg->addU32Fast(_PREHASH_Flags, mFlags);
+ mSaleInfo.packMessage(msg);
+ msg->addStringFast(_PREHASH_Name, mName);
+ msg->addStringFast(_PREHASH_Description, mDescription);
+ msg->addS32Fast(_PREHASH_CreationDate, mCreationDate);
+ U32 crc = getCRC32();
+ msg->addU32Fast(_PREHASH_CRC, crc);
+}
+// virtual
+BOOL LLViewerInventoryItem::importFile(FILE* fp)
+{
+ BOOL rv = LLInventoryItem::importFile(fp);
+ mIsComplete = TRUE;
+ return rv;
+}
+
+// virtual
+BOOL LLViewerInventoryItem::importLegacyStream(std::istream& input_stream)
+{
+ BOOL rv = LLInventoryItem::importLegacyStream(input_stream);
+ mIsComplete = TRUE;
+ return rv;
+}
+
+bool LLViewerInventoryItem::importFileLocal(FILE* fp)
+{
+ // TODO: convert all functions that return BOOL to return bool
+ bool rv = (LLInventoryItem::importFile(fp) ? true : false);
+ mIsComplete = false;
+ return rv;
+}
+
+bool LLViewerInventoryItem::exportFileLocal(FILE* fp) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ fprintf(fp, "\tinv_item\t0\n\t{\n");
+ mUUID.toString(uuid_str);
+ fprintf(fp, "\t\titem_id\t%s\n", uuid_str);
+ mParentUUID.toString(uuid_str);
+ fprintf(fp, "\t\tparent_id\t%s\n", uuid_str);
+ mPermissions.exportFile(fp);
+ fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ const char* inv_type_str = LLInventoryType::lookup(mInventoryType);
+ if(inv_type_str) fprintf(fp, "\t\tinv_type\t%s\n", inv_type_str);
+ fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
+ fprintf(fp, "\t\tcreation_date\t%d\n", mCreationDate);
+ fprintf(fp,"\t}\n");
+ return true;
+}
+
+void LLViewerInventoryItem::updateParentOnServer(BOOL restamp) const
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, restamp);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, mUUID);
+ msg->addUUIDFast(_PREHASH_FolderID, mParentUUID);
+ msg->addString("NewName", NULL);
+ gAgent.sendReliableMessage();
+}
+
+//void LLViewerInventoryItem::setCloneCount(S32 clones)
+//{
+// mClones = clones;
+//}
+
+//S32 LLViewerInventoryItem::getCloneCount() const
+//{
+// return mClones;
+//}
+
+///----------------------------------------------------------------------------
+/// Class LLViewerInventoryCategory
+///----------------------------------------------------------------------------
+
+LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ LLAssetType::EType pref,
+ const LLString& name,
+ const LLUUID& owner_id) :
+ LLInventoryCategory(uuid, parent_uuid, pref, name),
+ mOwnerID(owner_id),
+ mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
+ mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+{
+ mDescendentsRequested.reset();
+}
+
+LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) :
+ mOwnerID(owner_id),
+ mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
+ mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+{
+ mDescendentsRequested.reset();
+}
+
+LLViewerInventoryCategory::LLViewerInventoryCategory(const LLViewerInventoryCategory* other)
+{
+ copy(other);
+}
+
+LLViewerInventoryCategory::~LLViewerInventoryCategory()
+{
+}
+
+// virtual
+void LLViewerInventoryCategory::copy(const LLViewerInventoryCategory* other)
+{
+ LLInventoryCategory::copy(other);
+ mOwnerID = other->mOwnerID;
+ mVersion = other->mVersion;
+ mDescendentCount = other->mDescendentCount;
+ mDescendentsRequested = other->mDescendentsRequested;
+}
+
+
+void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->addBOOL("Stamp", restamp);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_FolderID, mUUID);
+ msg->addUUIDFast(_PREHASH_ParentID, mParentUUID);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryCategory::updateServer(BOOL is_new) const
+{
+ // communicate that change with the server.
+ if(LLAssetType::AT_NONE != mPreferredType)
+ {
+ LLNotifyBox::showXml("CannotModifyProtectedCategories");
+ return;
+ }
+
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FolderData);
+ packMessage(msg);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryCategory::removeFromServer( void )
+{
+ llinfos << "Removing inventory category " << mUUID << " from server."
+ << llendl;
+ // communicate that change with the server.
+ if(LLAssetType::AT_NONE != mPreferredType)
+ {
+ LLNotifyBox::showXml("CannotRemoveProtectedCategories");
+ return;
+ }
+
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, mUUID);
+ gAgent.sendReliableMessage();
+}
+
+bool LLViewerInventoryCategory::fetchDescendents()
+{
+ if((VERSION_UNKNOWN == mVersion)
+ && mDescendentsRequested.hasExpired())
+ {
+ const F32 FETCH_TIMER_EXPIRY = 10.0f;
+ mDescendentsRequested.reset();
+ mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("FetchInventoryDescendents");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("FolderID", mUUID);
+ msg->addUUID("OwnerID", mOwnerID);
+ // bitfield
+ // 1 = by date
+ // 2 = folders by date
+ // Need to mask off anything but the first bit.
+ // This comes from LLInventoryFilter from llfolderview.h
+ U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1;
+ msg->addS32("SortOrder", sort_order);
+ msg->addBOOL("FetchFolders", FALSE);
+ msg->addBOOL("FetchItems", TRUE);
+ gAgent.sendReliableMessage();
+ return true;
+ }
+ return false;
+}
+
+bool LLViewerInventoryCategory::importFileLocal(FILE* fp)
+{
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+ while(!feof(fp))
+ {
+ fgets(buffer, MAX_STRING, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("cat_id", keyword))
+ {
+ mUUID.set(valuestr);
+ }
+ else if(0 == strcmp("parent_id", keyword))
+ {
+ mParentUUID.set(valuestr);
+ }
+ else if(0 == strcmp("type", keyword))
+ {
+ mType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("pref_type", keyword))
+ {
+ mPreferredType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("name", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %s %[^|]", keyword, valuestr);
+ mName.assign(valuestr);
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ }
+ else if(0 == strcmp("owner_id", keyword))
+ {
+ mOwnerID.set(valuestr);
+ }
+ else if(0 == strcmp("version", keyword))
+ {
+ sscanf(valuestr, "%d", &mVersion);
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in inventory import category " << mUUID << llendl;
+ }
+ }
+ return true;
+}
+
+bool LLViewerInventoryCategory::exportFileLocal(FILE* fp) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ fprintf(fp, "\tinv_category\t0\n\t{\n");
+ mUUID.toString(uuid_str);
+ fprintf(fp, "\t\tcat_id\t%s\n", uuid_str);
+ mParentUUID.toString(uuid_str);
+ fprintf(fp, "\t\tparent_id\t%s\n", uuid_str);
+ fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ fprintf(fp, "\t\tpref_type\t%s\n", LLAssetType::lookup(mPreferredType));
+ fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
+ mOwnerID.toString(uuid_str);
+ fprintf(fp, "\t\towner_id\t%s\n", uuid_str);
+ fprintf(fp, "\t\tversion\t%d\n", mVersion);
+ fprintf(fp,"\t}\n");
+ return true;
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+/*
+void inventory_reliable_callback(void**, S32 status)
+{
+ if(0 != status)
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Reliable packet failure: %d", status);
+ llwarns << buffer << llendl;
+ }
+}
+*/
+LLInventoryCallbackManager::LLInventoryCallbackManager() :
+ mLastCallback(0)
+{
+}
+
+LLInventoryCallbackManager::~LLInventoryCallbackManager()
+{
+}
+
+U32 LLInventoryCallbackManager::registerCB(LLPointer<LLInventoryCallback> cb)
+{
+ if (cb.isNull())
+ return 0;
+
+ mLastCallback++;
+ if (!mLastCallback)
+ mLastCallback++;
+
+ mMap[mLastCallback] = cb;
+ return mLastCallback;
+}
+
+void LLInventoryCallbackManager::fire(U32 callback_id, const LLUUID& item_id)
+{
+ if (!callback_id)
+ return;
+
+ std::map<U32, LLPointer<LLInventoryCallback> >::iterator i;
+
+ i = mMap.find(callback_id);
+ if (i != mMap.end())
+ {
+ (*i).second->fire(item_id);
+ mMap.erase(i);
+ }
+}
+
+void WearOnAvatarCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ LLViewerInventoryItem *item = gInventory.getItem(inv_item);
+ if (item)
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+}
+
+RezAttachmentCallback::RezAttachmentCallback(LLViewerJointAttachment *attachmentp)
+{
+ mAttach = attachmentp;
+}
+RezAttachmentCallback::~RezAttachmentCallback()
+{
+}
+
+void RezAttachmentCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ LLViewerInventoryItem *item = gInventory.getItem(inv_item);
+ if (item)
+ {
+ rez_attachment(item, mAttach);
+ }
+}
+
+extern LLGestureManager gGestureManager;
+void ActivateGestureCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ gGestureManager.activateGesture(inv_item);
+}
+
+LLInventoryCallbackManager gInventoryCallbacks;
+
+void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
+ const LLUUID& parent, const LLTransactionID& transaction_id,
+ const std::string& name,
+ const std::string& desc, LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type, EWearableType wtype,
+ U32 next_owner_perm,
+ LLPointer<LLInventoryCallback> cb)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CreateInventoryItem);
+ msg->nextBlock(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, session_id);
+ msg->nextBlock(_PREHASH_InventoryBlock);
+ msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
+ msg->addUUIDFast(_PREHASH_FolderID, parent);
+ msg->addUUIDFast(_PREHASH_TransactionID, transaction_id);
+ msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_perm);
+ msg->addS8Fast(_PREHASH_Type, (S8)asset_type);
+ msg->addS8Fast(_PREHASH_InvType, (S8)inv_type);
+ msg->addU8Fast(_PREHASH_WearableType, (U8)wtype);
+ msg->addStringFast(_PREHASH_Name, name);
+ msg->addStringFast(_PREHASH_Description, desc);
+
+ gAgent.sendReliableMessage();
+}
+
+void copy_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& current_owner,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CopyInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
+ msg->addUUIDFast(_PREHASH_OldAgentID, current_owner);
+ msg->addUUIDFast(_PREHASH_OldItemID, item_id);
+ msg->addUUIDFast(_PREHASH_NewFolderID, parent_id);
+ msg->addString("NewName", new_name);
+ gAgent.sendReliableMessage();
+}
+
+void move_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, session_id);
+ msg->addBOOLFast(_PREHASH_Stamp, false);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->addUUIDFast(_PREHASH_FolderID, parent_id);
+ msg->addString("NewName", new_name);
+ gAgent.sendReliableMessage();
+}
+
+void _copy_inventory_from_notecard_hdr(const LLUUID& object_id, const LLUUID& notecard_inv_id)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CopyInventoryFromNotecard);
+ msg->nextBlock(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock(_PREHASH_NotecardData);
+ msg->addUUIDFast(_PREHASH_NotecardItemID, notecard_inv_id);
+ msg->addUUIDFast(_PREHASH_ObjectID, object_id);
+}
+
+void copy_inventory_from_notecard(const LLUUID& object_id, const LLUUID& notecard_inv_id, const LLInventoryItem *src)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ /*
+ * I was going to handle multiple inventory items here, but then I realized that
+ * we are only handling one at a time. Perhaps you can only select one at a
+ * time from the notecard?
+ */
+ _copy_inventory_from_notecard_hdr(object_id, notecard_inv_id);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, src->getUUID());
+ msg->addUUIDFast(_PREHASH_FolderID, gInventory.findCategoryUUIDForType(src->getType()));
+ gAgent.sendReliableMessage();
+}
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
new file mode 100644
index 0000000000..2668e1121c
--- /dev/null
+++ b/indra/newview/llviewerinventory.h
@@ -0,0 +1,253 @@
+/**
+ * @file llviewerinventory.h
+ * @brief Declaration of the inventory bits that only used on the viewer.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERINVENTORY_H
+#define LL_LLVIEWERINVENTORY_H
+
+#include "llinventory.h"
+#include "llframetimer.h"
+#include "llwearable.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLViewerInventoryItem
+//
+// An inventory item represents something that the current user has in
+// their inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLViewerInventoryItem : public LLInventoryItem
+{
+public:
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryItem> > item_array_t;
+
+protected:
+ ~LLViewerInventoryItem( void ); // ref counted
+
+public:
+ // construct a complete viewer inventory item
+ LLViewerInventoryItem(const LLUUID& uuid, const LLUUID& parent_uuid,
+ const LLPermissions& permissions,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ LLInventoryType::EType inv_type,
+ const LLString& name,
+ const LLString& desc,
+ const LLSaleInfo& sale_info,
+ U32 flags,
+ S32 creation_date_utc);
+
+ // construct a viewer inventory item which has the minimal amount
+ // of information to use in the UI.
+ LLViewerInventoryItem(
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const char* name,
+ LLInventoryType::EType inv_type);
+
+ // construct an invalid and incomplete viewer inventory item.
+ // usually useful for unpacking or importing or what have you.
+ // *NOTE: it is important to call setComplete() if you expect the
+ // operations to provide all necessary information.
+ LLViewerInventoryItem();
+ // Create a copy of an inventory item from a pointer to another item
+ // Note: Because InventoryItems are ref counted,
+ // reference copy (a = b) is prohibited
+ LLViewerInventoryItem(const LLViewerInventoryItem* other);
+ LLViewerInventoryItem(const LLInventoryItem* other);
+
+ virtual void copy(const LLViewerInventoryItem* other);
+ virtual void copy(const LLInventoryItem* other);
+
+ // construct a new clone of this item - it creates a new viewer
+ // inventory item using the copy constructor, and returns it.
+ // It is up to the caller to delete (unref) the item.
+ virtual void clone(LLPointer<LLViewerInventoryItem>& newitem) const;
+
+ // virtual methods
+ virtual void removeFromServer( void );
+ virtual void updateParentOnServer(BOOL restamp) const;
+ virtual void updateServer(BOOL is_new) const;
+ void fetchFromServer(void) const;
+
+ //virtual void packMessage(LLMessageSystem* msg) const;
+ virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
+ virtual BOOL importFile(FILE* fp);
+ virtual BOOL importLegacyStream(std::istream& input_stream);
+
+ // file handling on the viewer. These are not meant for anything
+ // other than cacheing.
+ bool exportFileLocal(FILE* fp) const;
+ bool importFileLocal(FILE* fp);
+
+ // new methods
+ BOOL isComplete() const { return mIsComplete; }
+ void setComplete(BOOL complete) { mIsComplete = complete; }
+ //void updateAssetOnServer() const;
+
+ virtual void packMessage(LLMessageSystem* msg) const;
+ virtual void setTransactionID(const LLTransactionID& transaction_id);
+ struct comparePointers
+ {
+ bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b)
+ {
+ return a->getName().compare(b->getName()) < 0;
+ }
+ };
+ LLTransactionID getTransactionID() const { return mTransactionID; }
+
+protected:
+ BOOL mIsComplete;
+ LLTransactionID mTransactionID;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLViewerInventoryCategory
+//
+// An instance of this class represents a category of inventory
+// items. Users come with a set of default categories, and can create
+// new ones as needed.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLViewerInventoryCategory : public LLInventoryCategory
+{
+public:
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryCategory> > cat_array_t;
+
+protected:
+ ~LLViewerInventoryCategory();
+
+public:
+ LLViewerInventoryCategory(const LLUUID& uuid, const LLUUID& parent_uuid,
+ LLAssetType::EType preferred_type,
+ const LLString& name,
+ const LLUUID& owner_id);
+ LLViewerInventoryCategory(const LLUUID& owner_id);
+ // Create a copy of an inventory category from a pointer to another category
+ // Note: Because InventoryCategorys are ref counted, reference copy (a = b)
+ // is prohibited
+ LLViewerInventoryCategory(const LLViewerInventoryCategory* other);
+ virtual void copy(const LLViewerInventoryCategory* other);
+
+ virtual void removeFromServer();
+ virtual void updateParentOnServer(BOOL restamp_children) const;
+ virtual void updateServer(BOOL is_new) const;
+ //virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
+
+ const LLUUID& getOwnerID() const { return mOwnerID; }
+
+ // Version handling
+ enum { VERSION_UNKNOWN = -1, VERSION_INITIAL = 1 };
+ S32 getVersion() const { return mVersion; }
+ void setVersion(S32 version) { mVersion = version; }
+
+ // Returns true if a fetch was issued.
+ bool fetchDescendents();
+
+ // used to help make cacheing more robust - for example, if
+ // someone is getting 4 packets but logs out after 3. the viewer
+ // may never know the cache is wrong.
+ enum { DESCENDENT_COUNT_UNKNOWN = -1 };
+ S32 getDescendentCount() const { return mDescendentCount; }
+ void setDescendentCount(S32 descendents) { mDescendentCount = descendents; }
+
+ // file handling on the viewer. These are not meant for anything
+ // other than cacheing.
+ bool exportFileLocal(FILE* fp) const;
+ bool importFileLocal(FILE* fp);
+
+protected:
+ LLUUID mOwnerID;
+ S32 mVersion;
+ S32 mDescendentCount;
+ LLFrameTimer mDescendentsRequested;
+};
+
+class LLInventoryCallback : public LLRefCount
+{
+public:
+ virtual ~LLInventoryCallback() {}
+ virtual void fire(const LLUUID& inv_item) = 0;
+};
+
+class WearOnAvatarCallback : public LLInventoryCallback
+{
+ void fire(const LLUUID& inv_item);
+};
+
+class LLViewerJointAttachment;
+
+class RezAttachmentCallback : public LLInventoryCallback
+{
+public:
+ RezAttachmentCallback(LLViewerJointAttachment *attachmentp);
+ ~RezAttachmentCallback();
+ void fire(const LLUUID& inv_item);
+private:
+ LLViewerJointAttachment* mAttach;
+};
+
+class ActivateGestureCallback : public LLInventoryCallback
+{
+public:
+ void fire(const LLUUID& inv_item);
+};
+
+// misc functions
+//void inventory_reliable_callback(void**, S32 status);
+
+class LLInventoryCallbackManager
+{
+public:
+ LLInventoryCallbackManager();
+ ~LLInventoryCallbackManager();
+
+ void fire(U32 callback_id, const LLUUID& item_id);
+ U32 registerCB(LLPointer<LLInventoryCallback> cb);
+private:
+ std::map<U32, LLPointer<LLInventoryCallback> > mMap;
+ U32 mLastCallback;
+};
+extern LLInventoryCallbackManager gInventoryCallbacks;
+
+
+#define NOT_WEARABLE (EWearableType)0
+
+void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
+ const LLUUID& parent, const LLTransactionID& transaction_id,
+ const std::string& name,
+ const std::string& desc, LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type, EWearableType wtype,
+ U32 next_owner_perm,
+ LLPointer<LLInventoryCallback> cb);
+
+/**
+ * @brief Securely create a new inventory item by copying from another.
+ */
+void copy_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& current_owner,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb);
+
+void move_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb);
+
+void copy_inventory_from_notecard(const LLUUID& object_id,
+ const LLUUID& notecard_inv_id,
+ const LLInventoryItem *src);
+
+
+#endif // LL_LLVIEWERINVENTORY_H
diff --git a/indra/newview/llviewerjoint.cpp b/indra/newview/llviewerjoint.cpp
new file mode 100644
index 0000000000..cfd23e7120
--- /dev/null
+++ b/indra/newview/llviewerjoint.cpp
@@ -0,0 +1,607 @@
+/**
+ * @file llviewerjoint.cpp
+ * @brief Implementation of LLViewerJoint class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerjoint.h"
+
+#include "llgl.h"
+#include "llmath.h"
+#include "llglheaders.h"
+#include "llsphere.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+
+#define DEFAULT_LOD 0.0f
+
+const S32 MIN_PIXEL_AREA_3PASS_HAIR = 64*64;
+
+//-----------------------------------------------------------------------------
+// Static Data
+//-----------------------------------------------------------------------------
+BOOL LLViewerJoint::sDisableLOD = FALSE;
+
+//-----------------------------------------------------------------------------
+// LLViewerJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLViewerJoint::LLViewerJoint()
+{
+ mUpdateXform = TRUE;
+ mValid = FALSE;
+ mComponents = SC_JOINT | SC_BONE | SC_AXES;
+ mMinPixelArea = DEFAULT_LOD;
+ mPickName = PN_DEFAULT;
+ mVisible = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLViewerJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLViewerJoint::LLViewerJoint(const std::string &name, LLJoint *parent) :
+ LLJoint(name, parent)
+{
+ mValid = FALSE;
+ mComponents = SC_JOINT | SC_BONE | SC_AXES;
+ mMinPixelArea = DEFAULT_LOD;
+ mPickName = PN_DEFAULT;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLViewerJoint()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLViewerJoint::~LLViewerJoint()
+{
+}
+
+
+//--------------------------------------------------------------------
+// setValid()
+//--------------------------------------------------------------------
+void LLViewerJoint::setValid( BOOL valid, BOOL recursive )
+{
+ //----------------------------------------------------------------
+ // set visibility for this joint
+ //----------------------------------------------------------------
+ mValid = valid;
+
+ //----------------------------------------------------------------
+ // set visibility for children
+ //----------------------------------------------------------------
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->setValid(valid, TRUE);
+ }
+ }
+
+}
+
+//--------------------------------------------------------------------
+// renderSkeleton()
+//--------------------------------------------------------------------
+void LLViewerJoint::renderSkeleton(BOOL recursive)
+{
+ F32 nc = 0.57735f;
+
+ //----------------------------------------------------------------
+ // push matrix stack
+ //----------------------------------------------------------------
+ glPushMatrix();
+
+ //----------------------------------------------------------------
+ // render the bone to my parent
+ //----------------------------------------------------------------
+ if (mComponents & SC_BONE)
+ {
+ drawBone();
+ }
+
+ //----------------------------------------------------------------
+ // offset to joint position and
+ // rotate to our orientation
+ //----------------------------------------------------------------
+ glLoadIdentity();
+ glMultMatrixf( &getWorldMatrix().mMatrix[0][0] );
+
+ //----------------------------------------------------------------
+ // render joint axes
+ //----------------------------------------------------------------
+ if (mComponents & SC_AXES)
+ {
+ glBegin(GL_LINES);
+ glColor3f( 1.0f, 0.0f, 0.0f );
+ glVertex3f( 0.0f, 0.0f, 0.0f );
+ glVertex3f( 0.1f, 0.0f, 0.0f );
+
+ glColor3f( 0.0f, 1.0f, 0.0f );
+ glVertex3f( 0.0f, 0.0f, 0.0f );
+ glVertex3f( 0.0f, 0.1f, 0.0f );
+
+ glColor3f( 0.0f, 0.0f, 1.0f );
+ glVertex3f( 0.0f, 0.0f, 0.0f );
+ glVertex3f( 0.0f, 0.0f, 0.1f );
+ glEnd();
+ }
+
+ //----------------------------------------------------------------
+ // render the joint graphic
+ //----------------------------------------------------------------
+ if (mComponents & SC_JOINT)
+ {
+ glColor3f( 1.0f, 1.0f, 0.0f );
+
+ glBegin(GL_TRIANGLES);
+
+ // joint top half
+ glNormal3f(nc, nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+
+ glNormal3f(-nc, nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+
+ glNormal3f(-nc, -nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+
+ glNormal3f(nc, -nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+
+ // joint bottom half
+ glNormal3f(nc, nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+
+ glNormal3f(-nc, nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+
+ glNormal3f(-nc, -nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+
+ glNormal3f(nc, -nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+
+ glEnd();
+ }
+
+ //----------------------------------------------------------------
+ // render children
+ //----------------------------------------------------------------
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->renderSkeleton();
+ }
+ }
+
+ //----------------------------------------------------------------
+ // pop matrix stack
+ //----------------------------------------------------------------
+ glPopMatrix();
+}
+
+
+//--------------------------------------------------------------------
+// render()
+//--------------------------------------------------------------------
+U32 LLViewerJoint::render( F32 pixelArea )
+{
+ U32 triangle_count = 0;
+
+ //----------------------------------------------------------------
+ // ignore invisible objects
+ //----------------------------------------------------------------
+ if ( mValid )
+ {
+
+ //----------------------------------------------------------------
+ // if object is transparent, defer it, otherwise
+ // give the joint subclass a chance to draw itself
+ //----------------------------------------------------------------
+ if ( gRenderForSelect )
+ {
+ triangle_count += drawShape( pixelArea );
+ }
+ else if ( isTransparent() )
+ {
+ LLGLEnable blend(GL_BLEND);
+ // Hair and Skirt
+ if ((pixelArea > MIN_PIXEL_AREA_3PASS_HAIR))
+ {
+ // render all three passes
+ LLGLEnable alpha_test(GL_ALPHA_TEST);
+ LLGLDisable cull(GL_CULL_FACE);
+ // first pass renders without writing to the z buffer
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ triangle_count += drawShape( pixelArea );
+ }
+ // second pass writes to z buffer only
+ glColorMask(FALSE, FALSE, FALSE, FALSE);
+ {
+ triangle_count += drawShape( pixelArea );
+ }
+ // third past respects z buffer and writes color
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ triangle_count += drawShape( pixelArea );
+ }
+ }
+ else
+ {
+ LLGLEnable alpha_test(GL_ALPHA_TEST);
+ // Render Inside (no Z buffer write)
+ glCullFace(GL_FRONT);
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ triangle_count += drawShape( pixelArea );
+ }
+ // Render Outside (write to the Z buffer)
+ glCullFace(GL_BACK);
+ {
+ triangle_count += drawShape( pixelArea );
+ }
+ }
+ }
+ else
+ {
+ // set up render state
+ LLGLDisable blend(GL_BLEND);
+ LLGLSPipelineAvatar gls_pipeline_avatar;
+ triangle_count += drawShape( pixelArea );
+ }
+ }
+
+ //----------------------------------------------------------------
+ // render children
+ //----------------------------------------------------------------
+ LLViewerJoint *joint;
+ for ( joint = (LLViewerJoint *)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint *)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+ if (pixelArea >= jointLOD || sDisableLOD)
+ {
+ triangle_count += joint->render( pixelArea );
+
+ if (jointLOD != DEFAULT_LOD)
+ {
+ break;
+ }
+ }
+ }
+
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ return triangle_count;
+}
+
+
+//--------------------------------------------------------------------
+// drawBone()
+//--------------------------------------------------------------------
+void LLViewerJoint::drawBone()
+{
+ if ( mParent == NULL )
+ return;
+
+ F32 boneSize = 0.02f;
+
+ // rotate to point to child (bone direction)
+ glPushMatrix();
+
+ LLVector3 boneX = getPosition();
+ F32 length = boneX.normVec();
+
+ LLVector3 boneZ(1.0f, 0.0f, 1.0f);
+
+ LLVector3 boneY = boneZ % boneX;
+ boneY.normVec();
+
+ boneZ = boneX % boneY;
+
+ LLMatrix4 rotateMat;
+ rotateMat.setFwdRow( boneX );
+ rotateMat.setLeftRow( boneY );
+ rotateMat.setUpRow( boneZ );
+ glMultMatrixf( &rotateMat.mMatrix[0][0] );
+
+ // render the bone
+ glColor3f( 0.5f, 0.5f, 0.0f );
+
+ glBegin(GL_TRIANGLES);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, boneSize, 0.0f);
+ glVertex3f( 0.0f, 0.0f, boneSize);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, 0.0f, -boneSize);
+ glVertex3f( 0.0f, boneSize, 0.0f);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, -boneSize, 0.0f);
+ glVertex3f( 0.0f, 0.0f, -boneSize);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, 0.0f, boneSize);
+ glVertex3f( 0.0f, -boneSize, 0.0f);
+
+ glEnd();
+
+ // restore matrix
+ glPopMatrix();
+}
+
+//--------------------------------------------------------------------
+// isTransparent()
+//--------------------------------------------------------------------
+BOOL LLViewerJoint::isTransparent()
+{
+ return FALSE;
+}
+
+//--------------------------------------------------------------------
+// drawShape()
+//--------------------------------------------------------------------
+U32 LLViewerJoint::drawShape( F32 pixelArea )
+{
+ return 0;
+}
+
+//--------------------------------------------------------------------
+// setSkeletonComponents()
+//--------------------------------------------------------------------
+void LLViewerJoint::setSkeletonComponents( U32 comp, BOOL recursive )
+{
+ mComponents = comp;
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint *)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint *)mChildren.getNextData() )
+ {
+ joint->setSkeletonComponents(comp, recursive);
+ }
+ }
+}
+
+void LLViewerJoint::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
+{
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+ if (pixel_area >= jointLOD || sDisableLOD)
+ {
+ joint->updateFaceSizes(num_vertices, pixel_area);
+
+ if (jointLOD != DEFAULT_LOD)
+ {
+ break;
+ }
+ }
+ }
+}
+
+void LLViewerJoint::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
+{
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+ if (pixel_area >= jointLOD || sDisableLOD)
+ {
+ joint->updateFaceData(face, pixel_area, damp_wind);
+
+ if (jointLOD != DEFAULT_LOD)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+BOOL LLViewerJoint::updateLOD(F32 pixel_area, BOOL activate)
+{
+ BOOL lod_changed = FALSE;
+ BOOL found_lod = FALSE;
+
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+
+ if (found_lod || jointLOD == DEFAULT_LOD)
+ {
+ // we've already found a joint to enable, so enable the rest as alternatives
+ lod_changed |= joint->updateLOD(pixel_area, TRUE);
+ }
+ else
+ {
+ if (pixel_area >= jointLOD || sDisableLOD)
+ {
+ lod_changed |= joint->updateLOD(pixel_area, TRUE);
+ found_lod = TRUE;
+ }
+ else
+ {
+ lod_changed |= joint->updateLOD(pixel_area, FALSE);
+ }
+ }
+ }
+ return lod_changed;
+}
+
+void LLViewerJoint::dump()
+{
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->dump();
+ }
+}
+
+void LLViewerJoint::setVisible(BOOL visible, BOOL recursive)
+{
+ mVisible = visible;
+
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->setVisible(visible, recursive);
+ }
+ }
+}
+
+void LLViewerJoint::writeCAL3D(apr_file_t* fp)
+{
+ LLVector3 bone_pos = mXform.getPosition();
+ if (mParent)
+ {
+ bone_pos.scaleVec(mParent->getScale());
+ bone_pos *= 100.f;
+ }
+ else
+ {
+ bone_pos.clearVec();
+ }
+
+ LLQuaternion bone_rot;
+
+ S32 num_children = 0;
+ for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ if (joint->mJointNum != -1)
+ {
+ num_children++;
+ }
+ }
+
+ LLJoint* cur_joint = this;
+ LLVector3 rootSkinOffset;
+ if (mParent)
+ {
+ while (cur_joint)
+ {
+ rootSkinOffset -= cur_joint->getSkinOffset();
+ cur_joint = (LLViewerJoint*)cur_joint->getParent();
+ }
+
+ rootSkinOffset *= 100.f;
+ }
+
+ apr_file_printf(fp, " <BONE ID=\"%d\" NAME=\"%s\" NUMCHILDS=\"%d\">\n", mJointNum + 1, mName.c_str(), num_children);
+ apr_file_printf(fp, " <TRANSLATION>%.6f %.6f %.6f</TRANSLATION>\n", bone_pos.mV[VX], bone_pos.mV[VY], bone_pos.mV[VZ]);
+ apr_file_printf(fp, " <ROTATION>%.6f %.6f %.6f %.6f</ROTATION>\n", bone_rot.mQ[VX], bone_rot.mQ[VY], bone_rot.mQ[VZ], bone_rot.mQ[VW]);
+ apr_file_printf(fp, " <LOCALTRANSLATION>%.6f %.6f %.6f</LOCALTRANSLATION>\n", rootSkinOffset.mV[VX], rootSkinOffset.mV[VY], rootSkinOffset.mV[VZ]);
+ apr_file_printf(fp, " <LOCALROTATION>0 0 0 1</LOCALROTATION>\n");
+ apr_file_printf(fp, " <PARENTID>%d</PARENTID>\n", mParent ? mParent->mJointNum + 1 : -1);
+
+ for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ if (joint->mJointNum != -1)
+ {
+ apr_file_printf(fp, " <CHILDID>%d</CHILDID>\n", joint->mJointNum + 1);
+ }
+ }
+ apr_file_printf(fp, " </BONE>\n");
+
+ // recurse
+ for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ if (joint->mJointNum != -1)
+ {
+ joint->writeCAL3D(fp);
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerJointCollisionVolume()
+//-----------------------------------------------------------------------------
+
+LLViewerJointCollisionVolume::LLViewerJointCollisionVolume()
+{
+ mUpdateXform = FALSE;
+}
+
+LLViewerJointCollisionVolume::LLViewerJointCollisionVolume(const std::string &name, LLJoint *parent) : LLViewerJoint(name, parent)
+{
+
+}
+
+void LLViewerJointCollisionVolume::render()
+{
+ updateWorldMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glMultMatrixf( &mXform.getWorldMatrix().mMatrix[0][0] );
+
+ glColor3f( 0.f, 0.f, 1.f );
+ gSphere.render();
+
+ glPopMatrix();
+}
+
+LLVector3 LLViewerJointCollisionVolume::getVolumePos(LLVector3 &offset)
+{
+ mUpdateXform = TRUE;
+
+ LLVector3 result = offset;
+ result.scaleVec(getScale());
+ result.rotVec(getWorldRotation());
+ result += getWorldPosition();
+
+ return result;
+}
+
+// End
diff --git a/indra/newview/llviewerjoint.h b/indra/newview/llviewerjoint.h
new file mode 100644
index 0000000000..e38c1c8e7d
--- /dev/null
+++ b/indra/newview/llviewerjoint.h
@@ -0,0 +1,137 @@
+/**
+ * @file llviewerjoint.h
+ * @brief Implementation of LLViewerJoint class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERJOINT_H
+#define LL_LLVIEWERJOINT_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "lljoint.h"
+#include "llapr.h"
+
+class LLFace;
+
+//-----------------------------------------------------------------------------
+// class LLViewerJoint
+//-----------------------------------------------------------------------------
+class LLViewerJoint :
+ public LLJoint
+{
+public:
+ LLViewerJoint();
+ LLViewerJoint(const std::string &name, LLJoint *parent = NULL);
+ virtual ~LLViewerJoint();
+
+ // Gets the validity of this joint
+ BOOL getValid() { return mValid; }
+
+ // Sets the validity of this joint
+ virtual void setValid( BOOL valid, BOOL recursive=FALSE );
+
+ // Primarily for debugging and character setup
+ // Derived classes may add text/graphic output.
+ // Draw skeleton graphic for debugging and character setup
+ virtual void renderSkeleton(BOOL recursive=TRUE);
+
+ // Render character hierarchy.
+ // Traverses the entire joint hierarchy, setting up
+ // transforms and calling the drawShape().
+ // Derived classes may add text/graphic output.
+ virtual U32 render( F32 pixelArea ); // Returns triangle count
+
+ // Draws a bone graphic to the parent joint.
+ // Derived classes may add text/graphic output.
+ // Called by renderSkeleton().
+ virtual void drawBone();
+
+ // Returns true if this object is transparent.
+ // This is used to determine in which order to draw objects.
+ virtual BOOL isTransparent();
+
+ // Returns true if this object should inherit scale modifiers from its immediate parent
+ virtual BOOL inheritScale() { return FALSE; }
+
+ // Draws the shape attached to a joint.
+ // Called by render().
+ virtual U32 drawShape( F32 pixelArea );
+ virtual void drawNormals() {}
+
+ enum Components
+ {
+ SC_BONE = 1,
+ SC_JOINT = 2,
+ SC_AXES = 4
+ };
+
+ // Selects which skeleton components to draw
+ void setSkeletonComponents( U32 comp, BOOL recursive = TRUE );
+
+ // Returns which skeleton components are enables for drawing
+ U32 getSkeletonComponents() { return mComponents; }
+
+ // Sets the level of detail for this node as a minimum
+ // pixel area threshold. If the current pixel area for this
+ // object is less than the specified threshold, the node is
+ // not traversed. In additin, if a value is specified (not
+ // default of 0.0), and the pixel area is larger than the
+ // specified miniumn, the node is rendered, but no other siblings
+ // of this node under the same parent will be.
+ F32 getLOD() { return mMinPixelArea; }
+ void setLOD( F32 pixelArea ) { mMinPixelArea = pixelArea; }
+
+ // Sets the OpenGL selection stack name that is pushed and popped
+ // with this joint state. The default value indicates that no name
+ // should be pushed/popped.
+ enum PickName
+ {
+ PN_DEFAULT = -1,
+ PN_0 = 0,
+ PN_1 = 1,
+ PN_2 = 2,
+ PN_3 = 3,
+ PN_4 = 4,
+ PN_5 = 5
+ };
+ void setPickName(PickName name) { mPickName = name; }
+ PickName getPickName() { return mPickName; }
+
+ virtual void updateFaceSizes(U32 &num_vertices, F32 pixel_area);
+ virtual void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE);
+ virtual BOOL updateLOD(F32 pixel_area, BOOL activate);
+ virtual void dump();
+
+ void setVisible( BOOL visible, BOOL recursive );
+ virtual void writeCAL3D(apr_file_t* fp);
+
+public:
+ static BOOL sDisableLOD;
+
+protected:
+ BOOL mValid;
+ U32 mComponents;
+ F32 mMinPixelArea;
+ PickName mPickName;
+ BOOL mVisible;
+};
+
+class LLViewerJointCollisionVolume : public LLViewerJoint
+{
+public:
+ LLViewerJointCollisionVolume();
+ LLViewerJointCollisionVolume(const std::string &name, LLJoint *parent = NULL);
+ virtual ~LLViewerJointCollisionVolume() {};
+
+ virtual BOOL inheritScale() { return TRUE; }
+
+ void render();
+ LLVector3 getVolumePos(LLVector3 &offset);
+};
+
+#endif // LL_LLVIEWERJOINT_H
+
diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp
new file mode 100644
index 0000000000..a8fd7b9d90
--- /dev/null
+++ b/indra/newview/llviewerjointattachment.cpp
@@ -0,0 +1,381 @@
+/**
+ * @file llviewerjointattachment.cpp
+ * @brief Implementation of LLViewerJointAttachment class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerjointattachment.h"
+
+#include "llagentconstants.h"
+
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llgl.h"
+#include "llvoavatar.h"
+#include "llvolume.h"
+#include "pipeline.h"
+#include "llinventorymodel.h"
+#include "llviewerobjectlist.h"
+#include "llface.h"
+#include "llvoavatar.h"
+
+#include "llglheaders.h"
+
+extern LLPipeline gPipeline;
+
+//-----------------------------------------------------------------------------
+// LLViewerJointAttachment()
+//-----------------------------------------------------------------------------
+LLViewerJointAttachment::LLViewerJointAttachment()
+{
+ mJoint = NULL;
+ mAttachedObject = NULL;
+ mAttachmentDirty = FALSE;
+ mGroup = 0;
+ mUpdateXform = FALSE;
+ mIsHUDAttachment = FALSE;
+ mValid = FALSE;
+ mPieSlice = -1;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLViewerJointAttachment()
+//-----------------------------------------------------------------------------
+LLViewerJointAttachment::~LLViewerJointAttachment()
+{
+}
+
+//-----------------------------------------------------------------------------
+// isTransparent()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointAttachment::isTransparent()
+{
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// drawShape()
+//-----------------------------------------------------------------------------
+U32 LLViewerJointAttachment::drawShape( F32 pixelArea )
+{
+ if (LLVOAvatar::sShowAttachmentPoints)
+ {
+ LLGLDisable cull_face(GL_CULL_FACE);
+
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ glBegin(GL_QUADS);
+ {
+ glVertex3f(-0.1f, 0.1f, 0.f);
+ glVertex3f(-0.1f, -0.1f, 0.f);
+ glVertex3f(0.1f, -0.1f, 0.f);
+ glVertex3f(0.1f, 0.1f, 0.f);
+ }glEnd();
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// lazyAttach()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::lazyAttach()
+{
+ if (!mAttachedObject)
+ {
+ return;
+ }
+ LLDrawable *drawablep = mAttachedObject->mDrawable;
+
+ if (mAttachmentDirty && drawablep)
+ {
+ setupDrawable(drawablep);
+ mAttachmentDirty = FALSE;
+ }
+}
+
+void LLViewerJointAttachment::setupDrawable(LLDrawable* drawablep)
+{
+ drawablep->mXform.setParent(&mXform); // LLViewerJointAttachment::lazyAttach
+ drawablep->makeActive();
+ LLVector3 current_pos = mAttachedObject->getRenderPosition();
+ LLQuaternion current_rot = mAttachedObject->getRenderRotation();
+ LLQuaternion attachment_pt_inv_rot = ~getWorldRotation();
+
+ current_pos -= getWorldPosition();
+ current_pos.rotVec(attachment_pt_inv_rot);
+
+ current_rot = current_rot * attachment_pt_inv_rot;
+
+ drawablep->mXform.setPosition(current_pos);
+ drawablep->mXform.setRotation(current_rot);
+ gPipeline.markMoved(drawablep);
+ gPipeline.markTextured(drawablep); // face may need to change draw pool to/from POOL_HUD
+ drawablep->setState(LLDrawable::USE_BACKLIGHT);
+
+ if(mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < drawablep->getNumFaces(); face_num++)
+ {
+ drawablep->getFace(face_num)->setState(LLFace::HUD_RENDER);
+ }
+ }
+
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ childp->mDrawable->setState(LLDrawable::USE_BACKLIGHT);
+ gPipeline.markTextured(childp->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ if(mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < childp->mDrawable->getNumFaces(); face_num++)
+ {
+ childp->mDrawable->getFace(face_num)->setState(LLFace::HUD_RENDER);
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addObject()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointAttachment::addObject(LLViewerObject* object)
+{
+ if (mAttachedObject)
+ {
+ llwarns << "Attempted to attach object where an attachment already exists!" << llendl;
+ return FALSE;
+ }
+ mAttachedObject = object;
+
+ LLUUID item_id;
+
+ // Find the inventory item ID of the attached object
+ LLNameValue* item_id_nv = object->getNVPair("AttachItemID");
+ if( item_id_nv )
+ {
+ const char* s = item_id_nv->getString();
+ if( s )
+ {
+ item_id.set( s );
+ lldebugs << "getNVPair( AttachItemID ) = " << item_id << llendl;
+ }
+ }
+
+ mItemID = item_id;
+
+ LLDrawable* drawablep = object->mDrawable;
+
+ if (drawablep)
+ {
+ setupDrawable(drawablep);
+ }
+ else
+ {
+ // do lazy update once we have a drawable for this object
+ mAttachmentDirty = TRUE;
+ }
+
+ if (mIsHUDAttachment)
+ {
+ if (object->mText.notNull())
+ {
+ object->mText->setOnHUDAttachment(TRUE);
+ }
+ for (LLViewerObject::child_list_t::iterator iter = object->mChildList.begin();
+ iter != object->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mText.notNull())
+ {
+ childp->mText->setOnHUDAttachment(TRUE);
+ }
+ }
+ }
+ calcLOD();
+ mUpdateXform = TRUE;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// removeObject()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::removeObject(LLViewerObject *object)
+{
+ // force object visibile
+ setAttachmentVisibility(TRUE);
+
+ if (object->mDrawable.notNull())
+ {
+ LLVector3 cur_position = object->getRenderPosition();
+ LLQuaternion cur_rotation = object->getRenderRotation();
+
+ object->mDrawable->mXform.setPosition(cur_position);
+ object->mDrawable->mXform.setRotation(cur_rotation);
+ gPipeline.markMoved(object->mDrawable, TRUE);
+ gPipeline.markTextured(object->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ object->mDrawable->clearState(LLDrawable::USE_BACKLIGHT);
+
+ if (mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < object->mDrawable->getNumFaces(); face_num++)
+ {
+ object->mDrawable->getFace(face_num)->clearState(LLFace::HUD_RENDER);
+ }
+ }
+ }
+
+ for (LLViewerObject::child_list_t::iterator iter = object->mChildList.begin();
+ iter != object->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ childp->mDrawable->clearState(LLDrawable::USE_BACKLIGHT);
+ gPipeline.markTextured(childp->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ if (mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < childp->mDrawable->getNumFaces(); face_num++)
+ {
+ childp->mDrawable->getFace(face_num)->clearState(LLFace::HUD_RENDER);
+ }
+ }
+ }
+ }
+
+ if (mIsHUDAttachment)
+ {
+ if (object->mText.notNull())
+ {
+ object->mText->setOnHUDAttachment(FALSE);
+ }
+ for (LLViewerObject::child_list_t::iterator iter = object->mChildList.begin();
+ iter != object->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp->mText.notNull())
+ {
+ childp->mText->setOnHUDAttachment(FALSE);
+ }
+ }
+ }
+
+ mAttachedObject = NULL;
+ mUpdateXform = FALSE;
+ mItemID.setNull();
+}
+
+//-----------------------------------------------------------------------------
+// setAttachmentVisibility()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::setAttachmentVisibility(BOOL visible)
+{
+ if (!mAttachedObject || mAttachedObject->mDrawable.isNull())
+ return;
+
+ if (visible)
+ {
+ // Hack to make attachments not visible by disabling their type mask!
+ // This will break if you can ever attach non-volumes! - djs 02/14/03
+ mAttachedObject->mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ childp->mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+ }
+ }
+ }
+ else
+ {
+ //RN: sometimes we call this on objects that are already invisible, so check for that case
+ if (!mAttachedObject->mDrawable->isRenderType(LLPipeline::RENDER_TYPE_VOLUME) &&
+ !mAttachedObject->mDrawable->isRenderType(0))
+ {
+ llerrs << "Tried to attach non-volume to a joint, visibility hack dangerous!" << llendl;
+ }
+ mAttachedObject->mDrawable->setRenderType(0);
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ if (!childp->mDrawable->isRenderType(LLPipeline::RENDER_TYPE_VOLUME) &&
+ !mAttachedObject->mDrawable->isRenderType(0))
+ {
+ llerrs << "Tried to attach non-volume to a joint, visibility hack dangerous!" << llendl;
+ }
+ childp->mDrawable->setRenderType(0);
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setOriginalPosition()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::setOriginalPosition(LLVector3& position)
+{
+ mOriginalPos = position;
+ setPosition(position);
+}
+
+//-----------------------------------------------------------------------------
+// clampObjectPosition()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::clampObjectPosition()
+{
+ if (mAttachedObject)
+ {
+ //FIXME: object can drift when hitting maximum radius
+ LLVector3 attachmentPos = mAttachedObject->getPosition();
+ F32 dist = attachmentPos.normVec();
+ dist = llmin(dist, MAX_ATTACHMENT_DIST);
+ attachmentPos *= dist;
+ mAttachedObject->setPosition(attachmentPos);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcLOD()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::calcLOD()
+{
+ F32 maxarea = mAttachedObject->getMaxScale() * mAttachedObject->getMidScale();
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ F32 area = childp->getMaxScale() * childp->getMidScale();
+ maxarea = llmax(maxarea, area);
+ }
+ maxarea = llclamp(maxarea, .01f*.01f, 1.f);
+ F32 avatar_area = (4.f * 4.f); // pixels for an avatar sized attachment
+ F32 min_pixel_area = avatar_area / maxarea;
+ setLOD(min_pixel_area);
+}
+
+//-----------------------------------------------------------------------------
+// updateLOD()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointAttachment::updateLOD(F32 pixel_area, BOOL activate)
+{
+ BOOL res = FALSE;
+ if (!mValid)
+ {
+ setValid(TRUE, TRUE);
+ res = TRUE;
+ }
+ return res;
+}
+
diff --git a/indra/newview/llviewerjointattachment.h b/indra/newview/llviewerjointattachment.h
new file mode 100644
index 0000000000..b3a6ccb89c
--- /dev/null
+++ b/indra/newview/llviewerjointattachment.h
@@ -0,0 +1,92 @@
+/**
+ * @file llviewerjointattachment.h
+ * @brief Implementation of LLViewerJointAttachment class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERJOINTATTACHMENT_H
+#define LL_LLVIEWERJOINTATTACHMENT_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerjoint.h"
+#include "llstring.h"
+#include "lluuid.h"
+
+class LLDrawable;
+class LLViewerObject;
+
+//-----------------------------------------------------------------------------
+// class LLViewerJointAttachment
+//-----------------------------------------------------------------------------
+class LLViewerJointAttachment :
+ public LLViewerJoint
+{
+public:
+ LLViewerJointAttachment();
+ virtual ~LLViewerJointAttachment();
+
+ //virtual U32 render( F32 pixelArea ); // Returns triangle count
+
+ // Returns true if this object is transparent.
+ // This is used to determine in which order to draw objects.
+ /*virtual*/ BOOL isTransparent();
+
+ // Draws the shape attached to a joint.
+ // Called by render().
+ /*virtual*/ U32 drawShape( F32 pixelArea );
+
+ /*virtual*/ BOOL updateLOD(F32 pixel_area, BOOL activate);
+
+ //
+ // accessors
+ //
+
+ void setJoint (LLJoint* joint) { mJoint = joint; }
+ void setPieSlice(S32 pie_slice) { mPieSlice = pie_slice; }
+ void setVisibleInFirstPerson(BOOL visibility) { mVisibleInFirst = visibility; }
+ BOOL getVisibleInFirstPerson() { return mVisibleInFirst; }
+ void setGroup(S32 group) { mGroup = group; }
+ void setOriginalPosition(LLVector3 &position);
+ void setAttachmentVisibility(BOOL visible);
+ void setIsHUDAttachment(BOOL is_hud) { mIsHUDAttachment = is_hud; }
+ BOOL getIsHUDAttachment() { return mIsHUDAttachment; }
+
+ BOOL isAnimatable() { return FALSE; }
+
+ S32 getGroup() { return mGroup; }
+ S32 getPieSlice() { return mPieSlice; }
+ BOOL getAttachmentDirty() { return mAttachmentDirty && mAttachedObject; }
+ LLViewerObject *getObject(S32 i) { return mAttachedObject; }
+ S32 getNumObjects() { return (mAttachedObject ? 1 : 0); }
+ const LLUUID& getItemID() { return mItemID; }
+
+ //
+ // unique methods
+ //
+ BOOL addObject(LLViewerObject* object);
+ void removeObject(LLViewerObject *object);
+
+ void lazyAttach();
+ void setupDrawable(LLDrawable* drawable);
+ void clampObjectPosition();
+
+protected:
+ void calcLOD();
+
+protected:
+ LLJoint* mJoint;
+ LLViewerObject* mAttachedObject;
+ BOOL mAttachmentDirty; // does attachment drawable need to be fixed up?
+ BOOL mVisibleInFirst;
+ LLVector3 mOriginalPos;
+ S32 mGroup;
+ BOOL mIsHUDAttachment;
+ S32 mPieSlice;
+ LLUUID mItemID; // Inventory item id of the attached item (null if not in inventory)
+};
+
+#endif // LL_LLVIEWERJOINTATTACHMENT_H
diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp
new file mode 100644
index 0000000000..23abba1c9f
--- /dev/null
+++ b/indra/newview/llviewerjointmesh.cpp
@@ -0,0 +1,1601 @@
+/**
+ * @file llviewerjointmesh.cpp
+ * @brief Implementation of LLViewerJointMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#if LL_WINDOWS // For Intel vector classes
+ #include "fvec.h"
+#endif
+
+#include "imageids.h"
+#include "llfasttimer.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llbox.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolbump.h"
+#include "lldynamictexture.h"
+#include "llface.h"
+#include "llgldbg.h"
+#include "llglheaders.h"
+#include "lltexlayer.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerjointmesh.h"
+#include "llvoavatar.h"
+#include "llsky.h"
+#include "pipeline.h"
+
+#if !LL_DARWIN && !LL_LINUX
+extern PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB;
+extern PFNGLWEIGHTFVARBPROC glWeightfvARB;
+extern PFNGLVERTEXBLENDARBPROC glVertexBlendARB;
+#endif
+extern BOOL gRenderForSelect;
+
+LLMatrix4 gBlendMat;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::LLSkinJoint
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLSkinJoint
+//-----------------------------------------------------------------------------
+LLSkinJoint::LLSkinJoint()
+{
+ mJoint = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLSkinJoint
+//-----------------------------------------------------------------------------
+LLSkinJoint::~LLSkinJoint()
+{
+ mJoint = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLSkinJoint::setupSkinJoint()
+//-----------------------------------------------------------------------------
+BOOL LLSkinJoint::setupSkinJoint( LLViewerJoint *joint)
+{
+ // find the named joint
+ mJoint = joint;
+ if ( !mJoint )
+ {
+ llinfos << "Can't find joint" << llendl;
+ }
+
+ // compute the inverse root skin matrix
+ mRootToJointSkinOffset.clearVec();
+
+ LLVector3 rootSkinOffset;
+ while (joint)
+ {
+ rootSkinOffset += joint->getSkinOffset();
+ joint = (LLViewerJoint*)joint->getParent();
+ }
+
+ mRootToJointSkinOffset = -rootSkinOffset;
+ mRootToParentJointSkinOffset = mRootToJointSkinOffset;
+ mRootToParentJointSkinOffset += mJoint->getSkinOffset();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+BOOL LLViewerJointMesh::sPipelineRender = FALSE;
+EAvatarRenderPass LLViewerJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
+U32 LLViewerJointMesh::sClothingMaskImageName = 0;
+LLColor4 LLViewerJointMesh::sClothingInnerColor;
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh()
+//-----------------------------------------------------------------------------
+LLViewerJointMesh::LLViewerJointMesh()
+ :
+ mTexture( NULL ),
+ mLayerSet( NULL ),
+ mTestImageName( 0 ),
+ mIsTransparent(FALSE)
+{
+
+ mColor[0] = 1.0f;
+ mColor[1] = 1.0f;
+ mColor[2] = 1.0f;
+ mColor[3] = 1.0f;
+ mShiny = 0.0f;
+ mCullBackFaces = TRUE;
+
+ mMesh = NULL;
+
+ mNumSkinJoints = 0;
+ mSkinJoints = NULL;
+
+ mFace = NULL;
+
+ mMeshID = 0;
+ mUpdateXform = FALSE;
+
+ mValid = FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLViewerJointMesh()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLViewerJointMesh::~LLViewerJointMesh()
+{
+ mMesh = NULL;
+ mTexture = NULL;
+ freeSkinData();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::allocateSkinData()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointMesh::allocateSkinData( U32 numSkinJoints )
+{
+ mSkinJoints = new LLSkinJoint[ numSkinJoints ];
+ mNumSkinJoints = numSkinJoints;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// getSkinJointByIndex()
+//-----------------------------------------------------------------------------
+S32 LLViewerJointMesh::getBoundJointsByIndex(S32 index, S32 &joint_a, S32& joint_b)
+{
+ S32 num_joints = 0;
+ if (mNumSkinJoints == 0)
+ {
+ return num_joints;
+ }
+
+ joint_a = -1;
+ joint_b = -1;
+
+ LLPolyMesh *reference_mesh = mMesh->getReferenceMesh();
+
+ if (index < reference_mesh->mJointRenderData.count())
+ {
+ LLJointRenderData* render_datap = reference_mesh->mJointRenderData[index];
+ if (render_datap->mSkinJoint)
+ {
+ joint_a = render_datap->mSkinJoint->mJoint->mJointNum;
+ }
+ num_joints++;
+ }
+ if (index + 1 < reference_mesh->mJointRenderData.count())
+ {
+ LLJointRenderData* render_datap = reference_mesh->mJointRenderData[index + 1];
+ if (render_datap->mSkinJoint)
+ {
+ joint_b = render_datap->mSkinJoint->mJoint->mJointNum;
+ }
+
+ if (joint_a == -1)
+ {
+ joint_a = render_datap->mSkinJoint->mJoint->getParent()->mJointNum;
+ }
+ num_joints++;
+ }
+ return num_joints;
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::freeSkinData()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::freeSkinData()
+{
+ mNumSkinJoints = 0;
+ delete [] mSkinJoints;
+ mSkinJoints = NULL;
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::getColor()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::getColor( F32 *red, F32 *green, F32 *blue, F32 *alpha )
+{
+ *red = mColor[0];
+ *green = mColor[1];
+ *blue = mColor[2];
+ *alpha = mColor[3];
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::setColor()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::setColor( F32 red, F32 green, F32 blue, F32 alpha )
+{
+ mColor[0] = red;
+ mColor[1] = green;
+ mColor[2] = blue;
+ mColor[3] = alpha;
+}
+
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::getTexture()
+//--------------------------------------------------------------------
+//LLViewerImage *LLViewerJointMesh::getTexture()
+//{
+// return mTexture;
+//}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::setTexture()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::setTexture( LLViewerImage *texture )
+{
+ mTexture = texture;
+
+ // texture and dynamic_texture are mutually exclusive
+ if( texture )
+ {
+ mLayerSet = NULL;
+ //texture->bindTexture(0);
+ //texture->setClamp(TRUE, TRUE);
+ }
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::setLayerSet()
+// Sets the shape texture (takes precedence over normal texture)
+//--------------------------------------------------------------------
+void LLViewerJointMesh::setLayerSet( LLTexLayerSet* layer_set )
+{
+ mLayerSet = layer_set;
+
+ // texture and dynamic_texture are mutually exclusive
+ if( layer_set )
+ {
+ mTexture = NULL;
+ }
+}
+
+
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::getMesh()
+//--------------------------------------------------------------------
+LLPolyMesh *LLViewerJointMesh::getMesh()
+{
+ return mMesh;
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::setMesh()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::setMesh( LLPolyMesh *mesh )
+{
+ // set the mesh pointer
+ mMesh = mesh;
+
+ // release any existing skin joints
+ freeSkinData();
+
+ if ( mMesh == NULL )
+ {
+ return;
+ }
+
+ // acquire the transform from the mesh object
+ setPosition( mMesh->getPosition() );
+ setRotation( mMesh->getRotation() );
+ setScale( mMesh->getScale() );
+
+ // create skin joints if necessary
+ if ( mMesh->hasWeights() && !mMesh->isLOD())
+ {
+ U32 numJointNames = mMesh->getNumJointNames();
+
+ allocateSkinData( numJointNames );
+ std::string *jointNames = mMesh->getJointNames();
+
+ U32 jn;
+ for (jn = 0; jn < numJointNames; jn++)
+ {
+ //llinfos << "Setting up joint " << jointNames[jn].c_str() << llendl;
+ LLViewerJoint* joint = (LLViewerJoint*)(getRoot()->findJoint(jointNames[jn]) );
+ mSkinJoints[jn].setupSkinJoint( joint );
+ }
+ }
+
+ // setup joint array
+ if (!mMesh->isLOD())
+ {
+ setupJoint((LLViewerJoint*)getRoot());
+ }
+
+// llinfos << "joint render entries: " << mMesh->mJointRenderData.count() << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// setupJoint()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::setupJoint(LLViewerJoint* current_joint)
+{
+// llinfos << "Mesh: " << getName() << llendl;
+
+// S32 joint_count = 0;
+ U32 sj;
+ for (sj=0; sj<mNumSkinJoints; sj++)
+ {
+ LLSkinJoint &js = mSkinJoints[sj];
+
+ if (js.mJoint != current_joint)
+ {
+ continue;
+ }
+
+ // we've found a skinjoint for this joint..
+
+ // is the last joint in the array our parent?
+ if(mMesh->mJointRenderData.count() && mMesh->mJointRenderData[mMesh->mJointRenderData.count() - 1]->mWorldMatrix == &current_joint->getParent()->getWorldMatrix())
+ {
+ // ...then just add ourselves
+ LLViewerJoint* jointp = js.mJoint;
+ mMesh->mJointRenderData.put(new LLJointRenderData(&jointp->getWorldMatrix(), &js));
+// llinfos << "joint " << joint_count << js.mJoint->getName() << llendl;
+// joint_count++;
+ }
+ // otherwise add our parent and ourselves
+ else
+ {
+ mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getParent()->getWorldMatrix(), NULL));
+// llinfos << "joint " << joint_count << current_joint->getParent()->getName() << llendl;
+// joint_count++;
+ mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getWorldMatrix(), &js));
+// llinfos << "joint " << joint_count << current_joint->getName() << llendl;
+// joint_count++;
+ }
+ }
+
+ // depth-first traversal
+ for (LLJoint *child_joint = current_joint->mChildren.getFirstData();
+ child_joint;
+ child_joint = current_joint->mChildren.getNextData())
+ {
+ setupJoint((LLViewerJoint*)child_joint);
+ }
+}
+
+const S32 NUM_AXES = 3;
+
+// register layoud
+// rotation X 0-n
+// rotation Y 0-n
+// rotation Z 0-n
+// pivot parent 0-n -- child = n+1
+
+static LLMatrix4 gJointMat[32];
+static LLMatrix3 gJointRot[32];
+static LLVector4 gJointPivot[32];
+
+//-----------------------------------------------------------------------------
+// uploadJointMatrices()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::uploadJointMatrices()
+{
+ S32 joint_num;
+ LLPolyMesh *reference_mesh = mMesh->getReferenceMesh();
+ LLDrawPool *poolp = mFace ? mFace->getPool() : NULL;
+ BOOL hardware_skinning = (poolp && poolp->getVertexShaderLevel() > 0) ? TRUE : FALSE;
+
+ //calculate joint matrices
+ for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
+ {
+ LLMatrix4 joint_mat = *reference_mesh->mJointRenderData[joint_num]->mWorldMatrix;
+
+ if (hardware_skinning)
+ {
+ joint_mat *= gCamera->getModelview();
+ }
+ gJointMat[joint_num] = joint_mat;
+ gJointRot[joint_num] = joint_mat.getMat3();
+ }
+
+ BOOL last_pivot_uploaded = FALSE;
+ S32 j = 0;
+
+ //upload joint pivots
+ for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
+ {
+ LLSkinJoint *sj = reference_mesh->mJointRenderData[joint_num]->mSkinJoint;
+ if (sj)
+ {
+ if (!last_pivot_uploaded)
+ {
+ LLVector4 parent_pivot(sj->mRootToParentJointSkinOffset);
+ parent_pivot.mV[VW] = 0.f;
+ gJointPivot[j++] = parent_pivot;
+ }
+
+ LLVector4 child_pivot(sj->mRootToJointSkinOffset);
+ child_pivot.mV[VW] = 0.f;
+
+ gJointPivot[j++] = child_pivot;
+
+ last_pivot_uploaded = TRUE;
+ }
+ else
+ {
+ last_pivot_uploaded = FALSE;
+ }
+ }
+
+ //add pivot point into transform
+ for (S32 i = 0; i < j; i++)
+ {
+ LLVector3 pivot;
+ pivot = LLVector3(gJointPivot[i]);
+ pivot = pivot * gJointRot[i];
+ gJointMat[i].translate(pivot);
+ }
+
+ // upload matrices
+ if (hardware_skinning)
+ {
+ GLfloat mat[45*4];
+ memset(mat, 0, sizeof(GLfloat)*45*4);
+
+ for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
+ {
+ gJointMat[joint_num].transpose();
+
+ for (S32 axis = 0; axis < NUM_AXES; axis++)
+ {
+ F32* vector = gJointMat[joint_num].mMatrix[axis];
+ //glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, LL_CHARACTER_MAX_JOINTS_PER_MESH * axis + joint_num+5, (GLfloat*)vector);
+ U32 offset = LL_CHARACTER_MAX_JOINTS_PER_MESH*axis+joint_num;
+ memcpy(mat+offset*4, vector, sizeof(GLfloat)*4);
+ //glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, LL_CHARACTER_MAX_JOINTS_PER_MESH * axis + joint_num+6, (GLfloat*)vector);
+ //cgGLSetParameterArray4f(gPipeline.mAvatarMatrix, offset, 1, vector);
+ }
+ }
+ glUniform4fvARB(gPipeline.mAvatarMatrixParam, 45, mat);
+ }
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::drawBone()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::drawBone()
+{
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::isTransparent()
+//--------------------------------------------------------------------
+BOOL LLViewerJointMesh::isTransparent()
+{
+ return mIsTransparent;
+}
+
+//--------------------------------------------------------------------
+// DrawElementsBLEND and utility code
+//--------------------------------------------------------------------
+
+// compate_int is used by the qsort function to sort the index array
+int compare_int(const void *a, const void *b)
+{
+ if (*(U32*)a < *(U32*)b)
+ {
+ return -1;
+ }
+ else if (*(U32*)a > *(U32*)b)
+ {
+ return 1;
+ }
+ else return 0;
+}
+
+#if LL_WINDOWS || (LL_DARWIN && __i386__) // SSE optimizations in avatar code
+
+#if LL_DARWIN
+#include <xmmintrin.h>
+
+ // On Windows, this class is defined in fvec.h. I've only reproduced the parts of it we use here for now.
+ #pragma pack(push,16) /* Must ensure class & union 16-B aligned */
+ class F32vec4
+ {
+ protected:
+ __m128 vec;
+ public:
+
+ /* Constructors: __m128, 4 floats, 1 float */
+ F32vec4() {}
+
+ /* initialize 4 SP FP with __m128 data type */
+ F32vec4(__m128 m) { vec = m;}
+
+ /* Explicitly initialize each of 4 SP FPs with same float */
+ explicit F32vec4(float f) { vec = _mm_set_ps1(f); }
+ };
+ #pragma pack(pop) /* 16-B aligned */
+
+
+#endif
+
+void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
+ LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
+{
+ F32 last_weight = F32_MAX;
+ LLMatrix4 *blend_mat = &gBlendMat;
+
+ for (S32 index = vert_offset; index < vert_offset + vert_count; index++)
+ {
+ F32 w = weights [index]; // register copy of weight
+ F32 *vin = &vertices[index].mV[0]; // pointer to input vertex data, assumed to be V3+T2+N3+whatever
+ F32 *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32)); // pointer to the output vertex data, assumed to be 16 byte aligned
+
+ if (w == last_weight)
+ {
+ // load input and output vertices, and last blended matrix
+ __asm {
+ mov esi, vin
+ mov edi, vout
+
+ mov edx, blend_mat
+ movaps xmm4, [edx]
+ movaps xmm5, [edx+0x10]
+ movaps xmm6, [edx+0x20]
+ movaps xmm7, [edx+0x30]
+ }
+ }
+ else
+ {
+ last_weight = w;
+ S32 joint = llfloor(w);
+ w -= joint;
+
+ LLMatrix4 *m0 = &(gJointMat[joint+1]);
+ LLMatrix4 *m1 = &(gJointMat[joint+0]);
+
+ // some initial code to load Matrix 0 into SSE registers
+ __asm {
+ mov esi, vin
+ mov edi, vout
+
+ //matrix2
+ mov edx, m0
+ movaps xmm4, [edx]
+ movaps xmm5, [edx+0x10]
+ movaps xmm6, [edx+0x20]
+ movaps xmm7, [edx+0x30]
+ };
+
+ // if w == 1.0f, we don't need to blend.
+ // but since we do the trick of blending the matrices, here, if w != 1.0,
+ // we load Matrix 1 into the other 4 SSE registers and blend both matrices
+ // based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
+
+ if (w != 1.0f)
+ {
+ F32vec4 weight(w);
+
+ __asm { // do blending of matrices instead of verts and normals -- faster
+ mov edx, m1
+ movaps xmm0, [edx]
+ movaps xmm1, [edx+0x10]
+ movaps xmm2, [edx+0x20]
+ movaps xmm3, [edx+0x30]
+
+ subps xmm4, xmm0 // do blend for each matrix column
+ subps xmm5, xmm1 // diff, then multiply weight and re-add
+ subps xmm6, xmm2
+ subps xmm7, xmm3
+
+ mulps xmm4, weight
+ mulps xmm5, weight
+ mulps xmm6, weight
+ mulps xmm7, weight
+
+ addps xmm4, xmm0
+ addps xmm5, xmm1
+ addps xmm6, xmm2
+ addps xmm7, xmm3
+ };
+ }
+
+ __asm {
+ // save off blended matrix
+ mov edx, blend_mat;
+ movaps [edx], xmm4;
+ movaps [edx+0x10], xmm5;
+ movaps [edx+0x20], xmm6;
+ movaps [edx+0x30], xmm7;
+ }
+ }
+
+ // now, we have either a blended matrix in xmm4-7 or the original Matrix 0
+ // we then multiply each vertex and normal by this one matrix.
+
+ // For SSE2, we would try to keep the original two matrices in other registers
+ // and avoid reloading them. However, they should ramain in L1 cache in the
+ // current case.
+
+ // One possible optimization would be to sort the vertices by weight instead
+ // of just index (we still want to uniqify). If we note when two or more vertices
+ // share the same weight, we can avoid doing the middle SSE code above and just
+ // re-use the blended matrix for those vertices
+
+
+ // now, we do the actual vertex blending
+ __asm {
+ // load Vertex into xmm0.
+ movaps xmm0, [esi] // change aps to ups when input is no longer 16-baligned
+ movaps xmm1, xmm0 // copy vector into xmm0 through xmm2 (x,y,z)
+ movaps xmm2, xmm0
+ shufps xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // clone vertex (x) across vector
+ shufps xmm1, xmm1, _MM_SHUFFLE(1,1,1,1); // clone vertex (y) across vector
+ shufps xmm2, xmm2, _MM_SHUFFLE(2,2,2,2); // same for Z
+ mulps xmm0, xmm4 // do the actual matrix multipication for r0
+ mulps xmm1, xmm5 // for r1
+ mulps xmm2, xmm6 // for r2
+ addps xmm0, xmm1 // accumulate
+ addps xmm0, xmm2 // accumulate
+ addps xmm0, xmm7 // add in the row 4 which holds the x,y,z translation. assumes w=1 (vertex-w, not weight)
+
+ movaps [edi], xmm0 // store aligned in output array
+
+ // load Normal into xmm0.
+ movaps xmm0, [esi + 0x10] // change aps to ups when input no longer 16-byte aligned
+ movaps xmm1, xmm0 //
+ movaps xmm2, xmm0
+ shufps xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // since UV sits between vertex and normal, normal starts at element 1, not 0
+ shufps xmm1, xmm1, _MM_SHUFFLE(1,1,1,1);
+ shufps xmm2, xmm2, _MM_SHUFFLE(2,2,2,2);
+ mulps xmm0, xmm4 // multiply by matrix
+ mulps xmm1, xmm5 // multiply
+ mulps xmm2, xmm6 // multiply
+ addps xmm0, xmm1 // accumulate
+ addps xmm0, xmm2 // accumulate. note: do not add translation component to normals, save time too
+ movaps [edi + 0x10], xmm0 // store aligned
+ }
+
+ *(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot.
+ }
+}
+
+#elif LL_LINUX
+
+void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
+ LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
+{
+ assert(0);
+}
+
+#elif LL_DARWIN
+// AltiVec versions of the same...
+
+static inline vector float loadAlign(int offset, vector float *addr)
+{
+ vector float in0 = vec_ld(offset, addr);
+ vector float in1 = vec_ld(offset + 16, addr);
+ vector unsigned char perm = vec_lvsl(0, (unsigned char*)addr);
+
+ return(vec_perm(in0, in1, perm));
+}
+
+static inline void storeAlign(vector float v, int offset, vector float *addr)
+{
+ vector float in0 = vec_ld(offset, addr);
+ vector float in1 = vec_ld(offset + 16, addr);
+ vector unsigned char perm = vec_lvsr(0, (unsigned char *)addr);
+ vector float temp = vec_perm(v, v, perm);
+ vector unsigned char mask = (vector unsigned char)vec_cmpgt(perm, vec_splat_u8(15));
+
+ in0 = vec_sel(in0, temp, (vector unsigned int)mask);
+ in1 = vec_sel(temp, in1, (vector unsigned int)mask);
+
+ vec_st(in0, offset, addr);
+ vec_st(in1, offset + 16, addr);
+}
+
+void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
+ LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
+{
+ F32 last_weight = F32_MAX;
+// LLMatrix4 &blend_mat = gBlendMat;
+
+ vector float matrix0_0, matrix0_1, matrix0_2, matrix0_3;
+ vector unsigned char out0perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F );
+// vector unsigned char out1perm = (vector unsigned char) ( 0x00,0x01,0x02,0x03, 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B );
+ vector unsigned char out1perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F );
+
+ vector float zero = (vector float)vec_splat_u32(0);
+
+ for (U32 index = vert_offset; index < vert_offset + vert_count; index++)
+ {
+ F32 w = weights [index]; // register copy of weight
+ F32 *vin = &vertices[index].mV[0]; // pointer to input vertex data, assumed to be V3+T2+N3+whatever
+ F32 *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32)); // pointer to the output vertex data, assumed to be 16 byte aligned
+
+ // MBW -- XXX -- If this isn't the case, this code gets more complicated.
+ if(0x0000000F & (U32)vin)
+ {
+ llerrs << "blend_SSE_batch: input not 16-byte aligned!" << llendl;
+ }
+ if(0x0000000F & (U32)vout)
+ {
+ llerrs << "blend_SSE_batch: output not 16-byte aligned!" << llendl;
+ }
+// if(0x0000000F & (U32)&(blend_mat.mMatrix))
+// {
+// llerrs << "blend_SSE_batch: blend_mat not 16-byte aligned!" << llendl;
+// }
+
+ if (w == last_weight)
+ {
+ // load last blended matrix
+ // Still loaded from last time through the loop.
+// matrix0_0 = vec_ld(0x00, (vector float*)&(blend_mat.mMatrix));
+// matrix0_1 = vec_ld(0x10, (vector float*)&(blend_mat.mMatrix));
+// matrix0_2 = vec_ld(0x20, (vector float*)&(blend_mat.mMatrix));
+// matrix0_3 = vec_ld(0x30, (vector float*)&(blend_mat.mMatrix));
+ }
+ else
+ {
+ last_weight = w;
+ S32 joint = llfloor(w);
+ w -= joint;
+
+ LLMatrix4 &m0 = gJointMat[joint+1];
+ LLMatrix4 &m1 = gJointMat[joint+0];
+
+ // load Matrix 0 into vector registers
+ matrix0_0 = vec_ld(0x00, (vector float*)&(m0.mMatrix));
+ matrix0_1 = vec_ld(0x10, (vector float*)&(m0.mMatrix));
+ matrix0_2 = vec_ld(0x20, (vector float*)&(m0.mMatrix));
+ matrix0_3 = vec_ld(0x30, (vector float*)&(m0.mMatrix));
+
+ // if w == 1.0f, we don't need to blend.
+ // but since we do the trick of blending the matrices, here, if w != 1.0,
+ // we load Matrix 1 into the other 4 SSE registers and blend both matrices
+ // based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
+
+ if (w != 1.0f)
+ {
+ vector float matrix1_0, matrix1_1, matrix1_2, matrix1_3;
+
+ // This loads the weight somewhere in the vector register
+ vector float weight = vec_lde(0, &(w));
+ // and this splats it to all elements.
+ weight = vec_splat(vec_perm(weight, weight, vec_lvsl(0, &(w))), 0);
+
+ // do blending of matrices instead of verts and normals -- faster
+ matrix1_0 = vec_ld(0x00, (vector float*)&(m1.mMatrix));
+ matrix1_1 = vec_ld(0x10, (vector float*)&(m1.mMatrix));
+ matrix1_2 = vec_ld(0x20, (vector float*)&(m1.mMatrix));
+ matrix1_3 = vec_ld(0x30, (vector float*)&(m1.mMatrix));
+
+ // m0[col] = ((m0[col] - m1[col]) * weight) + m1[col];
+ matrix0_0 = vec_madd(vec_sub(matrix0_0, matrix1_0), weight, matrix1_0);
+ matrix0_1 = vec_madd(vec_sub(matrix0_1, matrix1_1), weight, matrix1_1);
+ matrix0_2 = vec_madd(vec_sub(matrix0_2, matrix1_2), weight, matrix1_2);
+ matrix0_3 = vec_madd(vec_sub(matrix0_3, matrix1_3), weight, matrix1_3);
+ }
+
+ // save off blended matrix
+// vec_st(matrix0_0, 0x00, (vector float*)&(blend_mat.mMatrix));
+// vec_st(matrix0_1, 0x10, (vector float*)&(blend_mat.mMatrix));
+// vec_st(matrix0_2, 0x20, (vector float*)&(blend_mat.mMatrix));
+// vec_st(matrix0_3, 0x30, (vector float*)&(blend_mat.mMatrix));
+ }
+
+ // now, we have either a blended matrix in matrix0_0-3 or the original Matrix 0
+ // we then multiply each vertex and normal by this one matrix.
+
+ // For SSE2, we would try to keep the original two matrices in other registers
+ // and avoid reloading them. However, they should ramain in L1 cache in the
+ // current case.
+
+ // One possible optimization would be to sort the vertices by weight instead
+ // of just index (we still want to uniqify). If we note when two or more vertices
+ // share the same weight, we can avoid doing the middle SSE code above and just
+ // re-use the blended matrix for those vertices
+
+
+ // now, we do the actual vertex blending
+
+ vector float in0 = vec_ld(AVATAR_OFFSET_POS, (vector float*)vin);
+ vector float in1 = vec_ld(AVATAR_OFFSET_NORMAL, (vector float*)vin);
+
+ // Matrix multiply vertex
+ vector float out0 = vec_madd
+ (
+ vec_splat(in0, 0),
+ matrix0_0,
+ vec_madd
+ (
+ vec_splat(in0, 1),
+ matrix0_1,
+ vec_madd
+ (
+ vec_splat(in0, 2),
+ matrix0_2,
+ matrix0_3
+ )
+ )
+ );
+
+ // Matrix multiply normal
+ vector float out1 = vec_madd
+ (
+ vec_splat(in1, 0),
+ matrix0_0,
+ vec_madd
+ (
+ vec_splat(in1, 1),
+ matrix0_1,
+ vec_madd
+ (
+ vec_splat(in1, 2),
+ matrix0_2,
+ // no translation for normals
+ (vector float)vec_splat_u32(0)
+ )
+ )
+ );
+
+ // indexed store
+ vec_stl(vec_perm(in0, out0, out0perm), AVATAR_OFFSET_POS, (vector float*)vout); // Pos
+ vec_stl(vec_perm(in1, out1, out1perm), AVATAR_OFFSET_NORMAL, (vector float*)vout); // Norm
+ *(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot.
+ }
+}
+
+#endif
+
+
+void llDrawElementsBatchBlend(const U32 vert_offset, const U32 vert_count, LLFace *face, const S32 index_count, const U32 *indices)
+{
+ U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
+
+ if (gAGPVertices)
+ {
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector2> tcoords0;
+ LLStrider<F32> weights;
+
+ LLStrider<LLVector3> o_vertices;
+ LLStrider<LLVector3> o_normals;
+ LLStrider<LLVector2> o_texcoords0;
+
+
+ LLStrider<LLVector3> binormals;
+ LLStrider<LLVector2> o_texcoords1;
+ // get the source vertices from the draw pool. We index these ourselves, as there was
+ // no guarantee the indices for a single jointmesh were contigious
+
+ LLDrawPool *pool = face->getPool();
+ pool->getVertexStrider (vertices, 0);
+ pool->getTexCoordStrider (tcoords0, 0, 0);
+ pool->getNormalStrider (normals, 0);
+ pool->getBinormalStrider (binormals, 0);
+ pool->getVertexWeightStrider(weights, 0);
+
+ // load the addresses of the output striders
+ o_vertices = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS); o_vertices.setStride( AVATAR_VERTEX_BYTES);
+ o_normals = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL); o_normals.setStride( AVATAR_VERTEX_BYTES);
+ o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0); o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
+ o_texcoords1= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX1); o_texcoords1.setStride(AVATAR_VERTEX_BYTES);
+
+#if !LL_LINUX // !!! FIXME
+ if (gGLManager.mSoftwareBlendSSE)
+ {
+ // do SSE blend without binormals or extra texcoords
+ blend_SSE_32_32_batch(vert_offset, vert_count, (float*)gAGPVertices,
+ vertices, tcoords0, normals, weights);
+ }
+ else // fully backwards compatible software blending, no SSE
+#endif
+ {
+ LLVector4 tpos0, tnorm0, tpos1, tnorm1, tbinorm0, tbinorm1;
+ F32 last_weight = F32_MAX;
+ LLMatrix3 gBlendRotMat;
+
+ {
+ for (U32 index=vert_offset; index < vert_offset + vert_count; index++)
+ {
+ // blend by first matrix
+ F32 w = weights [index];
+
+ if (w != last_weight)
+ {
+ last_weight = w;
+
+ S32 joint = llfloor(w);
+ w -= joint;
+
+ LLMatrix4 &m0 = gJointMat[joint+1];
+ LLMatrix4 &m1 = gJointMat[joint+0];
+ LLMatrix3 &n0 = gJointRot[joint+1];
+ LLMatrix3 &n1 = gJointRot[joint+0];
+
+ if (w == 1.0f)
+ {
+ gBlendMat = m0;
+ gBlendRotMat = n0;
+ }
+ else
+ {
+ gBlendMat.mMatrix[VX][VX] = lerp(m1.mMatrix[VX][VX], m0.mMatrix[VX][VX], w);
+ gBlendMat.mMatrix[VX][VY] = lerp(m1.mMatrix[VX][VY], m0.mMatrix[VX][VY], w);
+ gBlendMat.mMatrix[VX][VZ] = lerp(m1.mMatrix[VX][VZ], m0.mMatrix[VX][VZ], w);
+
+ gBlendMat.mMatrix[VY][VX] = lerp(m1.mMatrix[VY][VX], m0.mMatrix[VY][VX], w);
+ gBlendMat.mMatrix[VY][VY] = lerp(m1.mMatrix[VY][VY], m0.mMatrix[VY][VY], w);
+ gBlendMat.mMatrix[VY][VZ] = lerp(m1.mMatrix[VY][VZ], m0.mMatrix[VY][VZ], w);
+
+ gBlendMat.mMatrix[VZ][VX] = lerp(m1.mMatrix[VZ][VX], m0.mMatrix[VZ][VX], w);
+ gBlendMat.mMatrix[VZ][VY] = lerp(m1.mMatrix[VZ][VY], m0.mMatrix[VZ][VY], w);
+ gBlendMat.mMatrix[VZ][VZ] = lerp(m1.mMatrix[VZ][VZ], m0.mMatrix[VZ][VZ], w);
+
+ gBlendMat.mMatrix[VW][VX] = lerp(m1.mMatrix[VW][VX], m0.mMatrix[VW][VX], w);
+ gBlendMat.mMatrix[VW][VY] = lerp(m1.mMatrix[VW][VY], m0.mMatrix[VW][VY], w);
+ gBlendMat.mMatrix[VW][VZ] = lerp(m1.mMatrix[VW][VZ], m0.mMatrix[VW][VZ], w);
+
+ gBlendRotMat.mMatrix[VX][VX] = lerp(n1.mMatrix[VX][VX], n0.mMatrix[VX][VX], w);
+ gBlendRotMat.mMatrix[VX][VY] = lerp(n1.mMatrix[VX][VY], n0.mMatrix[VX][VY], w);
+ gBlendRotMat.mMatrix[VX][VZ] = lerp(n1.mMatrix[VX][VZ], n0.mMatrix[VX][VZ], w);
+
+ gBlendRotMat.mMatrix[VY][VX] = lerp(n1.mMatrix[VY][VX], n0.mMatrix[VY][VX], w);
+ gBlendRotMat.mMatrix[VY][VY] = lerp(n1.mMatrix[VY][VY], n0.mMatrix[VY][VY], w);
+ gBlendRotMat.mMatrix[VY][VZ] = lerp(n1.mMatrix[VY][VZ], n0.mMatrix[VY][VZ], w);
+
+ gBlendRotMat.mMatrix[VZ][VX] = lerp(n1.mMatrix[VZ][VX], n0.mMatrix[VZ][VX], w);
+ gBlendRotMat.mMatrix[VZ][VY] = lerp(n1.mMatrix[VZ][VY], n0.mMatrix[VZ][VY], w);
+ gBlendRotMat.mMatrix[VZ][VZ] = lerp(n1.mMatrix[VZ][VZ], n0.mMatrix[VZ][VZ], w);
+ }
+ }
+
+ // write result
+ o_vertices [index] = vertices[index] * gBlendMat;
+ o_normals [index] = normals [index] * gBlendRotMat;
+ o_texcoords0[index] = tcoords0[index];
+
+ /*
+ // Verification code. Leave this here. It's useful for keeping the SSE and non-SSE versions in sync.
+ LLVector3 temp;
+ temp = tpos0;
+ if( (o_vertices[index] - temp).magVecSquared() > 0.001f )
+ {
+ llerrs << "V SSE: " << o_vertices[index] << " v. " << temp << llendl;
+ }
+
+ temp = tnorm0;
+ if( (o_normals[index] - temp).magVecSquared() > 0.001f )
+ {
+ llerrs << "N SSE: " << o_normals[index] << " v. " << temp << llendl;
+ }
+
+ if( (o_texcoords0[index] - tcoords0[index]).magVecSquared() > 0.001f )
+ {
+ llerrs << "T0 SSE: " << o_texcoords0[index] << " v. " << tcoords0[index] << llendl;
+ }
+ */
+ }
+ }
+ }
+
+#if LL_DARWIN
+ // *HACK* *CHOKE* *PUKE*
+ // No way does this belong here.
+ glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * vert_count, gAGPVertices + (AVATAR_VERTEX_BYTES * vert_offset));
+#endif
+ glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices); // draw it!
+ }
+ else
+ {
+ glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices);
+ }
+}
+
+
+
+//--------------------------------------------------------------------
+// DrawElements
+
+// works just like glDrawElements, except it assumes GL_TRIANGLES and GL_UNSIGNED_INT indices
+
+// why? because the destination buffer may not be the AGP buffer and the eyes do not use blending
+// separate the eyes into their own drawpools and this code goes away.
+
+//--------------------------------------------------------------------
+
+void llDrawElements(const S32 count, const U32 *indices, LLFace *face)
+{
+ U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
+
+ if (gAGPVertices)
+ {
+#if LL_DARWIN
+ U32 minIndex = indices[0];
+ U32 maxIndex = indices[0];
+#endif
+ {
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector2> tcoords;
+ LLStrider<F32> weights;
+
+ LLStrider<LLVector3> o_vertices;
+ LLStrider<LLVector3> o_normals;
+ LLStrider<LLVector2> o_texcoords0;
+
+ LLDrawPool *pool = face->getPool();
+ pool->getVertexStrider (vertices,0);
+ pool->getNormalStrider (normals, 0);
+ pool->getTexCoordStrider (tcoords, 0);
+
+ o_vertices = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS); o_vertices.setStride( AVATAR_VERTEX_BYTES);
+ o_normals = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL); o_normals.setStride( AVATAR_VERTEX_BYTES);
+ o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0); o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
+
+ for (S32 i=0; i < count; i++)
+ {
+ U32 index = indices[i];
+
+ o_vertices [index] = vertices[index];
+ o_normals [index] = normals [index];
+ o_texcoords0[index] = tcoords [index];
+
+#if LL_DARWIN
+ maxIndex = llmax(index, maxIndex);
+ minIndex = llmin(index, minIndex);
+#endif
+ }
+ }
+
+#if LL_DARWIN
+ // *HACK* *CHOKE* *PUKE*
+ // No way does this belong here.
+ glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * (maxIndex + 1 - minIndex), gAGPVertices + (AVATAR_VERTEX_BYTES * minIndex));
+#endif
+
+ glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
+ }
+ else
+ {
+ glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
+ }
+}
+
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::drawShape()
+//--------------------------------------------------------------------
+U32 LLViewerJointMesh::drawShape( F32 pixelArea )
+{
+ if (!mValid || !mVisible) return 0;
+
+ U32 triangle_count = 0;
+
+ //----------------------------------------------------------------
+ // if no mesh bail out now
+ //----------------------------------------------------------------
+ if ( !mMesh || !mFace)
+ {
+ return 0;
+ }
+
+ //----------------------------------------------------------------
+ // if we have no faces, bail out now
+ //----------------------------------------------------------------
+ if ( mMesh->getNumFaces() == 0 )
+ {
+ return 0;
+ }
+
+ stop_glerror();
+
+ //----------------------------------------------------------------
+ // setup current color
+ //----------------------------------------------------------------
+ if (gRenderForSelect)
+ {
+ S32 name = mFace->getDrawable() ? mFace->getDrawable()->getVObj()->mGLName : 0;
+ LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name, 0xff);
+ LLColor4 color_float(color);
+
+ glColor4f(color_float.mV[0], color_float.mV[1], color_float.mV[2], 1.f);
+ }
+ else
+ {
+ if ((mFace->getPool()->getVertexShaderLevel() > 0))
+ {
+ glColor4f(0,0,0,1);
+
+ if (gPipeline.mMaterialIndex > 0)
+ {
+ glVertexAttrib4fvARB(gPipeline.mMaterialIndex, mColor.mV);
+ }
+
+ if (mShiny && gPipeline.mSpecularIndex > 0)
+ {
+ glVertexAttrib4fARB(gPipeline.mSpecularIndex, 1,1,1,1);
+ }
+ }
+ else
+ {
+ glColor4fv(mColor.mV);
+ }
+ }
+
+ stop_glerror();
+
+// LLGLSSpecular specular(mSpecular, gRenderForSelect ? 0.0f : mShiny);
+ LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), gRenderForSelect ? 0.0f : mShiny && !(mFace->getPool()->getVertexShaderLevel() > 0));
+
+ LLGLEnable texture_2d((gRenderForSelect && isTransparent()) ? GL_TEXTURE_2D : 0);
+
+ //----------------------------------------------------------------
+ // setup current texture
+ //----------------------------------------------------------------
+ llassert( !(mTexture.notNull() && mLayerSet) ); // mutually exclusive
+
+ //GLuint test_image_name = 0;
+
+ //
+ LLGLState force_alpha_test(GL_ALPHA_TEST, isTransparent());
+
+ if (mTestImageName)
+ {
+ LLImageGL::bindExternalTexture( mTestImageName, 0, GL_TEXTURE_2D );
+
+ if (mIsTransparent)
+ {
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.7f, 0.6f, 0.3f, 1.f);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ else if( mLayerSet )
+ {
+ if( mLayerSet->hasComposite() )
+ {
+ mLayerSet->getComposite()->bindTexture();
+ }
+ else
+ {
+ llwarns << "Layerset without composite" << llendl;
+ gImageList.getImage(IMG_DEFAULT)->bind();
+ }
+ }
+ else
+ if ( mTexture.notNull() )
+ {
+ mTexture->bind();
+ if (!mTexture->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ }
+ if (!mTexture->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ }
+ else
+ {
+ gImageList.getImage(IMG_DEFAULT_AVATAR)->bind();
+ }
+
+ if (gRenderForSelect)
+ {
+ if (isTransparent())
+ {
+ //gGLSObjectSelectDepthAlpha.set();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); // GL_TEXTURE_ENV_COLOR is set in renderPass1
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+ }
+ else
+ {
+ //gGLSObjectSelectDepth.set();
+ }
+ }
+ else
+ {
+ //----------------------------------------------------------------
+ // by default, backface culling is enabled
+ //----------------------------------------------------------------
+ if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_INNER)
+ {
+ //LLGLSPipelineAvatar gls_pipeline_avatar;
+ LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, sClothingInnerColor.mV);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);
+ }
+ else if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_OUTER)
+ {
+ //gGLSPipelineAvatarAlphaPass1.set();
+ glAlphaFunc(GL_GREATER, 0.1f);
+ LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+ }
+ else if ( isTransparent())
+ {
+ //gGLSNoCullFaces.set();
+ }
+ else
+ {
+ //gGLSCullFaces.set();
+ }
+ }
+
+ if (mMesh->hasWeights())
+ {
+ uploadJointMatrices();
+
+
+ if ((mFace->getPool()->getVertexShaderLevel() > 0))
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
+
+ glPopMatrix();
+ }
+ else
+ {
+ if (mFace->getGeomIndex() < 0)
+ {
+ llerrs << "Invalid geometry index in LLViewerJointMesh::drawShape() " << mFace->getGeomIndex() << llendl;
+ }
+
+ if ((S32)(mMesh->mFaceVertexOffset + mMesh->mFaceVertexCount) > mFace->getGeomCount())
+ {
+ ((LLVOAvatar*)mFace->getDrawable()->getVObj())->mRoot.dump();
+ llerrs << "Rendering outside of vertex bounds with mesh " << mName << " at pixel area " << pixelArea << llendl;
+ }
+ llDrawElementsBatchBlend(mMesh->mFaceVertexOffset, mMesh->mFaceVertexCount,
+ mFace, mMesh->mFaceIndexCount, mMesh->getIndices());
+ }
+
+ }
+ else
+ {
+ glPushMatrix();
+ LLMatrix4 jointToWorld = getWorldMatrix();
+ jointToWorld *= gCamera->getModelview();
+ glLoadMatrixf((GLfloat*)jointToWorld.mMatrix);
+
+ if ((mFace->getPool()->getVertexShaderLevel() > 0))
+ {
+ glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
+ }
+ else // this else clause handles non-weighted vertices. llDrawElements just copies and draws
+ {
+ llDrawElements(mMesh->mFaceIndexCount, mMesh->getIndices(), mFace);
+ }
+
+ glPopMatrix();
+ }
+
+ triangle_count += mMesh->mFaceIndexCount;
+
+ if (gRenderForSelect)
+ {
+ glColor4fv(mColor.mV);
+ }
+
+ if (mTestImageName)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+
+ if (sRenderPass != AVATAR_RENDER_PASS_SINGLE)
+ {
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ // Return to the default texture.
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glAlphaFunc(GL_GREATER, 0.01f);
+ }
+
+ if (mTexture.notNull()) {
+ if (!mTexture->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ }
+ if (!mTexture->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+ }
+
+ return triangle_count;
+}
+
+//-----------------------------------------------------------------------------
+// updateFaceSizes()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
+{
+ // Do a pre-alloc pass to determine sizes of data.
+ if (mMesh && mValid)
+ {
+ mMesh->mFaceVertexOffset = num_vertices;
+ mMesh->mFaceVertexCount = mMesh->getNumVertices();
+ mMesh->getReferenceMesh()->mCurVertexCount = mMesh->mFaceVertexCount;
+ num_vertices += mMesh->getNumVertices();
+
+ mMesh->mFaceIndexCount = mMesh->getSharedData()->mNumTriangleIndices;
+
+ mMesh->getSharedData()->genIndices(mMesh->mFaceVertexOffset);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateFaceData()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
+{
+ U32 i;
+
+ if (!mValid) return;
+
+ mFace = face;
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector3> binormalsp;
+ LLStrider<LLVector2> tex_coordsp;
+ LLStrider<F32> vertex_weightsp;
+ LLStrider<LLVector4> clothing_weightsp;
+
+ // Copy data into the faces from the polymesh data.
+ if (mMesh)
+ {
+ if (mMesh->getNumVertices())
+ {
+ S32 index = face->getGeometryAvatar(verticesp, normalsp, binormalsp, tex_coordsp, vertex_weightsp, clothing_weightsp);
+
+ if (-1 == index)
+ {
+ return;
+ }
+
+ for (i = 0; i < mMesh->getNumVertices(); i++)
+ {
+ verticesp[mMesh->mFaceVertexOffset + i] = *(mMesh->getCoords() + i);
+ tex_coordsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getTexCoords() + i);
+ normalsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getNormals() + i);
+ binormalsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getBinormals() + i);
+ vertex_weightsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getWeights() + i);
+ if (damp_wind)
+ {
+ clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(0,0,0,0);
+ }
+ else
+ {
+ clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(*(mMesh->getClothingWeights() + i));
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateLOD()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointMesh::updateLOD(F32 pixel_area, BOOL activate)
+{
+ BOOL valid = mValid;
+ setValid(activate, TRUE);
+ return (valid != activate);
+}
+
+void LLViewerJointMesh::dump()
+{
+ if (mValid)
+ {
+ llinfos << "Usable LOD " << mName << llendl;
+ }
+}
+
+void LLViewerJointMesh::writeCAL3D(apr_file_t* fp, S32 material_num, LLCharacter* characterp)
+{
+ apr_file_printf(fp, "\t<SUBMESH NUMVERTICES=\"%d\" NUMFACES=\"%d\" MATERIAL=\"%d\" NUMLODSTEPS=\"0\" NUMSPRINGS=\"0\" NUMTEXCOORDS=\"1\">\n", mMesh->getNumVertices(), mMesh->getNumFaces(), material_num);
+
+ const LLVector3* mesh_coords = mMesh->getCoords();
+ const LLVector3* mesh_normals = mMesh->getNormals();
+ const LLVector2* mesh_uvs = mMesh->getTexCoords();
+ const F32* mesh_weights = mMesh->getWeights();
+ LLVector3 mesh_offset;
+ LLVector3 scale(1.f, 1.f, 1.f);
+ S32 joint_a = -1;
+ S32 joint_b = -1;
+ S32 num_bound_joints = 0;
+
+ if(!mMesh->hasWeights())
+ {
+ num_bound_joints = 1;
+ LLJoint* cur_joint = this;
+ while(cur_joint)
+ {
+ if (cur_joint->mJointNum != -1 && joint_a == -1)
+ {
+ joint_a = cur_joint->mJointNum;
+ }
+ mesh_offset += cur_joint->getSkinOffset();
+ cur_joint = cur_joint->getParent();
+ }
+ }
+
+ for (S32 i = 0; i < (S32)mMesh->getNumVertices(); i++)
+ {
+ LLVector3 coord = mesh_coords[i];
+
+ if (mMesh->hasWeights())
+ {
+ // calculate joint to which this skinned vertex is bound
+ num_bound_joints = getBoundJointsByIndex(llfloor(mesh_weights[i]), joint_a, joint_b);
+ LLJoint* first_joint = characterp->getCharacterJoint(joint_a);
+ LLJoint* second_joint = characterp->getCharacterJoint(joint_b);
+
+ LLVector3 first_joint_offset;
+ LLJoint* cur_joint = first_joint;
+ while(cur_joint)
+ {
+ first_joint_offset += cur_joint->getSkinOffset();
+ cur_joint = cur_joint->getParent();
+ }
+
+ LLVector3 second_joint_offset;
+ cur_joint = second_joint;
+ while(cur_joint)
+ {
+ second_joint_offset += cur_joint->getSkinOffset();
+ cur_joint = cur_joint->getParent();
+ }
+
+ LLVector3 first_coord = coord - first_joint_offset;
+ first_coord.scaleVec(first_joint->getScale());
+ LLVector3 second_coord = coord - second_joint_offset;
+ if (second_joint)
+ {
+ second_coord.scaleVec(second_joint->getScale());
+ }
+
+ coord = lerp(first_joint_offset + first_coord, second_joint_offset + second_coord, fmodf(mesh_weights[i], 1.f));
+ }
+
+ // add offset to move rigid mesh to target location
+ coord += mesh_offset;
+ coord *= 100.f;
+
+ apr_file_printf(fp, " <VERTEX ID=\"%d\" NUMINFLUENCES=\"%d\">\n", i, num_bound_joints);
+ apr_file_printf(fp, " <POS>%.4f %.4f %.4f</POS>\n", coord.mV[VX], coord.mV[VY], coord.mV[VZ]);
+ apr_file_printf(fp, " <NORM>%.6f %.6f %.6f</NORM>\n", mesh_normals[i].mV[VX], mesh_normals[i].mV[VY], mesh_normals[i].mV[VZ]);
+ apr_file_printf(fp, " <TEXCOORD>%.6f %.6f</TEXCOORD>\n", mesh_uvs[i].mV[VX], 1.f - mesh_uvs[i].mV[VY]);
+ if (num_bound_joints >= 1)
+ {
+ apr_file_printf(fp, " <INFLUENCE ID=\"%d\">%.2f</INFLUENCE>\n", joint_a + 1, 1.f - fmod(mesh_weights[i], 1.f));
+ }
+ if (num_bound_joints == 2)
+ {
+ apr_file_printf(fp, " <INFLUENCE ID=\"%d\">%.2f</INFLUENCE>\n", joint_b + 1, fmod(mesh_weights[i], 1.f));
+ }
+ apr_file_printf(fp, " </VERTEX>\n");
+ }
+
+ LLPolyFace* mesh_faces = mMesh->getFaces();
+ for (S32 i = 0; i < mMesh->getNumFaces(); i++)
+ {
+ apr_file_printf(fp, " <FACE VERTEXID=\"%d %d %d\" />\n", mesh_faces[i][0], mesh_faces[i][1], mesh_faces[i][2]);
+ }
+
+ apr_file_printf(fp, " </SUBMESH>\n");
+}
+
+// End
diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h
new file mode 100644
index 0000000000..963ad05595
--- /dev/null
+++ b/indra/newview/llviewerjointmesh.h
@@ -0,0 +1,138 @@
+/**
+ * @file llviewerjointmesh.h
+ * @brief Implementation of LLViewerJointMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERJOINTMESH_H
+#define LL_LLVIEWERJOINTMESH_H
+
+#include "llviewerjoint.h"
+#include "llviewerimage.h"
+#include "llpolymesh.h"
+#include "v4color.h"
+#include "llapr.h"
+
+class LLDrawable;
+class LLFace;
+class LLCharacter;
+class LLTexLayerSet;
+
+typedef enum e_avatar_render_pass
+{
+ AVATAR_RENDER_PASS_SINGLE,
+ AVATAR_RENDER_PASS_CLOTHING_INNER,
+ AVATAR_RENDER_PASS_CLOTHING_OUTER
+} EAvatarRenderPass;
+
+class LLSkinJoint
+{
+public:
+ LLSkinJoint();
+ ~LLSkinJoint();
+ BOOL setupSkinJoint( LLViewerJoint *joint);
+
+ LLViewerJoint *mJoint;
+ LLVector3 mRootToJointSkinOffset;
+ LLVector3 mRootToParentJointSkinOffset;
+};
+
+//-----------------------------------------------------------------------------
+// class LLViewerJointMesh
+//-----------------------------------------------------------------------------
+class LLViewerJointMesh : public LLViewerJoint
+{
+protected:
+ LLColor4 mColor; // color value
+// LLColor4 mSpecular; // specular color (always white for now)
+ F32 mShiny; // shiny value
+ LLPointer<LLViewerImage> mTexture; // ptr to a global texture
+ LLTexLayerSet* mLayerSet; // ptr to a layer set owned by the avatar
+ U32 mTestImageName; // handle to a temporary texture for previewing uploads
+ LLPolyMesh* mMesh; // ptr to a global polymesh
+ BOOL mCullBackFaces; // true by default
+ LLFace* mFace; // ptr to a face w/ AGP copy of mesh
+
+ U32 mFaceIndexCount;
+ BOOL mIsTransparent;
+
+ U32 mNumSkinJoints;
+ LLSkinJoint* mSkinJoints;
+ S32 mMeshID;
+
+public:
+ static BOOL sPipelineRender;
+ //RN: this is here for testing purposes
+ static U32 sClothingMaskImageName;
+ static EAvatarRenderPass sRenderPass;
+ static LLColor4 sClothingInnerColor;
+
+public:
+ // Constructor
+ LLViewerJointMesh();
+
+ // Destructor
+ virtual ~LLViewerJointMesh();
+
+ // Gets the shape color
+ void getColor( F32 *red, F32 *green, F32 *blue, F32 *alpha );
+
+ // Sets the shape color
+ void setColor( F32 red, F32 green, F32 blue, F32 alpha );
+
+ // Sets the shininess
+ void setSpecular( const LLColor4& color, F32 shiny ) { /*mSpecular = color;*/ mShiny = shiny; };
+
+ // Sets the shape texture
+ void setTexture( LLViewerImage *texture );
+
+ void setTestTexture( U32 name ) { mTestImageName = name; }
+
+ // Sets layer set responsible for a dynamic shape texture (takes precedence over normal texture)
+ void setLayerSet( LLTexLayerSet* layer_set );
+
+ // Gets the poly mesh
+ LLPolyMesh *getMesh();
+
+ // Sets the poly mesh
+ void setMesh( LLPolyMesh *mesh );
+
+ // Sets up joint matrix data for rendering
+ void setupJoint(LLViewerJoint* current_joint);
+
+ // Render time method to upload batches of joint matrices
+ void uploadJointMatrices();
+
+ // Sets ID for picking
+ void setMeshID( S32 id ) {mMeshID = id;}
+
+ // Gets ID for picking
+ S32 getMeshID() { return mMeshID; }
+
+ // overloaded from base class
+ /*virtual*/ void drawBone();
+ /*virtual*/ BOOL isTransparent();
+ /*virtual*/ U32 drawShape( F32 pixelArea );
+
+ /*virtual*/ void updateFaceSizes(U32 &num_vertices, F32 pixel_area);
+ /*virtual*/ void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE);
+ /*virtual*/ BOOL updateLOD(F32 pixel_area, BOOL activate);
+ /*virtual*/ void dump();
+
+ void setIsTransparent(BOOL is_transparent) { mIsTransparent = is_transparent; }
+
+ /*virtual*/ BOOL isAnimatable() { return FALSE; }
+ void writeCAL3D(apr_file_t* fp, S32 material_num, LLCharacter* characterp);
+private:
+ // Allocate skin data
+ BOOL allocateSkinData( U32 numSkinJoints );
+
+ S32 getBoundJointsByIndex(S32 index, S32 &joint_a, S32& joint_b);
+
+ // Free skin data
+ void freeSkinData();
+};
+
+#endif // LL_LLVIEWERJOINTMESH_H
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
new file mode 100644
index 0000000000..8f31f1192a
--- /dev/null
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -0,0 +1,833 @@
+/**
+ * @file llviewerkeyboard.cpp
+ * @brief LLViewerKeyboard class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerkeyboard.h"
+#include "llmath.h"
+#include "llagent.h"
+#include "llchatbar.h"
+#include "llviewercontrol.h"
+#include "llfocusmgr.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "lltoolfocus.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+
+//
+// Constants
+//
+
+const F32 FLY_TIME = 0.5f;
+const F32 FLY_FRAMES = 4;
+
+const F32 NUDGE_TIME = 0.25f; // in seconds
+const S32 NUDGE_FRAMES = 2;
+const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
+const F32 YAW_NUDGE_RATE = 0.05f; // fraction of normal speed
+
+LLViewerKeyboard gViewerKeyboard;
+
+void agent_jump( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < FLY_TIME
+ || frame_count <= FLY_FRAMES
+ || gAgent.upGrabbed()
+ || !gSavedSettings.getBOOL("AutomaticFly"))
+ {
+ gAgent.moveUp(1);
+ }
+ else
+ {
+ gAgent.setFlying(TRUE);
+ gAgent.moveUp(1);
+ }
+}
+
+void agent_push_down( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.moveUp(-1);
+}
+
+void agent_push_forward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ gAgent.moveAtNudge(1);
+ }
+ else
+ {
+ gAgent.moveAt(1);
+ }
+}
+
+
+void agent_push_backward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ gAgent.moveAtNudge(-1);
+ }
+ else
+ {
+ gAgent.moveAt(-1);
+ }
+}
+
+void agent_slide_left( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ if (frame_count == 0)
+ {
+ // only send nudge on first frame
+ gAgent.moveLeftNudge(1);
+ }
+ }
+ else
+ {
+ gAgent.moveLeft(1);
+ }
+}
+
+
+void agent_slide_right( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ if (frame_count == 0)
+ {
+ // only send nudge on first frame
+ gAgent.moveLeftNudge(-1);
+ }
+ }
+ else
+ {
+ gAgent.moveLeft(-1);
+ }
+}
+
+void agent_turn_left( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ if (gToolCamera->mouseSteerMode())
+ {
+ agent_slide_left(s);
+ }
+ else
+ {
+ gAgent.moveYaw( LLFloaterMove::getYawRate( time ) );
+ }
+}
+
+
+void agent_turn_right( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ if (gToolCamera->mouseSteerMode())
+ {
+ agent_slide_right(s);
+ }
+ else
+ {
+ gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) );
+ }
+}
+
+void agent_look_up( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.movePitch(-1);
+ //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() );
+}
+
+
+void agent_look_down( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.movePitch(1);
+ //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() );
+}
+
+void agent_toggle_fly( EKeystate s )
+{
+ // Only catch the edge
+ if (KEYSTATE_DOWN == s )
+ {
+ gAgent.toggleFlying();
+ }
+}
+
+F32 get_orbit_rate()
+{
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
+ //llinfos << rate << llendl;
+ return rate;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+void camera_spin_around_ccw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitLeftKey( get_orbit_rate() );
+}
+
+
+void camera_spin_around_cw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitRightKey( get_orbit_rate() );
+}
+
+void camera_spin_around_ccw_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.rotateGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_turn_right(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitLeftKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_spin_around_cw_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.rotateGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_turn_left(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitRightKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_spin_over( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitUpKey( get_orbit_rate() );
+}
+
+
+void camera_spin_under( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitDownKey( get_orbit_rate() );
+}
+
+void camera_spin_over_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.upGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_jump(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitUpKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_spin_under_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.downGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_push_down(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitDownKey( get_orbit_rate() );
+ }
+}
+
+void camera_move_forward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitInKey( get_orbit_rate() );
+}
+
+
+void camera_move_backward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey( get_orbit_rate() );
+}
+
+void camera_move_forward_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.forwardGrabbed() || gAgent.sitCameraEnabled())
+ {
+ agent_push_forward(s);
+ }
+ else
+ {
+ gAgent.setOrbitInKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_move_backward_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+
+ if (gAgent.backwardGrabbed() || gAgent.sitCameraEnabled())
+ {
+ agent_push_backward(s);
+ }
+ else
+ {
+ gAgent.setOrbitOutKey( get_orbit_rate() );
+ }
+}
+
+void camera_pan_up( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanUpKey( get_orbit_rate() );
+}
+
+void camera_pan_down( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanDownKey( get_orbit_rate() );
+}
+
+void camera_pan_left( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanLeftKey( get_orbit_rate() );
+}
+
+void camera_pan_right( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanRightKey( get_orbit_rate() );
+}
+
+void camera_pan_in( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanInKey( get_orbit_rate() );
+}
+
+void camera_pan_out( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanOutKey( get_orbit_rate() );
+}
+
+void camera_move_forward_fast( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitInKey(2.5f);
+}
+
+void camera_move_backward_fast( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey(2.5f);
+}
+
+
+void edit_avatar_spin_ccw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitLeftKey( get_orbit_rate() );
+ //gMorphView->orbitLeft( get_orbit_rate() );
+}
+
+
+void edit_avatar_spin_cw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitRightKey( get_orbit_rate() );
+ //gMorphView->orbitRight( get_orbit_rate() );
+}
+
+void edit_avatar_spin_over( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitUpKey( get_orbit_rate() );
+ //gMorphView->orbitUp( get_orbit_rate() );
+}
+
+
+void edit_avatar_spin_under( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitDownKey( get_orbit_rate() );
+ //gMorphView->orbitDown( get_orbit_rate() );
+}
+
+void edit_avatar_move_forward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitInKey( get_orbit_rate() );
+ //gMorphView->orbitIn();
+}
+
+
+void edit_avatar_move_backward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitOutKey( get_orbit_rate() );
+ //gMorphView->orbitOut();
+}
+
+void stop_moving( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ // stop agent
+ gAgent.setControlFlags(AGENT_CONTROL_STOP);
+
+ // cancel autopilot
+ gAgent.stopAutoPilot();
+}
+
+void start_chat( EKeystate s )
+{
+ if (!gChatBar->inputEditorHasFocus())
+ {
+ // start chat
+ gChatBar->startChat(NULL);
+ }
+}
+
+void bind_keyboard_functions()
+{
+ gViewerKeyboard.bindNamedFunction("jump", agent_jump);
+ gViewerKeyboard.bindNamedFunction("push_down", agent_push_down);
+ gViewerKeyboard.bindNamedFunction("push_forward", agent_push_forward);
+ gViewerKeyboard.bindNamedFunction("push_backward", agent_push_backward);
+ gViewerKeyboard.bindNamedFunction("look_up", agent_look_up);
+ gViewerKeyboard.bindNamedFunction("look_down", agent_look_down);
+ gViewerKeyboard.bindNamedFunction("toggle_fly", agent_toggle_fly);
+ gViewerKeyboard.bindNamedFunction("turn_left", agent_turn_left);
+ gViewerKeyboard.bindNamedFunction("turn_right", agent_turn_right);
+ gViewerKeyboard.bindNamedFunction("slide_left", agent_slide_left);
+ gViewerKeyboard.bindNamedFunction("slide_right", agent_slide_right);
+ gViewerKeyboard.bindNamedFunction("spin_around_ccw", camera_spin_around_ccw);
+ gViewerKeyboard.bindNamedFunction("spin_around_cw", camera_spin_around_cw);
+ gViewerKeyboard.bindNamedFunction("spin_around_ccw_sitting", camera_spin_around_ccw_sitting);
+ gViewerKeyboard.bindNamedFunction("spin_around_cw_sitting", camera_spin_around_cw_sitting);
+ gViewerKeyboard.bindNamedFunction("spin_over", camera_spin_over);
+ gViewerKeyboard.bindNamedFunction("spin_under", camera_spin_under);
+ gViewerKeyboard.bindNamedFunction("spin_over_sitting", camera_spin_over_sitting);
+ gViewerKeyboard.bindNamedFunction("spin_under_sitting", camera_spin_under_sitting);
+ gViewerKeyboard.bindNamedFunction("move_forward", camera_move_forward);
+ gViewerKeyboard.bindNamedFunction("move_backward", camera_move_backward);
+ gViewerKeyboard.bindNamedFunction("move_forward_sitting", camera_move_forward_sitting);
+ gViewerKeyboard.bindNamedFunction("move_backward_sitting", camera_move_backward_sitting);
+ gViewerKeyboard.bindNamedFunction("pan_up", camera_pan_up);
+ gViewerKeyboard.bindNamedFunction("pan_down", camera_pan_down);
+ gViewerKeyboard.bindNamedFunction("pan_left", camera_pan_left);
+ gViewerKeyboard.bindNamedFunction("pan_right", camera_pan_right);
+ gViewerKeyboard.bindNamedFunction("pan_in", camera_pan_in);
+ gViewerKeyboard.bindNamedFunction("pan_out", camera_pan_out);
+ gViewerKeyboard.bindNamedFunction("move_forward_fast", camera_move_forward_fast);
+ gViewerKeyboard.bindNamedFunction("move_backward_fast", camera_move_backward_fast);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_ccw", edit_avatar_spin_ccw);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_cw", edit_avatar_spin_cw);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_over", edit_avatar_spin_over);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_under", edit_avatar_spin_under);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_move_forward", edit_avatar_move_forward);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_move_backward", edit_avatar_move_backward);
+ gViewerKeyboard.bindNamedFunction("stop_moving", stop_moving);
+ gViewerKeyboard.bindNamedFunction("start_chat", start_chat);
+}
+
+LLViewerKeyboard::LLViewerKeyboard()
+{
+ for (S32 i = 0; i < MODE_COUNT; i++)
+ {
+ mBindingCount[i] = 0;
+ }
+
+ for (S32 i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyHandledByUI[i] = FALSE;
+ }
+ // we want the UI to never see these keys so that they can always control the avatar/camera
+ for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++)
+ {
+ mKeysSkippedByUI.insert(k);
+ }
+}
+
+
+void LLViewerKeyboard::bindNamedFunction(const char *name, LLKeyFunc func)
+{
+ S32 i = mNamedFunctionCount;
+ mNamedFunctions[i].mName = name;
+ mNamedFunctions[i].mFunction = func;
+ mNamedFunctionCount++;
+}
+
+
+BOOL LLViewerKeyboard::modeFromString(const char *string, S32 *mode)
+{
+ if (!strcmp(string, "FIRST_PERSON"))
+ {
+ *mode = MODE_FIRST_PERSON;
+ return TRUE;
+ }
+ else if (!strcmp(string, "THIRD_PERSON"))
+ {
+ *mode = MODE_THIRD_PERSON;
+ return TRUE;
+ }
+ else if (!strcmp(string, "EDIT"))
+ {
+ *mode = MODE_EDIT;
+ return TRUE;
+ }
+ else if (!strcmp(string, "EDIT_AVATAR"))
+ {
+ *mode = MODE_EDIT_AVATAR;
+ return TRUE;
+ }
+ else if (!strcmp(string, "SITTING"))
+ {
+ *mode = MODE_SITTING;
+ return TRUE;
+ }
+ else
+ {
+ *mode = MODE_THIRD_PERSON;
+ return FALSE;
+ }
+}
+
+BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated)
+{
+ // check for re-map
+ EKeyboardMode mode = gViewerKeyboard.getMode();
+ U32 keyidx = (translated_mask<<16) | translated_key;
+ key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx);
+ if (iter != mRemapKeys[mode].end())
+ {
+ translated_key = (iter->second) & 0xff;
+ translated_mask = (iter->second)>>16;
+ }
+
+ // No repeats of F-keys
+ BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12);
+ if (!repeatable_key && repeated)
+ {
+ return FALSE;
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "keydown -" << translated_key << "-" << llendl;
+ // skip skipped keys
+ if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end())
+ {
+ mKeyHandledByUI[translated_key] = FALSE;
+ }
+ else
+ {
+ // it is sufficient to set this value once per call to handlekey
+ // without clearing it, as it is only used in the subsequent call to scanKey
+ mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask);
+ }
+ return mKeyHandledByUI[translated_key];
+}
+
+
+
+BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, const char *function_name)
+{
+ S32 i,index;
+ void (*function)(EKeystate keystate) = NULL;
+ const char *name = NULL;
+
+ // Allow remapping of F2-F12
+ if (function_name[0] == 'F')
+ {
+ int c1 = function_name[1] - '0';
+ int c2 = function_name[2] ? function_name[2] - '0' : -1;
+ if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9)
+ {
+ int idx = c1;
+ if (c2 >= 0)
+ idx = idx*10 + c2;
+ if (idx >=2 && idx <= 12)
+ {
+ U32 keyidx = ((mask<<16)|key);
+ (mRemapKeys[mode])[keyidx] = ((0<<16)|KEY_F1+(idx-1));
+ return TRUE;
+ }
+ }
+ }
+
+ // Not remapped, look for a function
+ for (i = 0; i < mNamedFunctionCount; i++)
+ {
+ if (!strcmp(function_name, mNamedFunctions[i].mName))
+ {
+ function = mNamedFunctions[i].mFunction;
+ name = mNamedFunctions[i].mName;
+ }
+ }
+
+ if (!function)
+ {
+ llerrs << "Can't bind key to function " << function_name << ", no function with this name found" << llendl;
+ return FALSE;
+ }
+
+ // check for duplicate first and overwrite
+ for (index = 0; index < mBindingCount[mode]; index++)
+ {
+ if (key == mBindings[mode][index].mKey && mask == mBindings[mode][index].mMask)
+ break;
+ }
+
+ if (index >= MAX_KEY_BINDINGS)
+ {
+ llerrs << "LLKeyboard::bindKey() - too many keys for mode " << mode << llendl;
+ return FALSE;
+ }
+
+ if (mode >= MODE_COUNT)
+ {
+ llerror("LLKeyboard::bindKey() - unknown mode passed", mode);
+ return FALSE;
+ }
+
+ mBindings[mode][index].mKey = key;
+ mBindings[mode][index].mMask = mask;
+// mBindings[mode][index].mName = name;
+ mBindings[mode][index].mFunction = function;
+
+ if (index == mBindingCount[mode])
+ mBindingCount[mode]++;
+
+ // printf("Bound key %c to %s\n", key, name);
+
+ return TRUE;
+}
+
+
+S32 LLViewerKeyboard::loadBindings(const char *filename)
+{
+ FILE *fp;
+ const S32 BUFFER_SIZE = 2048;
+ char buffer[BUFFER_SIZE];
+ char mode_string[MAX_STRING];
+ char key_string[MAX_STRING];
+ char mask_string[MAX_STRING];
+ char function_string[MAX_STRING];
+ S32 mode = MODE_THIRD_PERSON;
+ KEY key = 0;
+ MASK mask = 0;
+ S32 tokens_read;
+ S32 binding_count = 0;
+ S32 line_count = 0;
+
+ fp = LLFile::fopen(filename, "r");
+
+ if (!fp)
+ {
+ return 0;
+ }
+
+
+ while (!feof(fp))
+ {
+ line_count++;
+ if (!fgets(buffer, BUFFER_SIZE, fp))
+ break;
+
+ // skip over comments, blank lines
+ if (buffer[0] == '#' || buffer[0] == '\n') continue;
+
+ // grab the binding strings
+ tokens_read = sscanf(buffer, "%s %s %s %s", mode_string, key_string, mask_string, function_string);
+
+ if (tokens_read == EOF)
+ {
+ llinfos << "Unexpected end-of-file at line " << line_count << " of key binding file " << filename << llendl;
+ fclose(fp);
+ return 0;
+ }
+ else if (tokens_read < 4)
+ {
+ llinfos << "Can't read line " << line_count << " of key binding file " << filename << llendl;
+ continue;
+ }
+
+ // convert mode
+ if (!modeFromString(mode_string, &mode))
+ {
+ llinfos << "Unknown mode on line " << line_count << " of key binding file " << filename << llendl;
+ llinfos << "Mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR" << llendl;
+ continue;
+ }
+
+ // convert key
+ if (!LLKeyboard::keyFromString(key_string, &key))
+ {
+ llinfos << "Can't interpret key on line " << line_count << " of key binding file " << filename << llendl;
+ continue;
+ }
+
+ // convert mask
+ if (!LLKeyboard::maskFromString(mask_string, &mask))
+ {
+ llinfos << "Can't interpret mask on line " << line_count << " of key binding file " << filename << llendl;
+ continue;
+ }
+
+ // bind key
+ if (bindKey(mode, key, mask, function_string))
+ {
+ binding_count++;
+ }
+ }
+
+ fclose(fp);
+
+ return binding_count;
+}
+
+
+EKeyboardMode LLViewerKeyboard::getMode()
+{
+ if ( gAgent.cameraMouselook() )
+ {
+ return MODE_FIRST_PERSON;
+ }
+ else if ( gMorphView && gMorphView->getVisible())
+ {
+ return MODE_EDIT_AVATAR;
+ }
+ else if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->mIsSitting)
+ {
+ return MODE_SITTING;
+ }
+ else
+ {
+ return MODE_THIRD_PERSON;
+ }
+}
+
+
+// Called from scanKeyboard.
+void LLViewerKeyboard::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
+{
+ S32 mode = getMode();
+ // Consider keyboard scanning as NOT mouse event. JC
+ MASK mask = gKeyboard->currentMask(FALSE);
+
+ LLKeyBinding* binding = mBindings[mode];
+ S32 binding_count = mBindingCount[mode];
+
+
+ if (mKeyHandledByUI[key])
+ {
+ return;
+ }
+
+ // don't process key down on repeated keys
+ BOOL repeat = gKeyboard->getKeyRepeated(key);
+
+ for (S32 i = 0; i < binding_count; i++)
+ {
+ //for (S32 key = 0; key < KEY_COUNT; key++)
+ if (binding[i].mKey == key)
+ {
+ //if (binding[i].mKey == key && binding[i].mMask == mask)
+ if (binding[i].mMask == mask)
+ {
+ if (key_down && !repeat)
+ {
+ // ...key went down this frame, call function
+ (*binding[i].mFunction)( KEYSTATE_DOWN );
+ }
+ else if (key_up)
+ {
+ // ...key went down this frame, call function
+ (*binding[i].mFunction)( KEYSTATE_UP );
+ }
+ else if (key_level)
+ {
+ // ...key held down from previous frame
+ // Not windows, just call the function.
+ (*binding[i].mFunction)( KEYSTATE_LEVEL );
+ }//if
+ }//if
+ }//for
+ }//for
+}
diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h
new file mode 100644
index 0000000000..2272aa5d11
--- /dev/null
+++ b/indra/newview/llviewerkeyboard.h
@@ -0,0 +1,71 @@
+/**
+ * @file llviewerkeyboard.h
+ * @brief LLViewerKeyboard class header file
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERKEYBOARD_H
+#define LL_LLVIEWERKEYBOARD_H
+
+#include "llkeyboard.h" // For EKeystate
+
+const S32 MAX_NAMED_FUNCTIONS = 100;
+const S32 MAX_KEY_BINDINGS = 128; // was 60
+
+class LLNamedFunction
+{
+public:
+ const char *mName;
+ LLKeyFunc mFunction;
+};
+
+typedef enum e_keyboard_mode
+{
+ MODE_FIRST_PERSON,
+ MODE_THIRD_PERSON,
+ MODE_EDIT,
+ MODE_EDIT_AVATAR,
+ MODE_SITTING,
+ MODE_COUNT
+} EKeyboardMode;
+
+
+void agent_push_forward( EKeystate s );
+void agent_turn_right( EKeystate s );
+void bind_keyboard_functions();
+
+class LLViewerKeyboard
+{
+public:
+ LLViewerKeyboard();
+
+ BOOL handleKey(KEY key, MASK mask, BOOL repeated);
+
+ void bindNamedFunction(const char *name, LLKeyFunc func);
+
+ S32 loadBindings(const char *filename); // returns number bound, 0 on error
+ EKeyboardMode getMode();
+
+ BOOL modeFromString(const char *string, S32 *mode); // False on failure
+
+ void scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level);
+protected:
+ BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const char *function_name);
+ S32 mNamedFunctionCount;
+ LLNamedFunction mNamedFunctions[MAX_NAMED_FUNCTIONS];
+
+ // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here
+ S32 mBindingCount[MODE_COUNT];
+ LLKeyBinding mBindings[MODE_COUNT][MAX_KEY_BINDINGS];
+
+ typedef std::map<U32, U32> key_remap_t;
+ key_remap_t mRemapKeys[MODE_COUNT];
+ std::set<KEY> mKeysSkippedByUI;
+ BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI
+};
+
+extern LLViewerKeyboard gViewerKeyboard;
+
+#endif // LL_LLVIEWERKEYBOARD_H
diff --git a/indra/newview/llviewerlayer.cpp b/indra/newview/llviewerlayer.cpp
new file mode 100644
index 0000000000..a3ac9e0859
--- /dev/null
+++ b/indra/newview/llviewerlayer.cpp
@@ -0,0 +1,82 @@
+/**
+ * @file llviewerlayer.cpp
+ * @brief LLViewerLayer class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerlayer.h"
+#include "llerror.h"
+#include "llmath.h"
+
+LLViewerLayer::LLViewerLayer(const S32 width, const F32 scale)
+{
+ mWidth = width;
+ mScale = scale;
+ mScaleInv = 1.f/scale;
+ mDatap = new F32[width*width];
+
+ for (S32 i = 0; i < width*width; i++)
+ {
+ *(mDatap + i) = 0.f;
+ }
+}
+
+LLViewerLayer::~LLViewerLayer()
+{
+ delete[] mDatap;
+ mDatap = NULL;
+}
+
+F32 LLViewerLayer::getValue(const S32 x, const S32 y) const
+{
+// llassert(x >= 0);
+// llassert(x < mWidth);
+// llassert(y >= 0);
+// llassert(y < mWidth);
+
+ return *(mDatap + x + y*mWidth);
+}
+
+F32 LLViewerLayer::getValueScaled(const F32 x, const F32 y) const
+{
+ S32 x1, x2, y1, y2;
+ F32 x_frac, y_frac;
+
+ x_frac = x*mScaleInv;
+ x1 = llfloor(x_frac);
+ x2 = x1 + 1;
+ x_frac -= x1;
+
+ y_frac = y*mScaleInv;
+ y1 = llfloor(y_frac);
+ y2 = y1 + 1;
+ y_frac -= y1;
+
+ x1 = llmin((S32)mWidth-1, x1);
+ x1 = llmax(0, x1);
+ x2 = llmin((S32)mWidth-1, x2);
+ x2 = llmax(0, x2);
+ y1 = llmin((S32)mWidth-1, y1);
+ y1 = llmax(0, y1);
+ y2 = llmin((S32)mWidth-1, y2);
+ y2 = llmax(0, y2);
+
+ // Take weighted average of all four points (bilinear interpolation)
+ S32 row1 = y1 * mWidth;
+ S32 row2 = y2 * mWidth;
+
+ // Access in squential order in memory, and don't use immediately.
+ F32 row1_left = mDatap[ row1 + x1 ];
+ F32 row1_right = mDatap[ row1 + x2 ];
+ F32 row2_left = mDatap[ row2 + x1 ];
+ F32 row2_right = mDatap[ row2 + x2 ];
+
+ F32 row1_interp = row1_left - x_frac * (row1_left - row1_right);
+ F32 row2_interp = row2_left - x_frac * (row2_left - row2_right);
+
+ return row1_interp - y_frac * (row1_interp - row2_interp);
+}
diff --git a/indra/newview/llviewerlayer.h b/indra/newview/llviewerlayer.h
new file mode 100644
index 0000000000..47f4780009
--- /dev/null
+++ b/indra/newview/llviewerlayer.h
@@ -0,0 +1,30 @@
+/**
+ * @file llviewerlayer.h
+ * @brief LLViewerLayer class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERLAYER_H
+#define LL_LLVIEWERLAYER_H
+
+// Viewer-side representation of a layer...
+
+class LLViewerLayer
+{
+public:
+ LLViewerLayer(const S32 width, const F32 scale = 1.f);
+ virtual ~LLViewerLayer();
+
+ F32 getValueScaled(const F32 x, const F32 y) const;
+protected:
+ F32 getValue(const S32 x, const S32 y) const;
+protected:
+ S32 mWidth;
+ F32 mScale;
+ F32 mScaleInv;
+ F32 *mDatap;
+};
+
+#endif // LL_LLVIEWERLAYER_H
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
new file mode 100644
index 0000000000..1bf279bf2b
--- /dev/null
+++ b/indra/newview/llviewermenu.cpp
@@ -0,0 +1,8810 @@
+/**
+ * @file llviewermenu.cpp
+ * @brief Builds menus out of items.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewermenu.h"
+
+// system library includes
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <boost/tokenizer.hpp>
+
+// linden library includes
+#include "audioengine.h"
+#include "indra_constants.h"
+#include "llassetuploadresponders.h"
+#include "llassetstorage.h"
+#include "llchat.h"
+#include "lleconomy.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llinstantmessage.h"
+#include "llpermissionsflags.h"
+#include "llrect.h"
+#include "llsecondlifeurls.h"
+#include "lltransactiontypes.h"
+#include "llui.h"
+#include "llview.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "raytrace.h"
+#include "llsdserialize.h"
+#include "lltimer.h"
+#include "vorbisencode.h"
+#include "llvfile.h"
+#include "llvolumemgr.h"
+#include "llwindow.h" // for shell_open()
+
+// newview includes
+#include "llagent.h"
+#include "llagentpilot.h"
+#include "llbox.h"
+#include "llcallingcard.h"
+#include "llcameraview.h"
+#include "llclipboard.h"
+#include "llcompilequeue.h"
+#include "llconsole.h"
+#include "llviewercontrol.h"
+#include "lldebugview.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolhud.h"
+#include "lldrawpooltree.h"
+#include "llface.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloaterabout.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloateranimpreview.h"
+#include "llfloateravatarinfo.h"
+#include "llfloateravatartextures.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterbump.h"
+#include "llfloaterbuy.h"
+#include "llfloaterbuycontents.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloaterbuyland.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterdirectory.h"
+#include "llfloatereditui.h"
+#include "llfloaterfriends.h"
+#include "llfloatergesture.h"
+#include "llfloatergodtools.h"
+#include "llfloatergroupinfo.h"
+#include "llfloatergroups.h"
+#include "llfloaterhtmlhelp.h"
+#include "llfloaterhtmlfind.h"
+#include "llfloaterimport.h"
+#include "llfloaterland.h"
+#include "llfloaterlandholdings.h"
+#include "llfloatermap.h"
+#include "llfloateraccounthistory.h"
+#include "llfloaterimagepreview.h"
+#include "llfloatermute.h"
+#include "llfloaternamedesc.h"
+#include "llfloateropenobject.h"
+#include "llfloaterpermissionsmgr.h"
+#include "llfloaterpreference.h"
+#include "llfloaterrate.h"
+#include "llfloaterregioninfo.h"
+#include "llfloaterreporter.h"
+#include "llfloaterscriptdebug.h"
+#include "llfloatersnapshot.h"
+#include "llfloatertest.h"
+#include "llfloatertools.h"
+#include "llfloaterworldmap.h"
+#include "llframestats.h"
+#include "llframestatview.h"
+#include "llfasttimerview.h"
+#include "llmemoryview.h"
+#include "llgivemoney.h"
+#include "llgroupmgr.h"
+#include "llhoverview.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llimage.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "llpanellogin.h"
+#include "llmenucommands.h"
+#include "llmenugl.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llmutelist.h"
+#include "llnotify.h"
+#include "llpanelobject.h"
+#include "llparcel.h"
+#include "llpreviewscript.h"
+#include "llpreviewtexture.h"
+#include "llprimitive.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llstatusbar.h"
+#include "llstatview.h"
+#include "llstring.h"
+#include "llsurfacepatch.h"
+#include "llimview.h"
+#include "lltextureview.h"
+#include "lltool.h"
+#include "lltoolbar.h"
+#include "lltoolcomp.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolplacer.h"
+#include "lltoolselectland.h"
+#include "llvieweruictrlfactory.h"
+#include "lluploaddialog.h"
+#include "lluserauth.h"
+#include "lluuid.h"
+#include "llvelocitybar.h"
+#include "llviewercamera.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermessage.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvolume.h"
+#include "llweb.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "object_flags.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "roles_constants.h"
+
+#include "lltexlayer.h"
+
+void init_client_menu(LLMenuGL* menu);
+void init_server_menu(LLMenuGL* menu);
+
+void init_debug_world_menu(LLMenuGL* menu);
+void init_debug_rendering_menu(LLMenuGL* menu);
+void init_debug_ui_menu(LLMenuGL* menu);
+void init_debug_xui_menu(LLMenuGL* menu);
+void init_debug_avatar_menu(LLMenuGL* menu);
+void init_debug_baked_texture_menu(LLMenuGL* menu);
+
+BOOL enable_land_build(void*);
+BOOL enable_object_build(void*);
+
+LLVOAvatar* find_avatar_from_object( LLViewerObject* object );
+LLVOAvatar* find_avatar_from_object( const LLUUID& object_id );
+
+void handle_test_load_url(void*);
+
+extern void disconnect_viewer(void *);
+
+//
+// Evil hackish imported globals
+//
+extern BOOL gRenderLightGlows;
+extern BOOL gRenderAvatar;
+extern BOOL gHideSelectedObjects;
+extern BOOL gShowOverlayTitle;
+extern BOOL gRandomizeFramerate;
+extern BOOL gPeriodicSlowFrame;
+extern BOOL gOcclusionCull;
+extern BOOL gAllowSelectAvatar;
+
+//
+// Globals
+//
+
+LLMenuBarGL *gMenuBarView = NULL;
+LLViewerMenuHolderGL *gMenuHolder = NULL;
+LLMenuGL *gPopupMenuView = NULL;
+
+// Pie menus
+LLPieMenu *gPieSelf = NULL;
+LLPieMenu *gPieAvatar = NULL;
+LLPieMenu *gPieObject = NULL;
+LLPieMenu *gPieAttachment = NULL;
+LLPieMenu *gPieLand = NULL;
+
+// local constants
+const LLString CLIENT_MENU_NAME("Client");
+const LLString SERVER_MENU_NAME("Server");
+
+const LLString SAVE_INTO_INVENTORY("Save Object Back to My Inventory");
+const LLString SAVE_INTO_TASK_INVENTORY("Save Object Back to Object Contents");
+
+#if LL_WINDOWS
+static const char* SOUND_EXTENSIONS = ".wav";
+static const char* IMAGE_EXTENSIONS = ".tga .bmp .jpg .jpeg";
+static const char* ANIM_EXTENSIONS = ".bvh";
+#ifdef _CORY_TESTING
+static const char* GEOMETRY_EXTENSIONS = ".slg";
+#endif
+static const char* XML_EXTENSIONS = ".xml";
+static const char* SLOBJECT_EXTENSIONS = ".slobject";
+#endif
+static const char* ALL_FILE_EXTENSIONS = "*.*";
+
+LLMenuGL* gAttachSubMenu = NULL;
+LLMenuGL* gDetachSubMenu = NULL;
+LLMenuGL* gTakeOffClothes = NULL;
+LLPieMenu* gPieRate = NULL;
+LLPieMenu* gAttachScreenPieMenu = NULL;
+LLPieMenu* gAttachPieMenu = NULL;
+LLPieMenu* gAttachBodyPartPieMenus[8];
+LLPieMenu* gDetachPieMenu = NULL;
+LLPieMenu* gDetachScreenPieMenu = NULL;
+LLPieMenu* gDetachBodyPartPieMenus[8];
+
+LLMenuItemCallGL* gAFKMenu = NULL;
+LLMenuItemCallGL* gBusyMenu = NULL;
+
+typedef LLMemberListener<LLView> view_listener_t;
+
+//
+// Local prototypes
+//
+BOOL enable_attach(void*);
+void handle_leave_group(void *);
+
+// File Menu
+const char* upload_pick(void* data);
+void handle_upload(void* data);
+void handle_upload_object(void* data);
+void handle_compress_image(void*);
+BOOL enable_save_as(void *);
+
+// Edit menu
+void handle_dump_group_info(void *);
+void handle_dump_focus(void*);
+
+void handle_region_dump_settings(void*);
+void handle_region_dump_temp_asset_data(void*);
+void handle_region_clear_temp_asset_data(void*);
+
+// Object pie menu
+BOOL sitting_on_selection();
+
+void near_sit_object();
+void label_sit_or_stand(LLString& label, void*);
+// buy and take alias into the same UI positions, so these
+// declarations handle this mess.
+BOOL is_selection_buy_not_take();
+S32 selection_price();
+BOOL enable_take();
+void handle_take();
+void confirm_take(S32 option, void* data);
+BOOL enable_buy(void*);
+void handle_buy(void *);
+void handle_buy_object(LLSaleInfo sale_info);
+void handle_buy_contents(LLSaleInfo sale_info);
+void label_touch(LLString& label, void*);
+
+// Land pie menu
+void near_sit_down_point(BOOL success, void *);
+
+// Avatar pie menu
+void handle_follow(void *userdata);
+void handle_talk_to(void *userdata);
+
+// Debug menu
+void show_permissions_control(void*);
+void load_url_local_file(const char* file_name);
+void toggle_build_options(void* user_data);
+#if 0 // Unused
+void handle_audio_status_1(void*);
+void handle_audio_status_2(void*);
+void handle_audio_status_3(void*);
+void handle_audio_status_4(void*);
+#endif
+void reload_ui(void*);
+void handle_agent_stop_moving(void*);
+void print_packets_lost(void*);
+void drop_packet(void*);
+void velocity_interpolate( void* data );
+void update_fov(S32 increments);
+void toggle_wind_audio(void);
+void toggle_water_audio(void);
+void handle_rebake_textures(void*);
+BOOL check_admin_override(void*);
+void handle_admin_override_toggle(void*);
+#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+void handle_toggle_hacked_godmode(void*);
+BOOL check_toggle_hacked_godmode(void*);
+#endif
+
+void toggle_glow(void *);
+BOOL check_glow(void *);
+
+void toggle_vbo(void *);
+BOOL check_vbo(void *);
+
+void toggle_vertex_shaders(void *);
+BOOL check_vertex_shaders(void *);
+
+void toggle_cull_small(void *);
+
+void toggle_show_xui_names(void *);
+BOOL check_show_xui_names(void *);
+
+// Debug UI
+void handle_save_to_xml(void*);
+void handle_load_from_xml(void*);
+
+void handle_god_mode(void*);
+
+// God menu
+void handle_leave_god_mode(void*);
+
+BOOL is_inventory_visible( void* user_data );
+void handle_reset_view();
+
+void disabled_duplicate(void*);
+void handle_duplicate_in_place(void*);
+void handle_repeat_duplicate(void*);
+
+void handle_export(void*);
+void handle_deed_object_to_group(void*);
+BOOL enable_deed_object_to_group(void*);
+void handle_object_owner_self(void*);
+void handle_object_owner_permissive(void*);
+void handle_object_lock(void*);
+void handle_object_asset_ids(void*);
+void force_take_copy(void*);
+#ifdef _CORY_TESTING
+void force_export_copy(void*);
+void force_import_geometry(void*);
+#endif
+
+void handle_force_parcel_owner_to_me(void*);
+void handle_force_parcel_to_content(void*);
+void handle_claim_public_land(void*);
+
+void handle_god_expunge_user(void*);
+
+void handle_god_request_havok(void *);
+void handle_god_request_avatar_geometry(void *); // Hack for easy testing of new avatar geometry
+void reload_personal_settings_overrides(void *);
+void force_breakpoint(void *);
+void reload_vertex_shader(void *);
+void flush_animations(void *);
+void slow_mo_animations(void *);
+void handle_disconnect_viewer(void *);
+
+void handle_stopall(void*);
+void handle_hinge(void*);
+void handle_ptop(void*);
+void handle_lptop(void*);
+void handle_wheel(void*);
+void handle_dehinge(void*);
+BOOL enable_dehinge(void*);
+void handle_force_delete(void*);
+void print_object_info(void*);
+void show_debug_menus();
+void toggle_debug_menus(void*);
+void toggle_map( void* user_data );
+void export_info_callback(LLAssetInfo *info, void **user_data, S32 result);
+void export_data_callback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, void **user_data, S32 result);
+void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result);
+BOOL menu_check_build_tool( void* user_data );
+void handle_reload_settings(void*);
+void focus_here(void*);
+void dump_select_mgr(void*);
+void dump_volume_mgr(void*);
+void dump_inventory(void*);
+void edit_ui(void*);
+void toggle_visibility(void*);
+BOOL get_visibility(void*);
+
+// Avatar Pie menu
+void request_friendship(const LLUUID& agent_id);
+
+// Tools menu
+void handle_first_tool(void*);
+void handle_next_tool(void*);
+void handle_previous_tool(void*);
+void handle_force_unlock(void*);
+void handle_selected_texture_info(void*);
+void handle_dump_image_list(void*);
+
+void handle_fullscreen_debug(void*);
+void handle_crash(void*);
+void handle_dump_followcam(void*);
+void handle_viewer_enable_circuit_log(void*);
+void handle_viewer_disable_circuit_log(void*);
+void handle_viewer_enable_message_log(void*);
+void handle_viewer_disable_message_log(void*);
+void handle_send_postcard(void*);
+void handle_gestures_old(void*);
+void handle_focus(void *);
+BOOL enable_buy_land(void*);
+void handle_move(void*);
+void handle_show_inventory(void*);
+void handle_activate(void*);
+BOOL enable_activate(void*);
+
+// Help menu
+void handle_buy_currency(void*);
+
+void handle_test_male(void *);
+void handle_test_female(void *);
+void handle_toggle_pg(void*);
+void handle_dump_attachments(void *);
+void handle_show_overlay_title(void*);
+void handle_dump_avatar_local_textures(void*);
+void handle_debug_avatar_textures(void*);
+void handle_grab_texture(void*);
+BOOL enable_grab_texture(void*);
+
+BOOL menu_ui_enabled(void *user_data);
+void check_toggle_control( LLUICtrl *, void* user_data );
+BOOL menu_check_control( void* user_data);
+void menu_toggle_variable( void* user_data );
+BOOL menu_check_variable( void* user_data);
+BOOL enable_land_selected( void* );
+BOOL enable_more_than_one_selected(void* );
+BOOL enable_selection_you_own_all(void*);
+BOOL enable_selection_you_own_one(void*);
+BOOL enable_save_into_inventory(void*);
+BOOL enable_save_into_task_inventory(void*);
+BOOL enable_not_thirdperson(void*);
+BOOL enable_export_selected(void *);
+BOOL enable_have_card(void*);
+BOOL enable_detach(void*);
+BOOL enable_region_owner(void*);
+
+
+class LLMenuParcelObserver : public LLParcelObserver
+{
+public:
+ LLMenuParcelObserver();
+ ~LLMenuParcelObserver();
+ virtual void changed();
+};
+
+static LLMenuParcelObserver* gMenuParcelObserver = NULL;
+
+LLMenuParcelObserver::LLMenuParcelObserver()
+{
+ gParcelMgr->addObserver(this);
+}
+
+LLMenuParcelObserver::~LLMenuParcelObserver()
+{
+ gParcelMgr->removeObserver(this);
+}
+
+void LLMenuParcelObserver::changed()
+{
+ gMenuHolder->childSetEnabled("Land Buy Pass", LLPanelLandGeneral::enableBuyPass(NULL));
+
+ BOOL buyable = enable_buy_land(NULL);
+ gMenuHolder->childSetEnabled("Land Buy", buyable);
+ gMenuHolder->childSetEnabled("Buy Land...", buyable);
+}
+
+
+//-----------------------------------------------------------------------------
+// Menu Construction
+//-----------------------------------------------------------------------------
+
+// code required to calculate anything about the menus
+void pre_init_menus()
+{
+ // static information
+ LLColor4 color;
+ color = gColors.getColor( "MenuDefaultBgColor" );
+ LLMenuGL::setDefaultBackgroundColor( color );
+ color = gColors.getColor( "MenuItemEnabledColor" );
+ LLMenuItemGL::setEnabledColor( color );
+ color = gColors.getColor( "MenuItemDisabledColor" );
+ LLMenuItemGL::setDisabledColor( color );
+ color = gColors.getColor( "MenuItemHighlightBgColor" );
+ LLMenuItemGL::setHighlightBGColor( color );
+ color = gColors.getColor( "MenuItemHighlightFgColor" );
+ LLMenuItemGL::setHighlightFGColor( color );
+}
+
+void initialize_menu_actions();
+
+//-----------------------------------------------------------------------------
+// Initialize main menus
+//
+// HOW TO NAME MENUS:
+//
+// First Letter Of Each Word Is Capitalized, Even At Or And
+//
+// Items that lead to dialog boxes end in "..."
+//
+// Break up groups of more than 6 items with separators
+//-----------------------------------------------------------------------------
+void init_menus()
+{
+ S32 top = gViewerWindow->getRootView()->getRect().getHeight();
+ S32 width = gViewerWindow->getRootView()->getRect().getWidth();
+
+ //
+ // Main menu bar
+ //
+ gMenuHolder = new LLViewerMenuHolderGL();
+ gMenuHolder->setRect(LLRect(0, top - MENU_BAR_HEIGHT, width, STATUS_BAR_HEIGHT));
+ gMenuHolder->setFollowsAll();
+
+ LLMenuGL::sDefaultMenuContainer = gMenuHolder;
+
+ // Initialize actions
+ initialize_menu_actions();
+
+ gMenuBarView = (LLMenuBarGL*)gUICtrlFactory->buildMenu("menu_viewer.xml", gMenuHolder);
+ gMenuBarView->setRect(LLRect(0, top, width, top - MENU_BAR_HEIGHT));
+ gViewerWindow->getRootView()->addChild(gMenuBarView);
+
+ // menu holder appears on top of menu bar so you can see the menu title
+ // flash when an item is triggered (the flash occurs in the holder)
+ gViewerWindow->getRootView()->addChild(gMenuHolder);
+
+ gMenuHolder->childSetLabelArg("Upload Image", "[COST]", "10");
+ gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", "10");
+ gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", "10");
+ gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", "10");
+
+ gAFKMenu = (LLMenuItemCallGL*)gMenuBarView->getChildByName("Set Away", TRUE);
+ gBusyMenu = (LLMenuItemCallGL*)gMenuBarView->getChildByName("Set Busy", TRUE);
+ gAttachSubMenu = gMenuBarView->getChildMenuByName("Attach Object", TRUE);
+ gDetachSubMenu = gMenuBarView->getChildMenuByName("Detach Object", TRUE);
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ gMenuBarView->getChildByName("Menu Underpants", TRUE)->setVisible(FALSE);
+ gMenuBarView->getChildByName("Menu Undershirt", TRUE)->setVisible(FALSE);
+ }
+
+ // TomY TODO convert these two
+ LLMenuGL*menu;
+ menu = new LLMenuGL(CLIENT_MENU_NAME);
+ init_client_menu(menu);
+ gMenuBarView->appendMenu( menu );
+ menu->updateParent(gMenuHolder);
+
+ menu = new LLMenuGL(SERVER_MENU_NAME);
+ init_server_menu(menu);
+ gMenuBarView->appendMenu( menu );
+ menu->updateParent(gMenuHolder);
+
+ gMenuBarView->createJumpKeys();
+
+ ///
+ /// Popup menu
+ ///
+ /// The popup menu is now populated by the show_context_menu()
+ /// method.
+
+ gPopupMenuView = new LLMenuGL( "Popup" );
+ gPopupMenuView->setVisible( FALSE );
+ gMenuHolder->addChild( gPopupMenuView );
+
+ ///
+ /// Pie menus
+ ///
+ gPieSelf = gUICtrlFactory->buildPieMenu("menu_pie_self.xml", gMenuHolder);
+
+ // TomY TODO: what shall we do about these?
+ gDetachScreenPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Detach HUD", true);
+ gDetachPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Detach", true);
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ gMenuHolder->getChildByName("Self Underpants", TRUE)->setVisible(FALSE);
+ gMenuHolder->getChildByName("Self Undershirt", TRUE)->setVisible(FALSE);
+ }
+
+ gPieAvatar = gUICtrlFactory->buildPieMenu("menu_pie_avatar.xml", gMenuHolder);
+
+ gPieObject = gUICtrlFactory->buildPieMenu("menu_pie_object.xml", gMenuHolder);
+
+ gAttachScreenPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Attach HUD", true);
+ gAttachPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Attach", true);
+ gPieRate = (LLPieMenu*)gMenuHolder->getChildByName("Rate Menu", true);
+
+ gPieAttachment = gUICtrlFactory->buildPieMenu("menu_pie_attachment.xml", gMenuHolder);
+
+ gPieLand = gUICtrlFactory->buildPieMenu("menu_pie_land.xml", gMenuHolder);
+
+ ///
+ /// set up the colors
+ ///
+ LLColor4 color;
+
+ // If we are not in production, use a different color to make it apparent.
+ if (gInProductionGrid)
+ {
+ color = gColors.getColor( "MenuBarBgColor" );
+ }
+ else
+ {
+ color = gColors.getColor( "MenuNonProductionBgColor" );
+ }
+
+ gMenuBarView->setBackgroundColor( color );
+
+ LLColor4 pie_color = gColors.getColor("PieMenuBgColor");
+ gPieSelf->setBackgroundColor( pie_color );
+ gPieAvatar->setBackgroundColor( pie_color );
+ gPieObject->setBackgroundColor( pie_color );
+ gPieAttachment->setBackgroundColor( pie_color );
+ gPieLand->setBackgroundColor( pie_color );
+
+ color = gColors.getColor( "MenuPopupBgColor" );
+ gPopupMenuView->setBackgroundColor( color );
+
+ // Let land based option enable when parcel changes
+ gMenuParcelObserver = new LLMenuParcelObserver();
+
+ //
+ // Debug menu visiblity
+ //
+ show_debug_menus();
+}
+
+void init_client_menu(LLMenuGL* menu)
+{
+ LLMenuGL* sub_menu = NULL;
+
+ //menu->append(new LLMenuItemCallGL("Permissions Control", &show_permissions_control));
+
+// this is now in the view menu so we don't need it here!
+ {
+ LLMenuGL* sub = new LLMenuGL("Consoles");
+ menu->appendMenu(sub);
+ sub->append(new LLMenuItemCheckGL("Frame Console",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gDebugView->mFrameStatView,
+ '2', MASK_CONTROL|MASK_SHIFT ) );
+ sub->append(new LLMenuItemCheckGL("Texture Console",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gTextureView,
+ '3', MASK_CONTROL|MASK_SHIFT ) );
+ LLView* debugview = gDebugView->mDebugConsolep;
+ sub->append(new LLMenuItemCheckGL("Debug Console",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ debugview,
+ '4', MASK_CONTROL|MASK_SHIFT ) );
+#if 0 // Unused
+ {
+ LLMenuGL* sub = new LLMenuGL("Audio");
+ menu->appendMenu(sub);
+
+ sub->append(new LLMenuItemCallGL("Global Pos",
+ &handle_audio_status_1, NULL, NULL ,'5', MASK_CONTROL|MASK_SHIFT) );
+ sub->append(new LLMenuItemCallGL("Cone",
+ &handle_audio_status_2, NULL, NULL ,'6', MASK_CONTROL|MASK_SHIFT) );
+ sub->append(new LLMenuItemCallGL("Local Pos",
+ &handle_audio_status_3, NULL, NULL ,'7', MASK_CONTROL|MASK_SHIFT) );
+ sub->append(new LLMenuItemCallGL("Duration",
+ &handle_audio_status_4, NULL, NULL ,'8', MASK_CONTROL|MASK_SHIFT) );
+ sub->createJumpKeys();
+ }
+#endif
+ sub->append(new LLMenuItemCheckGL("Fast Timers",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gDebugView->mFastTimerView,
+ '9', MASK_CONTROL|MASK_SHIFT ) );
+ sub->append(new LLMenuItemCheckGL("Memory",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gDebugView->mMemoryView,
+ '0', MASK_CONTROL|MASK_SHIFT ) );
+ sub->appendSeparator();
+ sub->append(new LLMenuItemCallGL("Region Info to Debug Console",
+ &handle_region_dump_settings, NULL));
+ sub->append(new LLMenuItemCallGL("Group Info to Debug Console",
+ &handle_dump_group_info, NULL, NULL));
+ sub->createJumpKeys();
+ }
+
+ // neither of these works particularly well at the moment
+ /*menu->append(new LLMenuItemCallGL( "Reload UI XML", &reload_ui,
+ NULL, NULL, 'R', MASK_ALT | MASK_CONTROL ) );*/
+ /*menu->append(new LLMenuItemCallGL("Reload settings/colors",
+ &handle_reload_settings, NULL, NULL));*/
+ menu->append(new LLMenuItemCallGL("Reload personal setting overrides",
+ &reload_personal_settings_overrides, NULL, NULL, KEY_F2, MASK_CONTROL|MASK_SHIFT));
+
+ sub_menu = new LLMenuGL("HUD Info");
+ {
+ sub_menu->append(new LLMenuItemCheckGL("Velocity",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gVelocityBar));
+
+ sub_menu->append(new LLMenuItemToggleGL("Camera", &gDisplayCameraPos ) );
+ sub_menu->append(new LLMenuItemToggleGL("Wind", &gDisplayWindInfo) );
+ sub_menu->append(new LLMenuItemToggleGL("FOV", &gDisplayFOV ) );
+ sub_menu->createJumpKeys();
+ }
+ menu->appendMenu(sub_menu);
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCheckGL( "High-res Snapshot",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"HighResSnapshot"));
+
+ menu->append(new LLMenuItemToggleGL("Quiet Snapshots to Disk",
+ &gQuietSnapshot));
+
+ menu->append(new LLMenuItemCheckGL("Show Mouselook Crosshairs",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"ShowCrosshairs"));
+
+ menu->append(new LLMenuItemCheckGL("Debug Permissions",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"DebugPermissions"));
+
+
+
+#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid)
+ {
+ menu->append(new LLMenuItemCheckGL("Hacked Godmode",
+ &handle_toggle_hacked_godmode,
+ NULL,
+ &check_toggle_hacked_godmode,
+ (void*)"HackedGodmode"));
+ }
+#endif
+
+ menu->append(new LLMenuItemCallGL("Clear Group Cache",
+ LLGroupMgr::debugClearAllGroups));
+ menu->appendSeparator();
+
+ sub_menu = new LLMenuGL("Rendering");
+ init_debug_rendering_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("World");
+ init_debug_world_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("UI");
+ init_debug_ui_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("XUI");
+ init_debug_xui_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("Character");
+ init_debug_avatar_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+{
+ LLMenuGL* sub = NULL;
+ sub = new LLMenuGL("Network");
+
+ sub->append(new LLMenuItemCallGL("Enable Circuit Log",
+ &handle_viewer_enable_circuit_log, NULL));
+ sub->append(new LLMenuItemCallGL("Disable Circuit Log",
+ &handle_viewer_disable_circuit_log, NULL));
+ sub->append(new LLMenuItemCallGL("Enable Message Log",
+ &handle_viewer_enable_message_log, NULL));
+ sub->append(new LLMenuItemCallGL("Disable Message Log",
+ &handle_viewer_disable_message_log, NULL));
+
+ sub->appendSeparator();
+
+ sub->append(new LLMenuItemCheckGL("Velocity Interpolate Objects",
+ &velocity_interpolate,
+ NULL,
+ &menu_check_control,
+ (void*)"VelocityInterpolate"));
+ sub->append(new LLMenuItemCheckGL("Ping Interpolate Object Positions",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"PingInterpolate"));
+
+ sub->appendSeparator();
+
+ sub->append(new LLMenuItemCallGL("Drop a Packet",
+ &drop_packet, NULL, NULL,
+ 'L', MASK_ALT | MASK_CONTROL));
+
+ menu->appendMenu( sub );
+ sub->createJumpKeys();
+ }
+ {
+ LLMenuGL* sub = NULL;
+ sub = new LLMenuGL("Recorder");
+
+ sub->append(new LLMenuItemCheckGL("Full Session Logging", &menu_toggle_control, NULL, &menu_check_control, (void*)"StatsSessionTrackFrameStats"));
+
+ sub->append(new LLMenuItemCallGL("Start Logging", &LLFrameStats::startLogging, NULL));
+ sub->append(new LLMenuItemCallGL("Stop Logging", &LLFrameStats::stopLogging, NULL));
+ sub->append(new LLMenuItemCallGL("Log 10 Seconds", &LLFrameStats::timedLogging10, NULL));
+ sub->append(new LLMenuItemCallGL("Log 30 Seconds", &LLFrameStats::timedLogging30, NULL));
+ sub->append(new LLMenuItemCallGL("Log 60 Seconds", &LLFrameStats::timedLogging60, NULL));
+ sub->appendSeparator();
+ sub->append(new LLMenuItemCallGL("Start Playback", &LLAgentPilot::startPlayback, NULL));
+ sub->append(new LLMenuItemCallGL("Stop Playback", &LLAgentPilot::stopPlayback, NULL));
+ sub->append(new LLMenuItemToggleGL("Loop Playback", &LLAgentPilot::sLoop) );
+ sub->append(new LLMenuItemCallGL("Start Record", &LLAgentPilot::startRecord, NULL));
+ sub->append(new LLMenuItemCallGL("Stop Record", &LLAgentPilot::saveRecord, NULL));
+
+ menu->appendMenu( sub );
+ sub->createJumpKeys();
+ }
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemToggleGL("Show Updates",
+ &gShowObjectUpdates,
+ 'U', MASK_ALT | MASK_SHIFT | MASK_CONTROL));
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCallGL("Compress Image...",
+ &handle_compress_image, NULL, NULL));
+
+ menu->append(new LLMenuItemCheckGL("Limit Select Distance",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"LimitSelectDistance"));
+
+ menu->append(new LLMenuItemToggleGL("Disable Camera Constraints",
+ &LLViewerCamera::sDisableCameraConstraints));
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCheckGL( "Console Window",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"ShowConsoleWindow"));
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ {
+ LLMenuGL* sub = NULL;
+ sub = new LLMenuGL("Debugging");
+ sub->append(new LLMenuItemCallGL("Force Breakpoint", &force_breakpoint, NULL, NULL, 'B', MASK_CONTROL | MASK_ALT));
+ sub->append(new LLMenuItemCallGL("LLError And Crash", &handle_crash));
+ sub->createJumpKeys();
+ menu->appendMenu(sub);
+ }
+#endif
+
+ // TomY Temporary menu item so we can test this floater
+ menu->append(new LLMenuItemCheckGL("Clothing...",
+ &handle_clothing,
+ NULL,
+ NULL,
+ NULL));
+
+ menu->append(new LLMenuItemCallGL("Debug Settings", LLFloaterSettingsDebug::show, NULL, NULL));
+ menu->append(new LLMenuItemCheckGL("View Admin Options", &handle_admin_override_toggle, NULL, &check_admin_override, NULL, 'V', MASK_CONTROL | MASK_ALT));
+ menu->createJumpKeys();
+}
+
+void handle_upload_data(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if(!picker.getOpenFile())
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ const char* filename = picker.getFirstFile();
+ S32 index = strlen(filename);
+ char delim = gDirUtilp->getDirDelimiter()[0];
+ while(index && filename[index--] != delim);
+ index += 2;
+ const char* basename = &filename[index];
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("InitiateUpload");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->nextBlock("FileData");
+ msg->addString("BaseFilename", basename);
+ msg->addString("SourceFilename", filename);
+ gAgent.sendReliableMessage();
+}
+
+void init_debug_world_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCheckGL("Mouse Moves Sun",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"MouseSun",
+ 'M', MASK_CONTROL|MASK_ALT));
+ menu->append(new LLMenuItemCheckGL("Sim Sun Override",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"SkyOverrideSimSunPosition"));
+ menu->append(new LLMenuItemCallGL("Dump Scripted Camera",
+ &handle_dump_followcam, NULL, NULL));
+ menu->append(new LLMenuItemCheckGL("Fixed Weather",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"FixedWeather"));
+ menu->createJumpKeys();
+}
+
+
+void handle_export_menus_to_xml(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if(!picker.getSaveFile(LLFilePicker::FFSAVE_XML))
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ const char* filename = picker.getFirstFile();
+
+ llofstream out(filename);
+ LLXMLNodePtr node = gMenuBarView->getXML();
+ node->writeToOstream(out);
+ out.close();
+}
+
+extern BOOL gDebugClicks;
+extern BOOL gDebugWindowProc;
+extern BOOL gDebugTextEditorTips;
+extern BOOL gDebugSelectMgr;
+
+void init_debug_ui_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCallGL("Editable UI", &edit_ui));
+ menu->append(new LLMenuItemToggleGL("Async Keystrokes", &gHandleKeysAsync));
+ menu->append(new LLMenuItemCallGL( "Dump SelectMgr", &dump_select_mgr));
+ menu->append(new LLMenuItemCallGL( "Dump Inventory", &dump_inventory));
+ menu->append(new LLMenuItemCallGL( "Dump Focus Holder", &handle_dump_focus, NULL, NULL, 'F', MASK_ALT | MASK_CONTROL));
+ menu->append(new LLMenuItemCallGL( "Dump VolumeMgr", &dump_volume_mgr, NULL, NULL));
+ menu->append(new LLMenuItemCallGL( "Print Selected Object Info", &print_object_info, NULL, NULL, 'P', MASK_CONTROL|MASK_SHIFT ));
+ menu->append(new LLMenuItemCallGL( "Print Agent Info", &print_agent_nvpairs, NULL, NULL, 'P', MASK_SHIFT ));
+ menu->append(new LLMenuItemCallGL( "Print Texture Memory Stats", &output_statistics, NULL, NULL, 'M', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ menu->append(new LLMenuItemCheckGL("Double-Click Auto-Pilot",
+ menu_toggle_control, NULL, menu_check_control,
+ (void*)"DoubleClickAutoPilot"));
+ menu->appendSeparator();
+// menu->append(new LLMenuItemCallGL( "Print Packets Lost", &print_packets_lost, NULL, NULL, 'L', MASK_SHIFT ));
+ menu->append(new LLMenuItemToggleGL("Debug SelectMgr", &gDebugSelectMgr));
+ menu->append(new LLMenuItemToggleGL("Debug Clicks", &gDebugClicks));
+ menu->append(new LLMenuItemToggleGL("Debug Views", &LLView::sDebugRects));
+ menu->append(new LLMenuItemToggleGL("Debug Mouse Events", &LLView::sDebugMouseHandling));
+ menu->append(new LLMenuItemToggleGL("Debug Keys", &LLView::sDebugKeys));
+ menu->append(new LLMenuItemToggleGL("Debug WindowProc", &gDebugWindowProc));
+ menu->append(new LLMenuItemToggleGL("Debug Text Editor Tips", &gDebugTextEditorTips));
+ menu->createJumpKeys();
+}
+
+void init_debug_xui_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCallGL("Floater Test...", LLFloaterTest::show));
+ menu->append(new LLMenuItemCallGL("Export Menus to XML...", handle_export_menus_to_xml));
+ menu->append(new LLMenuItemCallGL("Edit UI...", LLFloaterEditUI::show));
+ menu->append(new LLMenuItemCallGL("Load from XML...", handle_load_from_xml));
+ menu->append(new LLMenuItemCallGL("Save to XML...", handle_save_to_xml));
+ menu->append(new LLMenuItemCheckGL("Show XUI Names", toggle_show_xui_names, NULL, check_show_xui_names, NULL));
+
+ //menu->append(new LLMenuItemCallGL("Buy Currency...", handle_buy_currency));
+ menu->createJumpKeys();
+}
+
+void init_debug_rendering_menu(LLMenuGL* menu)
+{
+ LLMenuGL* sub_menu = NULL;
+
+ ///////////////////////////
+ //
+ // Debug menu for types/pools
+ //
+ sub_menu = new LLMenuGL("Types");
+ menu->appendMenu(sub_menu);
+
+ sub_menu->append(new LLMenuItemCheckGL("Simple",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_SIMPLE, '1', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Alpha",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_ALPHA, '2', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Tree",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_TREE, '3', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Character",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_AVATAR, '4', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("SurfacePatch",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_TERRAIN, '5', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Sky",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_SKY, '6', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Water",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_WATER, '7', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Ground",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_GROUND, '8', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Volume",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_VOLUME, '9', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Grass",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_GRASS, '0', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Clouds",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_CLOUDS, '-', MASK_CONTROL|MASK_ALT| MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Particles",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_PARTICLES, '=', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Bump",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_BUMP, '\\', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->createJumpKeys();
+ sub_menu = new LLMenuGL("Features");
+ menu->appendMenu(sub_menu);
+ sub_menu->append(new LLMenuItemCheckGL("UI",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_UI, '1', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Selected",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_SELECTED, '2', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Highlighted",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_HIGHLIGHTED, '3', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Dynamic Textures",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES, '4', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Fog",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_FOG, '6', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Palletized Textures",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_PALETTE, '7', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Test FRInfo",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_FR_INFO, '8', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL( "Flexible Objects",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE, '9', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL( "Chain Faces",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES, '0', MASK_ALT|MASK_CONTROL));
+ sub_menu->createJumpKeys();
+
+ /////////////////////////////
+ //
+ // Debug menu for info displays
+ //
+ sub_menu = new LLMenuGL("Info Displays");
+ menu->appendMenu(sub_menu);
+
+ sub_menu->append(new LLMenuItemCheckGL("Verify", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_VERIFY));
+ sub_menu->append(new LLMenuItemCheckGL("AGP Map", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_AGP_MEM));
+ sub_menu->append(new LLMenuItemCheckGL("BBoxes", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_BBOXES));
+ sub_menu->append(new LLMenuItemCheckGL("Points", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_POINTS));
+ sub_menu->append(new LLMenuItemCheckGL("Octree", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_OCTREE));
+ sub_menu->append(new LLMenuItemCheckGL("Occlusion", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_OCCLUSION));
+ sub_menu->append(new LLMenuItemCheckGL("Face Chains", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_FACE_CHAINS));
+ sub_menu->append(new LLMenuItemCheckGL("Texture Priority", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY));
+ sub_menu->append(new LLMenuItemCheckGL("Composition", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_COMPOSITION));
+ sub_menu->append(new LLMenuItemCheckGL("ShadowMap", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_SHADOW_MAP));
+ sub_menu->append(new LLMenuItemCheckGL("LightTrace",&LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_LIGHT_TRACE));
+ sub_menu->append(new LLMenuItemCheckGL("Pools", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_POOLS));
+ sub_menu->append(new LLMenuItemCheckGL("Queues", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_QUEUES));
+ sub_menu->append(new LLMenuItemCheckGL("Map", &LLPipeline::toggleRenderDebug, NULL,
+ LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_MAP));
+
+ sub_menu->append(new LLMenuItemCheckGL("Show Depth Buffer",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"ShowDepthBuffer"));
+ sub_menu->append(new LLMenuItemToggleGL("Show Select Buffer", &gDebugSelect));
+
+ sub_menu = new LLMenuGL("Render Tests");
+
+ sub_menu->append(new LLMenuItemCheckGL("Camera Offset",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"CameraOffset"));
+
+ sub_menu->append(new LLMenuItemToggleGL("Randomize Framerate", &gRandomizeFramerate));
+
+ sub_menu->append(new LLMenuItemToggleGL("Periodic Slow Frame", &gPeriodicSlowFrame));
+ sub_menu->createJumpKeys();
+
+ menu->appendMenu( sub_menu );
+
+ menu->appendSeparator();
+ menu->append(new LLMenuItemCheckGL("Axes", menu_toggle_control, NULL, menu_check_control, (void*)"ShowAxes"));
+ menu->append(new LLMenuItemCheckGL("Use VBO", toggle_vbo, NULL, check_vbo, NULL));
+ menu->append(new LLMenuItemCheckGL("Light Glows", toggle_glow, NULL, check_glow, NULL));
+// menu->append(new LLMenuItemCheckGL("Cull Small Objects", toggle_cull_small, NULL, menu_check_control, (void*)"RenderCullBySize"));
+
+ menu->appendSeparator();
+ menu->append(new LLMenuItemToggleGL("Hide Selected", &gHideSelectedObjects));
+ menu->appendSeparator();
+ menu->append(new LLMenuItemCheckGL("Tangent Basis", menu_toggle_control, NULL, menu_check_control, (void*)"ShowTangentBasis"));
+ menu->append(new LLMenuItemCallGL("Selected Texture Info", handle_selected_texture_info, NULL, NULL, 'T', MASK_CONTROL|MASK_SHIFT|MASK_ALT));
+ //menu->append(new LLMenuItemCallGL("Dump Image List", handle_dump_image_list, NULL, NULL, 'I', MASK_CONTROL|MASK_SHIFT));
+
+ menu->append(new LLMenuItemToggleGL("Wireframe", &gUseWireframe,
+ 'R', MASK_CONTROL|MASK_SHIFT));
+
+ LLMenuItemCheckGL* item;
+ item = new LLMenuItemCheckGL("Object-Object Occlusion", menu_toggle_control, NULL, menu_check_control, (void*)"UseOcclusion", 'O', MASK_CONTROL|MASK_SHIFT);
+ item->setEnabled(gGLManager.mHasOcclusionQuery);
+ menu->append(item);
+
+
+ item = new LLMenuItemCheckGL("Animate Textures", menu_toggle_control, NULL, menu_check_control, (void*)"AnimateTextures", 'A', MASK_CONTROL|MASK_ALT);
+ menu->append(item);
+
+ item = new LLMenuItemCheckGL("Disable Textures", menu_toggle_variable, NULL, menu_check_variable, (void*)&LLViewerImage::sDontLoadVolumeTextures);
+ menu->append(item);
+
+ item = new LLMenuItemCheckGL("Cheesy Beacon", menu_toggle_control, NULL, menu_check_control, (void*)"CheesyBeacon");
+ menu->append(item);
+
+#if 0 // 1.9.2
+ item = new LLMenuItemCheckGL("Vertex Shaders", toggle_vertex_shaders, NULL, check_vertex_shaders, (void*)"VertexShaderEnable", 'V', MASK_CONTROL|MASK_ALT);
+ item->setEnabled(gGLManager.mHasVertexShader);
+ menu->append(item);
+#endif
+ menu->createJumpKeys();
+}
+
+extern BOOL gDebugAvatarRotation;
+
+void init_debug_avatar_menu(LLMenuGL* menu)
+{
+ LLMenuGL* sub_menu = new LLMenuGL("Grab Baked Texture");
+ init_debug_baked_texture_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("Character Tests");
+ sub_menu->append(new LLMenuItemToggleGL("Go Away/AFK When Idle",
+ &gAllowAFK));
+
+ sub_menu->append(new LLMenuItemCallGL("Appearance To XML",
+ &LLVOAvatar::dumpArchetypeXML));
+
+ // HACK for easy testing of avatar geometry
+ sub_menu->append(new LLMenuItemCallGL( "Toggle Character Geometry",
+ &handle_god_request_avatar_geometry, &enable_god_customer_service, NULL));
+
+ sub_menu->append(new LLMenuItemCallGL("Test Male",
+ handle_test_male));
+
+ sub_menu->append(new LLMenuItemCallGL("Test Female",
+ handle_test_female));
+
+ sub_menu->append(new LLMenuItemCallGL("Toggle PG", handle_toggle_pg));
+
+ sub_menu->append(new LLMenuItemToggleGL("Allow Select Avatar", &gAllowSelectAvatar));
+ sub_menu->createJumpKeys();
+
+ menu->appendMenu(sub_menu);
+
+ menu->append(new LLMenuItemCallGL("Force Params to Default", &LLAgent::clearVisualParams, NULL));
+ menu->append(new LLMenuItemCallGL("Reload Vertex Shader", &reload_vertex_shader, NULL));
+ menu->append(new LLMenuItemToggleGL("Animation Info", &LLVOAvatar::sShowAnimationDebug));
+ menu->append(new LLMenuItemCallGL("Flush Animations", &flush_animations, NULL));
+ menu->append(new LLMenuItemCallGL("Slow Motion Animations", &slow_mo_animations, NULL));
+ menu->append(new LLMenuItemToggleGL("Show Look At", &LLHUDEffectLookAt::sDebugLookAt));
+ menu->append(new LLMenuItemToggleGL("Show Point At", &LLHUDEffectPointAt::sDebugPointAt));
+ menu->append(new LLMenuItemToggleGL("Debug Joint Updates", &LLVOAvatar::sJointDebug));
+ menu->append(new LLMenuItemToggleGL("Disable LOD", &LLViewerJoint::sDisableLOD));
+ menu->append(new LLMenuItemToggleGL("Debug Character Vis", &LLVOAvatar::sDebugInvisible));
+ //menu->append(new LLMenuItemToggleGL("Show Attachment Points", &LLVOAvatar::sShowAttachmentPoints));
+ menu->append(new LLMenuItemToggleGL("Show Collision Plane", &LLVOAvatar::sShowFootPlane));
+ menu->append(new LLMenuItemToggleGL("Show Collision Skeleton", &LLVOAvatar::sShowCollisionVolumes));
+ menu->append(new LLMenuItemToggleGL("Software Blending SSE", &gGLManager.mSoftwareBlendSSE));
+#if 0 // Removed since this feature doesn't actually work as of 1.9.1 --TomY
+ menu->append(new LLMenuItemToggleGL("Character Load Test", &LLVOAvatar::sAvatarLoadTest));
+#endif
+ menu->append(new LLMenuItemToggleGL( "Display Agent Target", &LLAgent::sDebugDisplayTarget));
+ menu->append(new LLMenuItemToggleGL( "Debug Rotation", &gDebugAvatarRotation));
+ menu->append(new LLMenuItemCallGL("Dump Attachments", handle_dump_attachments));
+ menu->append(new LLMenuItemCallGL("Rebake Textures", handle_rebake_textures));
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ menu->append(new LLMenuItemCallGL("Debug Avatar Textures", handle_debug_avatar_textures, NULL, NULL, 'A', MASK_SHIFT|MASK_CONTROL|MASK_ALT));
+ menu->append(new LLMenuItemCallGL("Dump Local Textures", handle_dump_avatar_local_textures, NULL, NULL, 'M', MASK_SHIFT|MASK_ALT ));
+#endif
+ menu->createJumpKeys();
+}
+
+void init_debug_baked_texture_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCallGL("Iris", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_EYES_BAKED));
+ menu->append(new LLMenuItemCallGL("Head", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_HEAD_BAKED));
+ menu->append(new LLMenuItemCallGL("Upper Body", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_UPPER_BAKED));
+ menu->append(new LLMenuItemCallGL("Lower Body", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_LOWER_BAKED));
+ menu->append(new LLMenuItemCallGL("Skirt", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_SKIRT_BAKED));
+ menu->createJumpKeys();
+}
+
+void init_server_menu(LLMenuGL* menu)
+{
+ /*
+ {
+ // These messages are now trusted. We can write scripts to do
+ // this, and the message is unchecked for source.
+ LLMenuGL* sub_menu = NULL;
+ sub_menu = new LLMenuGL("Sim Logging");
+
+ sub_menu->append(new LLMenuItemCallGL("Turn off llinfos Log",
+ &handle_reduce_llinfo_log, &enable_god_customer_service));
+
+ sub_menu->append(new LLMenuItemCallGL("Normal Logging",
+ &handle_normal_llinfo_log, &enable_god_customer_service));
+
+ sub_menu->appendSeparator();
+ sub_menu->append(new LLMenuItemCallGL("Enable Circuit Log",
+ &handle_sim_enable_circuit_log, &enable_god_customer_service));
+ sub_menu->append(new LLMenuItemCallGL("Disable Circuit Log",
+ &handle_sim_disable_circuit_log, &enable_god_customer_service));
+ sub_menu->appendSeparator();
+ sub_menu->append(new LLMenuItemCallGL("Enable Message Log",
+ &handle_sim_enable_message_log, &enable_god_customer_service));
+ sub_menu->append(new LLMenuItemCallGL("Disable Message Log",
+ &handle_sim_disable_message_log, &enable_god_customer_service));
+
+ sub_menu->appendSeparator();
+
+ sub_menu->append(new LLMenuItemCallGL("Fetch Message Log",
+ &handle_sim_fetch_message_log, &enable_god_customer_service));
+
+ sub_menu->append(new LLMenuItemCallGL("Fetch Log",
+ &handle_sim_fetch_log, &enable_god_customer_service));
+
+ menu->appendMenu( sub_menu );
+ }
+ */
+
+ {
+ LLMenuGL* sub = new LLMenuGL("Object");
+ menu->appendMenu(sub);
+
+ sub->append(new LLMenuItemCallGL( "Take Copy",
+ &force_take_copy, &enable_god_customer_service, NULL));
+#ifdef _CORY_TESTING
+ sub->append(new LLMenuItemCallGL( "Export Copy",
+ &force_export_copy, NULL, NULL));
+ sub->append(new LLMenuItemCallGL( "Import Geometry",
+ &force_import_geometry, NULL, NULL));
+#endif
+ //sub->append(new LLMenuItemCallGL( "Force Public",
+ // &handle_object_owner_none, NULL, NULL));
+ //sub->append(new LLMenuItemCallGL( "Force Ownership/Permissive",
+ // &handle_object_owner_self_and_permissive, NULL, NULL, 'K', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->append(new LLMenuItemCallGL( "Force Owner To Me",
+ &handle_object_owner_self, &enable_god_customer_service));
+ sub->append(new LLMenuItemCallGL( "Force Owner Permissive",
+ &handle_object_owner_permissive, &enable_god_customer_service));
+ //sub->append(new LLMenuItemCallGL( "Force Totally Permissive",
+ // &handle_object_permissive));
+ sub->append(new LLMenuItemCallGL( "Delete",
+ &handle_force_delete, &enable_god_customer_service, NULL, KEY_DELETE, MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->append(new LLMenuItemCallGL( "Lock",
+ &handle_object_lock, &enable_god_customer_service, NULL, 'L', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->append(new LLMenuItemCallGL( "Get Asset IDs",
+ &handle_object_asset_ids, &enable_god_customer_service, NULL, 'I', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->createJumpKeys();
+ }
+ {
+ LLMenuGL* sub = new LLMenuGL("Parcel");
+ menu->appendMenu(sub);
+
+ sub->append(new LLMenuItemCallGL("Owner To Me",
+ &handle_force_parcel_owner_to_me,
+ &enable_god_customer_service, NULL));
+ sub->append(new LLMenuItemCallGL("Set to Linden Content",
+ &handle_force_parcel_to_content,
+ &enable_god_customer_service, NULL,
+ 'C', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ //sub->append(new LLMenuItemCallGL("Toggle First Land bit",
+ // &handle_toggle_parcel_newbie));
+ sub->appendSeparator();
+ sub->append(new LLMenuItemCallGL("Claim Public Land",
+ &handle_claim_public_land, &enable_god_customer_service));
+
+ sub->createJumpKeys();
+ }
+ {
+ LLMenuGL* sub = new LLMenuGL("Region");
+ menu->appendMenu(sub);
+ sub->append(new LLMenuItemCallGL("Dump Temp Asset Data",
+ &handle_region_dump_temp_asset_data,
+ &enable_god_customer_service, NULL));
+ sub->createJumpKeys();
+ }
+ menu->append(new LLMenuItemCallGL( "God Tools...",
+ &LLFloaterGodTools::show, &enable_god_basic, NULL));
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCallGL("Upload Data File...",
+ &handle_upload_data,
+ &enable_god_customer_service,
+ NULL));
+
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCallGL("Save Region State",
+ &LLPanelRegionTools::onSaveState, &enable_god_customer_service, NULL));
+
+// menu->append(new LLMenuItemCallGL("Force Join Group", handle_force_join_group));
+
+
+
+ menu->appendSeparator();
+//
+// menu->append(new LLMenuItemCallGL( "OverlayTitle",
+// &handle_show_overlay_title, &enable_god_customer_service, NULL));
+
+ menu->append(new LLMenuItemCallGL("Request Admin Status",
+ &handle_god_mode, NULL, NULL, 'G', MASK_ALT | MASK_CONTROL));
+
+ menu->append(new LLMenuItemCallGL("Leave Admin Status",
+ &handle_leave_god_mode, NULL, NULL, 'G', MASK_ALT | MASK_SHIFT | MASK_CONTROL));
+ menu->createJumpKeys();
+}
+
+//-----------------------------------------------------------------------------
+// cleanup_menus()
+//-----------------------------------------------------------------------------
+void cleanup_menus()
+{
+ delete gMenuParcelObserver;
+ gMenuParcelObserver = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Object pie menu
+//-----------------------------------------------------------------------------
+
+class LLObjectRateCreator : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterRate::show(LLFloaterRate::RS_CREATOR);
+ return true;
+ }
+};
+
+class LLObjectRateOwner : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Don't allow rating of group owned objects.
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return true;
+ if (node->mPermissions->isGroupOwned())
+ {
+ gViewerWindow->alertXml("CantRateOwnedByGroup");
+ return true;
+ }
+
+ LLFloaterRate::show(LLFloaterRate::RS_OWNER);
+ return true;
+ }
+};
+
+class LLObjectReportAbuse : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterReporter::showFromObject(gLastHitObjectID);
+ return true;
+ }
+};
+
+// Enable only when you didn't create it, and the creator
+// is not the owner.
+class LLObjectEnableRateCreator : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID creator_id;
+ LLUUID owner_id;
+ LLString dummy;
+ BOOL identical_creator = gSelectMgr->selectGetCreator(creator_id, dummy);
+
+ BOOL new_value;
+ if (!identical_creator)
+ {
+ new_value = FALSE;
+ }
+ else
+ {
+ new_value = (creator_id != gAgent.getID());
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// Enabled if object owner isn't the agent.
+class LLObjectEnableRateOwner : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID owner_id;
+ LLString dummy;
+ BOOL identical_owner = gSelectMgr->selectGetOwner(owner_id, dummy);
+
+ BOOL new_value;
+ if (!identical_owner)
+ {
+ new_value = FALSE;
+ }
+ else if (owner_id.isNull())
+ {
+ new_value = FALSE;
+ }
+ else
+ {
+ new_value = (owner_id != gAgent.getID());
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+// Enabled it you clicked an object
+class LLObjectEnableReportAbuse : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = !gLastHitObjectID.isNull();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+BOOL enable_attach(void*)
+{
+ // All root objects must be owned by agent.
+ BOOL rv = FALSE;
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ if(obj)
+ {
+ rv = TRUE;
+ for(obj = gSelectMgr->getFirstRootObject() ; obj != NULL; obj = gSelectMgr->getNextRootObject())
+ {
+ for (U32 child_num = 0; child_num < obj->mChildList.size(); child_num++ )
+ {
+ LLViewerObject *child = obj->mChildList[child_num];
+ if (child->isAvatar())
+ {
+ return FALSE;
+ }
+ }
+ if(!obj->permMove())
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+
+class LLObjectTouch : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* object = gObjectList.findObject(gLastHitObjectID);
+ if (!object) return true;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ObjectGrab);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast( _PREHASH_ObjectData);
+ msg->addU32Fast( _PREHASH_LocalID, object->mLocalID);
+ msg->addVector3Fast(_PREHASH_GrabOffset, LLVector3::zero );
+ msg->sendMessage( object->getRegion()->getHost());
+
+ // HACK: Hope the packets arrive safely and in order.
+ msg->newMessageFast(_PREHASH_ObjectDeGrab);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_LocalID, object->mLocalID);
+ msg->sendMessage(object->getRegion()->getHost());
+
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+};
+
+
+// One object must have touch sensor
+class LLObjectEnableTouch : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+ bool new_value = obj && obj->flagHandleTouch();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+
+ // HACK Update label
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node && node->mValid && !node->mTouchName.empty())
+ {
+ gMenuHolder->childSetText("Object Touch", node->mTouchName);
+ }
+ else
+ {
+ gMenuHolder->childSetText("Object Touch", userdata["data"].asString());
+ }
+
+ return true;
+ }
+};
+
+void label_touch(LLString& label, void*)
+{
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node && node->mValid && !node->mTouchName.empty())
+ {
+ label.assign(node->mTouchName);
+ }
+ else
+ {
+ label.assign("Touch");
+ }
+}
+
+bool handle_object_open()
+{
+ LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+ if(!obj) return true;
+
+ // transient selection must be made permanent
+ gSelectMgr->convertTransient();
+ LLFloaterOpenObject::show();
+ return true;
+}
+
+class LLObjectOpen : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_object_open();
+ }
+};
+
+class LLObjectEnableOpen : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Look for contents in root object, which is all the LLFloaterOpenObject
+ // understands.
+ LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+ bool new_value = (obj != NULL);
+ if (new_value)
+ {
+ LLViewerObject* root = obj->getRootEdit();
+ if (!root) new_value = false;
+ else new_value = root->allowOpen();
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+class LLViewCheckBuildMode : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gToolMgr->inEdit();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+bool toggle_build_mode()
+{
+ if (gToolMgr->inEdit())
+ {
+ // just reset the view, will pull us out of edit mode
+ handle_reset_view();
+ }
+ else
+ {
+ if (gAgent.getFocusOnAvatar() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gAgent.getPositionGlobal() + 2.0 * LLVector3d(gAgent.getAtAxis()));
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ }
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolCreate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ }
+ return true;
+}
+
+class LLViewBuildMode : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return toggle_build_mode();
+ }
+};
+
+
+class LLObjectBuild : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getFocusOnAvatar() && !gToolMgr->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ gViewerWindow->moveCursorToCenter();
+ }
+ else if ( gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolCreate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ return true;
+ }
+};
+
+class LLObjectEdit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gParcelMgr->deselectLand();
+ // convert transient selections to permanent
+ gSelectMgr->convertTransient();
+
+ if (gAgent.getFocusOnAvatar() && !gToolMgr->inEdit())
+ {
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD || !gSavedSettings.getBOOL("EditCameraMovement"))
+ {
+ // always freeze camera in space, even if camera doesn't move
+ // so, for example, follow cam scripts can't affect you when in build mode
+ gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null);
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ }
+ else
+ {
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ // zoom in on object center instead of where we clicked, as we need to see the manipulator handles
+ gAgent.setFocusGlobal(gLastHitPosGlobal /*+ gLastHitObjectOffset*/, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ gViewerWindow->moveCursorToCenter();
+ }
+ }
+
+ gFloaterTools->open();
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolTranslate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ return true;
+ }
+};
+
+
+//---------------------------------------------------------------------------
+// Land pie menu
+//---------------------------------------------------------------------------
+class LLLandBuild : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gParcelMgr->deselectLand();
+
+ if (gAgent.getFocusOnAvatar() && !gToolMgr->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ gViewerWindow->moveCursorToCenter();
+ }
+ else if ( gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // otherwise just move focus
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gViewerWindow->moveCursorToCenter();
+ }
+
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolCreate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ return true;
+ }
+};
+
+class LLLandBuyPass : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLPanelLandGeneral::onClickBuyPass((void *)FALSE);
+ return true;
+ }
+};
+
+class LLLandEnableBuyPass : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLPanelLandGeneral::enableBuyPass(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// BUG: Should really check if CLICK POINT is in a parcel where you can build.
+BOOL enable_land_build(void*)
+{
+ if (gAgent.isGodlike()) return TRUE;
+ if (gAgent.inPrelude()) return FALSE;
+
+ BOOL can_build = FALSE;
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (agent_parcel)
+ {
+ can_build = agent_parcel->getAllowModify();
+ }
+ return can_build;
+}
+
+// BUG: Should really check if OBJECT is in a parcel where you can build.
+BOOL enable_object_build(void*)
+{
+ if (gAgent.isGodlike()) return TRUE;
+ if (gAgent.inPrelude()) return FALSE;
+
+ BOOL can_build = FALSE;
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (agent_parcel)
+ {
+ can_build = agent_parcel->getAllowModify();
+ }
+ return can_build;
+}
+
+class LLEnableEdit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike() || !gAgent.inPrelude();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLSelfRemoveAllAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLAgent::userRemoveAllAttachments(NULL);
+ return true;
+ }
+};
+
+class LLSelfEnableRemoveAllAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ if (gAgent.getAvatarObject())
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ for (LLViewerJointAttachment* attachmentp = avatarp->mAttachmentPoints.getFirstData();
+ attachmentp;
+ attachmentp = avatarp->mAttachmentPoints.getNextData())
+ {
+ if (attachmentp->getObject(0))
+ {
+ new_value = true;
+ break;
+ }
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_has_attachments(void*)
+{
+
+ return FALSE;
+}
+
+//---------------------------------------------------------------------------
+// Avatar pie menu
+//---------------------------------------------------------------------------
+void handle_follow(void *userdata)
+{
+ // follow a given avatar, ID in gLastHitObjectID
+ gAgent.startFollowPilot(gLastHitObjectID);
+}
+
+class LLObjectEnableMute : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ bool new_value = (object != NULL);
+ if (new_value)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(object);
+ if (avatar)
+ {
+ // It's an avatar
+ LLNameValue *lastname = avatar->getNVPair("LastName");
+ BOOL is_linden = lastname && !LLString::compareStrings(lastname->getString(), "Linden");
+ BOOL is_self = avatar->isSelf();
+ new_value = !is_linden && !is_self;
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLObjectMute : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ if (!object) return true;
+
+ LLUUID id;
+ LLString name;
+ LLMute::EType type;
+ LLVOAvatar* avatar = find_avatar_from_object(object);
+ if (avatar)
+ {
+ id = avatar->getID();
+
+ LLNameValue *firstname = avatar->getNVPair("FirstName");
+ LLNameValue *lastname = avatar->getNVPair("LastName");
+ if (firstname && lastname)
+ {
+ name = firstname->getString();
+ name += " ";
+ name += lastname->getString();
+ }
+
+ type = LLMute::AGENT;
+ }
+ else
+ {
+ // it's an object
+ id = object->getID();
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node)
+ {
+ name = node->mName;
+ }
+
+ type = LLMute::OBJECT;
+ }
+
+ LLMute mute(id, name, type);
+ if (gMuteListp->isMuted(mute.mID, mute.mName))
+ {
+ gMuteListp->remove(mute);
+ }
+ else
+ {
+ gMuteListp->add(mute);
+ gFloaterMute->show();
+ }
+
+ gSelectMgr->deselectAll();
+ return true;
+ }
+};
+
+bool handle_go_to()
+{
+ // JAMESDEBUG try simulator autopilot
+ std::vector<std::string> strings;
+ std::string val;
+ val = llformat("%g", gLastHitPosGlobal.mdV[VX]);
+ strings.push_back(val);
+ val = llformat("%g", gLastHitPosGlobal.mdV[VY]);
+ strings.push_back(val);
+ val = llformat("%g", gLastHitPosGlobal.mdV[VZ]);
+ strings.push_back(val);
+ send_generic_message("autopilot", strings);
+
+ // Don't select anything
+ gSelectMgr->deselectTransient();
+
+ gParcelMgr->deselectLand();
+
+ if (gAgent.getAvatarObject() && !gSavedSettings.getBOOL("AutoPilotLocksCamera"))
+ {
+ gAgent.setFocusGlobal(gAgent.getFocusTargetGlobal(), gAgent.getAvatarObject()->getID());
+ }
+ else
+ {
+ // Snap camera back to behind avatar
+ gAgent.setFocusOnAvatar(TRUE, ANIMATE);
+ }
+
+ // Could be first use
+ LLFirstUse::useGoTo();
+ return true;
+}
+
+class LLGoToObject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_go_to();
+ }
+};
+
+//---------------------------------------------------------------------------
+// Parcel freeze, eject, etc.
+//---------------------------------------------------------------------------
+void callback_freeze(S32 option, void* data)
+{
+ LLUUID* avatar_id = (LLUUID*) data;
+
+ if (0 == option || 1 == option)
+ {
+ U32 flags = 0x0;
+ if (1 == option)
+ {
+ // unfreeze
+ flags |= 0x1;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ LLViewerObject* avatar = gObjectList.findObject(*avatar_id);
+
+ if (avatar)
+ {
+ msg->newMessage("FreezeUser");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("TargetID", *avatar_id );
+ msg->addU32("Flags", flags );
+ msg->sendReliable( avatar->getRegion()->getHost() );
+ }
+ }
+
+ delete avatar_id;
+ avatar_id = NULL;
+}
+
+class LLAvatarFreeze : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ LLUUID* avatar_id = new LLUUID( avatar->getID() );
+
+ gViewerWindow->alertXml("FreezeAvatar",
+ callback_freeze, (void*)avatar_id);
+
+ }
+ return true;
+ }
+};
+
+class LLAvatarVisibleDebug : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLAvatarEnableDebug : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLAvatarDebug : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ avatar->dumpLocalTextures();
+ }
+ llinfos << "Dumping temporary asset data to simulator logs for avatar " << avatar->getID() << llendl;
+ std::vector<std::string> strings;
+ strings.push_back( avatar->getID().getString() );
+ LLUUID invoice;
+ send_generic_message("dumptempassetdata", strings, invoice);
+ LLFloaterAvatarTextures::show( avatar->getID() );
+ return true;
+ }
+};
+
+void callback_eject(S32 option, void* data)
+{
+ LLUUID* avatar_id = (LLUUID*) data;
+
+ if (0 == option || 1 == option)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ LLViewerObject* avatar = gObjectList.findObject(*avatar_id);
+
+ if (avatar)
+ {
+ U32 flags = 0x0;
+ if (1 == option)
+ {
+ // eject and add to ban list
+ flags |= 0x1;
+ }
+
+ msg->newMessage("EjectUser");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID() );
+ msg->nextBlock("Data");
+ msg->addUUID("TargetID", *avatar_id );
+ msg->addU32("Flags", flags );
+ msg->sendReliable( avatar->getRegion()->getHost() );
+ }
+ }
+
+ delete avatar_id;
+ avatar_id = NULL;
+}
+
+class LLAvatarEject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ LLUUID* avatar_id = new LLUUID( avatar->getID() );
+ gViewerWindow->alertXml("EjectAvatar",
+ callback_eject, (void*)avatar_id);
+
+ }
+ return true;
+ }
+};
+
+class LLAvatarEnableFreezeEject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ bool new_value = (avatar != NULL);
+
+ if (new_value)
+ {
+ const LLVector3& pos = avatar->getPositionRegion();
+ LLViewerRegion* region = avatar->getRegion();
+ new_value = (region != NULL);
+
+ if (new_value)
+ {
+ new_value = (region->isOwnedSelf(pos) || region->isOwnedGroup(pos));
+ }
+ }
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLAvatarGiveCard : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ llinfos << "handle_give_card()" << llendl;
+ LLViewerObject* dest = gViewerWindow->lastObjectHit();
+ if(dest && dest->isAvatar())
+ {
+ bool found_name = false;
+ LLString::format_map_t args;
+ LLNameValue* nvfirst = dest->getNVPair("FirstName");
+ LLNameValue* nvlast = dest->getNVPair("LastName");
+ if(nvfirst && nvlast)
+ {
+ args["[FIRST]"] = nvfirst->getString();
+ args["[LAST]"] = nvlast->getString();
+ found_name = true;
+ }
+ LLViewerRegion* region = dest->getRegion();
+ LLHost dest_host;
+ if(region)
+ {
+ dest_host = region->getHost();
+ }
+ if(found_name && dest_host.isOk())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("OfferCallingCard");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_AgentBlock);
+ msg->addUUIDFast(_PREHASH_DestID, dest->getID());
+ LLUUID transaction_id;
+ transaction_id.generate();
+ msg->addUUIDFast(_PREHASH_TransactionID, transaction_id);
+ msg->sendReliable(dest_host);
+ LLNotifyBox::showXml("OfferedCard", args);
+ }
+ else
+ {
+ gViewerWindow->alertXml("CantOfferCallingCard", args);
+ }
+ }
+ return true;
+ }
+};
+
+
+
+void login_done(S32 which, void *user)
+{
+ llinfos << "Login done " << which << llendl;
+
+ LLPanelLogin::close();
+}
+
+
+
+class LLAvatarRate : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ LLFloaterRate::show( avatar->getID() );
+ }
+ return true;
+ }
+};
+
+void callback_leave_group(S32 option, void *userdata)
+{
+ if (option == 0)
+ {
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_LeaveGroupRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.mGroupID );
+ //msg->sendReliable( gUserServer );
+ gAgent.sendReliableMessage();
+ }
+}
+
+void handle_leave_group(void *)
+{
+ if (gAgent.getGroupID() != LLUUID::null)
+ {
+ LLString::format_map_t args;
+ args["[GROUP]"] = gAgent.mGroupName;
+ gViewerWindow->alertXml("GroupLeaveConfirmMember", args, callback_leave_group);
+ }
+}
+
+void append_aggregate(LLString& string, const LLAggregatePermissions& ag_perm, PermissionBit bit, const char* txt)
+{
+ LLAggregatePermissions::EValue val = ag_perm.getValue(bit);
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+ switch(val)
+ {
+ case LLAggregatePermissions::AP_NONE:
+ sprintf(buffer, "* %s None\n", txt);
+ break;
+ case LLAggregatePermissions::AP_SOME:
+ sprintf(buffer, "* %s Some\n", txt);
+ break;
+ case LLAggregatePermissions::AP_ALL:
+ sprintf(buffer, "* %s All\n", txt);
+ break;
+ case LLAggregatePermissions::AP_EMPTY:
+ default:
+ break;
+ }
+ string.append(buffer);
+}
+
+const char* build_extensions_string(LLFilePicker::ELoadFilter filter)
+{
+ switch(filter)
+ {
+#if LL_WINDOWS
+ case LLFilePicker::FFLOAD_IMAGE:
+ return IMAGE_EXTENSIONS;
+ case LLFilePicker::FFLOAD_WAV:
+ return SOUND_EXTENSIONS;
+ case LLFilePicker::FFLOAD_ANIM:
+ return ANIM_EXTENSIONS;
+ case LLFilePicker::FFLOAD_SLOBJECT:
+ return SLOBJECT_EXTENSIONS;
+#ifdef _CORY_TESTING
+ case LLFilePicker::FFLOAD_GEOMETRY:
+ return GEOMETRY_EXTENSIONS;
+#endif
+ case LLFilePicker::FFLOAD_XML:
+ return XML_EXTENSIONS;
+ case LLFilePicker::FFLOAD_ALL:
+ return ALL_FILE_EXTENSIONS;
+#endif
+ default:
+ return ALL_FILE_EXTENSIONS;
+ }
+}
+
+
+BOOL enable_buy(void*)
+{
+ // In order to buy, there must only be 1 purchaseable object in
+ // the selection manger.
+ if(gSelectMgr->getRootObjectCount() != 1) return FALSE;
+ LLViewerObject* obj = NULL;
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(node)
+ {
+ obj = node->getObject();
+ if(!obj) return FALSE;
+
+ if(node->mSaleInfo.isForSale() && node->mPermissions->getMaskOwner() & PERM_TRANSFER &&
+ (node->mPermissions->getMaskOwner() & PERM_COPY || node->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY))
+ {
+ if(obj->permAnyOwner()) return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+class LLObjectEnableBuy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_buy(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// Note: This will only work if the selected object's data has been
+// received by the viewer and cached in the selection manager.
+void handle_buy_object(LLSaleInfo sale_info)
+{
+ if(!gSelectMgr->selectGetAllRootsValid())
+ {
+ LLNotifyBox::showXml("UnableToBuyWhileDownloading");
+ return;
+ }
+
+ LLUUID owner_id;
+ LLString owner_name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+ if (!owners_identical)
+ {
+ LLNotifyBox::showXml("CannotBuyObjectsFromDifferentOwners");
+ return;
+ }
+
+ LLPermissions perm;
+ BOOL valid = gSelectMgr->selectGetPermissions(perm);
+ LLAggregatePermissions ag_perm;
+ valid &= gSelectMgr->selectGetAggregatePermissions(ag_perm);
+ if(!valid || !sale_info.isForSale() || !perm.allowTransferTo(gAgent.getID()))
+ {
+ LLNotifyBox::showXml("ObjectNotForSale");
+ return;
+ }
+
+ if(sale_info.getSalePrice() > gStatusBar->getBalance())
+ {
+ LLFloaterBuyCurrency::buyCurrency(
+ "This object costs", sale_info.getSalePrice());
+ return;
+ }
+
+ LLFloaterBuy::show(sale_info);
+}
+
+
+void handle_buy_contents(LLSaleInfo sale_info)
+{
+ LLFloaterBuyContents::show(sale_info);
+}
+
+class LLFileEnableSaveAs : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gFloaterView->getFrontmost() && gFloaterView->getFrontmost()->canSaveAs();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+void handle_region_dump_temp_asset_data(void*)
+{
+ llinfos << "Dumping temporary asset data to simulator logs" << llendl;
+ std::vector<std::string> strings;
+ LLUUID invoice;
+ send_generic_message("dumptempassetdata", strings, invoice);
+}
+
+void handle_region_clear_temp_asset_data(void*)
+{
+ llinfos << "Clearing temporary asset data" << llendl;
+ std::vector<std::string> strings;
+ LLUUID invoice;
+ send_generic_message("cleartempassetdata", strings, invoice);
+}
+
+void handle_region_dump_settings(void*)
+{
+ LLViewerRegion* regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ llinfos << "Damage: " << (regionp->getAllowDamage() ? "on" : "off") << llendl;
+ llinfos << "Landmark: " << (regionp->getAllowLandmark() ? "on" : "off") << llendl;
+ llinfos << "SetHome: " << (regionp->getAllowSetHome() ? "on" : "off") << llendl;
+ llinfos << "ResetHome: " << (regionp->getResetHomeOnTeleport() ? "on" : "off") << llendl;
+ llinfos << "SunFixed: " << (regionp->getSunFixed() ? "on" : "off") << llendl;
+ llinfos << "BlockFly: " << (regionp->getBlockFly() ? "on" : "off") << llendl;
+ llinfos << "AllowP2P: " << (regionp->getAllowDirectTeleport() ? "on" : "off") << llendl;
+ llinfos << "Water: " << (regionp->getWaterHeight()) << llendl;
+ }
+}
+
+void handle_dump_group_info(void *)
+{
+ llinfos << "group " << gAgent.mGroupName << llendl;
+ llinfos << "ID " << gAgent.mGroupID << llendl;
+ llinfos << "powers " << gAgent.mGroupPowers << llendl;
+ llinfos << "title " << gAgent.mGroupTitle << llendl;
+ //llinfos << "insig " << gAgent.mGroupInsigniaID << llendl;
+}
+
+
+void handle_dump_focus(void *)
+{
+ LLView *view = gFocusMgr.getKeyboardFocus();
+
+ llinfos << "Keyboard focus " << (view ? view->getName() : "(none)") << llendl;
+}
+
+class LLSelfStandUp : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ return true;
+ }
+};
+
+class LLSelfEnableStandUp : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.getAvatarObject() && gAgent.getAvatarObject()->mIsSitting;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL check_admin_override(void*)
+{
+ return gAgent.getAdminOverride();
+}
+
+void handle_admin_override_toggle(void*)
+{
+ if(!gAgent.getAdminOverride())
+ {
+ gAgent.setAdminOverride(TRUE);
+ show_debug_menus();
+ }
+ else gAgent.setAdminOverride(FALSE);
+}
+
+void handle_god_mode(void*)
+{
+ gAgent.requestEnterGodMode();
+}
+
+void handle_leave_god_mode(void*)
+{
+ gAgent.requestLeaveGodMode();
+}
+
+void set_god_level(U8 god_level)
+{
+ U8 old_god_level = gAgent.getGodLevel();
+ gAgent.setGodLevel( god_level );
+ show_debug_menus();
+ gIMView->refresh();
+ gParcelMgr->notifyObservers();
+
+ // Some classifieds change visibility on god mode
+ LLFloaterDirectory::requestClassified();
+
+ // God mode changes sim visibility
+ gWorldMap->reset();
+ gWorldMap->setCurrentLayer(0);
+
+ // inventory in items may change in god mode
+ gObjectList.dirtyAllObjectInventory();
+
+ LLString::format_map_t args;
+ if(god_level > GOD_NOT)
+ {
+ args["[LEVEL]"] = llformat("%d",(S32)god_level);
+ if (gInProductionGrid)
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuBarGodBgColor" ) );
+ }
+ else
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuNonProductionGodBgColor" ) );
+ }
+ LLNotifyBox::showXml("EnteringGodMode", args);
+ }
+ else
+ {
+ args["[LEVEL]"] = llformat("%d",(S32)old_god_level);
+ if (gInProductionGrid)
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuBarBgColor" ) );
+ }
+ else
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuNonProductionBgColor" ) );
+ }
+ LLNotifyBox::showXml("LeavingGodMode", args);
+ }
+}
+
+#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+void handle_toggle_hacked_godmode(void*)
+{
+ gHackGodmode = !gHackGodmode;
+ set_god_level(gHackGodmode ? GOD_MAINTENANCE : GOD_NOT);
+}
+
+BOOL check_toggle_hacked_godmode(void*)
+{
+ return gHackGodmode;
+}
+#endif
+
+void process_grant_godlike_powers(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if((agent_id == gAgent.getID()) && (session_id == gAgent.getSessionID()))
+ {
+ U8 god_level;
+ msg->getU8Fast(_PREHASH_GrantData, _PREHASH_GodLevel, god_level);
+ set_god_level(god_level);
+ }
+ else
+ {
+ llwarns << "Grant godlike for wrong agent " << agent_id << llendl;
+ }
+}
+
+void load_url_local_file(const char* file_name)
+{
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+#if LL_DARWIN || LL_LINUX
+ // MBW -- If the Mac client is in fullscreen mode, it needs to go windowed so the browser will be visible.
+ if(gViewerWindow->mWindow->getFullscreen())
+ {
+ gViewerWindow->toggleFullscreen(TRUE);
+ }
+#endif
+
+ // JC - system() blocks until IE has launched.
+ // spawn() runs asynchronously, but opens a command prompt.
+ // ShellExecute() just opens the damn file with the default
+ // web browser.
+ std::string full_path = "file:///";
+ full_path.append(gDirUtilp->getAppRODataDir());
+ full_path.append(gDirUtilp->getDirDelimiter());
+ full_path.append(file_name);
+
+ LLWeb::loadURL(full_path.c_str());
+}
+
+/*
+class LLHaveCallingcard : public LLInventoryCollectFunctor
+{
+public:
+ LLHaveCallingcard(const LLUUID& agent_id);
+ virtual ~LLHaveCallingcard() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+ BOOL isThere() const { return mIsThere;}
+protected:
+ LLUUID mID;
+ BOOL mIsThere;
+};
+
+LLHaveCallingcard::LLHaveCallingcard(const LLUUID& agent_id) :
+ mID(agent_id),
+ mIsThere(FALSE)
+{
+}
+
+bool LLHaveCallingcard::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((item->getType() == LLAssetType::AT_CALLINGCARD)
+ && (item->getCreatorUUID() == mID))
+ {
+ mIsThere = TRUE;
+ }
+ }
+ return FALSE;
+}
+*/
+
+BOOL is_agent_friend(const LLUUID& agent_id)
+{
+ return (LLAvatarTracker::instance().getBuddyInfo(agent_id) != NULL);
+}
+
+BOOL is_agent_mappable(const LLUUID& agent_id)
+{
+ return (is_agent_friend(agent_id) &&
+ LLAvatarTracker::instance().getBuddyInfo(agent_id)->isOnline() &&
+ LLAvatarTracker::instance().getBuddyInfo(agent_id)->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)
+ );
+}
+
+// Enable a menu item when you have someone's card.
+/*
+BOOL enable_have_card(void *userdata)
+{
+ LLUUID* avatar_id = (LLUUID *)userdata;
+ if (gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+ else if(avatar_id)
+ {
+ return is_agent_friend(*avatar_id);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+*/
+
+// Enable a menu item when you don't have someone's card.
+class LLAvatarEnableAddFriend : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(gViewerWindow->lastObjectHit());
+ bool new_value = avatar && !is_agent_friend(avatar->getID());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+void request_friendship(const LLUUID& dest_id)
+{
+ LLViewerObject* dest = gObjectList.findObject(dest_id);
+ if(dest && dest->isAvatar())
+ {
+ LLString fullname;
+ LLString::format_map_t args;
+ LLNameValue* nvfirst = dest->getNVPair("FirstName");
+ LLNameValue* nvlast = dest->getNVPair("LastName");
+ if(nvfirst && nvlast)
+ {
+ args["[FIRST]"] = nvfirst->getString();
+ args["[LAST]"] = nvlast->getString();
+ fullname = nvfirst->getString();
+ fullname += " ";
+ fullname += nvlast->getString();
+ }
+ if (!fullname.empty())
+ {
+ LLFloaterFriends::requestFriendship(dest_id, fullname);
+ LLNotifyBox::showXml("OfferedFriendship", args);
+ }
+ else
+ {
+ gViewerWindow->alertXml("CantOfferFriendship");
+ }
+ }
+}
+
+
+class LLEditEnableCustomizeAvatar : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.getWearablesLoaded();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+bool handle_sit_or_stand()
+{
+ LLViewerObject *object = gObjectList.findObject(gLastHitNonFloraObjectID);
+ if (!object)
+ {
+ return true;
+ }
+
+ if (sitting_on_selection())
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+
+ // get object selection offset
+
+ if (object && object->getPCode() == LL_PCODE_VOLUME)
+ {
+ LLVector3d offset_double = gViewerWindow->lastNonFloraObjectHitOffset();
+ LLVector3 offset_single;
+ offset_single.setVec(offset_double);
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
+ gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID);
+ gMessageSystem->addVector3Fast(_PREHASH_Offset, offset_single);
+
+ object->getRegion()->sendReliableMessage();
+
+ gSelectMgr->deselectTransient();
+ }
+ return true;
+}
+
+class LLObjectSitOrStand : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_sit_or_stand();
+ }
+};
+
+void near_sit_down_point(BOOL success, void *)
+{
+ gAgent.setFlying(FALSE);
+ gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+
+ // Might be first sit
+ LLFirstUse::useSit();
+}
+
+class LLLandSit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ gParcelMgr->deselectLand();
+
+ LLVector3d posGlobal = gLastHitPosGlobal;
+
+ LLQuaternion target_rot;
+ if (gAgent.getAvatarObject())
+ {
+ target_rot = gAgent.getAvatarObject()->getRotation();
+ }
+ else
+ {
+ target_rot = gAgent.getFrameAgent().getQuaternion();
+ }
+ gAgent.startAutoPilotGlobal(posGlobal, "Sit", &target_rot, near_sit_down_point, NULL, 0.7f);
+ return true;
+ }
+};
+
+void show_permissions_control(void*)
+{
+ LLFloaterPermissionsMgr* floaterp = LLFloaterPermissionsMgr::show();
+ floaterp->mPermissions->addPermissionsData("foo1", LLUUID::null, 0);
+ floaterp->mPermissions->addPermissionsData("foo2", LLUUID::null, 0);
+ floaterp->mPermissions->addPermissionsData("foo3", LLUUID::null, 0);
+}
+
+#if 0 // Unused (these just modify AudioInfoPage which is not used anywhere in the code
+void handle_audio_status_1(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (1 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 1;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+
+void handle_audio_status_2(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (2 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 2;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+
+void handle_audio_status_3(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (3 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 3;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+
+void handle_audio_status_4(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (4 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 4;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+#endif
+
+void reload_ui(void *)
+{
+ gUICtrlFactory->rebuild();
+}
+
+class LLWorldFly : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.toggleFlying();
+ return true;
+ }
+};
+
+void handle_agent_stop_moving(void*)
+{
+ // stop agent
+ gAgent.setControlFlags(AGENT_CONTROL_STOP);
+
+ // cancel autopilot
+ gAgent.stopAutoPilot();
+}
+
+void print_packets_lost(void*)
+{
+ gWorldPointer->printPacketsLost();
+}
+
+
+void drop_packet(void*)
+{
+ gMessageSystem->mPacketRing.dropPackets(1);
+}
+
+
+void velocity_interpolate( void* data )
+{
+ BOOL toggle = gSavedSettings.getBOOL("VelocityInterpolate");
+ LLMessageSystem* msg = gMessageSystem;
+ if ( !toggle )
+ {
+ msg->newMessageFast(_PREHASH_VelocityInterpolateOn);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ llinfos << "Velocity Interpolation On" << llendl;
+ }
+ else
+ {
+ msg->newMessageFast(_PREHASH_VelocityInterpolateOff);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ llinfos << "Velocity Interpolation Off" << llendl;
+ }
+ // BUG this is a hack because of the change in menu behavior. The
+ // old menu system would automatically change a control's value,
+ // but the new LLMenuGL system doesn't know what a control
+ // is. However, it's easy to distinguish between the two callers
+ // because LLMenuGL passes in the name of the user data (the
+ // control name) to the callback function, and the user data goes
+ // unused in the old menu code. Thus, if data is not null, then we
+ // need to swap the value of the control.
+ if( data )
+ {
+ gSavedSettings.setBOOL( static_cast<char*>(data), !toggle );
+ }
+}
+
+
+void update_fov(S32 increments)
+{
+ F32 old_fov = gCamera->getDefaultFOV();
+ // for each increment, FoV is 20% bigger
+ F32 new_fov = old_fov * pow(1.2f, increments);
+
+ // cap the FoV
+ new_fov = llclamp(new_fov, MIN_FIELD_OF_VIEW, MAX_FIELD_OF_VIEW);
+
+ if (new_fov != old_fov)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentFOV);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
+
+ msg->nextBlockFast(_PREHASH_FOVBlock);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+ msg->addF32Fast(_PREHASH_VerticalAngle, new_fov);
+
+ gAgent.sendReliableMessage();
+
+ // force agent to update dirty patches
+ gCamera->setDefaultFOV(new_fov);
+ gCamera->setView(new_fov);
+ }
+}
+
+class LLViewZoomOut : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ update_fov(1);
+ return true;
+ }
+};
+
+class LLViewZoomIn : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ update_fov(-1);
+ return true;
+ }
+};
+
+class LLViewZoomDefault : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ F32 old_fov = gCamera->getView();
+ // for each increment, FoV is 20% bigger
+ F32 new_fov = DEFAULT_FIELD_OF_VIEW;
+
+ if (new_fov != old_fov)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentFOV);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
+ msg->nextBlockFast(_PREHASH_FOVBlock);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+ msg->addF32Fast(_PREHASH_VerticalAngle, new_fov);
+
+ gAgent.sendReliableMessage();
+
+ // force agent to update dirty patches
+ gCamera->setDefaultFOV(new_fov);
+ gCamera->setView(new_fov);
+ }
+ return true;
+ }
+};
+
+
+
+void toggle_wind_audio(void)
+{
+ if (gAudiop)
+ {
+ gAudiop->enableWind(!(gAudiop->isWindEnabled()));
+ }
+}
+
+
+// Callback for enablement
+BOOL is_inventory_visible( void* user_data )
+{
+ LLInventoryView* iv = reinterpret_cast<LLInventoryView*>(user_data);
+ if( iv )
+ {
+ return iv->getVisible();
+ }
+ return FALSE;
+}
+
+void handle_show_newest_map(void*)
+{
+ LLFloaterWorldMap::show(NULL, FALSE);
+}
+
+//-------------------------------------------------------------------
+// Help menu functions
+//-------------------------------------------------------------------
+
+class LLHelpMOTD : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString::format_map_t args;
+ args["[MOTD]"] = gAgent.mMOTD;
+ gViewerWindow->alertXml("MOTD", args, NULL, NULL);
+ return true;
+ }
+};
+
+class LLHelpLiveHelp : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // the session_id of a 911 session will always be this agent's session id
+ static LLUUID session_id(LLUUID::null);
+ if (session_id.isNull())
+ {
+ session_id.generate();
+ }
+ gIMView->setFloaterOpen(TRUE);
+ LLDynamicArray<LLUUID> members;
+ members.put(gAgent.getID());
+ gIMView->addSession("Help Request", IM_SESSION_911_START, session_id, members); //xui: translate
+ return true;
+ }
+};
+
+//
+// Major mode switching
+//
+void reset_view_final( BOOL proceed, void* );
+
+void handle_reset_view()
+{
+ if( (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode()) && gFloaterCustomize )
+ {
+ // Show dialog box if needed.
+ gFloaterCustomize->askToSaveAllIfDirty( reset_view_final, NULL );
+ }
+ else
+ {
+ reset_view_final( TRUE, NULL );
+ }
+}
+
+class LLViewResetView : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_reset_view();
+ return true;
+ }
+};
+
+// Note: extra parameters allow this function to be called from dialog.
+void reset_view_final( BOOL proceed, void* )
+{
+ if( !proceed )
+ {
+ return;
+ }
+
+ gAgent.changeCameraToDefault();
+
+ gAgent.resetView(!gFloaterTools->getVisible());
+
+ gViewerWindow->showCursor();
+
+ // Switch back to basic toolset
+ gCurrentToolset = gBasicToolset;
+ gBasicToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gBasicToolset );
+}
+
+class LLViewLookAtLastChatter : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.lookAtLastChat();
+ return true;
+ }
+};
+
+class LLViewMouselook : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (!gAgent.cameraMouselook())
+ {
+ gAgent.changeCameraToMouselook();
+ }
+ else
+ {
+ gAgent.changeCameraToDefault();
+ }
+ return true;
+ }
+};
+
+class LLViewFullscreen : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gViewerWindow->toggleFullscreen(TRUE);
+ return true;
+ }
+};
+
+class LLViewDefaultUISize : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gSavedSettings.setF32("UIScaleFactor", 1.0f);
+ gSavedSettings.setBOOL("UIAutoScale", FALSE);
+ gViewerWindow->reshape(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight());
+ return true;
+ }
+};
+
+class LLEditDuplicate : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(gEditMenuHandler)
+ {
+ gEditMenuHandler->duplicate();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableDuplicate : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canDuplicate();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+void disabled_duplicate(void*)
+{
+ if (gSelectMgr->getFirstObject())
+ {
+ LLNotifyBox::showXml("CopyFailed");
+ }
+}
+
+void handle_duplicate_in_place(void*)
+{
+ llinfos << "handle_duplicate_in_place" << llendl;
+
+ LLVector3 offset(0.f, 0.f, 0.f);
+ gSelectMgr->selectDuplicate(offset, TRUE);
+}
+
+void handle_repeat_duplicate(void*)
+{
+ gSelectMgr->repeatDuplicate();
+}
+
+void handle_deed_object_to_group(void*)
+{
+ LLUUID group_id;
+
+ gSelectMgr->selectGetGroup(group_id);
+ gSelectMgr->sendOwner(LLUUID::null, group_id, FALSE);
+ gViewerStats->incStat(LLViewerStats::ST_RELEASE_COUNT);
+}
+
+BOOL enable_deed_object_to_group(void*)
+{
+ if(gSelectMgr->isEmpty()) return FALSE;
+ LLPermissions perm;
+ LLUUID group_id;
+
+ if (gSelectMgr->selectGetGroup(group_id) &&
+ gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) &&
+ gSelectMgr->selectGetPermissions(perm) &&
+ perm.deedToGroup(gAgent.getID(), group_id))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ * No longer able to support viewer side manipulations in this way
+ *
+void god_force_inv_owner_permissive(LLViewerObject* object,
+ InventoryObjectList* inventory,
+ S32 serial_num,
+ void*)
+{
+ typedef std::vector<LLPointer<LLViewerInventoryItem> > item_array_t;
+ item_array_t items;
+
+ InventoryObjectList::const_iterator inv_it = inventory->begin();
+ InventoryObjectList::const_iterator inv_end = inventory->end();
+ for ( ; inv_it != inv_end; ++inv_it)
+ {
+ if(((*inv_it)->getType() != LLAssetType::AT_CATEGORY)
+ && ((*inv_it)->getType() != LLAssetType::AT_ROOT_CATEGORY))
+ {
+ LLInventoryObject* obj = *inv_it;
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem((LLViewerInventoryItem*)obj);
+ LLPermissions perm(new_item->getPermissions());
+ perm.setMaskBase(PERM_ALL);
+ perm.setMaskOwner(PERM_ALL);
+ new_item->setPermissions(perm);
+ items.push_back(new_item);
+ }
+ }
+ item_array_t::iterator end = items.end();
+ item_array_t::iterator it;
+ for(it = items.begin(); it != end; ++it)
+ {
+ // since we have the inventory item in the callback, it should not
+ // invalidate iteration through the selection manager.
+ object->updateInventory((*it), TASK_INVENTORY_ITEM_KEY, false);
+ }
+}
+*/
+
+void handle_object_owner_permissive(void*)
+{
+ // only send this if they're a god.
+ if(gAgent.isGodlike())
+ {
+ // do the objects.
+ gSelectMgr->setObjectPermissions(PERM_BASE, TRUE, PERM_ALL, TRUE);
+ gSelectMgr->setObjectPermissions(PERM_OWNER, TRUE, PERM_ALL, TRUE);
+ }
+}
+
+void handle_object_owner_self(void*)
+{
+ // only send this if they're a god.
+ if(gAgent.isGodlike())
+ {
+ gSelectMgr->sendOwner(gAgent.getID(), gAgent.getGroupID(), TRUE);
+ }
+}
+
+// Shortcut to set owner permissions to not editable.
+void handle_object_lock(void*)
+{
+ gSelectMgr->setObjectPermissions(PERM_OWNER, FALSE, PERM_MODIFY);
+}
+
+void handle_object_asset_ids(void*)
+{
+ // only send this if they're a god.
+ if (gAgent.isGodlike())
+ {
+ gSelectMgr->sendGodlikeRequest("objectinfo", "assetids");
+ }
+}
+
+void handle_force_parcel_owner_to_me(void*)
+{
+ gParcelMgr->sendParcelGodForceOwner( gAgent.getID() );
+}
+
+void handle_force_parcel_to_content(void*)
+{
+ gParcelMgr->sendParcelGodForceToContent();
+}
+
+void handle_claim_public_land(void*)
+{
+ if (gParcelMgr->getSelectionRegion() != gAgent.getRegion())
+ {
+ LLNotifyBox::showXml("ClaimPublicLand");
+ return;
+ }
+
+ LLVector3d west_south_global;
+ LLVector3d east_north_global;
+ gParcelMgr->getSelection(west_south_global, east_north_global);
+ LLVector3 west_south = gAgent.getPosAgentFromGlobal(west_south_global);
+ LLVector3 east_north = gAgent.getPosAgentFromGlobal(east_north_global);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GodlikeMessage");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "claimpublicland");
+ msg->addUUID("Invoice", LLUUID::null);
+ char buffer[32];
+ sprintf(buffer, "%f", west_south.mV[VX]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ sprintf(buffer, "%f", west_south.mV[VY]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ sprintf(buffer, "%f", east_north.mV[VX]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ sprintf(buffer, "%f", east_north.mV[VY]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ gAgent.sendReliableMessage();
+}
+
+//void handle_toggle_parcel_newbie(void*)
+//{
+// gParcelMgr->toggleParcelGodReserveForNewbie();
+//}
+
+void on_expunge_user(S32 option, const LLString& text, void*)
+{
+ if(option == -1) return;
+ llinfos << "on_expunge_user(" << option << "," << text << ")" << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ LLUUID user_id;
+ if(user_id.set(text))
+ {
+ msg->newMessage("GodExpungeUser");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ExpungeData");
+ msg->addUUID("AgentID", user_id);
+ msg->sendReliable(gUserServer);
+ }
+ else
+ {
+ gViewerWindow->alertXml("InvalidUUID");
+ }
+}
+
+void handle_god_expunge_user(void*)
+{
+ gViewerWindow->alertXmlEditText("ExpungeUser", LLString::format_map_t(),
+ NULL, NULL,
+ on_expunge_user, NULL);
+}
+
+void handle_god_request_havok(void *)
+{
+ if (gAgent.isGodlike())
+ {
+ gSelectMgr->sendGodlikeRequest("havok", "infoverbose");
+ }
+}
+
+//void handle_god_request_foo(void *)
+//{
+// if (gAgent.isGodlike())
+// {
+// gSelectMgr->sendGodlikeRequest(GOD_WANTS_FOO);
+// }
+//}
+
+//void handle_god_request_terrain_save(void *)
+//{
+// if (gAgent.isGodlike())
+// {
+// gSelectMgr->sendGodlikeRequest("terrain", "save");
+// }
+//}
+
+//void handle_god_request_terrain_load(void *)
+//{
+// if (gAgent.isGodlike())
+// {
+// gSelectMgr->sendGodlikeRequest("terrain", "load");
+// }
+//}
+
+
+// HACK for easily testing new avatar geometry
+void handle_god_request_avatar_geometry(void *)
+{
+ if (gAgent.isGodlike())
+ {
+ gSelectMgr->sendGodlikeRequest("avatar toggle", NULL);
+ }
+}
+
+
+void handle_show_overlay_title(void*)
+{
+ gShowOverlayTitle = !gShowOverlayTitle;
+ gSavedSettings.setBOOL("ShowOverlayTitle", gShowOverlayTitle);
+}
+
+void derez_objects(EDeRezDestination dest, const LLUUID& dest_id)
+{
+ if(gAgent.cameraMouselook())
+ {
+ gAgent.changeCameraToDefault();
+ }
+ //gInventoryView->setPanelOpen(TRUE);
+
+ LLViewerObject* object = NULL;
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(!node) return;
+ object = node->getObject();
+ if(!object) return;
+ LLViewerRegion* region = object->getRegion();
+ char* error = NULL;
+
+ // Check conditions that we can't deal with, building a list of
+ // everything that we'll actually be derezzing.
+ LLDynamicArray<LLViewerObject*> derez_objects;
+ BOOL can_derez_current;
+ for( ; node != NULL; node = gSelectMgr->getNextRootNode())
+ {
+ object = node->getObject();
+ if(!object || !node->mValid) continue;
+ if(object->getRegion() != region)
+ {
+ // *FIX: This doesn't work at all if the some of the
+ // objects are in regions besides the first object
+ // selected. We should really support this.
+
+ // ...crosses region boundaries
+ error = "AcquireErrorObjectSpan";
+ break;
+ }
+ if (object->isAvatar())
+ {
+ // ...don't acquire avatars
+ continue;
+ }
+
+ // If AssetContainers are being sent back, they will appear as
+ // boxes in the owner's inventory.
+ if (object->getNVPair("AssetContainer")
+ && dest != DRD_RETURN_TO_OWNER)
+ {
+ // this object is an asset container, derez its contents, not it
+ llwarns << "Attempt to derez deprecated AssetContainer object type not supported." << llendl;
+ /*
+ object->requestInventory(container_inventory_arrived,
+ (void *)(BOOL)(DRD_TAKE_INTO_AGENT_INVENTORY == dest));
+ */
+ continue;
+ }
+ can_derez_current = FALSE;
+ switch(dest)
+ {
+ case DRD_TAKE_INTO_AGENT_INVENTORY:
+ case DRD_TRASH:
+ if( (node->mPermissions->allowTransferTo(gAgent.getID()) && object->permModify())
+ || (node->allowOperationOnNode(PERM_OWNER, GP_OBJECT_MANIPULATE)) )
+ {
+ can_derez_current = TRUE;
+ }
+ break;
+
+ case DRD_RETURN_TO_OWNER:
+ can_derez_current = TRUE;
+ break;
+
+ default:
+ if((node->mPermissions->allowTransferTo(gAgent.getID())
+ && object->permCopy())
+ || gAgent.isGodlike())
+ {
+ can_derez_current = TRUE;
+ }
+ break;
+ }
+ if(can_derez_current)
+ {
+ derez_objects.put(object);
+ }
+ }
+
+ // This constant is based on (1200 - HEADER_SIZE) / 4 bytes per
+ // root. I lopped off a few (33) to provide a bit
+ // pad. HEADER_SIZE is currently 67 bytes, most of which is UUIDs.
+ // This gives us a maximum of 63500 root objects - which should
+ // satisfy anybody.
+ const S32 MAX_ROOTS_PER_PACKET = 250;
+ const S32 MAX_PACKET_COUNT = 254;
+ F32 packets = ceil((F32)derez_objects.count() / (F32)MAX_ROOTS_PER_PACKET);
+ if(packets > (F32)MAX_PACKET_COUNT)
+ {
+ error = "AcquireErrorTooManyObjects";
+ }
+
+ if(!error && derez_objects.count() > 0)
+ {
+ U8 d = (U8)dest;
+ LLUUID tid;
+ tid.generate();
+ U8 packet_count = (U8)packets;
+ S32 object_index = 0;
+ S32 objects_in_packet = 0;
+ LLMessageSystem* msg = gMessageSystem;
+ for(U8 packet_number = 0;
+ packet_number < packet_count;
+ ++packet_number)
+ {
+ msg->newMessageFast(_PREHASH_DeRezObject);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_AgentBlock);
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ msg->addU8Fast(_PREHASH_Destination, d);
+ msg->addUUIDFast(_PREHASH_DestinationID, dest_id);
+ msg->addUUIDFast(_PREHASH_TransactionID, tid);
+ msg->addU8Fast(_PREHASH_PacketCount, packet_count);
+ msg->addU8Fast(_PREHASH_PacketNumber, packet_number);
+ objects_in_packet = 0;
+ while((object_index < derez_objects.count())
+ && (objects_in_packet++ < MAX_ROOTS_PER_PACKET))
+
+ {
+ object = derez_objects.get(object_index++);
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID());
+ // VEFFECT: DerezObject
+ LLHUDEffectSpiral* effectp = (LLHUDEffectSpiral*)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(object->getPositionGlobal());
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+ msg->sendReliable(region->getHost());
+ }
+ make_ui_sound("UISndObjectRezOut");
+
+ // Busy count decremented by inventory update, so only increment
+ // if will be causing an update.
+ if (dest != DRD_RETURN_TO_OWNER)
+ {
+ gViewerWindow->getWindow()->incBusyCount();
+ }
+ }
+ else if(error)
+ {
+ gViewerWindow->alertXml(error);
+ }
+}
+
+class LLToolsTakeCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr->isEmpty()) return true;
+
+ const LLUUID& category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+ derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id);
+
+ // Only deselect if we're not building
+ if (!gToolMgr->inEdit())
+ {
+ gSelectMgr->deselectTransient();
+ }
+ return true;
+ }
+};
+
+
+void callback_return_to_owner(S32 option, void* data)
+{
+ if (0 == option)
+ {
+ // Ignore category ID for this derez destination.
+ derez_objects(DRD_RETURN_TO_OWNER, LLUUID::null);
+ }
+}
+
+// You can return an object to its owner if it is on your land.
+class LLObjectReturn : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr->isEmpty()) return true;
+
+ gViewerWindow->alertXml("ReturnToOwner",
+ callback_return_to_owner,
+ NULL);
+ return true;
+ }
+};
+
+
+// Allow return to owner if one or more of the selected items is
+// over land you own.
+class LLObjectEnableReturn : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ bool new_value = true;
+#else
+ bool new_value = false;
+ if (gAgent.isGodlike())
+ {
+ new_value = true;
+ }
+ else
+ {
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ // Estate owners and managers can always return objects.
+ if (region->canManageEstate())
+ {
+ new_value = true;
+ }
+ else
+ {
+ LLViewerObject* obj = NULL;
+ for(obj = gSelectMgr->getFirstRootObject();
+ obj;
+ obj = gSelectMgr->getNextRootObject())
+ {
+ if (obj->isOverAgentOwnedLand()
+ || obj->isOverGroupOwnedLand()
+ || obj->permModify())
+ {
+ new_value = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+#endif
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+void force_take_copy(void*)
+{
+ if (gSelectMgr->isEmpty()) return;
+ const LLUUID& category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+ derez_objects(DRD_FORCE_TO_GOD_INVENTORY, category_id);
+
+ // Only deselect if we're not building
+ if (!gToolMgr->inEdit())
+ {
+ gSelectMgr->deselectTransient();
+ }
+}
+#ifdef _CORY_TESTING
+
+void force_export_copy(void*)
+{
+ LLViewerObject* object = NULL;
+ LLSelectNode* node = gSelectMgr->getFirstNode();
+ if(!node) return;
+ object = node->getObject();
+ if(!object) return;
+
+
+ LLString proposed_name;
+ proposed_name.append(node->mName);
+ proposed_name.append( ".slg" );
+
+ LLViewerRegion* region = object->getRegion();
+
+ // Check conditions that we can't deal with, building a list of
+ // everything that we'll actually be derezzing.
+
+ std::vector<LLViewerObject*> export_objects;
+ std::vector<std::string> export_names;
+ std::vector<std::string> export_descriptions;
+
+ S32 object_index = 0;
+
+ for( ; node != NULL; node = gSelectMgr->getNextNode())
+ {
+ object = node->getObject();
+ if(!object || !node->mValid)
+ {
+ // Clicked cancel
+ return;
+ }
+ if(object->getRegion() != region)
+ {
+ // Clicked cancel
+ return;
+ }
+ if (object->isAvatar())
+ {
+ continue;
+ }
+
+ if (object->getNVPair("AssetContainer"))
+ {
+ continue;
+ }
+ export_objects.push_back(node->getObject());
+ export_names.push_back(node->mName);
+ export_descriptions.push_back(node->mDescription);
+ }
+
+ if (export_objects.empty())
+ {
+ return;
+ }
+
+ // pick a save file
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_GEOMETRY, proposed_name))
+ {
+ // Clicked cancel
+ return;
+ }
+
+ // Copy the directory + file name
+ char filepath[LL_MAX_PATH];
+ strcpy(filepath, picker.getFirstFile());
+
+ apr_file_t* fp = ll_apr_file_open(filepath, LL_APR_W);
+
+ if (!fp)
+ {
+ return;
+ }
+
+ object = export_objects[object_index];
+ LLVector3 baseoffset = object->getPositionRegion();
+
+ apr_file_printf(fp, "<?xml version=\"1.0\" encoding=\"US-ASCII\" standalone=\"yes\"?>\n");
+ apr_file_printf(fp, "<LindenGeometry>\n");
+
+ while(object_index < export_objects.size())
+ {
+ apr_file_printf(fp, "<Object\n");
+ apr_file_printf(fp, "\tShape='%s'\n", export_names[object_index].c_str());
+ apr_file_printf(fp, "\tDescription='%s'\n", export_descriptions[object_index].c_str());
+
+ apr_file_printf(fp, "\tPCode='%d'\n", (U32)object->getPCode());
+ apr_file_printf(fp, "\tMaterial='%d'\n", object->getMaterial());
+ apr_file_printf(fp, "\tScale='%5f %5f %5f'\n", object->getScale().mV[VX], object->getScale().mV[VY], object->getScale().mV[VZ]);
+ LLVector3 delta = object->getPositionRegion() - baseoffset;
+ LLQuaternion rot = object->getRotationRegion();
+ apr_file_printf(fp, "\tOffset='%5f %5f %5f'\n", delta.mV[VX], delta.mV[VY], delta.mV[VZ]);
+ apr_file_printf(fp, "\tOrientation='%5f %5f %5f %5f'\n", rot.mQ[VX], rot.mQ[VY], rot.mQ[VZ], rot.mQ[VS]);
+ const LLProfileParams pparams = object->getVolume()->getProfile().mParams;
+ apr_file_printf(fp, "\tShapeProfile='%d %f %f %f'\n", pparams.getCurveType(), pparams.getBegin(), pparams.getEnd(), pparams.getHollow());
+ const LLPathParams paparams = object->getVolume()->getPath().mParams;
+ apr_file_printf(fp, "\tShapePath='%d %f %f %f %f %f %f %f %f %f %f %f %f %f'\n",
+ paparams.getCurveType(), paparams.getBegin(), paparams.getEnd(), paparams.getTwist(), paparams.getTwistBegin(), paparams.getScaleX(), paparams.getScaleY(),
+ paparams.getShearX(), paparams.getShearY(), paparams.getRadiusOffset(), paparams.getTaperX(), paparams.getTaperY(),
+ paparams.getRevolutions(), paparams.getSkew());
+ S32 face, numfaces;
+ numfaces = object->getNumTEs();
+ apr_file_printf(fp, "\tNumberOfFaces='%d'>\n", numfaces);
+ for (face = 0; face < numfaces; face++)
+ {
+ const LLTextureEntry *te = object->getTE(face);
+ LLColor4 color = te->getColor();
+ apr_file_printf(fp, "\t<Face\n\t\tFaceColor='%d %5f %5f %5f %5f'\n", face, color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW]);
+
+ char texture[UUID_STR_LENGTH];
+ LLUUID texid = te->getID();
+ texid.toString(texture);
+ F32 sx, sy, ox, oy;
+ te->getScale(&sx, &sy);
+ te->getOffset(&ox, &oy);
+
+ apr_file_printf(fp, "\t\tFace='%d %5f %5f %5f %5f %5f %d %s'\n\t/>\n", face, sx, sy, ox, oy, te->getRotation(), te->getBumpShinyFullbright(), texture);
+ }
+ apr_file_printf(fp, "</Object>\n");
+ object = export_objects[++object_index];
+ }
+
+ apr_file_printf(fp, "</LindenGeometry>\n");
+
+ fclose(fp);
+}
+
+void undo_find_local_contact_point(LLVector3 &contact,
+ const LLVector3& surface_norm,
+ const LLQuaternion& rot,
+ const LLVector3& scale )
+{
+ LLVector3 local_norm = surface_norm;
+ local_norm.rotVec( ~rot );
+
+ LLVector3 v[6];
+ v[0].mV[VX] = -1.f;
+ v[1].mV[VX] = 1.f;
+
+ v[2].mV[VY] = -1.f;
+ v[3].mV[VY] = 1.f;
+
+ v[4].mV[VZ] = -1.f;
+ v[5].mV[VZ] = 1.f;
+
+ contact = v[0];
+ F32 cur_val = 0;
+
+ for( S32 i = 0; i < 6; i++ )
+ {
+ F32 val = v[i] * local_norm;
+ if( val < cur_val )
+ {
+ contact = v[i];
+ cur_val = val;
+ }
+ }
+
+ contact.mV[VX] *= 0.5f * scale.mV[VX];
+ contact.mV[VY] *= 0.5f * scale.mV[VY];
+ contact.mV[VZ] *= 0.5f * scale.mV[VZ];
+ contact.rotVec( rot );
+}
+
+
+
+void force_import_geometry(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getOpenFile(LLFilePicker::FFLOAD_GEOMETRY))
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ return;
+ }
+
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ llinfos << "Loading LSG file " << directory << llendl;
+ LLXmlTree *xmlparser = new LLXmlTree();
+ xmlparser->parseFile(directory, TRUE);
+ LLXmlTreeNode *root = xmlparser->getRoot();
+ if( !root )
+ {
+ return;
+ }
+ // header
+ if( !root->hasName( "LindenGeometry" ) )
+ {
+ llwarns << "Invalid LindenGeometry file header: " << directory << llendl;
+ return;
+ }
+ // objects
+ for (LLXmlTreeNode *child = root->getChildByName( "Object" );
+ child;
+ child = root->getNextNamedChild())
+ {
+ // get object data
+ char name[255]; // Shape
+ char description[255]; // Description
+ U32 material; // Material
+ F32 sx, sy, sz; // Scale
+ LLVector3 scale;
+ F32 ox, oy, oz; // Offset
+ LLVector3 offset;
+ F32 rx, ry, rz, rs; // Orientation
+ LLQuaternion rot;
+ U32 curve;
+ F32 begin;
+ F32 end;
+ F32 hollow;
+ F32 twist;
+ F32 scx, scy;
+ F32 shx, shy;
+ F32 twist_begin;
+ F32 radius_offset;
+ F32 tx, ty;
+ F32 revolutions;
+ F32 skew;
+ S32 faces;
+ U32 pcode;
+ U32 flags = FLAGS_CREATE_SELECTED;
+
+ LLString attribute;
+
+ S32 count = 0;
+
+ child->getAttributeString("PCode", &attribute);
+ pcode = atoi(attribute.c_str());
+ child->getAttributeString("Shape", &attribute);
+ sscanf(attribute.c_str(), "%s", name);
+ child->getAttributeString("Description", &attribute);
+ sscanf(attribute.c_str(), "%s", description);
+ child->getAttributeString("Material", &attribute);
+ material = atoi(attribute.c_str());
+ child->getAttributeString("Scale", &attribute);
+ sscanf(attribute.c_str(), "%f %f %f", &sx, &sy, &sz);
+ scale.setVec(sx, sy, sz);
+ child->getAttributeString("Offset", &attribute);
+ sscanf(attribute.c_str(), "%f %f %f", &ox, &oy, &oz);
+ offset.setVec(ox, oy, oz);
+ child->getAttributeString("Orientation", &attribute);
+ sscanf(attribute.c_str(), "%f %f %f %f", &rx, &ry, &rz, &rs);
+ rot.mQ[VX] = rx;
+ rot.mQ[VY] = ry;
+ rot.mQ[VZ] = rz;
+ rot.mQ[VS] = rs;
+
+ child->getAttributeString("ShapeProfile", &attribute);
+ sscanf(attribute.c_str(), "%d %f %f %f", &curve, &begin, &end, &hollow);
+ LLProfileParams pparams(curve, begin, end, hollow);
+ child->getAttributeString("ShapePath", &attribute);
+ sscanf(attribute.c_str(), "%d %f %f %f %f %f %f %f %f %f %f %f %f %f",
+ &curve, &begin, &end, &twist, &twist_begin, &scx, &scy, &shx, &shy, &radius_offset, &tx, &ty, &revolutions, &skew);
+ LLPathParams paparams(curve, begin, end, scx, scy, shx, shy, twist, twist_begin, radius_offset, tx, ty, revolutions, skew);
+ child->getAttributeString("NumberOfFaces", &attribute);
+ faces = atoi(attribute.c_str());
+
+
+
+ gMessageSystem->newMessageFast(_PREHASH_ObjectAdd);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU8Fast(_PREHASH_PCode, pcode);
+ gMessageSystem->addU8Fast(_PREHASH_Material, material);
+ gMessageSystem->addU32Fast(_PREHASH_AddFlags, flags );
+ pparams.packMessage(gMessageSystem);
+ paparams.packMessage(gMessageSystem);
+
+ LLVector3 forward;
+ forward.setVec(3.f, 0.f, 1.f);
+ forward = forward * gAgent.getQuat();
+
+ LLVector3 start = gAgent.getPositionAgent() + forward;
+
+ start += offset;
+
+ // offset position to make up for error introduced by placement code
+ LLVector3 normal(0.f, 0.f, 1.f);
+ LLVector3 delta;
+
+ undo_find_local_contact_point(delta, normal, rot, scale);
+
+ start += delta;
+
+ gMessageSystem->addVector3Fast(_PREHASH_Scale, scale );
+ gMessageSystem->addQuatFast(_PREHASH_Rotation, rot );
+ gMessageSystem->addVector3Fast(_PREHASH_RayStart, start );
+ gMessageSystem->addVector3Fast(_PREHASH_RayEnd, start );
+ gMessageSystem->addBOOLFast(_PREHASH_BypassRaycast, TRUE );
+ gMessageSystem->addBOOLFast(_PREHASH_RayEndIsIntersection, FALSE );
+
+ U8 state = 0;
+ gMessageSystem->addU8Fast(_PREHASH_State, state);
+
+ LLUUID ray_target_id;
+ gMessageSystem->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
+
+ /* Setting TE info through ObjectAdd is no longer supported.
+ LLPrimitive temp_primitive;
+ temp_primitive.setNumTEs(faces);
+ for (LLXmlTreeNode *face = child->getChildByName( "Face" );
+ face;
+ face = child->getNextNamedChild())
+ {
+ // read the faces
+ U32 facenumber;
+ LLColor4 color;
+ char texture[UUID_STR_LENGTH];
+ LLUUID texid;
+ texid.toString(texture);
+ F32 sx, sy, ox, oy, rot;
+ U8 bump;
+ LLTextureEntry te;
+
+ face->getAttributeString("FaceColor", &attribute);
+ sscanf(attribute, "%d %f %f %f %f", &facenumber, &color.mV[VX], &color.mV[VY], &color.mV[VZ], &color.mV[VW]);
+ face->getAttributeString("Face", &attribute);
+ sscanf(attribute, "%d %f %f %f %f %f %d %s", &facenumber, &sx, &sy, &ox, &oy, &rot, &bump, texture);
+ texid.set(texture);
+ te.setColor(color);
+ te.setBumpShinyFullbright(bump);
+ te.setID(texid);
+ te.setRotation(rot);
+ te.setOffset(ox, oy);
+ te.setScale(sx, sy);
+
+ temp_primitive.setTE(facenumber, te);
+ }
+
+ temp_primitive.packTEMessage(gMessageSystem);
+ */
+ gMessageSystem->sendReliable(gAgent.getRegionHost());
+ }
+
+}
+#endif
+
+void handle_take()
+{
+ // we want to use the folder this was derezzed from if it's
+ // available. Otherwise, derez to the normal place.
+ if(gSelectMgr->isEmpty()) return;
+ LLSelectNode* node = NULL;
+ LLViewerObject* object = NULL;
+ BOOL you_own_everything = TRUE;
+
+ BOOL locked_but_takeable_object = FALSE;
+ LLUUID category_id;
+ for(node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ object = node->getObject();
+ if(object)
+ {
+ if(!object->permYouOwner())
+ {
+ you_own_everything = FALSE;
+ }
+
+ if(!object->permMove())
+
+ {
+
+ locked_but_takeable_object = TRUE;
+
+ }
+ }
+ if(node->mFolderID.notNull())
+ {
+ if(category_id.isNull())
+ {
+ category_id = node->mFolderID;
+ }
+ else if(category_id != node->mFolderID)
+ {
+ // we have found two potential destinations. break out
+ // now and send to the default location.
+ category_id.setNull();
+ break;
+ }
+ }
+ }
+ if(category_id.notNull())
+ {
+ // there is an unambiguous destination. See if this agent has
+ // such a location and it is not in the trash.
+ if(!gInventory.getCategory(category_id))
+ {
+ // nope, set to NULL.
+ category_id.setNull();
+ }
+ if(category_id.notNull())
+ {
+ LLUUID trash;
+ trash = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(category_id == trash || gInventory.isObjectDescendentOf(category_id, trash))
+ {
+ category_id.setNull();
+ }
+ }
+ }
+ if(category_id.isNull())
+ {
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+ }
+ LLUUID* cat_id = new LLUUID(category_id);
+ if(locked_but_takeable_object ||
+
+ !you_own_everything)
+ {
+
+ if(locked_but_takeable_object && you_own_everything)
+ {
+
+ gViewerWindow->alertXml("ConfirmObjectTakeLock",
+ confirm_take,
+ (void*)cat_id);
+
+ }
+ else if(!locked_but_takeable_object && !you_own_everything)
+ {
+
+ gViewerWindow->alertXml("ConfirmObjectTakeNoOwn",
+ confirm_take,
+ (void*)cat_id);
+ }
+ else
+ {
+ gViewerWindow->alertXml("ConfirmObjectTakeLockNoOwn",
+ confirm_take,
+ (void*)cat_id);
+ }
+
+
+ }
+
+ else
+ {
+ confirm_take(0, (void*)cat_id);
+ }
+}
+
+void confirm_take(S32 option, void* data)
+{
+ LLUUID* cat_id = (LLUUID*)data;
+ if(!cat_id) return;
+ if(enable_take() && (option == 0))
+ {
+ derez_objects(DRD_TAKE_INTO_AGENT_INVENTORY, *cat_id);
+ }
+ delete cat_id;
+}
+
+// You can take an item when it is public and transferrable, or when
+// you own it. We err on the side of enabling the item when at least
+// one item selected can be copied to inventory.
+BOOL enable_take()
+{
+ if (sitting_on_selection())
+ {
+ return FALSE;
+ }
+
+ LLViewerObject* object = NULL;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ object = node->getObject();
+ if(!object || !node->mValid) continue;
+ if (object->isAvatar())
+ {
+ // ...don't acquire avatars
+ continue;
+ }
+
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+# endif
+ if((node->mPermissions->allowTransferTo(gAgent.getID())
+ && object->permModify())
+ || (node->mPermissions->getOwner() == gAgent.getID()))
+ {
+ return TRUE;
+ }
+#endif
+ }
+ return FALSE;
+}
+
+class LLToolsBuyOrTake : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr->isEmpty())
+ {
+ return true;
+ }
+
+ if (is_selection_buy_not_take())
+ {
+ S32 total_price = selection_price();
+
+ if (total_price <= gStatusBar->getBalance())
+ {
+ handle_buy(NULL);
+ }
+ else
+ {
+ LLFloaterBuyCurrency::buyCurrency(
+ "Buying this costs", total_price);
+ }
+ }
+ else
+ {
+ handle_take();
+ }
+ return true;
+ }
+};
+
+class LLToolsEnableBuyOrTake : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool is_buy = is_selection_buy_not_take();
+ bool new_value = is_buy ? enable_buy(NULL) : enable_take();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+
+ // HACK: Update label
+ LLString label;
+ LLString buy_text;
+ LLString take_text;
+ LLString param = userdata["data"].asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ buy_text = param.substr(0, offset);
+ take_text = param.substr(offset+1);
+ }
+ if (is_buy)
+ {
+ label = buy_text;
+ }
+ else
+ {
+ label = take_text;
+ }
+ gMenuHolder->childSetText("Pie Object Take", label);
+ gMenuHolder->childSetText("Menu Object Take", label);
+
+ return true;
+ }
+};
+
+// This is a small helper function to determine if we have a buy or a
+// take in the selection. This method is to help with the aliasing
+// problems of putting buy and take in the same pie menu space. After
+// a fair amont of discussion, it was determined to prefer buy over
+// take. The reasoning follows from the fact that when users walk up
+// to buy something, they will click on one or more items. Thus, if
+// anything is for sale, it becomes a buy operation, and the server
+// will group all of the buy items, and copyable/modifiable items into
+// one package and give the end user as much as the permissions will
+// allow. If the user wanted to take something, they will select fewer
+// and fewer items until only 'takeable' items are left. The one
+// exception is if you own everything in the selection that is for
+// sale, in this case, you can't buy stuff from yourself, so you can
+// take it.
+// return value = TRUE if selection is a 'buy'.
+// FALSE if selection is a 'take'
+BOOL is_selection_buy_not_take()
+{
+ LLViewerObject* obj = NULL;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ obj = node->getObject();
+ if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale()))
+ {
+ // you do not own the object and it is for sale, thus,
+ // it's a buy
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+S32 selection_price()
+{
+ LLViewerObject* obj = NULL;
+ S32 total_price = 0;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ obj = node->getObject();
+ if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale()))
+ {
+ // you do not own the object and it is for sale.
+ // Add its price.
+ total_price += node->mSaleInfo.getSalePrice();
+ }
+ }
+
+ return total_price;
+}
+
+void callback_show_buy_currency(S32 option, void*)
+{
+ if (0 == option)
+ {
+ llinfos << "Loading page " << BUY_CURRENCY_URL << llendl;
+ LLWeb::loadURL(BUY_CURRENCY_URL);
+ }
+}
+
+
+void show_buy_currency(const char* extra)
+{
+ // Don't show currency web page for branded clients.
+
+ std::ostringstream mesg;
+ if (extra != NULL)
+ {
+ mesg << extra << "\n \n";
+ }
+ mesg << "Go to " << BUY_CURRENCY_URL << "\nfor information on purchasing currency?";
+
+ LLString::format_map_t args;
+ args["[EXTRA]"] = extra;
+ args["[URL]"] = BUY_CURRENCY_URL;
+ gViewerWindow->alertXml("PromptGoToCurrencyPage", args,
+ callback_show_buy_currency);
+}
+
+void handle_buy_currency(void*)
+{
+// LLFloaterBuyCurrency::buyCurrency();
+}
+
+void handle_buy(void*)
+{
+ if (gSelectMgr->isEmpty()) return;
+
+ LLSaleInfo sale_info;
+ BOOL valid = gSelectMgr->selectGetSaleInfo(sale_info);
+ if (!valid) return;
+
+ if (sale_info.getSaleType() == LLSaleInfo::FS_CONTENTS)
+ {
+ handle_buy_contents(sale_info);
+ }
+ else
+ {
+ handle_buy_object(sale_info);
+ }
+}
+
+class LLObjectBuy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_buy(NULL);
+ return true;
+ }
+};
+
+BOOL sitting_on_selection()
+{
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node)
+ {
+ return FALSE;
+ }
+
+ if (!node->mValid)
+ {
+ return FALSE;
+ }
+
+ LLViewerObject* root_object = node->getObject();
+ if (!root_object)
+ {
+ return FALSE;
+ }
+
+ // Need to determine if avatar is sitting on this object
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (!avatar)
+ {
+ return FALSE;
+ }
+
+ return (avatar->mIsSitting && avatar->getRoot() == root_object);
+}
+
+class LLToolsSaveToInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(enable_save_into_inventory(NULL))
+ {
+ derez_objects(DRD_SAVE_INTO_AGENT_INVENTORY, LLUUID::null);
+ }
+ return true;
+ }
+};
+
+class LLToolsSaveToObjectInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(gSelectMgr)
+ {
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(node && (node->mValid) && (!node->mFromTaskID.isNull()))
+ {
+ // *FIX: check to see if the fromtaskid object exists.
+ derez_objects(DRD_SAVE_INTO_TASK_INVENTORY, node->mFromTaskID);
+ }
+ }
+ return true;
+ }
+};
+
+// Round the position of all root objects to the grid
+class LLToolsSnapObjectXY : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ F64 snap_size = (F64)gSavedSettings.getF32("GridResolution");
+
+ LLViewerObject* obj;
+ for (obj = gSelectMgr->getFirstRootObject();
+ obj != NULL;
+ obj = gSelectMgr->getNextRootObject())
+ {
+ if (obj->permModify())
+ {
+ LLVector3d pos_global = obj->getPositionGlobal();
+ F64 round_x = fmod(pos_global.mdV[VX], snap_size);
+ if (round_x < snap_size * 0.5)
+ {
+ // closer to round down
+ pos_global.mdV[VX] -= round_x;
+ }
+ else
+ {
+ // closer to round up
+ pos_global.mdV[VX] -= round_x;
+ pos_global.mdV[VX] += snap_size;
+ }
+
+ F64 round_y = fmod(pos_global.mdV[VY], snap_size);
+ if (round_y < snap_size * 0.5)
+ {
+ pos_global.mdV[VY] -= round_y;
+ }
+ else
+ {
+ pos_global.mdV[VY] -= round_y;
+ pos_global.mdV[VY] += snap_size;
+ }
+
+ obj->setPositionGlobal(pos_global, FALSE);
+ }
+ }
+ gSelectMgr->sendMultipleUpdate(UPD_POSITION);
+ return true;
+ }
+};
+
+// in order to link, all objects must have the same owner, and the
+// agent must have the ability to modify all of the objects. However,
+// we're not answering that question with this method. The question
+// we're answering is: does the user have a reasonable expectation
+// that a link operation should work? If so, return true, false
+// otherwise. this allows the handle_link method to more finely check
+// the selection and give an error message when the uer has a
+// reasonable expectation for the link to work, but it will fail.
+class LLToolsEnableLink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ // check if there are at least 2 objects selected, and that the
+ // user can modify at least one of the selected objects.
+
+ // in component mode, can't link
+ if (gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ if(gSelectMgr->selectGetAllRootsValid() && gSelectMgr->getRootObjectCount() >= 2)
+ {
+ for(LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ object != NULL;
+ object = gSelectMgr->getNextRootObject())
+ {
+ if(object->permModify())
+ {
+ new_value = true;
+ break;
+ }
+ }
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsLink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(!gSelectMgr->selectGetAllRootsValid())
+ {
+ LLNotifyBox::showXml("UnableToLinkWhileDownloading");
+ return true;
+ }
+
+ S32 object_count = gSelectMgr->getObjectCount();
+ if (object_count > MAX_CHILDREN_PER_TASK + 1)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[COUNT]"] = llformat("%d", object_count);
+ int max = MAX_CHILDREN_PER_TASK+1;
+ args["[MAX]"] = llformat("%d", max);
+ gViewerWindow->alertXml("UnableToLinkObjects", args);
+ return true;
+ }
+
+ if(gSelectMgr->getRootObjectCount() < 2)
+ {
+ gViewerWindow->alertXml("CannotLinkIncompleteSet");
+ return true;
+ }
+ if(!gSelectMgr->selectGetRootsModify())
+ {
+ gViewerWindow->alertXml("CannotLinkModify");
+ return true;
+ }
+ LLUUID owner_id;
+ LLString owner_name;
+ if(!gSelectMgr->selectGetOwner(owner_id, owner_name))
+ {
+ // we don't actually care if you're the owner, but novices are
+ // the most likely to be stumped by this one, so offer the
+ // easiest and most likely solution.
+ gViewerWindow->alertXml("CannotLinkDifferentOwners");
+ return true;
+ }
+ gSelectMgr->sendLink();
+ return true;
+ }
+};
+
+class LLToolsEnableUnlink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gSelectMgr->selectGetAllRootsValid() &&
+ gSelectMgr->getFirstEditableObject() &&
+ !gSelectMgr->getFirstEditableObject()->isAttachment();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsUnlink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gSelectMgr->sendDelink();
+ return true;
+ }
+};
+
+
+class LLToolsStopAllAnimations : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+
+ if (!avatarp) return true;
+
+ LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.begin();
+ for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it)
+ {
+ avatarp->stopMotion( anim_it->second, TRUE );
+ }
+
+ avatarp->processAnimationStateChanges();
+ return true;
+ }
+};
+
+void handle_hinge(void*)
+{
+ gSelectMgr->sendHinge(1);
+}
+
+void handle_ptop(void*)
+{
+ gSelectMgr->sendHinge(2);
+}
+
+void handle_lptop(void*)
+{
+ gSelectMgr->sendHinge(3);
+}
+
+void handle_wheel(void*)
+{
+ gSelectMgr->sendHinge(4);
+}
+
+void handle_dehinge(void*)
+{
+ gSelectMgr->sendDehinge();
+}
+
+BOOL enable_dehinge(void*)
+{
+ LLViewerObject* obj = gSelectMgr->getFirstEditableObject();
+ return obj && !obj->isAttachment();
+}
+
+
+class LLEditEnableCut : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canCut();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditCut : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->cut();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canCopy();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->copy();
+ }
+ return true;
+ }
+};
+
+class LLEditEnablePaste : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canPaste();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditPaste : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->paste();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canDoDelete();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // If a text field can do a deletion, it gets precedence over deleting
+ // an object in the world.
+ if( gEditMenuHandler && gEditMenuHandler->canDoDelete())
+ {
+ gEditMenuHandler->doDelete();
+ }
+
+ // and close any pie/context menus when done
+ gMenuHolder->hideMenus();
+
+ // When deleting an object we may not actually be done
+ // Keep selection so we know what to delete when confirmation is needed about the delete
+ gPieObject->hide(TRUE);
+ return true;
+ }
+};
+
+class LLObjectEnableDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value =
+#ifdef HACKED_GODLIKE_VIEWER
+ TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ (!gInProductionGrid && gAgent.isGodlike()) ||
+# endif
+ (gSelectMgr && gSelectMgr->canDoDelete());
+#endif
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditSearch : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterDirectory::toggleFind(NULL);
+ return true;
+ }
+};
+
+class LLObjectDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr)
+ {
+ gSelectMgr->doDelete();
+ }
+
+ // and close any pie/context menus when done
+ gMenuHolder->hideMenus();
+
+ // When deleting an object we may not actually be done
+ // Keep selection so we know what to delete when confirmation is needed about the delete
+ gPieObject->hide(TRUE);
+ return true;
+ }
+};
+
+void handle_force_delete(void*)
+{
+ gSelectMgr->selectForceDelete();
+}
+
+class LLViewEnableLastChatter : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ //FIXME: add check that last chatter is in range
+ bool new_value = (gAgent.cameraThirdPerson() && gAgent.getLastChatter().notNull());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditEnableDeselect : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canDeselect();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditDeselect : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->deselect();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableSelectAll : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canSelectAll();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+class LLEditSelectAll : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->selectAll();
+ }
+ return true;
+ }
+};
+
+
+class LLEditEnableUndo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canUndo();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditUndo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler && gEditMenuHandler->canUndo() )
+ {
+ gEditMenuHandler->undo();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableRedo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canRedo();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditRedo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler && gEditMenuHandler->canRedo() )
+ {
+ gEditMenuHandler->redo();
+ }
+ return true;
+ }
+};
+
+
+
+void print_object_info(void*)
+{
+ gSelectMgr->selectionDump();
+}
+
+
+void show_debug_menus()
+{
+ // this can get called at login screen where there is no menu so only toggle it if one exists
+ if ( gMenuBarView )
+ {
+ BOOL debug = gSavedSettings.getBOOL("UseDebugMenus");
+ gMenuBarView->setItemVisible(CLIENT_MENU_NAME, debug);
+ gMenuBarView->setItemEnabled(CLIENT_MENU_NAME, debug);
+ gMenuBarView->setItemVisible(SERVER_MENU_NAME, debug);
+ gMenuBarView->setItemEnabled(SERVER_MENU_NAME, debug);
+ //gMenuBarView->setItemVisible(LLString("DebugOptions"), visible);
+ //gMenuBarView->setItemVisible(LLString(AVI_TOOLS), visible);
+ };
+}
+
+void toggle_debug_menus(void*)
+{
+ BOOL visible = ! gSavedSettings.getBOOL("UseDebugMenus");
+ gSavedSettings.setBOOL("UseDebugMenus", visible);
+ show_debug_menus();
+}
+
+
+void toggle_map( void* user_data )
+{
+ // Toggle the item
+ BOOL checked = gSavedSettings.getBOOL( static_cast<char*>(user_data) );
+ gSavedSettings.setBOOL( static_cast<char*>(user_data), !checked );
+ if (checked)
+ {
+ gFloaterMap->close();
+ }
+ else
+ {
+ gFloaterMap->open();
+ }
+}
+
+/**
+ char* upload_pick(void* data)
+
+ If applicable, brings up a file chooser in which the user selects a file
+ to upload for a particular task. If the file is valid for the given action,
+ returns the string to the full path filename, else returns NULL.
+ Data is the load filter for the type of file as defined in LLFilePicker.
+**/
+const char* upload_pick(void* data)
+{
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ // This doesn't seem necessary. JC
+ // display();
+ }
+
+ LLFilePicker::ELoadFilter type;
+ if(data)
+ {
+ type = (LLFilePicker::ELoadFilter)((S32)data);
+ }
+ else
+ {
+ type = LLFilePicker::FFLOAD_ALL;
+ }
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getOpenFile(type))
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ return NULL;
+ }
+
+ const char* filename = picker.getFirstFile();
+ const char* ext = strrchr(filename, '.');
+
+ //strincmp doesn't like NULL pointers
+ if (ext == NULL)
+ {
+ const char* short_name = strrchr(filename,
+ *gDirUtilp->getDirDelimiter().c_str());
+
+ // No extension
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = LLString(short_name + 1);
+ gViewerWindow->alertXml("NoFileExtension", args);
+ return NULL;
+ }
+ else
+ {
+ //so there is an extension
+ //loop over the valid extensions and compare to see
+ //if the extension is valid
+
+ //now grab the set of valid file extensions
+ const char* valids = build_extensions_string(type);
+ std::string valid_extensions = std::string(valids);
+
+ BOOL ext_valid = FALSE;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(valid_extensions, sep);
+ tokenizer::iterator token_iter;
+
+ //now loop over all valid file extensions
+ //and compare them to the extension of the file
+ //to be uploaded
+ for( token_iter = tokens.begin();
+ token_iter != tokens.end() && ext_valid != TRUE;
+ ++token_iter)
+ {
+ const char* cur_token = token_iter->c_str();
+
+ if (0 == strnicmp(cur_token, ext, strlen(cur_token)) ||
+ 0 == strnicmp(cur_token, "*.*", strlen(cur_token)))
+ {
+ //valid extension
+ //or the acceptable extension is any
+ ext_valid = TRUE;
+ }
+ }//end for (loop over all tokens)
+
+ if (ext_valid == FALSE)
+ {
+ //should only get here if the extension exists
+ //but is invalid
+ LLStringBase<char>::format_map_t args;
+ args["[EXTENSION]"] = ext;
+ args["[VALIDS]"] = valids;
+ gViewerWindow->alertXml("InvalidFileExtension", args);
+ return NULL;
+ }
+ }//end else (non-null extension)
+
+ //valid file extension
+
+ //now we check to see
+ //if the file is actually a valid image/sound/etc.
+ if (type == LLFilePicker::FFLOAD_WAV)
+ {
+ // pre-qualify wavs to make sure the format is acceptable
+ char error_msg[MAX_STRING];
+ if (check_for_invalid_wav_formats(filename,error_msg))
+ {
+ llinfos << error_msg << ": " << filename << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = filename;
+ gViewerWindow->alertXml( error_msg, args );
+ return NULL;
+ }
+ }//end if a wave/sound file
+
+
+ return filename;
+}
+
+void handle_upload_object(void* data)
+{
+ const char* filename = upload_pick(data);
+ if (filename)
+ {
+ // start the import
+ LLFloaterImport* floaterp = new LLFloaterImport(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_import.xml");
+ }
+}
+
+class LLFileUploadImage : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const char* filename = upload_pick((void *)(S32)LLFilePicker::FFLOAD_IMAGE);
+ if (filename)
+ {
+ LLFloaterImagePreview* floaterp = new LLFloaterImagePreview(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_image_preview.xml");
+ }
+ return TRUE;
+ }
+};
+
+class LLFileUploadSound : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const char* filename = upload_pick((void*)((S32)LLFilePicker::FFLOAD_WAV));
+ if (filename)
+ {
+ LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_sound_preview.xml");
+ }
+ return true;
+ }
+};
+
+class LLFileUploadAnim : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const char* filename = upload_pick((void*)((S32)LLFilePicker::FFLOAD_ANIM));
+ if (filename)
+ {
+ LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_animation_preview.xml");
+ }
+ return true;
+ }
+};
+
+class LLFileUploadBulk : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // TODO:
+ // Iterate over all files
+ // Check extensions for uploadability, cost
+ // Check user balance for entire cost
+ // Charge user entire cost
+ // Loop, uploading
+ // If an upload fails, refund the user for that one
+ //
+ // Also fix single upload to charge first, then refund
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getMultipleOpenFiles())
+ {
+ const char* filename = picker.getFirstFile();
+ const char* name = picker.getDirname();
+
+ LLString asset_name = name;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ upload_new_resource(filename, asset_name, asset_name, 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE); // file
+ }
+ else
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ }
+ return true;
+ }
+};
+
+void upload_error(const char* error_message, const char* label, const std::string filename, const LLStringBase<char>::format_map_t args)
+{
+ llwarns << error_message << llendl;
+ gViewerWindow->alertXml(label, args);
+ if(remove(filename.c_str()) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+}
+
+class LLFileCloseWindow : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloater *top = gFloaterView->getFrontmost();
+ if (top && top->hasFocus())
+ {
+ LLFloater::closeByMenu( top );
+ }
+ return true;
+ }
+};
+
+class LLFileSaveTexture : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloater* top = gFloaterView->getFrontmost();
+ if (top)
+ {
+ top->saveAs();
+ }
+ return true;
+ }
+};
+
+class LLFileTakeSnapshot : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterSnapshot::show(NULL);
+ return true;
+ }
+};
+
+class LLFileTakeSnapshotToDisk : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+
+ S32 width = gViewerWindow->getWindowDisplayWidth();
+ S32 height = gViewerWindow->getWindowDisplayHeight();
+
+ if (gSavedSettings.getBOOL("HighResSnapshot"))
+ {
+ width *= 2;
+ height *= 2;
+ }
+
+ if (gViewerWindow->rawSnapshot(raw,
+ width,
+ height,
+ TRUE,
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE))
+ {
+ if (!gQuietSnapshot)
+ {
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ LLImageBase::setSizeOverride(TRUE);
+ gViewerWindow->saveImageNumbered(raw);
+ LLImageBase::setSizeOverride(FALSE);
+ }
+ return true;
+ }
+};
+
+class LLFileSaveMovie : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerWindow::saveMovieNumbered(NULL);
+ return true;
+ }
+};
+
+class LLFileSetWindowSize : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString size = userdata.asString();
+ S32 width, height;
+ sscanf(size.c_str(), "%d,%d", &width, &height);
+ LLViewerWindow::movieSize(width, height);
+ return true;
+ }
+};
+
+class LLFileQuit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ app_request_quit();
+ return true;
+ }
+};
+
+void handle_upload(void* data)
+{
+ const char* filename = upload_pick(data);
+ if (filename)
+ {
+ LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_name_description.xml");
+ }
+}
+
+void handle_compress_image(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getOpenFile(LLFilePicker::FFLOAD_IMAGE))
+ {
+ std::string infile(picker.getFirstFile());
+ std::string outfile = infile + ".j2c";
+
+ llinfos << "Input: " << infile << llendl;
+ llinfos << "Output: " << outfile << llendl;
+
+ BOOL success;
+
+ success = LLViewerImageList::createUploadFile(infile, outfile, IMG_CODEC_TGA);
+
+ if (success)
+ {
+ llinfos << "Compression complete" << llendl;
+ }
+ else
+ {
+ llinfos << "Compression failed: " << LLImageBase::getLastError() << llendl;
+ }
+ }
+}
+
+void upload_new_resource(const LLString& src_filename, std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm,
+ const LLString& display_name,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ void *userdata)
+{
+ // Generate the temporary UUID.
+ LLString filename = gDirUtilp->getTempFilename();
+ LLTransactionID tid;
+ LLAssetID uuid;
+
+ LLStringBase<char>::format_map_t args;
+
+ LLString ext = src_filename.substr(src_filename.find_last_of('.'));
+ LLAssetType::EType asset_type = LLAssetType::AT_NONE;
+ char error_message[MAX_STRING];
+ error_message[0] = '\0';
+ LLString temp_str;
+
+ BOOL error = FALSE;
+
+ if (ext.empty())
+ {
+ LLString::size_type offset = filename.find_last_of(gDirUtilp->getDirDelimiter());
+ if (offset != LLString::npos)
+ offset++;
+ LLString short_name = filename.substr(offset);
+
+ // No extension
+ sprintf(error_message,
+ "No file extension for the file: '%s'\nPlease make sure the file has a correct file extension",
+ short_name.c_str());
+ args["[FILE]"] = short_name;
+ upload_error(error_message, "NofileExtension", filename, args);
+ return;
+ }
+ else if( LLString::compareInsensitive(ext.c_str(),".bmp") == 0 )
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_BMP ))
+ {
+ sprintf(error_message, "Problem with file %s:\n\n%s\n",
+ src_filename.c_str(), LLImageBase::getLastError().c_str());
+ args["[FILE]"] = src_filename;
+ args["[ERROR]"] = LLImageBase::getLastError();
+ upload_error(error_message, "ProblemWithFile", filename, args);
+ return;
+ }
+ }
+ else if( LLString::compareInsensitive(ext.c_str(),".tga") == 0 )
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_TGA ))
+ {
+ sprintf(error_message, "Problem with file %s:\n\n%s\n",
+ src_filename.c_str(), LLImageBase::getLastError().c_str());
+ args["[FILE]"] = src_filename;
+ args["[ERROR]"] = LLImageBase::getLastError();
+ upload_error(error_message, "ProblemWithFile", filename, args);
+ return;
+ }
+ }
+ else if( LLString::compareInsensitive(ext.c_str(),".jpg") == 0 || LLString::compareInsensitive(ext.c_str(),".jpeg") == 0)
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_JPEG ))
+ {
+ sprintf(error_message, "Problem with file %s:\n\n%s\n",
+ src_filename.c_str(), LLImageBase::getLastError().c_str());
+ args["[FILE]"] = src_filename;
+ args["[ERROR]"] = LLImageBase::getLastError();
+ upload_error(error_message, "ProblemWithFile", filename, args);
+ return;
+ }
+ }
+ else if(LLString::compareInsensitive(ext.c_str(),".wav") == 0)
+ {
+ asset_type = LLAssetType::AT_SOUND; // tag it as audio
+ S32 encode_result = 0;
+
+ S32 bitrate = 128;
+
+ if (compression_info)
+ {
+ bitrate = compression_info;
+ }
+ llinfos << "Attempting to encode wav as an ogg file at " << bitrate << "kbps" << llendl;
+
+ encode_result = encode_vorbis_file_at(src_filename.c_str(), filename.c_str(), bitrate*1000);
+
+ if (LLVORBISENC_NOERR != encode_result)
+ {
+ switch(encode_result)
+ {
+ case LLVORBISENC_DEST_OPEN_ERR:
+ sprintf(error_message, "Couldn't open temporary compressed sound file for writing: %s\n", filename.c_str());
+ args["[FILE]"] = filename;
+ upload_error(error_message, "CannotOpenTemporarySoundFile", filename, args);
+ break;
+
+ default:
+ sprintf(error_message, "Unknown vorbis encode failure on: %s\n", src_filename.c_str());
+ args["[FILE]"] = src_filename;
+ upload_error(error_message, "UnknownVorbisEncodeFailure", filename, args);
+ break;
+ }
+ return;
+ }
+ }
+ else if(LLString::compareInsensitive(ext.c_str(),".tmp") == 0)
+ {
+ // This is a generic .lin resource file
+ asset_type = LLAssetType::AT_OBJECT;
+ FILE *in = LLFile::fopen(src_filename.c_str(), "rb");
+ if (in)
+ {
+ // read in the file header
+ char buf[16384];
+ S32 read;
+ S32 version;
+ if (fscanf(in, "LindenResource\nversion %d\n", &version))
+ {
+ if (2 == version)
+ {
+ char label[MAX_STRING];
+ char value[MAX_STRING];
+ S32 tokens_read;
+ while (fgets(buf, 1024, in))
+ {
+ label[0] = '\0';
+ value[0] = '\0';
+ tokens_read = sscanf(buf, "%s %s\n", label, value);
+
+ llinfos << "got: " << label << " = " << value
+ << llendl;
+
+ if (EOF == tokens_read)
+ {
+ fclose(in);
+ sprintf(error_message, "corrupt resource file: %s", src_filename.c_str());
+ args["[FILE]"] = src_filename;
+ upload_error(error_message, "CorruptResourceFile", filename, args);
+ return;
+ }
+
+ if (2 == tokens_read)
+ {
+ if (! strcmp("type", label))
+ {
+ asset_type = (LLAssetType::EType)(atoi(value));
+ }
+ }
+ else
+ {
+ if (! strcmp("_DATA_", label))
+ {
+ // below is the data section
+ break;
+ }
+ }
+ // other values are currently discarded
+ }
+
+ }
+ else
+ {
+ fclose(in);
+ sprintf(error_message, "unknown linden resource file version in file: %s", src_filename.c_str());
+ args["[FILE]"] = src_filename;
+ upload_error(error_message, "UnknownResourceFileVersion", filename, args);
+ return;
+ }
+ }
+ else
+ {
+ // this is an original binary formatted .lin file
+ // start over at the beginning of the file
+ fseek(in, 0, SEEK_SET);
+
+ const S32 MAX_ASSET_DESCRIPTION_LENGTH = 256;
+ const S32 MAX_ASSET_NAME_LENGTH = 64;
+ S32 header_size = 34 + MAX_ASSET_DESCRIPTION_LENGTH + MAX_ASSET_NAME_LENGTH;
+ S16 type_num;
+
+ // read in and throw out most of the header except for the type
+ fread(buf, header_size, 1, in);
+ memcpy(&type_num, buf + 16, sizeof(S16));
+ asset_type = (LLAssetType::EType)type_num;
+ }
+
+ // copy the file's data segment into another file for uploading
+ FILE *out = LLFile::fopen(filename.c_str(), "wb");
+ if (out)
+ {
+ while((read = fread(buf, 1, 16384, in)))
+ {
+ fwrite(buf, 1, read, out);
+ }
+ fclose(out);
+ }
+ else
+ {
+ fclose(in);
+ sprintf(error_message, "Unable to create output file: %s", filename.c_str());
+ args["[FILE]"] = filename;
+ upload_error(error_message, "UnableToCreateOutputFile", filename, args);
+ return;
+ }
+
+ fclose(in);
+ }
+ else
+ {
+ llinfos << "Couldn't open .lin file " << src_filename << llendl;
+ }
+ }
+ else if (LLString::compareInsensitive(ext.c_str(),".bvh") == 0)
+ {
+ sprintf(error_message, "We do not currently support bulk upload of animation files\n");
+ upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args);
+ return;
+ }
+ else
+ {
+ // Unknown extension
+ sprintf(error_message, "Unknown file extension %s\nExpected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh", ext.c_str());
+ error = TRUE;;
+ }
+
+ // gen a new transaction ID for this asset
+ tid.generate();
+
+ if (!error)
+ {
+ uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+ // copy this file into the vfs for upload
+ S32 file_size;
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_RB, &file_size);
+ if (fp)
+ {
+ LLVFile file(gVFS, uuid, asset_type, LLVFile::WRITE);
+
+ file.setMaxSize(file_size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((file_size = ll_apr_file_read(fp, copy_buf, buf_size)))
+ {
+ file.write(copy_buf, file_size);
+ }
+ apr_file_close(fp);
+ }
+ else
+ {
+ sprintf(error_message, "Unable to access output file: %s", filename.c_str());
+ error = TRUE;
+ }
+ }
+
+ if (!error)
+ {
+ LLString t_disp_name = display_name;
+ if (t_disp_name.empty())
+ {
+ t_disp_name = src_filename;
+ }
+ upload_new_resource(tid, asset_type, name, desc, compression_info, // tid
+ destination_folder_type, inv_type, next_owner_perm,
+ display_name, callback, userdata);
+ }
+ else
+ {
+ llwarns << error_message << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[ERROR_MESSAGE]"] = error_message;
+ gViewerWindow->alertXml("ErrorMessage", args);
+ if(LLFile::remove(filename.c_str()) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+ }
+}
+
+void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_type,
+ std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm,
+ const LLString& display_name,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ void *userdata)
+{
+ LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ if( LLAssetType::AT_SOUND == asset_type )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_UPLOAD_SOUND_COUNT );
+ }
+ else
+ if( LLAssetType::AT_TEXTURE == asset_type )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_UPLOAD_TEXTURE_COUNT );
+ }
+ else
+ if( LLAssetType::AT_ANIMATION == asset_type)
+ {
+ gViewerStats->incStat(LLViewerStats::ST_UPLOAD_ANIM_COUNT );
+ }
+
+ if(LLInventoryType::IT_NONE == inv_type)
+ {
+ inv_type = LLInventoryType::defaultForAssetType(asset_type);
+ }
+ LLString::stripNonprintable(name);
+ LLString::stripNonprintable(desc);
+ if(name.empty())
+ {
+ name = "(No Name)";
+ }
+ if(desc.empty())
+ {
+ desc = "(No Description)";
+ }
+
+ // At this point, we're ready for the upload.
+ LLString upload_message = "Uploading...\n\n";
+ upload_message.append(display_name);
+ LLUploadDialog::modalUploadDialog(upload_message);
+
+ llinfos << "*** Uploading: " << llendl;
+ llinfos << "Type: " << LLAssetType::lookup(asset_type) << llendl;
+ llinfos << "UUID: " << uuid << llendl;
+ llinfos << "Name: " << name << llendl;
+ llinfos << "Desc: " << desc << llendl;
+ lldebugs << "Folder: " << gInventory.findCategoryUUIDForType(destination_folder_type) << llendl;
+ lldebugs << "Asset Type: " << LLAssetType::lookup(asset_type) << llendl;
+ std::string url = gAgent.getRegion()->getCapability("NewAgentInventory");
+ if (!url.empty())
+ {
+ llinfos << "New Agent Inventory via capability" << llendl;
+ LLSD body;
+ body["folder_id"] = gInventory.findCategoryUUIDForType((destination_folder_type == LLAssetType::AT_NONE) ? asset_type : destination_folder_type);
+ body["asset_type"] = LLAssetType::lookup(asset_type);
+ body["inventory_type"] = LLInventoryType::lookup(inv_type);
+ body["name"] = name;
+ body["description"] = desc;
+
+ std::ostringstream llsdxml;
+ LLSDSerialize::toXML(body, llsdxml);
+ lldebugs << "posting body to capability: " << llsdxml.str() << llendl;
+ LLHTTPClient::post(url, body, new LLNewAgentInventoryResponder(uuid, body));
+ }
+ else
+ {
+ llinfos << "NewAgentInventory capability not found, new agent inventory via asset system." << llendl;
+ // check for adequate funds
+ // TODO: do this check on the sim
+ if (LLAssetType::AT_SOUND == asset_type ||
+ LLAssetType::AT_TEXTURE == asset_type ||
+ LLAssetType::AT_ANIMATION == asset_type)
+ {
+ S32 upload_cost = gGlobalEconomy->getPriceUpload();
+ S32 balance = gStatusBar->getBalance();
+ if (balance < upload_cost)
+ {
+ // insufficient funds, bail on this upload
+ LLFloaterBuyCurrency::buyCurrency("Uploading costs", upload_cost);
+ return;
+ }
+ }
+
+ LLResourceData* data = new LLResourceData;
+ data->mAssetInfo.mTransactionID = tid;
+ data->mAssetInfo.mUuid = uuid;
+ data->mAssetInfo.mType = asset_type;
+ data->mAssetInfo.mCreatorID = gAgentID;
+ data->mInventoryType = inv_type;
+ data->mNextOwnerPerm = next_owner_perm;
+ data->mUserData = userdata;
+ data->mAssetInfo.setName(name);
+ data->mAssetInfo.setDescription(desc);
+ data->mPreferredLocation = destination_folder_type;
+
+ LLAssetStorage::LLStoreAssetCallback asset_callback = &upload_done_callback;
+ if (callback)
+ {
+ asset_callback = callback;
+ }
+ gAssetStorage->storeAssetData(data->mAssetInfo.mTransactionID, data->mAssetInfo.mType,
+ asset_callback,
+ (void*)data,
+ FALSE);
+ }
+}
+
+void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ LLResourceData* data = (LLResourceData*)user_data;
+ //LLAssetType::EType pref_loc = data->mPreferredLocation;
+ BOOL is_balance_sufficient = TRUE;
+ if(result >= 0)
+ {
+ LLAssetType::EType dest_loc = (data->mPreferredLocation == LLAssetType::AT_NONE) ? data->mAssetInfo.mType : data->mPreferredLocation;
+
+ if (LLAssetType::AT_SOUND == data->mAssetInfo.mType ||
+ LLAssetType::AT_TEXTURE == data->mAssetInfo.mType ||
+ LLAssetType::AT_ANIMATION == data->mAssetInfo.mType)
+ {
+ // Charge the user for the upload.
+ LLViewerRegion* region = gAgent.getRegion();
+ S32 upload_cost = gGlobalEconomy->getPriceUpload();
+
+ if(!(can_afford_transaction(upload_cost)))
+ {
+ LLFloaterBuyCurrency::buyCurrency(
+ llformat("Uploading %s costs",
+ data->mAssetInfo.getName().c_str()),
+ upload_cost);
+ is_balance_sufficient = FALSE;
+ }
+ else if(region)
+ {
+ // Charge user for upload
+ gStatusBar->debitBalance(upload_cost);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoneyTransferRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_DestID, LLUUID::null);
+ msg->addU8("Flags", 0);
+ msg->addS32Fast(_PREHASH_Amount, upload_cost);
+ msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addS32Fast(_PREHASH_TransactionType, TRANS_UPLOAD_CHARGE);
+ msg->addStringFast(_PREHASH_Description, NULL);
+ msg->sendReliable(region->getHost());
+ }
+ }
+
+ if(is_balance_sufficient)
+ {
+ // Actually add the upload to inventory
+ llinfos << "Adding " << uuid << " to inventory." << llendl;
+ LLUUID folder_id(gInventory.findCategoryUUIDForType(dest_loc));
+ if(folder_id.notNull())
+ {
+ U32 next_owner_perm = data->mNextOwnerPerm;
+ if(PERM_NONE == next_owner_perm)
+ {
+ next_owner_perm = PERM_MOVE | PERM_TRANSFER;
+ }
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ folder_id, data->mAssetInfo.mTransactionID, data->mAssetInfo.getName(),
+ data->mAssetInfo.getDescription(), data->mAssetInfo.mType,
+ data->mInventoryType, NOT_WEARABLE, next_owner_perm,
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ else
+ {
+ llwarns << "Can't find a folder to put it in" << llendl;
+ }
+ }
+ }
+ else // if(result >= 0)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = LLInventoryType::lookupHumanReadable(data->mInventoryType);
+ args["[REASON]"] = LLString(LLAssetStorage::getErrorString(result));
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ }
+
+ LLUploadDialog::modalUploadFinished();
+ delete data;
+
+ // *FIX: This is a pretty big hack. What this does is check the
+ // file picker if there are any more pending uploads. If so,
+ // upload that file.
+ const char* next_file = LLFilePicker::instance().getNextFile();
+ if(is_balance_sufficient && next_file)
+ {
+ const char* name = LLFilePicker::instance().getDirname();
+
+ LLString asset_name = name;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ upload_new_resource(next_file, asset_name, asset_name, // file
+ 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
+ }
+}
+
+LLUUID gExporterRequestID;
+LLString gExportDirectory;
+
+LLUploadDialog *gExportDialog = NULL;
+
+void handle_export_selected( void * )
+{
+ if (gSelectMgr->isEmpty())
+ {
+ return;
+ }
+ llinfos << "Exporting selected objects:" << llendl;
+ LLViewerObject *object = gSelectMgr->getFirstRootObject();
+
+ gExporterRequestID.generate();
+ gExportDirectory = "";
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectExportSelected);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_RequestID, gExporterRequestID);
+ msg->addS16Fast(_PREHASH_VolumeDetail, 4);
+
+ for (; object != NULL; object = gSelectMgr->getNextRootObject())
+ {
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, object->getID());
+ llinfos << "Object: " << object->getID() << llendl;
+ }
+ msg->sendReliable(gAgent.getRegion()->getHost());
+
+ gExportDialog = LLUploadDialog::modalUploadDialog("Exporting selected objects...");
+}
+
+BOOL menu_check_build_tool( void* user_data )
+{
+ S32 index = (S32) user_data;
+ return gCurrentToolset->isToolSelected( index );
+}
+
+void handle_reload_settings(void*)
+{
+ gSavedSettings.resetToDefaults();
+ gSavedSettings.loadFromFile(gSettingsFileName, TRUE);
+
+ llinfos << "Loading colors from colors.xml" << llendl;
+ std::string color_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"colors.xml");
+ gColors.resetToDefaults();
+ gColors.loadFromFile(color_file, FALSE, TYPE_COL4U);
+}
+
+class LLWorldSetHomeLocation : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // we just send the message and let the server check for failure cases
+ // server will echo back a "Home position set." alert if it succeeds
+ // and the home location screencapture happens when that alert is recieved
+ gAgent.setStartPosition(START_LOCATION_ID_HOME);
+ return true;
+ }
+};
+
+class LLWorldTeleportHome : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.teleportHome();
+ return true;
+ }
+};
+
+class LLWorldAlwaysRun : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getAlwaysRun())
+ {
+ gAgent.clearAlwaysRun();
+ }
+ else
+ {
+ gAgent.setAlwaysRun();
+ }
+ LLMessageSystem *msg = gMessageSystem;
+
+
+ msg->newMessageFast(_PREHASH_SetAlwaysRun);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_AlwaysRun, gAgent.getAlwaysRun() );
+ gAgent.sendReliableMessage();
+ return true;
+ }
+};
+
+class LLWorldCheckAlwaysRun : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.getAlwaysRun();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLWorldSetAway : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getAFK())
+ {
+ gAgent.clearAFK();
+ }
+ else
+ {
+ gAgent.setAFK();
+ }
+ return true;
+ }
+};
+
+class LLWorldSetBusy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getBusy())
+ {
+ gAgent.clearBusy();
+ }
+ else
+ {
+ gAgent.setBusy();
+ gViewerWindow->alertXml("BusyModeSet");
+ }
+ return true;
+ }
+};
+
+
+class LLWorldCreateLandmark : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerRegion* agent_region = gAgent.getRegion();
+ if(!agent_region)
+ {
+ llwarns << "No agent region" << llendl;
+ return true;
+ }
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (!agent_parcel)
+ {
+ llwarns << "No agent parcel" << llendl;
+ return true;
+ }
+ if (!agent_parcel->getAllowLandmark()
+ && !LLViewerParcelMgr::isParcelOwnedByAgent(agent_parcel, GP_LAND_ALLOW_LANDMARK))
+ {
+ gViewerWindow->alertXml("CannotCreateLandmarkNotOwner");
+ return true;
+ }
+
+ LLUUID folder_id;
+ folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
+ std::string pos_string;
+ gAgent.buildLocationString(pos_string);
+
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ folder_id, LLTransactionID::tnull,
+ pos_string, pos_string, // name, desc
+ LLAssetType::AT_LANDMARK,
+ LLInventoryType::IT_LANDMARK,
+ NOT_WEARABLE, PERM_ALL,
+ NULL);
+ return true;
+ }
+};
+
+class LLToolsLookAtSelection : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const F32 PADDING_FACTOR = 2.f;
+ BOOL zoom = (userdata.asString() == "zoom");
+ if (!gSelectMgr->isEmpty())
+ {
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+
+ LLBBox selection_bbox = gSelectMgr->getBBoxOfSelection();
+ F32 angle_of_view = llmax(0.1f, gCamera->getAspect() > 1.f ? gCamera->getView() * gCamera->getAspect() : gCamera->getView());
+ F32 distance = selection_bbox.getExtentLocal().magVec() * PADDING_FACTOR / atan(angle_of_view);
+
+ LLVector3 obj_to_cam = gCamera->getOrigin() - selection_bbox.getCenterAgent();
+ obj_to_cam.normVec();
+
+ if (zoom)
+ {
+ gAgent.setCameraPosAndFocusGlobal(gSelectMgr->getSelectionCenterGlobal() + LLVector3d(obj_to_cam * distance), gSelectMgr->getSelectionCenterGlobal(), gSelectMgr->getFirstObject()->mID );
+ }
+ else
+ {
+ gAgent.setFocusGlobal( gSelectMgr->getSelectionCenterGlobal(), gSelectMgr->getFirstObject()->mID );
+ }
+ }
+ return true;
+ }
+};
+
+/*
+void handle_reset_rotation(void*)
+{
+ gSelectMgr->selectionResetRotation();
+
+ dialog_refresh_all();
+}
+*/
+
+class LLAvatarAddFriend : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if(avatar && !is_agent_friend(avatar->getID()))
+ {
+ request_friendship(avatar->getID());
+ }
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+};
+
+void complete_give_money(S32 option, void* user_data)
+{
+ if (option == 0)
+ {
+ gAgent.clearBusy();
+ }
+
+ LLUUID* object_id = (LLUUID*)user_data;
+
+ LLViewerObject* object = gObjectList.findObject(*object_id);
+ if (object)
+ {
+ if (object->isAvatar())
+ {
+ const BOOL is_group = FALSE;
+ LLFloaterPay::payDirectly(&give_money,
+ *object_id,
+ is_group);
+ }
+ else
+ {
+ LLFloaterPay::payViaObject(&give_money, *object_id);
+ }
+ }
+
+ delete object_id;
+}
+
+bool handle_give_money_dialog()
+{
+ LLViewerObject *objectp = gViewerWindow->lastObjectHit();
+ LLUUID* object_id = new LLUUID();
+
+ // Show avatar's name if paying attachment
+ if (objectp && objectp->isAttachment())
+ {
+ while (objectp && !objectp->isAvatar())
+ {
+ objectp = (LLViewerObject*)objectp->getParent();
+ }
+ }
+
+ if (objectp)
+ {
+ *object_id = objectp->getID();
+ }
+
+ if (gAgent.getBusy())
+ {
+ // warn users of being in busy mode during a transaction
+ gViewerWindow->alertXml("BusyModePay", complete_give_money, object_id);
+ }
+ else
+ {
+ complete_give_money(1, object_id);
+ }
+ return true;
+}
+
+class LLPayObject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_give_money_dialog();
+ }
+};
+
+class LLEnablePayObject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(gViewerWindow->lastObjectHit());
+ bool new_value = (avatar != NULL);
+ if (!new_value)
+ {
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ if( object )
+ {
+ LLViewerObject *parent = (LLViewerObject *)object->getParent();
+ if((object->flagTakesMoney()) || (parent && parent->flagTakesMoney()))
+ {
+ new_value = true;
+ }
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLObjectEnableSitOrStand : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ LLViewerObject* dest_object = NULL;
+ if(dest_object = gObjectList.findObject(gLastHitObjectID))
+ {
+ if(dest_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ new_value = true;
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+
+ // HACK: Update label
+ LLString label;
+ LLString sit_text;
+ LLString stand_text;
+ LLString param = userdata["data"].asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ sit_text = param.substr(0, offset);
+ stand_text = param.substr(offset+1);
+ }
+ if (sitting_on_selection())
+ {
+ label = stand_text;
+ }
+ else
+ {
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node && node->mValid && !node->mSitName.empty())
+ {
+ label.assign(node->mSitName);
+ }
+ else
+ {
+ label = sit_text;
+ }
+ }
+ gMenuHolder->childSetText("Object Sit", label);
+
+ return true;
+ }
+};
+
+void edit_ui(void*)
+{
+ LLFloater::setEditModeEnabled(!LLFloater::getEditModeEnabled());
+}
+
+void dump_select_mgr(void*)
+{
+ gSelectMgr->dump();
+}
+
+void dump_volume_mgr(void*)
+{
+ gVolumeMgr->dump();
+}
+
+void dump_inventory(void*)
+{
+ gInventory.dumpInventory();
+}
+
+
+void handle_first_tool(void*)
+{
+ gCurrentToolset->selectFirstTool();
+}
+
+
+void handle_next_tool(void*)
+{
+ gCurrentToolset->selectNextTool();
+}
+
+
+void handle_previous_tool(void*)
+{
+ gCurrentToolset->selectPrevTool();
+}
+
+
+// forcibly unlock an object
+void handle_force_unlock(void*)
+{
+ // First, make it public.
+ gSelectMgr->sendOwner(LLUUID::null, LLUUID::null, TRUE);
+
+ // Second, lie to the viewer and mark it editable and unowned
+ LLViewerObject* object;
+ for (object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ object->mFlags |= FLAGS_OBJECT_MOVE;
+ object->mFlags |= FLAGS_OBJECT_MODIFY;
+ object->mFlags |= FLAGS_OBJECT_COPY;
+
+ object->mFlags &= ~FLAGS_OBJECT_ANY_OWNER;
+ object->mFlags &= ~FLAGS_OBJECT_YOU_OWNER;
+ }
+}
+
+// Fullscreen debug stuff
+void handle_fullscreen_debug(void*)
+{
+ llinfos << "Width " << gViewerWindow->getWindowWidth() << " Height " << gViewerWindow->getWindowHeight() << llendl;
+ llinfos << "mouse_x_from_center(100) " << mouse_x_from_center(100) << " y " << mouse_y_from_center(100) << llendl;
+}
+
+void handle_crash(void*)
+{
+ llerrs << "This is an llerror" << llendl;
+}
+
+class LLWorldForceSun : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString tod = userdata.asString();
+ LLVector3 sun_direction;
+ if (tod == "sunrise")
+ {
+ sun_direction.setVec(1.0f, 0.f, 0.2f);
+ }
+ else if (tod == "noon")
+ {
+ sun_direction.setVec(0.0f, 0.3f, 1.0f);
+ }
+ else if (tod == "sunset")
+ {
+ sun_direction.setVec(-1.0f, 0.f, 0.2f);
+ }
+ else if (tod == "midnight")
+ {
+ sun_direction.setVec(0.0f, 0.3f, -1.0f);
+ }
+ else
+ {
+ gSky.setOverrideSun(FALSE);
+ return true;
+ }
+ sun_direction.normVec();
+ gSky.setOverrideSun(TRUE);
+ gSky.setSunDirection( sun_direction, LLVector3(0.f, 0.f, 0.f));
+ return true;
+ }
+};
+
+void handle_dump_followcam(void*)
+{
+ LLFollowCamMgr::dump();
+}
+
+void handle_viewer_enable_circuit_log(void*)
+{
+ llinfos << "Showing circuit information every " << gMessageSystem->mCircuitPrintFreq << " seconds" << llendl;
+ gErrorStream.setLevel( LLErrorStream::DEBUG );
+ gErrorStream.setDebugFlag( LLERR_CIRCUIT_INFO );
+ // and dump stuff out immediately
+ gMessageSystem->dumpCircuitInfo();
+}
+
+void handle_viewer_disable_circuit_log(void*)
+{
+ llinfos << "Hiding circuit information" << llendl;
+#if !LL_DEBUG
+ gErrorStream.setLevel( LLErrorStream::INFO );
+#endif
+ gErrorStream.clearDebugFlag( LLERR_CIRCUIT_INFO );
+}
+
+void handle_viewer_enable_message_log(void*)
+{
+ gMessageSystem->startLogging();
+}
+
+void handle_viewer_disable_message_log(void*)
+{
+ gMessageSystem->stopLogging();
+}
+
+// TomY TODO: Move!
+class LLShowFloater : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString floater_name = userdata.asString();
+ if (floater_name == "gestures")
+ {
+ LLFloaterGesture::toggleVisibility();
+ }
+ else if (floater_name == "appearance")
+ {
+ if (gAgent.getWearablesLoaded())
+ {
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+ }
+ else if (floater_name == "friends")
+ {
+ LLFloaterFriends::toggle(NULL);
+ }
+ else if (floater_name == "preferences")
+ {
+ LLFloaterPreference::show(NULL);
+ }
+ else if (floater_name == "toolbar")
+ {
+ LLToolBar::toggle(NULL);
+ }
+ else if (floater_name == "chat history")
+ {
+ LLFloaterChat::toggle(NULL);
+ }
+ else if (floater_name == "im")
+ {
+ LLToolBar::onClickIM(NULL);
+ }
+ else if (floater_name == "inventory")
+ {
+ LLInventoryView::toggleVisibility(NULL);
+ }
+ else if (floater_name == "mute list")
+ {
+ LLFloaterMute::toggle(NULL);
+ }
+ else if (floater_name == "camera controls")
+ {
+ LLFloaterCamera::toggle(NULL);
+ }
+ else if (floater_name == "movement controls")
+ {
+ LLFloaterMove::show(NULL);
+ }
+ else if (floater_name == "world map")
+ {
+ LLFloaterWorldMap::toggle(NULL);
+ }
+ else if (floater_name == "mini map")
+ {
+ LLFloaterMap::toggle(NULL);
+ }
+ else if (floater_name == "stat bar")
+ {
+ gDebugView->mStatViewp->setVisible(!gDebugView->mStatViewp->getVisible());
+ }
+ else if (floater_name == "account history")
+ {
+ LLFloaterAccountHistory::show(NULL);
+ }
+ else if (floater_name == "my land")
+ {
+ LLFloaterLandHoldings::show(NULL);
+ }
+ else if (floater_name == "about land")
+ {
+ if (gParcelMgr->selectionEmpty())
+ {
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+ }
+
+ LLFloaterLand::show();
+ }
+ else if (floater_name == "buy land")
+ {
+ if (gParcelMgr->selectionEmpty())
+ {
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+ }
+
+ gParcelMgr->startBuyLand();
+ }
+ else if (floater_name == "about region")
+ {
+ LLFloaterRegionInfo::show((void *)NULL);
+ }
+ else if (floater_name == "grid options")
+ {
+ LLFloaterBuildOptions::show(NULL);
+ }
+ else if (floater_name == "script errors")
+ {
+ LLFloaterScriptDebug::show(LLUUID::null);
+ }
+ else if (floater_name == "help")
+ {
+#if LL_LIBXUL_ENABLED
+ LLHtmlHelp::show(NULL);
+#endif
+ }
+ else if (floater_name == "complaint reporter")
+ {
+ // Prevent menu from appearing in screen shot.
+ gMenuHolder->hideMenus();
+ LLFloaterReporter::showFromMenu(COMPLAINT_REPORT);
+ }
+ else if (floater_name == "mean events")
+ {
+ if (!gNoRender)
+ {
+ LLFloaterBump::show(NULL);
+ }
+ }
+ else if (floater_name == "bug reporter")
+ {
+ // Prevent menu from appearing in screen shot.
+ gMenuHolder->hideMenus();
+ LLFloaterReporter::showFromMenu(BUG_REPORT);
+ }
+ else if (floater_name == "buy currency")
+ {
+ LLFloaterBuyCurrency::buyCurrency();
+ }
+ else if (floater_name == "about")
+ {
+ LLFloaterAbout::show(NULL);
+ }
+ return true;
+ }
+};
+
+class LLFloaterVisible : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString control_name = userdata["control"].asString();
+ LLString floater_name = userdata["data"].asString();
+ bool new_value = false;
+ if (floater_name == "friends")
+ {
+ new_value = LLFloaterFriends::visible(NULL);
+ }
+ else if (floater_name == "toolbar")
+ {
+ new_value = LLToolBar::visible(NULL);
+ }
+ else if (floater_name == "chat history")
+ {
+ new_value = LLFloaterChat::visible(NULL);
+ }
+ else if (floater_name == "im")
+ {
+ new_value = gIMView && gIMView->mTalkFloater && gIMView->mTalkFloater->getVisible();
+ }
+ else if (floater_name == "mute list")
+ {
+ new_value = LLFloaterMute::visible(NULL);
+ }
+ else if (floater_name == "camera controls")
+ {
+ new_value = LLFloaterCamera::visible(NULL);
+ }
+ else if (floater_name == "movement controls")
+ {
+ new_value = LLFloaterMove::visible(NULL);
+ }
+ else if (floater_name == "stat bar")
+ {
+ new_value = gDebugView->mStatViewp->getVisible();
+ }
+ gMenuHolder->findControl(control_name)->setValue(new_value);
+ return true;
+ }
+};
+
+void callback_show_url(S32 option, void* url)
+{
+ const char* urlp = (const char*)url;
+ if (0 == option)
+ {
+ LLWeb::loadURL(urlp);
+ }
+ delete urlp;
+}
+
+class LLPromptShowURL : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString param = userdata.asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ LLString alert = param.substr(0, offset);
+ LLString url = param.substr(offset+1);
+ char *url_copy = new char[url.size()+1];
+ strcpy(url_copy, url.c_str());
+ gViewerWindow->alertXml(alert, callback_show_url, url_copy);
+ }
+ else
+ {
+ llinfos << "PromptShowURL invalid parameters! Expecting \"ALERT,URL\"." << llendl;
+ }
+ return true;
+ }
+};
+
+void callback_show_file(S32 option, void* filename)
+{
+ const char* filenamep = (const char*)filename;
+ if (0 == option)
+ {
+ load_url_local_file(filenamep);
+ }
+ delete filenamep;
+}
+
+class LLPromptShowFile : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString param = userdata.asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ LLString alert = param.substr(0, offset);
+ LLString file = param.substr(offset+1);
+ char *file_copy = new char[file.size()+1];
+ strcpy(file_copy, file.c_str());
+ gViewerWindow->alertXml(alert, callback_show_file, file_copy);
+ }
+ else
+ {
+ llinfos << "PromptShowFile invalid parameters! Expecting \"ALERT,FILE\"." << llendl;
+ }
+ return true;
+ }
+};
+
+class LLShowAgentProfile : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID agent_id;
+ if (userdata.asString() == "agent")
+ {
+ agent_id = gAgent.getID();
+ }
+ else if (userdata.asString() == "hit object")
+ {
+ agent_id = gLastHitObjectID;
+ }
+ else
+ {
+ agent_id = userdata.asUUID();
+ }
+
+ LLVOAvatar* avatar = find_avatar_from_object(agent_id);
+ if (avatar)
+ {
+ LLFloaterAvatarInfo::showFromAvatar(avatar);
+ }
+ return true;
+ }
+};
+
+class LLShowAgentGroups : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID agent_id;
+ if (userdata.asString() == "agent")
+ {
+ agent_id = gAgent.getID();
+ }
+ else
+ {
+ agent_id = userdata.asUUID();
+ }
+ if(agent_id.notNull())
+ {
+ LLFloaterGroups::show(agent_id, LLFloaterGroups::AGENT_GROUPS);
+ }
+ return true;
+ }
+};
+
+void handle_focus(void *)
+{
+ if (gDisconnected)
+ {
+ return;
+ }
+
+ if (gAgent.getFocusOnAvatar())
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ }
+ else
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ }
+
+ gViewerWindow->moveCursorToCenter();
+
+ // Switch to camera toolset
+// gCurrentToolset = gCameraToolset;
+ gCurrentToolset->selectTool( gToolCamera );
+}
+
+class LLLandEdit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getFocusOnAvatar() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+
+ gAgent.cameraOrbitOver( F_PI * 0.25f );
+ gViewerWindow->moveCursorToCenter();
+ }
+ else if ( gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gViewerWindow->moveCursorToCenter();
+ }
+
+
+ gParcelMgr->selectParcelAt( gLastHitPosGlobal );
+
+ gFloaterTools->showMore(TRUE);
+ gFloaterView->bringToFront( gFloaterTools );
+
+ // Switch to land edit toolset
+ gCurrentToolset->selectTool( gToolParcel );
+ return true;
+ }
+};
+
+class LLWorldEnableBuyLand : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gParcelMgr->canAgentBuyParcel(
+ gParcelMgr->selectionEmpty()
+ ? gParcelMgr->getAgentParcel()
+ : gParcelMgr->getSelectedParcel(),
+ false);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_buy_land(void*)
+{
+ return gParcelMgr->canAgentBuyParcel(
+ gParcelMgr->getSelectedParcel(), false);
+}
+
+
+void handle_move(void*)
+{
+ if (gAgent.getFocusOnAvatar())
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+
+ gAgent.cameraZoomIn(0.666f);
+ }
+ else
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ }
+
+ gViewerWindow->moveCursorToCenter();
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolGrab );
+}
+
+
+void near_attach_object(BOOL success, void *user_data)
+{
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ U8 attachment_id;
+ if (attachment)
+ {
+ attachment_id = gAgent.getAvatarObject()->mAttachmentPoints.reverseLookup(attachment);
+ }
+ else
+ {
+ // interpret 0 as "default location"
+ attachment_id = 0;
+ }
+
+ gSelectMgr->sendAttach(attachment_id);
+ gSelectMgr->deselectTransient();
+}
+
+class LLObjectAttachToAvatar : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* selectedObject = gSelectMgr->getFirstRootObject();
+ if (selectedObject)
+ {
+ confirm_replace_attachment(0, NULL);
+ }
+ return true;
+ }
+};
+
+// move this somewhere global
+void handle_attach_to_avatar(void* user_data)
+{
+ LLViewerObject* selectedObject = gSelectMgr->getFirstRootObject();
+ if (selectedObject)
+ {
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ if (attachment && attachment->getObject(0))
+ {
+ gViewerWindow->alertXml("ReplaceAttachment", confirm_replace_attachment, user_data);
+ }
+ else
+ {
+ confirm_replace_attachment(0, user_data);
+ }
+ }
+}
+void confirm_replace_attachment(S32 option, void* user_data)
+{
+ if (option == 0/*YES*/)
+ {
+ LLViewerObject* selectedObject = gSelectMgr->getFirstRootObject();
+ if (selectedObject)
+ {
+ const F32 MIN_STOP_DISTANCE = 1.f; // meters
+ const F32 ARM_LENGTH = 0.5f; // meters
+ const F32 SCALE_FUDGE = 1.5f;
+
+ F32 stop_distance = SCALE_FUDGE * selectedObject->getMaxScale() + ARM_LENGTH;
+ if (stop_distance < MIN_STOP_DISTANCE)
+ {
+ stop_distance = MIN_STOP_DISTANCE;
+ }
+
+ LLVector3 walkToSpot = selectedObject->getPositionAgent();
+
+ // make sure we stop in front of the object
+ LLVector3 delta = walkToSpot - gAgent.getPositionAgent();
+ delta.normVec();
+ delta = delta * 0.5f;
+ walkToSpot -= delta;
+
+ gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, near_attach_object, user_data, stop_distance);
+ gAgent.clearFocusObject();
+ }
+ }
+}
+
+class LLAttachmentDrop : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Called when the user clicked on an object attached to them
+ // and selected "Drop".
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ if (!object)
+ {
+ llwarns << "handle_drop_attachment() - no object to drop" << llendl;
+ return true;
+ }
+
+ LLViewerObject *parent = (LLViewerObject*)object->getParent();
+ while (parent)
+ {
+ if(parent->isAvatar())
+ {
+ break;
+ }
+ object = parent;
+ parent = (LLViewerObject*)parent->getParent();
+ }
+
+ if (!object)
+ {
+ llwarns << "handle_detach() - no object to detach" << llendl;
+ return true;
+ }
+
+ if (object->isAvatar())
+ {
+ llwarns << "Trying to detach avatar from avatar." << llendl;
+ return true;
+ }
+
+ // The sendDropAttachment() method works on the list of selected
+ // objects. Thus we need to clear the list, make sure it only
+ // contains the object the user clicked, send the message,
+ // then clear the list.
+ // We use deselectAll to update the simulator's notion of what's
+ // selected, and removeAll just to change things locally.
+ //gSelectMgr->deselectAll();
+ //gSelectMgr->selectObjectAndFamily(object);
+ gSelectMgr->sendDropAttachment();
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+};
+
+// called from avatar pie menu
+void handle_detach_from_avatar(void* user_data)
+{
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ LLViewerObject* attached_object = attachment->getObject(0);
+
+ if (attached_object)
+ {
+ gMessageSystem->newMessage("ObjectDetach");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, attached_object->getLocalID());
+ gMessageSystem->sendReliable( gAgent.getRegionHost() );
+ }
+}
+
+void attach_label(LLString& label, void* user_data)
+{
+ LLViewerJointAttachment* attachmentp = (LLViewerJointAttachment*)user_data;
+ if (attachmentp)
+ {
+ label = attachmentp->getName();
+ if (attachmentp->getObject(0))
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID());
+ if (itemp)
+ {
+ label += LLString(" (") + itemp->getName() + LLString(")");
+ }
+ }
+ }
+}
+
+void detach_label(LLString& label, void* user_data)
+{
+ LLViewerJointAttachment* attachmentp = (LLViewerJointAttachment*)user_data;
+ if (attachmentp)
+ {
+ label = attachmentp->getName();
+ if (attachmentp->getObject(0))
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID());
+ if (itemp)
+ {
+ label += LLString(" (") + itemp->getName() + LLString(")");
+ }
+ }
+ }
+}
+
+
+class LLAttachmentDetach : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Called when the user clicked on an object attached to them
+ // and selected "Detach".
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ if (!object)
+ {
+ llwarns << "handle_detach() - no object to detach" << llendl;
+ return true;
+ }
+
+ LLViewerObject *parent = (LLViewerObject*)object->getParent();
+ while (parent)
+ {
+ if(parent->isAvatar())
+ {
+ break;
+ }
+ object = parent;
+ parent = (LLViewerObject*)parent->getParent();
+ }
+
+ if (!object)
+ {
+ llwarns << "handle_detach() - no object to detach" << llendl;
+ return true;
+ }
+
+ if (object->isAvatar())
+ {
+ llwarns << "Trying to detach avatar from avatar." << llendl;
+ return true;
+ }
+
+ // The sendDetach() method works on the list of selected
+ // objects. Thus we need to clear the list, make sure it only
+ // contains the object the user clicked, send the message,
+ // then clear the list.
+ // We use deselectAll to update the simulator's notion of what's
+ // selected, and removeAll just to change things locally.
+ //RN: I thought it was more useful to detach everything that was selected
+ if (gSelectMgr->selectionIsAttachment())
+ {
+ gSelectMgr->sendDetach();
+ gSelectMgr->deselectAll();
+ }
+ return true;
+ }
+};
+
+//Adding an observer for a Jira 2422 and needs to be a fetch observer
+//for Jira 3119
+class LLWornItemFetchedObserver : public LLInventoryFetchObserver
+{
+public:
+ LLWornItemFetchedObserver() {}
+ virtual ~LLWornItemFetchedObserver() {}
+
+protected:
+ virtual void done()
+ {
+ gPieAttachment->buildDrawLabels();
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+// You can only drop items on parcels where you can build.
+class LLAttachmentEnableDrop : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ BOOL can_build = gAgent.isGodlike() || (parcel && parcel->getAllowModify());
+
+ //Add an inventory observer to only allow dropping the newly attached item
+ //once it exists in your inventory. Look at Jira 2422.
+ //-jwolk
+
+ // A bug occurs when you wear/drop an item before it actively is added to your inventory
+ // if this is the case (you're on a slow sim, etc.) a copy of the object,
+ // well, a newly created object with the same properties, is placed
+ // in your inventory. Therefore, we disable the drop option until the
+ // item is in your inventory
+
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ LLViewerJointAttachment* attachment_pt = NULL;
+ LLInventoryItem* item = NULL;
+
+ if ( object )
+ {
+ S32 attachmentID = ATTACHMENT_ID_FROM_STATE(object->getState());
+ attachment_pt = gAgent.getAvatarObject()->mAttachmentPoints.getIfThere(attachmentID);
+
+ if ( attachment_pt )
+ {
+ // make sure item is in your inventory (it could be a delayed attach message being sent from the sim)
+ // so check to see if the item is in the inventory already
+ item = gInventory.getItem(attachment_pt->getItemID());
+
+ if ( !item )
+ {
+ // Item does not exist, make an observer to enable the pie menu
+ // when the item finishes fetching worst case scenario
+ // if a fetch is already out there (being sent from a slow sim)
+ // we refetch and there are 2 fetches
+ LLWornItemFetchedObserver* wornItemFetched = new LLWornItemFetchedObserver();
+ LLInventoryFetchObserver::item_ref_t items; //add item to the inventory item to be fetched
+
+ items.push_back(attachment_pt->getItemID());
+
+ wornItemFetched->fetchItems(items);
+ gInventory.addObserver(wornItemFetched);
+ }
+ }
+ }
+
+ //now check to make sure that the item is actually in the inventory before we enable dropping it
+ bool new_value = enable_detach(NULL) && can_build && item;
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_detach(void*)
+{
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ if (!object) return FALSE;
+ if (!object->isAttachment()) return FALSE;
+
+ // Find the avatar who owns this attachment
+ LLViewerObject* avatar = object;
+ while (avatar)
+ {
+ // ...if it's you, good to detach
+ if (avatar->getID() == gAgent.getID())
+ {
+ return TRUE;
+ }
+
+ avatar = (LLViewerObject*)avatar->getParent();
+ }
+
+ return FALSE;
+}
+
+class LLAttachmentEnableDetach : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_detach(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// Used to tell if the selected object can be attached to your avatar.
+BOOL object_selected_and_point_valid(void *user_data)
+{
+ //LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ for (LLViewerObject *object = gSelectMgr->getFirstRootObject(); object; object = gSelectMgr->getNextRootObject())
+ {
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++ )
+ {
+ if (object->mChildList[child_num]->isAvatar())
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return ((gSelectMgr != NULL) &&
+ (gSelectMgr->getRootObjectCount() == 1) &&
+ (gSelectMgr->getFirstRootObject()->getPCode() == LL_PCODE_VOLUME) &&
+ gSelectMgr->getFirstRootObject()->permYouOwner() &&
+ !((LLViewerObject*)gSelectMgr->getFirstRootObject()->getRoot())->isAvatar() &&
+ (gSelectMgr->getFirstRootObject()->getNVPair("AssetContainer") == NULL));
+}
+
+// Also for seeing if object can be attached. See above.
+class LLObjectEnableWear : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ if (gSelectMgr)
+ {
+ LLViewerObject* first_root = gSelectMgr->getFirstRootObject();
+ if (first_root)
+ {
+ new_value = gSelectMgr->getRootObjectCount() == 1
+ && first_root->getPCode() == LL_PCODE_VOLUME
+ && first_root->permYouOwner()
+ && !((LLViewerObject*)gSelectMgr->getFirstRootObject()->getRoot())->isAvatar()
+ && (first_root->getNVPair("AssetContainer") == NULL);
+ }
+ }
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+BOOL object_attached(void *user_data)
+{
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ return attachment->getObject(0) != NULL;
+}
+
+class LLAvatarSendIM : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if(avatar)
+ {
+ LLString name("IM");
+ LLNameValue *first = avatar->getNVPair("FirstName");
+ LLNameValue *last = avatar->getNVPair("LastName");
+ if (first && last)
+ {
+ name.assign( first->getString() );
+ name.append(" ");
+ name.append( last->getString() );
+ }
+
+ gIMView->setFloaterOpen(TRUE);
+ //EInstantMessage type = have_agent_callingcard(gLastHitObjectID)
+ // ? IM_SESSION_ADD : IM_SESSION_CARDLESS_START;
+ gIMView->addSession(name,
+ IM_NOTHING_SPECIAL,
+ avatar->getID());
+ }
+ return true;
+ }
+};
+
+
+void handle_activate(void*)
+{
+}
+
+BOOL enable_activate(void*)
+{
+ return FALSE;
+}
+
+class LLToolsSelectedScriptAction : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString action = userdata.asString();
+ LLFloaterScriptQueue *queue = NULL;
+ if (action == "compile")
+ {
+ queue = LLFloaterCompileQueue::create();
+ }
+ else if (action == "reset")
+ {
+ queue = LLFloaterResetQueue::create();
+ }
+ else if (action == "start")
+ {
+ queue = LLFloaterRunQueue::create();
+ }
+ else if (action == "stop")
+ {
+ queue = LLFloaterNotRunQueue::create();
+ }
+ if (!queue) return true;
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotRecompileSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotRecompileSelectObjectsNoPermission");
+ }
+ }
+ return true;
+ }
+};
+
+void handle_reset_selection(void*)
+{
+ LLFloaterResetQueue* queue = LLFloaterResetQueue::create();
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotResetSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotResetSelectObjectsNoPermission");
+ }
+ }
+}
+
+void handle_set_run_selection(void*)
+{
+ LLFloaterRunQueue* queue = LLFloaterRunQueue::create();
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotSetRunningSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotSerRunningSelectObjectsNoPermission");
+ }
+ }
+}
+
+void handle_set_not_run_selection(void*)
+{
+ LLFloaterNotRunQueue* queue = LLFloaterNotRunQueue::create();
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotSetRunningNotSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotSerRunningNotSelectObjectsNoPermission");
+ }
+ }
+}
+
+void handle_selected_texture_info(void*)
+{
+ LLSelectNode* node = NULL;
+ for (node = gSelectMgr->getFirstNode(); node != NULL; node = gSelectMgr->getNextNode())
+ {
+ if (!node->mValid) continue;
+
+ std::string msg;
+ msg.assign("Texture info for: ");
+ msg.append(node->mName);
+ LLChat chat(msg);
+ LLFloaterChat::addChat(chat);
+
+ U8 te_count = node->getObject()->getNumTEs();
+ // map from texture ID to list of faces using it
+ typedef std::map< LLUUID, std::vector<U8> > map_t;
+ map_t faces_per_texture;
+ for (U8 i = 0; i < te_count; i++)
+ {
+ if (!node->isTESelected(i)) continue;
+
+ LLViewerImage* img = node->getObject()->getTEImage(i);
+ LLUUID image_id = img->getID();
+ faces_per_texture[image_id].push_back(i);
+ }
+ // Per-texture, dump which faces are using it.
+ map_t::iterator it;
+ for (it = faces_per_texture.begin(); it != faces_per_texture.end(); ++it)
+ {
+ LLUUID image_id = it->first;
+ U8 te = it->second[0];
+ LLViewerImage* img = node->getObject()->getTEImage(te);
+ S32 height = img->getHeight();
+ S32 width = img->getWidth();
+ S32 components = img->getComponents();
+ std::string image_id_string;
+ if (gAgent.isGodlike())
+ {
+ image_id_string = image_id.getString() + " ";
+ }
+ msg = llformat("%s%dx%d %s on face ",
+ image_id_string.c_str(),
+ width,
+ height,
+ (components == 4 ? "alpha" : "opaque"));
+ for (U8 i = 0; i < it->second.size(); ++i)
+ {
+ msg.append( llformat("%d ", (S32)(it->second[i])));
+ }
+ LLChat chat(msg);
+ LLFloaterChat::addChat(chat);
+ }
+ }
+}
+
+void handle_dump_image_list(void*)
+{
+ gImageList.dump();
+}
+
+void handle_test_male(void*)
+{
+ wear_outfit_by_name("Male Shape & Outfit");
+ //gGestureList.requestResetFromServer( TRUE );
+}
+
+void handle_test_female(void*)
+{
+ wear_outfit_by_name("Female Shape & Outfit");
+ //gGestureList.requestResetFromServer( FALSE );
+}
+
+void handle_toggle_pg(void*)
+{
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ gAgent.mAccess = SIM_ACCESS_MATURE;
+ }
+ else
+ {
+ gAgent.mAccess = SIM_ACCESS_PG;
+ }
+
+ LLFloaterWorldMap::reloadIcons(NULL);
+
+ llinfos << "Access set to " << (S32)gAgent.mAccess << llendl;
+}
+
+void handle_dump_attachments(void*)
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ llinfos << "NO AVATAR" << llendl;
+ return;
+ }
+
+ for( LLViewerJointAttachment* attachment = avatar->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = avatar->mAttachmentPoints.getNextData() )
+ {
+ S32 key = avatar->mAttachmentPoints.getCurrentKeyWithoutIncrement();
+ BOOL visible = (attachment->getObject(0) != NULL &&
+ attachment->getObject(0)->mDrawable.notNull() &&
+ !attachment->getObject(0)->mDrawable->isRenderType(0));
+ LLVector3 pos;
+ if (visible) pos = attachment->getObject(0)->mDrawable->getPosition();
+ llinfos << "ATTACHMENT " << key << ": item_id=" << attachment->getItemID()
+ << (attachment->getObject(0) ? " present " : " absent ")
+ << (visible ? "visible " : "invisible ")
+ << " at " << pos
+ << " and " << (visible ? attachment->getObject(0)->getPosition() : LLVector3::zero)
+ << llendl;
+ }
+}
+
+//---------------------------------------------------------------------
+// Callbacks for enabling/disabling items
+//---------------------------------------------------------------------
+
+BOOL menu_ui_enabled(void *user_data)
+{
+ BOOL high_res = gSavedSettings.getBOOL( "HighResSnapshot" );
+ return !high_res;
+}
+
+// TomY TODO DEPRECATE & REMOVE
+void menu_toggle_control( void* user_data )
+{
+ BOOL checked = gSavedSettings.getBOOL( static_cast<char*>(user_data) );
+ if (LLString(static_cast<char*>(user_data)) == "HighResSnapshot" && !checked)
+ {
+ // High Res Snapshot active, must uncheck RenderUIInSnapshot
+ gSavedSettings.setBOOL( "RenderUIInSnapshot", FALSE );
+ }
+ gSavedSettings.setBOOL( static_cast<char*>(user_data), !checked );
+}
+
+
+// these are used in the gl menus to set control values.
+class LLToggleControl : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString control_name = userdata.asString();
+ BOOL checked = gSavedSettings.getBOOL( control_name );
+ if (control_name == "HighResSnapshot" && !checked)
+ {
+ // High Res Snapshot active, must uncheck RenderUIInSnapshot
+ gSavedSettings.setBOOL( "RenderUIInSnapshot", FALSE );
+ }
+ gSavedSettings.setBOOL( control_name, !checked );
+ return true;
+ }
+};
+
+// As above, but can be a callback from a LLCheckboxCtrl
+void check_toggle_control( LLUICtrl *, void* user_data )
+{
+ BOOL checked = gSavedSettings.getBOOL( static_cast<char*>(user_data) );
+ gSavedSettings.setBOOL( static_cast<char*>(user_data), !checked );
+}
+
+BOOL menu_check_control( void* user_data)
+{
+ return gSavedSettings.getBOOL((char*)user_data);
+}
+
+//
+void menu_toggle_variable( void* user_data )
+{
+ BOOL checked = *(BOOL*)user_data;
+ *(BOOL*)user_data = !checked;
+}
+
+BOOL menu_check_variable( void* user_data)
+{
+ return *(BOOL*)user_data;
+}
+
+
+BOOL enable_land_selected( void* )
+{
+ return gParcelMgr && !(gParcelMgr->selectionEmpty());
+}
+
+class LLSomethingSelected : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = !(gSelectMgr->isEmpty());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLSomethingSelectedNoHUD : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = !(gSelectMgr->isEmpty()) && !(gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_more_than_one_selected(void* )
+{
+ return (gSelectMgr->getObjectCount() > 1);
+}
+
+class LLEditableSelected : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = (gSelectMgr->getFirstEditableObject() != NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsEnableTakeCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ if (gSelectMgr)
+ {
+ new_value = true;
+#ifndef HACKED_GODLIKE_VIEWER
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (gInProductionGrid || !gAgent.isGodlike())
+# endif
+ {
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ if(obj)
+ {
+ for( ; obj; obj = gSelectMgr->getNextRootObject())
+ {
+ if(!(obj->permCopy()) || obj->isAttachment())
+ {
+ new_value = false;
+ }
+ }
+ }
+ }
+#endif // HACKED_GODLIKE_VIEWER
+ }
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_selection_you_own_all(void*)
+{
+ LLViewerObject *obj;
+ for (obj = gSelectMgr->getFirstRootObject(); obj; obj = gSelectMgr->getNextRootObject())
+ {
+ if (!obj->permYouOwner())
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL enable_selection_you_own_one(void*)
+{
+ LLViewerObject *obj;
+ for (obj = gSelectMgr->getFirstRootObject(); obj; obj = gSelectMgr->getNextRootObject())
+ {
+ if (obj->permYouOwner())
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+class LLHasAsset : public LLInventoryCollectFunctor
+{
+public:
+ LLHasAsset(const LLUUID& id) : mAssetID(id), mHasAsset(FALSE) {}
+ virtual ~LLHasAsset() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+ BOOL hasAsset() const { return mHasAsset; }
+
+protected:
+ LLUUID mAssetID;
+ BOOL mHasAsset;
+};
+
+bool LLHasAsset::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item && item->getAssetUUID() == mAssetID)
+ {
+ mHasAsset = TRUE;
+ }
+ return FALSE;
+}
+
+BOOL enable_save_into_inventory(void*)
+{
+ if(gSelectMgr)
+ {
+ // find the last root
+ LLSelectNode* last_node = NULL;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ last_node = node;
+ }
+
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+# endif
+ // check all pre-req's for save into inventory.
+ if(last_node && last_node->mValid && !last_node->mItemID.isNull()
+ && (last_node->mPermissions->getOwner() == gAgent.getID())
+ && (gInventory.getItem(last_node->mItemID) != NULL))
+ {
+ LLViewerObject* obj = last_node->getObject();
+ if( obj && !obj->isAttachment() )
+ {
+ return TRUE;
+ }
+ }
+#endif
+ }
+ return FALSE;
+}
+
+class LLToolsEnableSaveToInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_save_into_inventory(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_save_into_task_inventory(void*)
+{
+ if(gSelectMgr)
+ {
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(node && (node->mValid) && (!node->mFromTaskID.isNull()))
+ {
+ // *FIX: check to see if the fromtaskid object exists.
+
+ LLViewerObject* obj = node->getObject();
+ if( obj && !obj->isAttachment() )
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+class LLToolsEnableSaveToObjectInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_save_into_task_inventory(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_not_thirdperson(void*)
+{
+ return !gAgent.cameraThirdPerson();
+}
+
+class LLFileEnableUpload : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gStatusBar && gGlobalEconomy && (gStatusBar->getBalance() >= gGlobalEconomy->getPriceUpload());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_export_selected(void *)
+{
+ if (gSelectMgr->isEmpty())
+ {
+ return FALSE;
+ }
+ if (!gExporterRequestID.isNull())
+ {
+ return FALSE;
+ }
+ if (!LLUploadDialog::modalUploadIsFinished())
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+class LLViewEnableMouselook : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // You can't go directly from customize avatar to mouselook.
+ // TODO: write code with appropriate dialogs to handle this transition.
+ bool new_value = (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() && !gSavedSettings.getBOOL("FreezeTime"));
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsEnableToolNotPie : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = ( gToolMgr->getCurrentTool(MASK_NONE) != gToolPie );
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLWorldEnableCreateLandmark : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike() ||
+ (gAgent.getRegion() && gAgent.getRegion()->getAllowLandmark());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLWorldEnableSetHomeLocation : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike() ||
+ (gAgent.getRegion() && gAgent.getRegion()->getAllowSetHome());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_region_owner(void*)
+{
+ if(gAgent.getRegion() && gAgent.getRegion()->getOwner() == gAgent.getID())
+ return TRUE;
+ return enable_god_customer_service(NULL);
+}
+
+BOOL enable_god_full(void*)
+{
+ return gAgent.getGodLevel() >= GOD_FULL;
+}
+
+BOOL enable_god_liaison(void*)
+{
+ return gAgent.getGodLevel() >= GOD_LIAISON;
+}
+
+BOOL enable_god_customer_service(void*)
+{
+ return gAgent.getGodLevel() >= GOD_CUSTOMER_SERVICE;
+}
+
+BOOL enable_god_basic(void*)
+{
+ return gAgent.getGodLevel() > GOD_NOT;
+}
+
+void toggle_vbo(void *)
+{
+ gPipeline.mUseVBO = !gPipeline.mUseVBO;
+
+ if (!gPipeline.usingAGP())
+ {
+ return;
+ }
+
+ gPipeline.setUseAGP(FALSE);
+ gPipeline.setUseAGP(TRUE);
+
+ gSavedSettings.setBOOL("RenderUseVBO", gPipeline.mUseVBO);
+}
+
+BOOL check_vbo(void *)
+{
+ return gPipeline.mUseVBO;
+}
+
+#if 0 // 1.9.2
+void toggle_vertex_shaders(void *)
+{
+ BOOL use_shaders = gPipeline.getUseVertexShaders();
+ gPipeline.setUseVertexShaders(use_shaders);
+}
+
+BOOL check_vertex_shaders(void *)
+{
+ return gPipeline.getUseVertexShaders();
+}
+#endif
+
+void toggle_glow(void *)
+{
+ gRenderLightGlows = !gRenderLightGlows;
+
+ gSavedSettings.setBOOL("RenderLightGlows", gRenderLightGlows);
+}
+
+BOOL check_glow(void *)
+{
+ return gRenderLightGlows;
+}
+
+
+void toggle_show_xui_names(void *)
+{
+ BOOL showXUINames = gSavedSettings.getBOOL("ShowXUINames");
+
+ showXUINames = !showXUINames;
+ gSavedSettings.setBOOL("ShowXUINames", showXUINames);
+}
+
+BOOL check_show_xui_names(void *)
+{
+ return gSavedSettings.getBOOL("ShowXUINames");
+}
+
+
+
+void toggle_cull_small(void *)
+{
+// gPipeline.mCullBySize = !gPipeline.mCullBySize;
+//
+// gSavedSettings.setBOOL("RenderCullBySize", gPipeline.mCullBySize);
+}
+
+class LLToolsSelectOnlyMyObjects : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ BOOL cur_val = gSavedSettings.getBOOL("SelectOwnedOnly");
+
+ gSavedSettings.setBOOL("SelectOwnedOnly", ! cur_val );
+
+ return true;
+ }
+};
+
+class LLToolsSelectOnlyMovableObjects : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ BOOL cur_val = gSavedSettings.getBOOL("SelectMovableOnly");
+
+ gSavedSettings.setBOOL("SelectMovableOnly", ! cur_val );
+
+ return true;
+ }
+};
+
+class LLToolsSelectBySurrounding : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLSelectMgr::sRectSelectInclusive = !LLSelectMgr::sRectSelectInclusive;
+
+ gSavedSettings.setBOOL("RectangleSelectInclusive", LLSelectMgr::sRectSelectInclusive);
+ return true;
+ }
+};
+
+class LLToolsShowHiddenSelection : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // TomY TODO Merge these
+ LLSelectMgr::sRenderHiddenSelections = !LLSelectMgr::sRenderHiddenSelections;
+
+ gSavedSettings.setBOOL("RenderHiddenSelections", LLSelectMgr::sRenderHiddenSelections);
+ return true;
+ }
+};
+
+class LLToolsShowSelectionLightRadius : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // TomY TODO merge these
+ LLSelectMgr::sRenderLightRadius = !LLSelectMgr::sRenderLightRadius;
+
+ gSavedSettings.setBOOL("RenderLightRadius", LLSelectMgr::sRenderLightRadius);
+ return true;
+ }
+};
+
+void reload_personal_settings_overrides(void *)
+{
+ llinfos << "Loading overrides from " << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,"overrides.xml") << llendl;
+
+ gSavedSettings.loadFromFile(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,"overrides.xml"));
+}
+
+void force_breakpoint(void *)
+{
+#if LL_WINDOWS // Forcing a breakpoint
+ DebugBreak();
+#endif
+}
+
+void reload_vertex_shader(void *)
+{
+ //THIS WOULD BE AN AWESOME PLACE TO RELOAD SHADERS... just a thought - DaveP
+}
+
+void flush_animations(void *)
+{
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->resetAnimations();
+ }
+}
+
+void slow_mo_animations(void*)
+{
+ static BOOL slow_mo = FALSE;
+ if (slow_mo)
+ {
+ gAgent.getAvatarObject()->setAnimTimeFactor(1.f);
+ slow_mo = FALSE;
+ }
+ else
+ {
+ gAgent.getAvatarObject()->setAnimTimeFactor(0.2f);
+ slow_mo = TRUE;
+ }
+}
+
+void handle_dump_avatar_local_textures(void*)
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ avatar->dumpLocalTextures();
+ }
+}
+
+void handle_debug_avatar_textures(void*)
+{
+ LLFloaterAvatarTextures::show(gLastHitObjectID);
+}
+
+void handle_grab_texture(void* data)
+{
+ LLVOAvatar::ETextureIndex index = (LLVOAvatar::ETextureIndex) ((U32) data);
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if ( avatar )
+ {
+ const LLUUID& asset_id = avatar->grabLocalTexture(index);
+ llinfos << "Adding baked texture " << asset_id << " to inventory." << llendl;
+ LLAssetType::EType asset_type = LLAssetType::AT_TEXTURE;
+ LLInventoryType::EType inv_type = LLInventoryType::IT_TEXTURE;
+ LLUUID folder_id(gInventory.findCategoryUUIDForType(asset_type));
+ if(folder_id.notNull())
+ {
+ LLString name = "Baked ";
+ switch (index)
+ {
+ case LLVOAvatar::TEX_EYES_BAKED:
+ name.append("Iris");
+ break;
+ case LLVOAvatar::TEX_HEAD_BAKED:
+ name.append("Head");
+ break;
+ case LLVOAvatar::TEX_UPPER_BAKED:
+ name.append("Upper Body");
+ break;
+ case LLVOAvatar::TEX_LOWER_BAKED:
+ name.append("Lower Body");
+ break;
+ case LLVOAvatar::TEX_SKIRT_BAKED:
+ name.append("Skirt");
+ break;
+ default:
+ name.append("Unknown");
+ break;
+ }
+ name.append(" Texture");
+
+ LLUUID item_id;
+ item_id.generate();
+ LLPermissions perm;
+ perm.init(gAgentID,
+ gAgentID,
+ LLUUID::null,
+ LLUUID::null);
+ U32 next_owner_perm = PERM_MOVE | PERM_TRANSFER;
+ perm.initMasks(PERM_ALL,
+ PERM_ALL,
+ PERM_NONE,
+ PERM_NONE,
+ next_owner_perm);
+ S32 creation_date_now = time_corrected();
+ LLPointer<LLViewerInventoryItem> item
+ = new LLViewerInventoryItem(item_id,
+ folder_id,
+ perm,
+ asset_id,
+ asset_type,
+ inv_type,
+ name,
+ "",
+ LLSaleInfo::DEFAULT,
+ LLInventoryItem::II_FLAGS_NONE,
+ creation_date_now);
+
+ item->updateServer(TRUE);
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+
+ // Show the preview panel for textures to let
+ // user know that the image is now in inventory.
+ if(view)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+
+ view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO);
+ view->getPanel()->openSelected();
+ //LLInventoryView::dumpSelectionInformation((void*)view);
+ // restore keyboard focus
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ }
+ }
+ else
+ {
+ llwarns << "Can't find a folder to put it in" << llendl;
+ }
+ }
+}
+
+BOOL enable_grab_texture(void* data)
+{
+ LLVOAvatar::ETextureIndex index = (LLVOAvatar::ETextureIndex) ((U32) data);
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if ( avatar )
+ {
+ return avatar->canGrabLocalTexture(index);
+ }
+ return FALSE;
+}
+
+// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing.
+// Returns NULL on failure.
+LLVOAvatar* find_avatar_from_object( LLViewerObject* object )
+{
+ if (object)
+ {
+ if( object->isAttachment() )
+ {
+ do
+ {
+ object = (LLViewerObject*) object->getParent();
+ }
+ while( object && !object->isAvatar() );
+ }
+ else
+ if( !object->isAvatar() )
+ {
+ object = NULL;
+ }
+ }
+
+ return (LLVOAvatar*) object;
+}
+
+
+// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing.
+// Returns NULL on failure.
+LLVOAvatar* find_avatar_from_object( const LLUUID& object_id )
+{
+ return find_avatar_from_object( gObjectList.findObject(object_id) );
+}
+
+
+void handle_disconnect_viewer(void *)
+{
+ char message[2048];
+ message[0] = '\0';
+
+ sprintf(message, "Testing viewer disconnect");
+
+ do_disconnect(message);
+}
+
+
+class LLToolsUseSelectionForGrid : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gSelectMgr->clearGridObjects();
+
+ for (LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ objectp;
+ objectp = gSelectMgr->getNextRootObject())
+ {
+ gSelectMgr->addGridObject(objectp);
+ }
+ gSelectMgr->setGridMode(GRID_MODE_REF_OBJECT);
+ if (gFloaterTools)
+ {
+ gFloaterTools->mComboGridMode->setCurrentByIndex((S32)GRID_MODE_REF_OBJECT);
+ }
+ return true;
+ }
+};
+
+void handle_test_load_url(void*)
+{
+ LLWeb::loadURL("");
+ LLWeb::loadURL("hacker://www.google.com/");
+ LLWeb::loadURL("http");
+ LLWeb::loadURL("http://www.google.com/");
+}
+
+//
+// LLViewerMenuHolderGL
+//
+
+BOOL LLViewerMenuHolderGL::hideMenus()
+{
+ BOOL handled = LLMenuHolderGL::hideMenus();
+ if (handled)
+ {
+ gSelectMgr->deselectTransient();
+ if (!gFloaterTools->getVisible() && !LLFloaterLand::floaterVisible())
+ {
+ gParcelMgr->deselectLand();
+ }
+ }
+ return handled;
+}
+
+void handle_save_to_xml(void*)
+{
+ LLFloater* frontmost = gFloaterView->getFrontmost();
+ if (!frontmost)
+ {
+ gViewerWindow->alertXml("NoFrontmostFloater");
+ return;
+ }
+
+ LLString default_name = "floater_";
+ default_name += frontmost->getTitle();
+ default_name += ".xml";
+
+ LLString::toLower(default_name);
+ LLString::replaceChar(default_name, ' ', '_');
+ LLString::replaceChar(default_name, '/', '_');
+ LLString::replaceChar(default_name, ':', '_');
+ LLString::replaceChar(default_name, '"', '_');
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getSaveFile(LLFilePicker::FFSAVE_XML, default_name.c_str()))
+ {
+ LLString filename = picker.getFirstFile();
+ gUICtrlFactory->saveToXML(frontmost, filename);
+ }
+}
+
+void handle_load_from_xml(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getOpenFile(LLFilePicker::FFLOAD_XML))
+ {
+ LLString filename = picker.getFirstFile();
+ LLFloater* floater = new LLFloater("sample_floater");
+ gUICtrlFactory->buildFloater(floater, filename);
+ }
+}
+
+void handle_rebake_textures(void*)
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (!avatar) return;
+
+ // Slam pending upload count to "unstick" things
+ bool slam_for_debug = true;
+ avatar->forceBakeAllTextures(slam_for_debug);
+}
+
+void toggle_visibility(void* user_data)
+{
+ LLView* viewp = (LLView*)user_data;
+ viewp->setVisible(!viewp->getVisible());
+}
+
+BOOL get_visibility(void* user_data)
+{
+ LLView* viewp = (LLView*)user_data;
+ return viewp->getVisible();
+}
+
+// TomY TODO: Get rid of these?
+class LLViewShowHoverTips : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLHoverView::sShowHoverTips = !LLHoverView::sShowHoverTips;
+ return true;
+ }
+};
+
+class LLViewCheckShowHoverTips : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLHoverView::sShowHoverTips;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// TomY TODO: Get rid of these?
+class LLViewHighlightTransparent : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLDrawPoolAlpha::sShowDebugAlpha = !LLDrawPoolAlpha::sShowDebugAlpha;
+ return true;
+ }
+};
+
+class LLViewCheckHighlightTransparent : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLDrawPoolAlpha::sShowDebugAlpha;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLViewToggleBeacon : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString beacon = userdata.asString();
+ if (beacon == "scripts")
+ {
+ LLPipeline::toggleRenderScriptedBeacons(NULL);
+ }
+ else if (beacon == "physical")
+ {
+ LLPipeline::toggleRenderPhysicalBeacons(NULL);
+ }
+ else if (beacon == "sounds")
+ {
+ LLPipeline::toggleRenderSoundBeacons(NULL);
+ }
+ else if (beacon == "particles")
+ {
+ LLPipeline::toggleRenderParticleBeacons(NULL);
+ }
+ return true;
+ }
+};
+
+class LLViewCheckBeaconEnabled : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString beacon = userdata["data"].asString();
+ bool new_value = false;
+ if (beacon == "scripts")
+ {
+ new_value = LLPipeline::getRenderScriptedBeacons(NULL);
+ }
+ else if (beacon == "physical")
+ {
+ new_value = LLPipeline::getRenderPhysicalBeacons(NULL);
+ }
+ else if (beacon == "sounds")
+ {
+ new_value = LLPipeline::getRenderSoundBeacons(NULL);
+ }
+ else if (beacon == "particles")
+ {
+ new_value = LLPipeline::getRenderParticleBeacons(NULL);
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLViewToggleRenderType : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString type = userdata.asString();
+ if (type == "particles")
+ {
+ LLPipeline::toggleRenderType((void *)(S32)LLPipeline::RENDER_TYPE_PARTICLES);
+ }
+ return true;
+ }
+};
+
+class LLViewCheckRenderType : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString type = userdata["data"].asString();
+ bool new_value = false;
+ if (type == "particles")
+ {
+ new_value = LLPipeline::toggleRenderTypeControlNegated((void *)(S32)LLPipeline::RENDER_TYPE_PARTICLES);
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// TomY TODO: Get rid of these?
+class LLViewShowHUDAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLDrawPoolHUD::sShowHUDAttachments = !LLDrawPoolHUD::sShowHUDAttachments;
+ return true;
+ }
+};
+
+class LLViewCheckHUDAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLDrawPoolHUD::sShowHUDAttachments;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditEnableTakeOff : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString control_name = userdata["control"].asString();
+ LLString clothing = userdata["data"].asString();
+ bool new_value = false;
+ if (clothing == "shirt")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SHIRT);
+ }
+ if (clothing == "pants")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_PANTS);
+ }
+ if (clothing == "shoes")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SHOES);
+ }
+ if (clothing == "socks")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SOCKS);
+ }
+ if (clothing == "jacket")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_JACKET);
+ }
+ if (clothing == "gloves")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_GLOVES);
+ }
+ if (clothing == "undershirt")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_UNDERSHIRT);
+ }
+ if (clothing == "underpants")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_UNDERPANTS);
+ }
+ if (clothing == "skirt")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SKIRT);
+ }
+ gMenuHolder->findControl(control_name)->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditTakeOff : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString clothing = userdata.asString();
+ if (clothing == "shirt")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SHIRT);
+ }
+ else if (clothing == "pants")
+ {
+ LLAgent::userRemoveWearable((void*)WT_PANTS);
+ }
+ else if (clothing == "shoes")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SHOES);
+ }
+ else if (clothing == "socks")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SOCKS);
+ }
+ else if (clothing == "jacket")
+ {
+ LLAgent::userRemoveWearable((void*)WT_JACKET);
+ }
+ else if (clothing == "gloves")
+ {
+ LLAgent::userRemoveWearable((void*)WT_GLOVES);
+ }
+ else if (clothing == "undershirt")
+ {
+ LLAgent::userRemoveWearable((void*)WT_UNDERSHIRT);
+ }
+ else if (clothing == "underpants")
+ {
+ LLAgent::userRemoveWearable((void*)WT_UNDERPANTS);
+ }
+ else if (clothing == "skirt")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SKIRT);
+ }
+ else if (clothing == "all")
+ {
+ LLAgent::userRemoveAllClothes(NULL);
+ }
+ return true;
+ }
+};
+
+class LLWorldChat : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_chat(NULL);
+ return true;
+ }
+};
+
+class LLWorldStartGesture : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_slash_key(NULL);
+ return true;
+ }
+};
+
+class LLToolsSelectTool : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString tool_name = userdata.asString();
+ if (tool_name == "focus")
+ {
+ gCurrentToolset->selectToolByIndex(1);
+ }
+ else if (tool_name == "move")
+ {
+ gCurrentToolset->selectToolByIndex(2);
+ }
+ else if (tool_name == "edit")
+ {
+ gCurrentToolset->selectToolByIndex(3);
+ }
+ else if (tool_name == "create")
+ {
+ gCurrentToolset->selectToolByIndex(4);
+ }
+ else if (tool_name == "land")
+ {
+ gCurrentToolset->selectToolByIndex(5);
+ }
+ return true;
+ }
+};
+
+void initialize_menu_actions()
+{
+ // File menu
+ (new LLFileUploadImage())->registerListener(gMenuHolder, "File.UploadImage");
+ (new LLFileUploadSound())->registerListener(gMenuHolder, "File.UploadSound");
+ (new LLFileUploadAnim())->registerListener(gMenuHolder, "File.UploadAnim");
+ (new LLFileUploadBulk())->registerListener(gMenuHolder, "File.UploadBulk");
+ (new LLFileCloseWindow())->registerListener(gMenuHolder, "File.CloseWindow");
+ (new LLFileSaveTexture())->registerListener(gMenuHolder, "File.SaveTexture");
+ (new LLFileTakeSnapshot())->registerListener(gMenuHolder, "File.TakeSnapshot");
+ (new LLFileTakeSnapshotToDisk())->registerListener(gMenuHolder, "File.TakeSnapshotToDisk");
+ (new LLFileSaveMovie())->registerListener(gMenuHolder, "File.SaveMovie");
+ (new LLFileSetWindowSize())->registerListener(gMenuHolder, "File.SetWindowSize");
+ (new LLFileQuit())->registerListener(gMenuHolder, "File.Quit");
+
+ (new LLFileEnableUpload())->registerListener(gMenuHolder, "File.EnableUpload");
+ (new LLFileEnableSaveAs())->registerListener(gMenuHolder, "File.EnableSaveAs");
+
+ // Edit menu
+ (new LLEditUndo())->registerListener(gMenuHolder, "Edit.Undo");
+ (new LLEditRedo())->registerListener(gMenuHolder, "Edit.Redo");
+ (new LLEditCut())->registerListener(gMenuHolder, "Edit.Cut");
+ (new LLEditCopy())->registerListener(gMenuHolder, "Edit.Copy");
+ (new LLEditPaste())->registerListener(gMenuHolder, "Edit.Paste");
+ (new LLEditDelete())->registerListener(gMenuHolder, "Edit.Delete");
+ (new LLEditSearch())->registerListener(gMenuHolder, "Edit.Search");
+ (new LLEditSelectAll())->registerListener(gMenuHolder, "Edit.SelectAll");
+ (new LLEditDeselect())->registerListener(gMenuHolder, "Edit.Deselect");
+ (new LLEditDuplicate())->registerListener(gMenuHolder, "Edit.Duplicate");
+ (new LLEditTakeOff())->registerListener(gMenuHolder, "Edit.TakeOff");
+
+ (new LLEditEnableUndo())->registerListener(gMenuHolder, "Edit.EnableUndo");
+ (new LLEditEnableRedo())->registerListener(gMenuHolder, "Edit.EnableRedo");
+ (new LLEditEnableCut())->registerListener(gMenuHolder, "Edit.EnableCut");
+ (new LLEditEnableCopy())->registerListener(gMenuHolder, "Edit.EnableCopy");
+ (new LLEditEnablePaste())->registerListener(gMenuHolder, "Edit.EnablePaste");
+ (new LLEditEnableDelete())->registerListener(gMenuHolder, "Edit.EnableDelete");
+ (new LLEditEnableSelectAll())->registerListener(gMenuHolder, "Edit.EnableSelectAll");
+ (new LLEditEnableDeselect())->registerListener(gMenuHolder, "Edit.EnableDeselect");
+ (new LLEditEnableDuplicate())->registerListener(gMenuHolder, "Edit.EnableDuplicate");
+ (new LLEditEnableTakeOff())->registerListener(gMenuHolder, "Edit.EnableTakeOff");
+ (new LLEditEnableCustomizeAvatar())->registerListener(gMenuHolder, "Edit.EnableCustomizeAvatar");
+
+ // View menu
+ (new LLViewMouselook())->registerListener(gMenuHolder, "View.Mouselook");
+ (new LLViewBuildMode())->registerListener(gMenuHolder, "View.BuildMode");
+ (new LLViewResetView())->registerListener(gMenuHolder, "View.ResetView");
+ (new LLViewLookAtLastChatter())->registerListener(gMenuHolder, "View.LookAtLastChatter");
+ (new LLViewShowHoverTips())->registerListener(gMenuHolder, "View.ShowHoverTips");
+ (new LLViewHighlightTransparent())->registerListener(gMenuHolder, "View.HighlightTransparent");
+ (new LLViewToggleBeacon())->registerListener(gMenuHolder, "View.ToggleBeacon");
+ (new LLViewToggleRenderType())->registerListener(gMenuHolder, "View.ToggleRenderType");
+ (new LLViewShowHUDAttachments())->registerListener(gMenuHolder, "View.ShowHUDAttachments");
+ (new LLViewZoomOut())->registerListener(gMenuHolder, "View.ZoomOut");
+ (new LLViewZoomIn())->registerListener(gMenuHolder, "View.ZoomIn");
+ (new LLViewZoomDefault())->registerListener(gMenuHolder, "View.ZoomDefault");
+ (new LLViewFullscreen())->registerListener(gMenuHolder, "View.Fullscreen");
+ (new LLViewDefaultUISize())->registerListener(gMenuHolder, "View.DefaultUISize");
+
+ (new LLViewEnableMouselook())->registerListener(gMenuHolder, "View.EnableMouselook");
+ (new LLViewEnableLastChatter())->registerListener(gMenuHolder, "View.EnableLastChatter");
+
+ (new LLViewCheckBuildMode())->registerListener(gMenuHolder, "View.CheckBuildMode");
+ (new LLViewCheckShowHoverTips())->registerListener(gMenuHolder, "View.CheckShowHoverTips");
+ (new LLViewCheckHighlightTransparent())->registerListener(gMenuHolder, "View.CheckHighlightTransparent");
+ (new LLViewCheckBeaconEnabled())->registerListener(gMenuHolder, "View.CheckBeaconEnabled");
+ (new LLViewCheckRenderType())->registerListener(gMenuHolder, "View.CheckRenderType");
+ (new LLViewCheckHUDAttachments())->registerListener(gMenuHolder, "View.CheckHUDAttachments");
+
+ // World menu
+ (new LLWorldChat())->registerListener(gMenuHolder, "World.Chat");
+ (new LLWorldStartGesture())->registerListener(gMenuHolder, "World.StartGesture");
+ (new LLWorldAlwaysRun())->registerListener(gMenuHolder, "World.AlwaysRun");
+ (new LLWorldFly())->registerListener(gMenuHolder, "World.Fly");
+ (new LLWorldCreateLandmark())->registerListener(gMenuHolder, "World.CreateLandmark");
+ (new LLWorldSetHomeLocation())->registerListener(gMenuHolder, "World.SetHomeLocation");
+ (new LLWorldTeleportHome())->registerListener(gMenuHolder, "World.TeleportHome");
+ (new LLWorldSetAway())->registerListener(gMenuHolder, "World.SetAway");
+ (new LLWorldSetBusy())->registerListener(gMenuHolder, "World.SetBusy");
+
+ (new LLWorldEnableCreateLandmark())->registerListener(gMenuHolder, "World.EnableCreateLandmark");
+ (new LLWorldEnableSetHomeLocation())->registerListener(gMenuHolder, "World.EnableSetHomeLocation");
+ (new LLWorldEnableBuyLand())->registerListener(gMenuHolder, "World.EnableBuyLand");
+
+ (new LLWorldCheckAlwaysRun())->registerListener(gMenuHolder, "World.CheckAlwaysRun");
+
+ (new LLWorldForceSun())->registerListener(gMenuHolder, "World.ForceSun");
+
+ // Tools menu
+ (new LLToolsSelectTool())->registerListener(gMenuHolder, "Tools.SelectTool");
+ (new LLToolsSelectOnlyMyObjects())->registerListener(gMenuHolder, "Tools.SelectOnlyMyObjects");
+ (new LLToolsSelectOnlyMovableObjects())->registerListener(gMenuHolder, "Tools.SelectOnlyMovableObjects");
+ (new LLToolsSelectBySurrounding())->registerListener(gMenuHolder, "Tools.SelectBySurrounding");
+ (new LLToolsShowHiddenSelection())->registerListener(gMenuHolder, "Tools.ShowHiddenSelection");
+ (new LLToolsShowSelectionLightRadius())->registerListener(gMenuHolder, "Tools.ShowSelectionLightRadius");
+ (new LLToolsSnapObjectXY())->registerListener(gMenuHolder, "Tools.SnapObjectXY");
+ (new LLToolsUseSelectionForGrid())->registerListener(gMenuHolder, "Tools.UseSelectionForGrid");
+ (new LLToolsLink())->registerListener(gMenuHolder, "Tools.Link");
+ (new LLToolsUnlink())->registerListener(gMenuHolder, "Tools.Unlink");
+ (new LLToolsStopAllAnimations())->registerListener(gMenuHolder, "Tools.StopAllAnimations");
+ (new LLToolsLookAtSelection())->registerListener(gMenuHolder, "Tools.LookAtSelection");
+ (new LLToolsBuyOrTake())->registerListener(gMenuHolder, "Tools.BuyOrTake");
+ (new LLToolsTakeCopy())->registerListener(gMenuHolder, "Tools.TakeCopy");
+ (new LLToolsSaveToInventory())->registerListener(gMenuHolder, "Tools.SaveToInventory");
+ (new LLToolsSaveToObjectInventory())->registerListener(gMenuHolder, "Tools.SaveToObjectInventory");
+ (new LLToolsSelectedScriptAction())->registerListener(gMenuHolder, "Tools.SelectedScriptAction");
+
+ (new LLToolsEnableToolNotPie())->registerListener(gMenuHolder, "Tools.EnableToolNotPie");
+ (new LLToolsEnableLink())->registerListener(gMenuHolder, "Tools.EnableLink");
+ (new LLToolsEnableUnlink())->registerListener(gMenuHolder, "Tools.EnableUnlink");
+ (new LLToolsEnableBuyOrTake())->registerListener(gMenuHolder, "Tools.EnableBuyOrTake");
+ (new LLToolsEnableTakeCopy())->registerListener(gMenuHolder, "Tools.EnableTakeCopy");
+ (new LLToolsEnableSaveToInventory())->registerListener(gMenuHolder, "Tools.SaveToInventory");
+ (new LLToolsEnableSaveToObjectInventory())->registerListener(gMenuHolder, "Tools.SaveToObjectInventory");
+
+ /*(new LLToolsVisibleBuyObject())->registerListener(gMenuHolder, "Tools.VisibleBuyObject");
+ (new LLToolsVisibleTakeObject())->registerListener(gMenuHolder, "Tools.VisibleTakeObject");*/
+
+ // Help menu
+ (new LLHelpLiveHelp())->registerListener(gMenuHolder, "Help.LiveHelp");
+ (new LLHelpMOTD())->registerListener(gMenuHolder, "Help.MOTD");
+
+ // Self pie menu
+ (new LLSelfStandUp())->registerListener(gMenuHolder, "Self.StandUp");
+ (new LLSelfRemoveAllAttachments())->registerListener(gMenuHolder, "Self.RemoveAllAttachments");
+
+ (new LLSelfEnableStandUp())->registerListener(gMenuHolder, "Self.EnableStandUp");
+ (new LLSelfEnableRemoveAllAttachments())->registerListener(gMenuHolder, "Self.EnableRemoveAllAttachments");
+
+ // Avatar pie menu
+ (new LLObjectMute())->registerListener(gMenuHolder, "Avatar.Mute");
+ (new LLAvatarRate())->registerListener(gMenuHolder, "Avatar.Rate");
+ (new LLAvatarAddFriend())->registerListener(gMenuHolder, "Avatar.AddFriend");
+ (new LLAvatarFreeze())->registerListener(gMenuHolder, "Avatar.Freeze");
+ (new LLAvatarDebug())->registerListener(gMenuHolder, "Avatar.Debug");
+ (new LLAvatarVisibleDebug())->registerListener(gMenuHolder, "Avatar.VisibleDebug");
+ (new LLAvatarEnableDebug())->registerListener(gMenuHolder, "Avatar.EnableDebug");
+ (new LLAvatarGiveCard())->registerListener(gMenuHolder, "Avatar.GiveCard");
+ (new LLAvatarEject())->registerListener(gMenuHolder, "Avatar.Eject");
+ (new LLAvatarSendIM())->registerListener(gMenuHolder, "Avatar.SendIM");
+
+ (new LLObjectEnableMute())->registerListener(gMenuHolder, "Avatar.EnableMute");
+ (new LLAvatarEnableAddFriend())->registerListener(gMenuHolder, "Avatar.EnableAddFriend");
+ (new LLAvatarEnableFreezeEject())->registerListener(gMenuHolder, "Avatar.EnableFreezeEject");
+
+ // Object pie menu
+ (new LLObjectOpen())->registerListener(gMenuHolder, "Object.Open");
+ (new LLObjectBuild())->registerListener(gMenuHolder, "Object.Build");
+ (new LLObjectTouch())->registerListener(gMenuHolder, "Object.Touch");
+ (new LLObjectSitOrStand())->registerListener(gMenuHolder, "Object.SitOrStand");
+ (new LLObjectDelete())->registerListener(gMenuHolder, "Object.Delete");
+ (new LLObjectAttachToAvatar())->registerListener(gMenuHolder, "Object.AttachToAvatar");
+ (new LLObjectReturn())->registerListener(gMenuHolder, "Object.Return");
+ (new LLObjectRateOwner())->registerListener(gMenuHolder, "Object.RateOwner");
+ (new LLObjectReportAbuse())->registerListener(gMenuHolder, "Object.ReportAbuse");
+ (new LLObjectRateCreator())->registerListener(gMenuHolder, "Object.RateCreator");
+ (new LLObjectMute())->registerListener(gMenuHolder, "Object.Mute");
+ (new LLObjectBuy())->registerListener(gMenuHolder, "Object.Buy");
+ (new LLObjectEdit())->registerListener(gMenuHolder, "Object.Edit");
+
+ (new LLObjectEnableOpen())->registerListener(gMenuHolder, "Object.EnableOpen");
+ (new LLObjectEnableTouch())->registerListener(gMenuHolder, "Object.EnableTouch");
+ (new LLObjectEnableSitOrStand())->registerListener(gMenuHolder, "Object.EnableSitOrStand");
+ (new LLObjectEnableDelete())->registerListener(gMenuHolder, "Object.EnableDelete");
+ (new LLObjectEnableWear())->registerListener(gMenuHolder, "Object.EnableWear");
+ (new LLObjectEnableReturn())->registerListener(gMenuHolder, "Object.EnableReturn");
+ (new LLObjectEnableRateOwner())->registerListener(gMenuHolder, "Object.EnableRateOwner");
+ (new LLObjectEnableReportAbuse())->registerListener(gMenuHolder, "Object.EnableReportAbuse");
+ (new LLObjectEnableRateCreator())->registerListener(gMenuHolder, "Object.EnableRateCreator");
+ (new LLObjectEnableMute())->registerListener(gMenuHolder, "Object.EnableMute");
+ (new LLObjectEnableBuy())->registerListener(gMenuHolder, "Object.EnableBuy");
+
+ /*(new LLObjectVisibleTouch())->registerListener(gMenuHolder, "Object.VisibleTouch");
+ (new LLObjectVisibleCustomTouch())->registerListener(gMenuHolder, "Object.VisibleCustomTouch");
+ (new LLObjectVisibleStandUp())->registerListener(gMenuHolder, "Object.VisibleStandUp");
+ (new LLObjectVisibleSitHere())->registerListener(gMenuHolder, "Object.VisibleSitHere");
+ (new LLObjectVisibleCustomSit())->registerListener(gMenuHolder, "Object.VisibleCustomSit");*/
+
+ // Attachment pie menu
+ (new LLAttachmentDrop())->registerListener(gMenuHolder, "Attachment.Drop");
+ (new LLAttachmentDetach())->registerListener(gMenuHolder, "Attachment.Detach");
+
+ (new LLAttachmentEnableDrop())->registerListener(gMenuHolder, "Attachment.EnableDrop");
+ (new LLAttachmentEnableDetach())->registerListener(gMenuHolder, "Attachment.EnableDetach");
+
+ // Land pie menu
+ (new LLLandBuild())->registerListener(gMenuHolder, "Land.Build");
+ (new LLLandSit())->registerListener(gMenuHolder, "Land.Sit");
+ (new LLLandBuyPass())->registerListener(gMenuHolder, "Land.BuyPass");
+ (new LLLandEdit())->registerListener(gMenuHolder, "Land.Edit");
+
+ (new LLLandEnableBuyPass())->registerListener(gMenuHolder, "Land.EnableBuyPass");
+
+ // Generic actions
+ (new LLShowFloater())->registerListener(gMenuHolder, "ShowFloater");
+ (new LLPromptShowURL())->registerListener(gMenuHolder, "PromptShowURL");
+ (new LLPromptShowFile())->registerListener(gMenuHolder, "PromptShowFile");
+ (new LLShowAgentProfile())->registerListener(gMenuHolder, "ShowAgentProfile");
+ (new LLShowAgentGroups())->registerListener(gMenuHolder, "ShowAgentGroups");
+ (new LLToggleControl())->registerListener(gMenuHolder, "ToggleControl");
+
+ (new LLGoToObject())->registerListener(gMenuHolder, "GoToObject");
+ (new LLPayObject())->registerListener(gMenuHolder, "PayObject");
+
+ (new LLEnablePayObject())->registerListener(gMenuHolder, "EnablePayObject");
+ (new LLEnableEdit())->registerListener(gMenuHolder, "EnableEdit");
+
+ (new LLFloaterVisible())->registerListener(gMenuHolder, "FloaterVisible");
+ (new LLSomethingSelected())->registerListener(gMenuHolder, "SomethingSelected");
+ (new LLSomethingSelectedNoHUD())->registerListener(gMenuHolder, "SomethingSelectedNoHUD");
+ (new LLEditableSelected())->registerListener(gMenuHolder, "EditableSelected");
+}
diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h
new file mode 100644
index 0000000000..37a7f0fa13
--- /dev/null
+++ b/indra/newview/llviewermenu.h
@@ -0,0 +1,174 @@
+/**
+ * @file llviewermenu.h
+ * @brief Builds menus out of objects
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERMENU_H
+#define LL_LLVIEWERMENU_H
+
+#include "llassetstorage.h"
+#include "llinventory.h"
+#include "llmenugl.h"
+
+//newview includes
+#include "llfilepicker.h"
+
+class LLUICtrl;
+class LLView;
+
+struct LLResourceData
+{
+ LLAssetInfo mAssetInfo;
+ LLAssetType::EType mPreferredLocation;
+ LLInventoryType::EType mInventoryType;
+ U32 mNextOwnerPerm;
+ void *mUserData;
+};
+
+void pre_init_menus();
+void init_menus();
+void cleanup_menus();
+
+void show_debug_menus(); // checks for if menus should be shown first.
+void show_context_menu( S32 x, S32 y, MASK mask );
+void show_build_mode_context_menu(S32 x, S32 y, MASK mask);
+void load_url_local_file(const char *file_name);
+BOOL enable_save_into_inventory(void*);
+void handle_reset_view();
+void handle_cut(void*);
+void handle_copy(void*);
+void handle_paste(void*);
+void handle_delete(void*);
+void handle_redo(void*);
+void handle_undo(void*);
+void handle_select_all(void*);
+void handle_deselect(void*);
+void handle_delete_object();
+void handle_duplicate(void*);
+void handle_duplicate_in_place(void*);
+BOOL enable_not_have_card(void *userdata);
+void process_grant_godlike_powers(LLMessageSystem* msg, void**);
+
+BOOL enable_cut(void*);
+BOOL enable_copy(void*);
+BOOL enable_paste(void*);
+BOOL enable_select_all(void*);
+BOOL enable_deselect(void*);
+BOOL enable_undo(void*);
+BOOL enable_redo(void*);
+
+// returns TRUE if we have a friend relationship with agent_id
+BOOL is_agent_friend(const LLUUID& agent_id);
+BOOL is_agent_mappable(const LLUUID& agent_id);
+
+void menu_toggle_control( void* user_data );
+void check_toggle_control( LLUICtrl *, void* user_data );
+void handle_attach_to_avatar(void* user_data);
+void confirm_replace_attachment(S32 option, void* user_data);
+void handle_detach_from_avatar(void* user_data);
+void attach_label(LLString& label, void* user_data);
+void detach_label(LLString& label, void* user_data);
+BOOL object_selected_and_point_valid(void* user_data);
+BOOL object_attached(void* user_data);
+void handle_detach(void*);
+BOOL enable_god_full(void* user_data);
+BOOL enable_god_liaison(void* user_data);
+BOOL enable_god_customer_service(void* user_data);
+BOOL enable_god_basic(void* user_data);
+void handle_show_newest_map(void*);
+
+void exchange_callingcard(const LLUUID& dest_id);
+
+void handle_gestures(void*);
+void handle_sit_down(void*);
+bool toggle_build_mode();
+void handle_object_build(void*);
+void handle_save_snapshot(void *);
+
+bool handle_sit_or_stand();
+bool handle_give_money_dialog();
+bool handle_object_open();
+bool handle_go_to();
+
+void upload_new_resource(const LLString& src_filename, std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm = PERM_NONE,
+ const LLString& display_name = LLString::null,
+ LLAssetStorage::LLStoreAssetCallback callback = NULL,
+ void *userdata = NULL);
+
+void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType type,
+ std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm = PERM_NONE,
+ const LLString& display_name = LLString::null,
+ LLAssetStorage::LLStoreAssetCallback callback = NULL,
+ void *userdata = NULL);
+
+// Export to XML or Collada
+void handle_export_selected( void * );
+
+//Retrieve a list of valid extensions for a given file "type"
+const char* build_extensions_string(LLFilePicker::ELoadFilter filter);
+
+// Pass in an empty string and this function will build a string that
+// describes buyer permissions.
+class LLSaleInfo;
+class LLPermissions;
+
+class LLViewerMenuHolderGL : public LLMenuHolderGL
+{
+public:
+ LLViewerMenuHolderGL() : LLMenuHolderGL() {};
+
+ virtual BOOL hideMenus();
+ //virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+};
+
+extern const LLString SAVE_INTO_INVENTORY;
+
+extern LLMenuBarGL* gMenuBarView;
+//extern LLView* gMenuBarHolder;
+extern LLMenuGL* gPopupMenuView;
+extern LLViewerMenuHolderGL* gMenuHolder;
+
+// Pie menus
+extern LLPieMenu *gPieSelf;
+extern LLPieMenu *gPieAvatar;
+extern LLPieMenu *gPieObject;
+extern LLPieMenu *gPieAttachment;
+extern LLPieMenu *gPieLand;
+extern LLPieMenu* gPieRate;
+
+// Pie menus
+extern LLPieMenu *gPieSelfSimple;
+extern LLPieMenu *gPieAvatarSimple;
+extern LLPieMenu *gPieObjectSimple;
+extern LLPieMenu *gPieAttachmentSimple;
+extern LLPieMenu *gPieLandSimple;
+
+// Needed to build menus when attachment site list available
+extern LLMenuGL* gAttachSubMenu;
+extern LLMenuGL* gDetachSubMenu;
+extern LLMenuGL* gTakeOffClothes;
+extern LLPieMenu* gAttachScreenPieMenu;
+extern LLPieMenu* gDetachScreenPieMenu;
+extern LLPieMenu* gAttachPieMenu;
+extern LLPieMenu* gDetachPieMenu;
+extern LLPieMenu* gAttachBodyPartPieMenus[8];
+extern LLPieMenu* gDetachBodyPartPieMenus[8];
+
+extern LLMenuItemCallGL* gAFKMenu;
+extern LLMenuItemCallGL* gBusyMenu;
+extern LLMenuItemCallGL* gMutePieMenu;
+extern LLMenuItemCallGL* gMuteObjectPieMenu;
+extern LLMenuItemCallGL* gBuyPassPieMenu;
+
+#endif
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
new file mode 100644
index 0000000000..c649229311
--- /dev/null
+++ b/indra/newview/llviewermessage.cpp
@@ -0,0 +1,5017 @@
+/**
+ * @file llviewermessage.cpp
+ * @brief Dumping ground for viewer-side message system callbacks.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewermessage.h"
+
+#include <deque>
+#include <stdio.h>
+#include <string.h>
+
+#include "audioengine.h"
+#include "audiosettings.h"
+#include "indra_constants.h"
+#include "lscript_byteformat.h"
+#include "mean_collision_data.h"
+#include "llfloaterbump.h"
+#include "llassetstorage.h"
+#include "llcachename.h"
+#include "llchat.h"
+#include "lldbstrings.h"
+#include "lleconomy.h"
+#include "llfilepicker.h"
+#include "llfloaterimport.h"
+#include "llfocusmgr.h"
+#include "llfollowcamparams.h"
+#include "llinstantmessage.h"
+#include "llquantize.h"
+#include "llregionflags.h"
+#include "llregionhandle.h"
+#include "llsdserialize.h"
+#include "llstring.h"
+#include "llteleportflags.h"
+#include "lltracker.h"
+#include "lltransactionflags.h"
+#include "llwindow.h" // shell_open()
+#include "llxfermanager.h"
+#include "message.h"
+#include "sound_ids.h"
+#include "lltimer.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llconsole.h"
+#include "llviewercontrol.h"
+#include "lldrawpool.h"
+#include "llfirstuse.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloaterbuyland.h"
+#include "llfloaterchat.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterimagepreview.h"
+#include "llfloaterland.h"
+#include "llfloaterregioninfo.h"
+#include "llfloaterlandholdings.h"
+#include "llfloatermap.h"
+#include "llfloatermute.h"
+#include "llfloaterpostcard.h"
+#include "llfloaterpreference.h"
+#include "llfollowcam.h"
+#include "llgroupnotify.h"
+#include "llhudeffect.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llimpanel.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llmenugl.h"
+#include "llmutelist.h"
+#include "llnetmap.h"
+#include "llnotify.h"
+#include "llpanelgrouplandmoney.h"
+#include "llselectmgr.h"
+#include "llstartup.h"
+#include "llsky.h"
+#include "llstatenums.h"
+#include "llstatusbar.h"
+#include "llimview.h"
+#include "lltool.h"
+#include "lltoolbar.h"
+#include "lltoolmgr.h"
+#include "llui.h" // for make_ui_sound
+#include "lluploaddialog.h"
+#include "llviewercamera.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerthrottle.h"
+#include "llviewerwindow.h"
+#include "llvlmanager.h"
+#include "llvoavatar.h"
+#include "llvotextbubble.h"
+#include "llweb.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llfloaterworldmap.h"
+
+#include <boost/tokenizer.hpp>
+
+#if LL_WINDOWS // For Windows specific error handler
+#include "llwindebug.h" // For the invalid message handler
+#endif
+
+//
+// Constants
+//
+const F32 BIRD_AUDIBLE_RADIUS = 32.0f;
+const F32 SIT_DISTANCE_FROM_TARGET = 0.25f;
+static const F32 LOGOUT_REPLY_TIME = 3.f; // Wait this long after LogoutReply before quitting.
+extern BOOL gDebugClicks;
+
+extern void bad_network_handler();
+
+LLDispatcher gGenericDispatcher;
+
+// function prototypes
+void open_offer(const std::vector<LLUUID>& items);
+void friendship_offer_callback(S32 option, void* user_data);
+
+struct LLFriendshipOffer
+{
+ LLUUID mFromID;
+ LLUUID mTransactionID;
+ BOOL mOnline;
+ LLHost mHost;
+};
+
+//const char BUSY_AUTO_RESPONSE[] = "The Resident you messaged is in 'busy mode' which means they have "
+// "requested not to be disturbed. Your message will still be shown in their IM "
+// "panel for later viewing.";
+
+//
+// Functions
+//
+
+void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group,
+ S32 trx_type, const LLString& desc)
+{
+ if(0 == amount) return;
+ amount = abs(amount);
+ llinfos << "give_money(" << uuid << "," << amount << ")"<< llendl;
+ if(can_afford_transaction(amount))
+ {
+// gStatusBar->debitBalance(amount);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoneyTransferRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_get_id());
+ msg->addUUIDFast(_PREHASH_SessionID, agent_get_session_id());
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_DestID, uuid);
+ msg->addU8Fast(_PREHASH_Flags, pack_transaction_flags(FALSE, is_group));
+ msg->addS32Fast(_PREHASH_Amount, amount);
+ msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addS32Fast(_PREHASH_TransactionType, trx_type );
+ msg->addStringFast(_PREHASH_Description, desc.c_str());
+ msg->sendReliable(region->getHost());
+ }
+ else
+ {
+ LLFloaterBuyCurrency::buyCurrency("Giving", amount);
+ }
+}
+
+BOOL can_afford_transaction(S32 cost)
+{
+ return((cost <= 0)||((gStatusBar) && (gStatusBar->getBalance() >=cost)));
+}
+
+void send_complete_agent_movement(const LLHost& sim_host)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CompleteAgentMovement);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode);
+ msg->sendReliable(sim_host);
+}
+
+void process_logout_reply(LLMessageSystem* msg, void**)
+{
+ // The server has told us it's ok to quit.
+ llinfos << "process_logout_reply" << llendl;
+
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ LLUUID session_id;
+ msg->getUUID("AgentData", "SessionID", session_id);
+ if((agent_id != agent_get_id()) || (session_id != agent_get_session_id()))
+ {
+ llwarns << "Bogus Logout Reply" << llendl;
+ }
+
+ S32 count = msg->getNumberOfBlocksFast( _PREHASH_InventoryData );
+ for( S32 i = 0; i < count; i++ )
+ {
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
+
+ if( (1 == count) && item_id.isNull() )
+ {
+ // Detect dummy item. Indicates an empty list.
+ break;
+ }
+
+
+ // Update our the asset ids of the inventory items we're currently wearing.
+ LLUUID new_asset_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_NewAssetID, new_asset_id, i);
+
+ llinfos << "process_logout_reply itemID=" << item_id << llendl;
+ LLInventoryItem* item = gInventory.getItem( item_id );
+ if( item )
+ {
+ item->setAssetUUID(new_asset_id);
+
+ gInventory.addChangedMask( LLInventoryObserver::INTERNAL, item_id );
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llinfos << "process_logout_reply item not found: " << item_id << llendl;
+ }
+ }
+
+ /*
+ gLogoutTimer.reset();
+ gLogoutMaxTime = LOGOUT_REPLY_TIME;
+ gViewerWindow->setProgressString("Logging out...");
+
+ if (gNoRender)
+ {
+ // Hack so drones can quit with new logout process
+ */
+
+ app_force_quit(NULL);
+}
+
+void process_layer_data(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLViewerRegion *regionp = gWorldp->getRegion(mesgsys->getSender());
+
+ if (!regionp || gNoRender)
+ {
+ return;
+ }
+
+
+ S32 size;
+ S8 type;
+
+ mesgsys->getS8Fast(_PREHASH_LayerID, _PREHASH_Type, type);
+ size = mesgsys->getSizeFast(_PREHASH_LayerData, _PREHASH_Data);
+ if(!size)
+ {
+ llwarns << "Layer data has zero size." << llendl;
+ return;
+ }
+ U8 *datap = new U8[size];
+ mesgsys->getBinaryDataFast(_PREHASH_LayerData, _PREHASH_Data, datap, size);
+ LLVLData *vl_datap = new LLVLData(regionp, type, datap, size);
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gVLManager.addLayerData(vl_datap, mesgsys->getReceiveCompressedSize());
+ }
+ else
+ {
+ gVLManager.addLayerData(vl_datap, mesgsys->getReceiveSize());
+ }
+}
+
+S32 exported_object_count = 0;
+S32 exported_image_count = 0;
+S32 current_object_count = 0;
+S32 current_image_count = 0;
+
+extern LLNotifyBox *gExporterNotify;
+extern LLUUID gExporterRequestID;
+extern LLString gExportDirectory;
+
+extern LLUploadDialog *gExportDialog;
+
+LLString gExportedFile;
+
+std::map<LLUUID, LLString> gImageChecksums;
+
+void export_complete()
+{
+ LLUploadDialog::modalUploadFinished();
+ gExporterRequestID.setNull();
+ gExportDirectory = "";
+
+ FILE *fXML = LLFile::fopen(gExportedFile.c_str(), "rb");
+ fseek(fXML, 0, SEEK_END);
+ U32 length = ftell(fXML);
+ fseek(fXML, 0, SEEK_SET);
+ U8 *buffer = new U8[length];
+ fread(buffer, 1, length, fXML);
+ fclose(fXML);
+
+ char *pos = (char *)buffer;
+ while ((pos = strstr(pos+1, "<sl:image ")) != 0)
+ {
+ char *pos_check = strstr(pos, "checksum=\"");
+ char *pos_uuid = strstr(pos_check, "\">");
+
+ if (pos_check && pos_uuid)
+ {
+ char image_uuid_str[UUID_STR_SIZE];
+ memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1);
+ image_uuid_str[UUID_STR_SIZE-1] = 0;
+
+ LLUUID image_uuid(image_uuid_str);
+
+ llinfos << "Found UUID: " << image_uuid << llendl;
+
+ std::map<LLUUID, LLString>::iterator itor = gImageChecksums.find(image_uuid);
+ if (itor != gImageChecksums.end())
+ {
+ llinfos << "Replacing with checksum: " << itor->second << llendl;
+ memcpy(&pos_check[10], itor->second.c_str(), 32);
+ }
+ }
+ }
+
+ FILE *fXMLOut = LLFile::fopen(gExportedFile.c_str(), "wb");
+ fwrite(buffer, 1, length, fXMLOut);
+ fclose(fXMLOut);
+
+ delete buffer;
+}
+
+
+void exported_item_complete(const LLTSCode status, void *user_data)
+{
+ //LLString *filename = (LLString *)user_data;
+
+ if (status < LLTS_OK)
+ {
+ llinfos << "Export failed!" << llendl;
+ }
+ else
+ {
+ ++current_object_count;
+ if (current_image_count == exported_image_count && current_object_count == exported_object_count)
+ {
+ llinfos << "*** Export complete ***" << llendl;
+
+ export_complete();
+ }
+ else
+ {
+ gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
+ }
+ }
+}
+
+struct exported_image_info
+{
+ LLUUID image_id;
+ LLString filename;
+ U32 image_num;
+};
+
+void exported_j2c_complete(const LLTSCode status, void *user_data)
+{
+ exported_image_info *info = (exported_image_info *)user_data;
+ LLUUID image_id = info->image_id;
+ U32 image_num = info->image_num;
+ LLString filename = info->filename;
+ delete info;
+
+ if (status < LLTS_OK)
+ {
+ llinfos << "Image download failed!" << llendl;
+ }
+ else
+ {
+ FILE *fIn = LLFile::fopen(filename.c_str(), "rb");
+ if (fIn)
+ {
+ LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C;
+ LLPointer<LLImageTGA> TargaUtility = new LLImageTGA;
+
+ fseek(fIn, 0, SEEK_END);
+ S32 length = ftell(fIn);
+ fseek(fIn, 0, SEEK_SET);
+ U8 *buffer = ImageUtility->allocateData(length);
+ fread(buffer, 1, length, fIn);
+ fclose(fIn);
+ LLFile::remove(filename.c_str());
+
+ // Convert to TGA
+ LLPointer<LLImageRaw> image = new LLImageRaw();
+
+ ImageUtility->updateData();
+ ImageUtility->decode(image, 100000.0f);
+
+ TargaUtility->encode(image);
+ U8 *data = TargaUtility->getData();
+ S32 data_size = TargaUtility->getDataSize();
+
+ char *file_path = new char[filename.size()+1];
+ strcpy(file_path, filename.c_str());
+ char *end = strrchr(file_path, gDirUtilp->getDirDelimiter()[0]);
+ end[0] = 0;
+ LLString output_file = llformat("%s/image-%03d.tga", file_path, image_num);//filename;
+ delete file_path;
+ //S32 name_len = output_file.length();
+ //strcpy(&output_file[name_len-3], "tga");
+ FILE *fOut = LLFile::fopen(output_file.c_str(), "wb");
+ char md5_hash_string[33];
+ strcpy(md5_hash_string, "00000000000000000000000000000000");
+ if (fOut)
+ {
+ fwrite(data, 1, data_size, fOut);
+ fseek(fOut, 0, SEEK_SET);
+ fclose(fOut);
+ fOut = LLFile::fopen(output_file.c_str(), "rb");
+ LLMD5 my_md5_hash(fOut);
+ my_md5_hash.hex_digest(md5_hash_string);
+ }
+
+ gImageChecksums.insert(std::pair<LLUUID, LLString>(image_id, md5_hash_string));
+ }
+ }
+
+ ++current_image_count;
+ if (current_image_count == exported_image_count && current_object_count == exported_object_count)
+ {
+ llinfos << "*** Export textures complete ***" << llendl;
+ export_complete();
+ }
+ else
+ {
+ gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
+ }
+}
+
+void process_derez_ack(LLMessageSystem*, void**)
+{
+ if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount();
+}
+
+void process_places_reply(LLMessageSystem* msg, void** data)
+{
+ LLUUID query_id;
+
+ msg->getUUID("AgentData", "QueryID", query_id);
+ if (query_id.isNull())
+ {
+ LLFloaterLandHoldings::processPlacesReply(msg, data);
+ }
+ else if(gAgent.isInGroup(query_id))
+ {
+ LLPanelGroupLandMoney::processPlacesReply(msg, data);
+ }
+ else
+ {
+ llwarns << "Got invalid PlacesReply message" << llendl;
+ }
+}
+
+void send_sound_trigger(const LLUUID& sound_id, F32 gain)
+{
+ if (sound_id.isNull())
+ {
+ // zero guids don't get sent (no sound)
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SoundTrigger);
+ msg->nextBlockFast(_PREHASH_SoundData);
+ msg->addUUIDFast(_PREHASH_SoundID, sound_id);
+ // Client untrusted, ids set on sim
+ msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null );
+ msg->addUUIDFast(_PREHASH_ObjectID, LLUUID::null );
+ msg->addUUIDFast(_PREHASH_ParentID, LLUUID::null );
+
+ msg->addU64Fast(_PREHASH_Handle, gAgent.getRegion()->getHandle());
+
+ LLVector3 position = gAgent.getPositionAgent();
+ msg->addVector3Fast(_PREHASH_Position, position);
+ msg->addF32Fast(_PREHASH_Gain, gain);
+
+ gAgent.sendMessage();
+}
+
+struct LLJoinGroupData
+{
+ LLUUID mGroupID;
+ LLUUID mTransactionID;
+ std::string mName;
+ std::string mMessage;
+ S32 mFee;
+};
+
+void join_group_callback(S32 option, void* user_data)
+{
+ LLJoinGroupData* data = (LLJoinGroupData*)user_data;
+ BOOL delete_context_data = TRUE;
+ bool accept_invite = false;
+ if(option == 0 && data && !data->mGroupID.isNull())
+ {
+ // check for promotion or demotion.
+ S32 max_groups = MAX_AGENT_GROUPS;
+ if(gAgent.isInGroup(data->mGroupID)) ++max_groups;
+
+ if(gAgent.mGroups.count() < max_groups)
+ {
+ accept_invite = true;
+ }
+ else
+ {
+ delete_context_data = FALSE;
+ LLString::format_map_t args;
+ args["[NAME]"] = data->mName;
+ args["[INVITE]"] = data->mMessage;
+ LLAlertDialog::showXml("JoinedTooManyGroupsMember", args, join_group_callback, (void*)data);
+ }
+ }
+
+ if (accept_invite)
+ {
+ // If there is a fee to join this group, make
+ // sure the user is sure they want to join.
+ if (data->mFee > 0)
+ {
+ delete_context_data = FALSE;
+ LLString::format_map_t args;
+ args["[COST]"] = llformat("%d", data->mFee);
+ // Set the fee to 0, so that we don't keep
+ // asking about a fee.
+ data->mFee = 0;
+ gViewerWindow->alertXml("JoinGroupCanAfford",
+ args,
+ join_group_callback,
+ (void*)data);
+ }
+ else
+ {
+ send_improved_im(data->mGroupID,
+ "name",
+ "message",
+ IM_ONLINE,
+ IM_GROUP_INVITATION_ACCEPT,
+ data->mTransactionID);
+ }
+ }
+ else if (data)
+ {
+ send_improved_im(data->mGroupID,
+ "name",
+ "message",
+ IM_ONLINE,
+ IM_GROUP_INVITATION_DECLINE,
+ data->mTransactionID);
+ }
+
+ if(delete_context_data)
+ {
+ delete data;
+ data = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Instant Message
+//-----------------------------------------------------------------------------
+class LLOpenAgentOffer : public LLInventoryFetchObserver
+{
+public:
+ LLOpenAgentOffer() {}
+ virtual ~LLOpenAgentOffer() {}
+
+ virtual void done()
+ {
+ open_offer(mComplete);
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+class LLOpenTaskOffer : public LLInventoryExistenceObserver
+{
+public:
+ LLOpenTaskOffer() {}
+ virtual ~LLOpenTaskOffer() {}
+
+protected:
+ virtual void done()
+ {
+ open_offer(mExist);
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+class LLDiscardAgentOffer : public LLInventoryFetchComboObserver
+{
+public:
+ LLDiscardAgentOffer(const LLUUID& folder_id, const LLUUID& object_id) :
+ mFolderID(folder_id),
+ mObjectID(object_id) {}
+ virtual ~LLDiscardAgentOffer() {}
+ virtual void done()
+ {
+ lldebugs << "LLDiscardAgentOffer::done()" << llendl;
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ bool notify = false;
+ if(trash_id.notNull() && mObjectID.notNull())
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(mFolderID, -1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+ gInventory.moveObject(mObjectID, trash_id);
+ LLInventoryObject* obj = gInventory.getObject(mObjectID);
+ if(obj)
+ {
+ // no need to restamp since this is already a freshly
+ // stamped item.
+ obj->updateParentOnServer(FALSE);
+ notify = true;
+ }
+ }
+ else
+ {
+ llwarns << "DiscardAgentOffer unable to find: "
+ << (trash_id.isNull() ? "trash " : "")
+ << (mObjectID.isNull() ? "object" : "") << llendl;
+ }
+ gInventory.removeObserver(this);
+ if(notify)
+ {
+ gInventory.notifyObservers();
+ }
+ delete this;
+ }
+protected:
+ LLUUID mFolderID;
+ LLUUID mObjectID;
+};
+
+
+void open_offer(const std::vector<LLUUID>& items)
+{
+ std::vector<LLUUID>::const_iterator it = items.begin();
+ std::vector<LLUUID>::const_iterator end = items.end();
+ LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH));
+ LLInventoryItem* item;
+ for(; it != end; ++it)
+ {
+ item = gInventory.getItem(*it);
+ if(!item)
+ {
+ llwarns << "Unable to show inventory item: " << *it << llendl;
+ continue;
+ }
+ if(gInventory.isObjectDescendentOf(*it, trash_id))
+ {
+ continue;
+ }
+ switch(item->getType())
+ {
+ case LLAssetType::AT_NOTECARD:
+ open_notecard(*it, LLString("Note: ") + item->getName(), TRUE, LLUUID::null, FALSE);
+ break;
+ case LLAssetType::AT_LANDMARK:
+ open_landmark(*it, LLString("Landmark: ") + item->getName(), TRUE, LLUUID::null, FALSE);
+ break;
+ case LLAssetType::AT_TEXTURE:
+ open_texture(*it, LLString("Texture: ") + item->getName(), TRUE, LLUUID::null, FALSE);
+ break;
+ default:
+ {
+ // Don't auto-open the inventory - just select it if we
+ // already have an active inventory.
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback;
+ callback = gFocusMgr.getFocusCallback();
+ view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO);
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void inventory_offer_mute_callback(const LLUUID& blocked_id,
+ const char* first_name,
+ const char* last_name,
+ BOOL is_group,
+ void*)
+{
+ LLString from_name;
+ LLMute::EType type;
+
+ if (is_group)
+ {
+ type = LLMute::GROUP;
+ from_name = first_name;
+ }
+ else
+ {
+ type = LLMute::AGENT;
+ from_name += first_name;
+ from_name += " ";
+ from_name += last_name;
+ }
+
+ LLMute mute(blocked_id, from_name, type);
+ if (gMuteListp->add(mute))
+ {
+ gFloaterMute->show();
+ gFloaterMute->selectMute(blocked_id);
+ }
+}
+
+void inventory_offer_callback(S32 option, void* user_data)
+{
+ LLChat chat;
+ LLString log_message;
+
+ LLOfferInfo* info = (LLOfferInfo*)user_data;
+ if(!info) return;
+
+ // For muting, we need to add the mute, then decline the offer.
+ // This must be done here because:
+ // * callback may be called immediately,
+ // * adding the mute sends a message,
+ // * we can't build two messages at once. JC
+ if (option == 2)
+ {
+ gCacheName->get(info->mFromID, info->mFromGroup, inventory_offer_mute_callback, NULL);
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
+ msg->addUUIDFast(_PREHASH_ToAgentID, info->mFromID);
+ msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
+ msg->addUUIDFast(_PREHASH_ID, info->mTransactionID);
+ msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
+ std::string name;
+ gAgent.buildFullname(name);
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ msg->addStringFast(_PREHASH_Message, "");
+ msg->addU32Fast(_PREHASH_ParentEstateID, 0);
+ msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
+ msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
+ LLInventoryObserver* opener = NULL;
+ LLViewerInventoryCategory* catp = NULL;
+ catp = (LLViewerInventoryCategory*)gInventory.getCategory(info->mObjectID);
+ LLViewerInventoryItem* itemp = NULL;
+ if(!catp)
+ {
+ itemp = (LLViewerInventoryItem*)gInventory.getItem(info->mObjectID);
+ }
+
+ // XUI:translate
+ LLString from_string;
+ if (info->mFromObject == TRUE)
+ {
+ if (info->mFromGroup)
+ {
+ char group_name[MAX_STRING];
+ if (gCacheName->getGroupName(info->mFromID, group_name))
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by the group '" + group_name + "'";
+ }
+ else
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by an unknown group";
+ }
+ }
+ else
+ {
+ char first_name[MAX_STRING];
+ char last_name[MAX_STRING];
+ if (gCacheName->getName(info->mFromID, first_name, last_name))
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by " + first_name + " " + last_name;
+ }
+ else
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by an unknown user";
+ }
+ }
+ }
+ else
+ {
+ from_string = info->mFromName;
+ }
+
+ switch(option)
+ {
+ case 0:
+ // ACCEPT. The math for the dialog works, because the accept
+ // for inventory_offered, task_inventory_offer or
+ // group_notice_inventory is 1 greater than the offer integer value.
+ // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED,
+ // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED
+ msg->addU8Fast(_PREHASH_Dialog, (U8)(info->mIM + 1));
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(info->mFolderID.mData),
+ sizeof(info->mFolderID.mData));
+ // send the message
+ msg->sendReliable(info->mHost);
+ log_message = info->mFromName + " gave you " + info->mDesc + ".";
+ chat.mText = log_message;
+ LLFloaterChat::addChatHistory(chat);
+ // we will want to open this item when it comes back.
+ lldebugs << "Initializing an opener for tid: " << info->mTransactionID
+ << llendl;
+ switch (info->mIM)
+ {
+ case IM_INVENTORY_OFFERED:
+ {
+ // This is an offer from an agent. In this case, the back
+ // end has already copied the items into your inventory,
+ // so we can fetch it out of our inventory.
+ LLInventoryFetchObserver::item_ref_t items;
+ items.push_back(info->mObjectID);
+ LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer;
+ open_agent_offer->fetchItems(items);
+ if(catp || (itemp && itemp->isComplete()))
+ {
+ open_agent_offer->done();
+ }
+ else
+ {
+ opener = open_agent_offer;
+ }
+ }
+ break;
+ case IM_TASK_INVENTORY_OFFERED:
+ case IM_GROUP_NOTICE:
+ case IM_GROUP_NOTICE_REQUESTED:
+ {
+ // This is an offer from a task or group.
+ // Because it would be easy
+ // to write a task which would overload your inventory, we
+ // force the offer to stay in an instant message until
+ // accepted. Thus, we have to respond, and then wait for
+ // the update to come back before we open the item.
+ LLOpenTaskOffer* open_task_offer = new LLOpenTaskOffer;
+ open_task_offer->watchItem(info->mObjectID);
+ if(itemp && itemp->isComplete())
+ {
+ opener->changed(0x0);
+ }
+ else
+ {
+ opener = open_task_offer;
+ }
+ }
+ break;
+ default:
+ llwarns << "inventory_offer_callback: unknown offer type" << llendl;
+ break;
+ }
+ break;
+
+ case 2:
+ // MUTE falls through to decline
+ case 1:
+ // DECLINE. The math for the dialog works, because the decline
+ // for inventory_offered, task_inventory_offer or
+ // group_notice_inventory is 2 greater than the offer integer value.
+ // Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED,
+ // or IM_GROUP_NOTICE_INVENTORY_DECLINED
+ default:
+ // close button probably
+ msg->addU8Fast(_PREHASH_Dialog, (U8)(info->mIM + 2));
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE);
+ // send the message
+ msg->sendReliable(info->mHost);
+
+ log_message = "You decline " + info->mDesc + " from " + info->mFromName + ".";
+ chat.mText = log_message;
+ LLFloaterChat::addChatHistory(chat);
+
+ // If it's from an agent, we have to fetch the item to throw
+ // it away. If it's from a task or group, just denying the
+ // request will suffice to discard the item.
+ if(IM_INVENTORY_OFFERED == info->mIM)
+ {
+ LLInventoryFetchComboObserver::folder_ref_t folders;
+ LLInventoryFetchComboObserver::item_ref_t items;
+ items.push_back(info->mObjectID);
+ LLDiscardAgentOffer* discard_agent_offer;
+ discard_agent_offer = new LLDiscardAgentOffer(info->mFolderID, info->mObjectID);
+ discard_agent_offer->fetch(folders, items);
+ if(catp || (itemp && itemp->isComplete()))
+ {
+ discard_agent_offer->done();
+ }
+ else
+ {
+ opener = discard_agent_offer;
+ }
+
+ }
+ if (!info->mFromGroup)
+ {
+ busy_message(msg,info->mFromID);
+ }
+ break;
+ }
+
+ if(opener)
+ {
+ gInventory.addObserver(opener);
+ }
+
+ delete info;
+ info = NULL;
+
+ // Allow these to stack up, but once you deal with one, reset the
+ // position.
+ gFloaterView->resetStartingFloaterPosition();
+}
+
+
+void inventory_offer_handler(LLOfferInfo* info, BOOL from_task)
+{
+ switch(info->mType)
+ {
+ // For certain types, just accept the items into the inventory,
+ // and we'll automatically open them on receipt.
+ case LLAssetType::AT_NOTECARD:
+ case LLAssetType::AT_LANDMARK:
+ case LLAssetType::AT_TEXTURE:
+ {
+ // 0 = accept button
+ inventory_offer_callback(0, info);
+ //LLInventoryView::sOpenNextNewItem = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_SOUND:
+ case LLAssetType::AT_CALLINGCARD:
+ case LLAssetType::AT_SCRIPT:
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_OBJECT:
+ case LLAssetType::AT_CATEGORY:
+ case LLAssetType::AT_ROOT_CATEGORY:
+ case LLAssetType::AT_LSL_TEXT:
+ case LLAssetType::AT_LSL_BYTECODE:
+ case LLAssetType::AT_TEXTURE_TGA:
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_TRASH:
+ case LLAssetType::AT_SNAPSHOT_CATEGORY:
+ case LLAssetType::AT_LOST_AND_FOUND:
+ case LLAssetType::AT_ANIMATION:
+ case LLAssetType::AT_GESTURE:
+ default:
+ {
+ LLString::format_map_t args;
+ args["[OBJECTNAME]"] = info->mDesc;
+ args["[OBJECTTYPE]"] = LLAssetType::lookupHumanReadable(info->mType);
+
+ // Name cache callbacks don't store userdata, so can't save
+ // off the LLOfferInfo. Argh. JC
+ BOOL name_found = FALSE;
+ char first_name[MAX_STRING];
+ char last_name[MAX_STRING];
+ if (info->mFromGroup)
+ {
+ if (gCacheName->getGroupName(info->mFromID, first_name))
+ {
+ args["[FIRST]"] = first_name;
+ args["[LAST]"] = "";
+ name_found = TRUE;
+ }
+ }
+ else
+ {
+ if (gCacheName->getName(info->mFromID, first_name, last_name))
+ {
+ args["[FIRST]"] = first_name;
+ args["[LAST]"] = last_name;
+ name_found = TRUE;
+ }
+ }
+ if (from_task)
+ {
+ args["[OBJECTFROMNAME]"] = info->mFromName;
+ if (name_found)
+ {
+ LLNotifyBox::showXml("ObjectGiveItem", args,
+ &inventory_offer_callback, (void*)info);
+ }
+ else
+ {
+ LLNotifyBox::showXml("ObjectGiveItemUnknownUser", args,
+ &inventory_offer_callback, (void*)info);
+ }
+ }
+ else
+ {
+ // XUI:translate -> [FIRST] [LAST]
+ args["[NAME]"] = info->mFromName;
+ LLNotifyBox::showXml("UserGiveItem", args,
+ &inventory_offer_callback, (void*)info);
+ }
+ break;
+ }
+ }
+}
+
+
+void group_vote_callback(S32 option, void *userdata)
+{
+ LLUUID *group_id = (LLUUID *)userdata;
+ if (!group_id) return;
+
+ switch(option)
+ {
+ case 0:
+ // Vote Now
+ // Open up the voting tab
+ LLFloaterGroupInfo::showFromUUID(*group_id, "voting_tab");
+ break;
+ default:
+ // Vote Later or
+ // close button
+ break;
+ }
+ delete group_id;
+ group_id = NULL;
+}
+
+struct LLLureInfo
+{
+ LLLureInfo(const LLUUID& from, const LLUUID& lure_id, BOOL godlike) :
+ mFromID(from),
+ mLureID(lure_id),
+ mGodlike(godlike)
+ {}
+
+ LLUUID mFromID;
+ LLUUID mLureID;
+ BOOL mGodlike;
+};
+
+void lure_callback(S32 option, void* user_data)
+{
+ LLLureInfo* info = (LLLureInfo*)user_data;
+ if(!info) return;
+ switch(option)
+ {
+ case 0:
+ {
+ // accept
+ send_simple_im(info->mFromID,
+ "",
+ IM_LURE_ACCEPTED,
+ info->mLureID);
+ gAgent.teleportViaLure(info->mLureID, info->mGodlike);
+ }
+ break;
+ case 1:
+ default:
+ // decline
+ send_simple_im(info->mFromID,
+ "",
+ IM_LURE_DECLINED,
+ info->mLureID);
+ break;
+ }
+ delete info;
+ info = NULL;
+}
+
+void goto_url_callback(S32 option, void* user_data)
+{
+ char* url = (char*)user_data;
+ if(1 == option)
+ {
+ LLWeb::loadURL(url);
+ }
+ delete[] url;
+}
+
+void process_improved_im(LLMessageSystem *msg, void **user_data)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+ LLUUID from_id;
+ BOOL from_group;
+ LLUUID to_id;
+ U8 offline;
+ U8 d = 0;
+ LLUUID session_id;
+ U32 t;
+ char name[DB_FULL_NAME_BUF_SIZE];
+ char message[DB_IM_MSG_BUF_SIZE];
+ U32 parent_estate_id = 0;
+ LLUUID region_id;
+ LLVector3 position;
+ char buffer[DB_IM_MSG_BUF_SIZE * 2];
+ U8 binary_bucket[MTUBYTES];
+ S32 binary_bucket_size;
+ LLChat chat;
+
+ //XUI:translate - need to fix the full name to first/last
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, from_id);
+ msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, from_group);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, to_id);
+ msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Offline, offline);
+ msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Dialog, d);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, session_id);
+ msg->getU32Fast( _PREHASH_MessageBlock, _PREHASH_Timestamp, t);
+ //msg->getData("MessageBlock", "Count", &count);
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, DB_FULL_NAME_BUF_SIZE, name);
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, DB_IM_MSG_BUF_SIZE, message);
+ msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, parent_estate_id);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, region_id);
+ msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, position);
+ msg->getBinaryDataFast( _PREHASH_MessageBlock, _PREHASH_BinaryBucket, binary_bucket, 0, 0, MTUBYTES);
+ binary_bucket_size = msg->getSizeFast(_PREHASH_MessageBlock, _PREHASH_BinaryBucket);
+ EInstantMessage dialog = (EInstantMessage)d;
+ time_t timestamp = (time_t)t;
+
+ BOOL is_busy = gAgent.getBusy();
+ BOOL is_muted = gMuteListp->isMuted(from_id, name);
+ BOOL is_linden = gMuteListp->isLinden(name);
+ BOOL is_owned_by_me = FALSE;
+
+ chat.mMuted = is_muted && !is_linden;
+ chat.mFromID = from_id;
+ chat.mFromName = name;
+
+ LLViewerObject *source = gObjectList.findObject(session_id);
+ if (source)
+ {
+ is_owned_by_me = source->permYouOwner();
+ }
+
+ char separator_string[3]=": ";
+ int message_offset=0;
+
+ //Handle IRC styled /me messages.
+ if (!strncmp(message, "/me ", 4) || !strncmp(message, "/me'", 4))
+ {
+ strcpy(separator_string,"");
+ message_offset=3;
+ }
+
+ LLString::format_map_t args;
+ switch(dialog)
+ {
+ case IM_CONSOLE_AND_CHAT_HISTORY:
+ // These are used for system messages, hence don't need the name,
+ // as it is always "Second Life".
+ // XUI:translate
+ args["[MESSAGE]"] = message;
+
+ // Note: don't put the message in the IM history, even though was sent
+ // via the IM mechanism.
+ LLNotifyBox::showXml("SystemMessageTip",args);
+ break;
+
+ case IM_NOTHING_SPECIAL:
+ // Don't show dialog, just do IM
+ if (!gAgent.isGodlike()
+ && gAgent.getRegion()->isPrelude()
+ && to_id.isNull() )
+ {
+ // do nothing -- don't distract newbies in
+ // Prelude with global IMs
+ }
+ else if (offline == IM_ONLINE && !is_linden && is_busy && strcmp(name, SYSTEM_FROM))
+ {
+ // return a standard "busy" message, but only do it to online IM
+ // (i.e. not other auto responses and not store-and-forward IM)
+ if (!gIMView->hasSession(session_id))
+ {
+ // if there is not a panel for this conversation (i.e. it is a new IM conversation
+ // initiated by the other party) then...
+ std::string my_name;
+ gAgent.buildFullname(my_name);
+ LLString response = gSavedPerAccountSettings.getText("BusyModeResponse");
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ from_id,
+ my_name.c_str(),
+ response.c_str(),
+ IM_ONLINE,
+ IM_BUSY_AUTO_RESPONSE,
+ session_id);
+ gAgent.sendReliableMessage();
+ }
+
+ // now store incoming IM in chat history
+
+ sprintf(buffer, "%s%s%s", name, separator_string, (message+message_offset));
+
+ if(from_id == gAgentID)
+ {
+ from_id = LLUUID::null;
+ }
+ llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
+
+ // add to IM panel, but do not bother the user
+ gIMView->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ NULL,
+ dialog,
+ parent_estate_id,
+ region_id,
+ position);
+
+ // pretend this is chat generated by self, so it does not show up on screen
+ sprintf(buffer, "IM: %s%s%s", name, separator_string, (message+message_offset));
+ chat.mText = buffer;
+ LLFloaterChat::addChat( chat, TRUE, TRUE );
+ }
+ else if (from_id.isNull())
+ {
+ // Messages from "Second Life" don't go to IM history
+ sprintf(buffer, "%s: %s", name, message);
+ chat.mText = buffer;
+ LLFloaterChat::addChat(chat, FALSE, FALSE);
+ }
+ else if (to_id.isNull())
+ {
+ // Message to everyone from GOD
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("GodMessage", args);
+
+ // Treat like a system message and put in chat history.
+ // Claim to be from a local agent so it doesn't go into
+ // console.
+ sprintf(buffer, "%s%s%s", name, separator_string, (message+message_offset));
+ chat.mText = buffer;
+ BOOL local_agent = TRUE;
+ LLFloaterChat::addChat(chat, FALSE, local_agent);
+ }
+ else
+ {
+ // standard message, not from system
+ char saved[MAX_STRING];
+ saved[0] = '\0';
+ if(offline == IM_OFFLINE)
+ {
+ char time_buf[TIME_STR_LENGTH];
+ sprintf(saved, "(Saved %s) ",
+ formatted_time(timestamp, time_buf));
+ }
+ sprintf(buffer, "%s%s%s%s", name, separator_string, saved,(message+message_offset));
+ if(from_id == gAgentID)
+ {
+ from_id = LLUUID::null;
+ }
+ llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
+
+ if (!is_muted || is_linden)
+ {
+ gIMView->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ NULL,
+ dialog,
+ parent_estate_id,
+ region_id,
+ position);
+ sprintf(buffer, "IM: %s%s%s%s", name, separator_string, saved, (message+message_offset));
+
+ chat.mText = buffer;
+ BOOL local_agent = FALSE;
+ LLFloaterChat::addChat( chat, TRUE, local_agent );
+ }
+ else
+ {
+ // muted user, so don't start an IM session, just record line in chat
+ // history. Pretend the chat is from a local agent,
+ // so it will go into the history but not be shown on screen.
+ chat.mText = buffer;
+ BOOL local_agent = TRUE;
+ LLFloaterChat::addChat( chat, TRUE, local_agent );
+ }
+ }
+ break;
+
+ case IM_TYPING_START:
+ {
+ LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
+ gIMView->processIMTypingStart(im_info);
+ }
+ break;
+
+ case IM_TYPING_STOP:
+ {
+ LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
+ gIMView->processIMTypingStop(im_info);
+ }
+ break;
+
+ case IM_MESSAGEBOX:
+ {
+ // This is a block, modeless dialog.
+ //XUI:translate
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("SystemMessage", args);
+ }
+ break;
+ case IM_GROUP_NOTICE:
+ case IM_GROUP_NOTICE_REQUESTED:
+ {
+ llinfos << "Received IM_GROUP_NOTICE message." << llendl;
+ // Read the binary bucket for more information.
+ struct notice_bucket_header_t
+ {
+ U8 has_inventory;
+ U8 asset_type;
+ LLUUID group_id;
+ };
+ struct notice_bucket_full_t
+ {
+ struct notice_bucket_header_t header;
+ U8 item_name[DB_INV_ITEM_NAME_BUF_SIZE];
+ }* notice_bin_bucket;
+
+ // Make sure the binary bucket is big enough to hold the header
+ // and a null terminated item name.
+ if ( (binary_bucket_size < (sizeof(notice_bucket_header_t) + sizeof(U8)))
+ || (binary_bucket[binary_bucket_size - 1] != '\0') )
+ {
+ llwarns << "Malformed group notice binary bucket" << llendl;
+ break;
+ }
+
+ notice_bin_bucket = (struct notice_bucket_full_t*) &binary_bucket[0];
+ U8 has_inventory = notice_bin_bucket->header.has_inventory;
+ U8 asset_type = notice_bin_bucket->header.asset_type;
+ LLUUID group_id = notice_bin_bucket->header.group_id;
+ const char* item_name = (const char*) notice_bin_bucket->item_name;
+
+ // If there is inventory, give the user the inventory offer.
+ LLOfferInfo* info = NULL;
+ if (has_inventory)
+ {
+ info = new LLOfferInfo;
+ info->mIM = IM_GROUP_NOTICE;
+ info->mFromID = from_id;
+ info->mFromGroup = from_group;
+ info->mTransactionID = session_id;
+ info->mType = (LLAssetType::EType) asset_type;
+ info->mFolderID = gInventory.findCategoryUUIDForType(info->mType);
+ std::string from_name;
+
+ from_name += "A group member named ";
+ from_name += name;
+
+ info->mFromName = from_name;
+ info->mDesc = item_name;
+ info->mHost = msg->getSender();
+ }
+
+ std::string str(message);
+
+ // Tokenize the string.
+ // TODO: Support escaped tokens ("||" -> "|")
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("|","",boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ LLString subj(*iter++);
+ LLString mes(*iter++);
+
+ if (IM_GROUP_NOTICE == dialog)
+ {
+ subj += "\n";
+ mes = "\n\n" + mes;
+ LLGroupNotifyBox::show(subj.c_str(),mes.c_str(),name,group_id,t,has_inventory,item_name,info);
+ }
+ else if (IM_GROUP_NOTICE_REQUESTED == dialog)
+ {
+ LLFloaterGroupInfo::showNotice(subj.c_str(),mes.c_str(),group_id,has_inventory,item_name,info);
+ }
+ }
+ break;
+ case IM_GROUP_INVITATION:
+ {
+ //if (!is_linden && (is_busy || is_muted))
+ if ((is_busy || is_muted))
+ {
+ LLMessageSystem *msg = gMessageSystem;
+ join_group_callback(1, NULL);
+ busy_message(msg,from_id);
+ }
+ else
+ {
+ llinfos << "Received IM_GROUP_INVITATION message." << llendl;
+ // Read the binary bucket for more information.
+ struct invite_bucket_t
+ {
+ S32 membership_fee;
+ LLUUID role_id;
+ }* invite_bucket;
+
+ // Make sure the binary bucket is the correct size.
+ if (binary_bucket_size != sizeof(invite_bucket_t))
+ {
+ llwarns << "Malformed group invite binary bucket" << llendl;
+ break;
+ }
+
+ invite_bucket = (struct invite_bucket_t*) &binary_bucket[0];
+ S32 membership_fee = ntohl(invite_bucket->membership_fee);
+
+ LLJoinGroupData* userdata = new LLJoinGroupData;
+ userdata->mTransactionID = session_id;
+ userdata->mGroupID = from_id;
+ userdata->mName.assign(name);
+ userdata->mMessage.assign(message);
+ userdata->mFee = membership_fee;
+
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("JoinGroup", args,
+ &join_group_callback,
+ (void*)userdata);
+ }
+ }
+ break;
+
+ case IM_INVENTORY_OFFERED:
+ case IM_TASK_INVENTORY_OFFERED:
+ // Someone has offered us some inventory.
+ {
+ LLOfferInfo* info = new LLOfferInfo;
+
+ if (IM_INVENTORY_OFFERED == dialog)
+ {
+ struct offer_agent_bucket_t
+ {
+ S8 asset_type;
+ LLUUID object_id;
+ }* bucketp;
+
+ if (sizeof(offer_agent_bucket_t) != binary_bucket_size)
+ {
+ llwarns << "Malformed inventory offer from agent" << llendl;
+ break;
+ }
+ bucketp = (struct offer_agent_bucket_t*) &binary_bucket[0];
+ info->mType = (LLAssetType::EType) bucketp->asset_type;
+ info->mObjectID = bucketp->object_id;
+ }
+ else
+ {
+ if (sizeof(S8) != binary_bucket_size)
+ {
+ llwarns << "Malformed inventory offer from object" << llendl;
+ break;
+ }
+ info->mType = (LLAssetType::EType) binary_bucket[0];
+ info->mObjectID = LLUUID::null;
+ }
+
+ info->mIM = dialog;
+ info->mFromID = from_id;
+ info->mFromGroup = from_group;
+ info->mTransactionID = session_id;
+ info->mFolderID = gInventory.findCategoryUUIDForType(info->mType);
+
+ if (dialog == IM_TASK_INVENTORY_OFFERED)
+ {
+ info->mFromObject = TRUE;
+ }
+ else
+ {
+ info->mFromObject = FALSE;
+ }
+ info->mFromName = name;
+ info->mDesc = message;
+ info->mHost = msg->getSender();
+ //if (!is_linden && ((is_busy && !is_owned_by_me) || is_muted))
+ if (((is_busy && !is_owned_by_me) || is_muted))
+ {
+ // Same as closing window
+ inventory_offer_callback(-1, info);
+ }
+ else
+ {
+ if (dialog == IM_TASK_INVENTORY_OFFERED)
+ inventory_offer_handler(info, TRUE);
+ else
+ inventory_offer_handler(info, FALSE);
+ }
+ }
+ break;
+
+ case IM_INVENTORY_ACCEPTED:
+ {
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("InventoryAccepted", args);
+ break;
+ }
+ case IM_INVENTORY_DECLINED:
+ {
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("InventoryDeclined", args);
+ break;
+ }
+ case IM_GROUP_VOTE:
+ {
+ LLUUID *userdata = new LLUUID(session_id);
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("GroupVote", args,
+ &group_vote_callback, userdata);
+ }
+ break;
+
+ case IM_GROUP_ELECTION_DEPRECATED:
+ {
+ llwarns << "Received IM: IM_GROUP_ELECTION_DEPRECATED" << llendl;
+ }
+ break;
+ case IM_SESSION_SEND:
+ {
+ if (!is_linden && is_busy)
+ {
+ return;
+ }
+
+ // standard message, not from system
+ char saved[MAX_STRING];
+ saved[0] = '\0';
+ if(offline == IM_OFFLINE)
+ {
+ char time_buf[TIME_STR_LENGTH];
+ sprintf(saved,
+ "(Saved %s) ",
+ formatted_time(timestamp, time_buf));
+ }
+ sprintf(buffer, "%s%s%s%s", name, separator_string, saved, (message+message_offset));
+ BOOL is_this_agent = FALSE;
+ if(from_id == gAgentID)
+ {
+ from_id = LLUUID::null;
+ is_this_agent = TRUE;
+ }
+ gIMView->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ (char*)binary_bucket,
+ IM_SESSION_ADD,
+ parent_estate_id,
+ region_id,
+ position);
+
+ sprintf(buffer, "IM: %s%s%s%s", name, separator_string, saved, (message+message_offset));
+ chat.mText = buffer;
+ LLFloaterChat::addChat(chat, TRUE, is_this_agent);
+ }
+ break;
+
+ case IM_FROM_TASK:
+ if (is_busy && !is_owned_by_me)
+ {
+ return;
+ }
+ sprintf(buffer, "%s%s%s", name, separator_string, (message+message_offset));
+ // Note: lie to LLFloaterChat::addChat(), pretending that this is NOT an IM, because
+ // IMs from objcts don't open IM sessions.
+ chat.mText = buffer;
+ chat.mSourceType = CHAT_SOURCE_OBJECT;
+ LLFloaterChat::addChat(chat, FALSE, FALSE);
+ break;
+ case IM_FROM_TASK_AS_ALERT:
+ if (is_busy && !is_owned_by_me)
+ {
+ return;
+ }
+ {
+ // Construct a viewer alert for this message.
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("ObjectMessage", args);
+ }
+ break;
+ case IM_BUSY_AUTO_RESPONSE:
+ gIMView->addMessage(session_id, from_id, name, message);
+ break;
+
+ case IM_LURE_USER:
+ {
+ if (is_muted)
+ {
+ return;
+ }
+ else if (is_busy)
+ {
+ busy_message(msg,from_id);
+ }
+ else
+ {
+ // XUI:translate -> [FIRST] [LAST]
+ LLLureInfo* info = new LLLureInfo(from_id, session_id, FALSE);
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("OfferTeleport", args,
+ lure_callback, (void*)info);
+ }
+ }
+ break;
+
+ case IM_GODLIKE_LURE_USER:
+ {
+ LLLureInfo* info = new LLLureInfo(from_id, session_id, TRUE);
+ // do not show a message box, because you're about to be
+ // teleported.
+ lure_callback(0, (void *)info);
+ }
+ break;
+
+ case IM_LURE_911:
+ {
+ // HACK -- the from_id is the im_session_id
+ LLFloaterIMPanel* panel = gIMView->findFloaterBySession(session_id);
+ if (panel)
+ {
+ panel->addTeleportButton(from_id);
+ }
+ else
+ {
+ llinfos << "LLFloaterIMPanel not found for " << session_id << " from " << from_id << llendl;
+ }
+ }
+ break;
+
+ case IM_GOTO_URL:
+ {
+ char* url = new char[binary_bucket_size];
+ strcpy(url, (char*)binary_bucket);
+ args["[MESSAGE]"] = message;
+ args["[URL]"] = url;
+ LLNotifyBox::showXml("GotoURL", args,
+ goto_url_callback, (void*)url);
+ }
+ break;
+
+ case IM_FRIENDSHIP_OFFERED:
+ {
+ LLFriendshipOffer* offer = new LLFriendshipOffer;
+ offer->mFromID = from_id;
+ offer->mTransactionID = session_id;
+ offer->mOnline = (offline == IM_ONLINE);
+ offer->mHost = msg->getSender();
+
+ if (is_busy)
+ {
+ busy_message(msg, from_id);
+ friendship_offer_callback(1, (void*)offer);
+ }
+ else if (is_muted)
+ {
+ friendship_offer_callback(1, (void*)offer);
+ }
+ else
+ {
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("OfferFriendship", args,
+ &friendship_offer_callback, (void*)offer);
+ }
+ }
+ break;
+
+ case IM_FRIENDSHIP_ACCEPTED:
+ {
+ // In the case of an offline IM, the formFriendship() may be extraneous
+ // as the database should already include the relationship. But it
+ // doesn't hurt for dupes.
+ LLAvatarTracker::formFriendship(from_id);
+
+ std::vector<std::string> strings;
+ strings.push_back( from_id.getString() );
+ send_generic_message("requestonlinenotification", strings);
+
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("FriendshipAccepted", args);
+ }
+ break;
+
+ case IM_FRIENDSHIP_DECLINED:
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("FriendshipDeclined", args);
+ break;
+
+ default:
+ llwarns << "Instant message calling for unknown dialog "
+ << (S32)dialog << llendl;
+ break;
+ }
+
+ LLWindow* viewer_window = gViewerWindow->getWindow();
+ if (viewer_window && viewer_window->getMinimized())
+ {
+ viewer_window->flashIcon(5.f);
+ }
+}
+
+void busy_message (LLMessageSystem* msg, LLUUID from_id)
+{
+ if (gAgent.getBusy())
+ {
+ LLString response = gSavedPerAccountSettings.getText("BusyModeResponse");
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ from_id,
+ SYSTEM_FROM,
+ response.c_str(),
+ IM_ONLINE,
+ IM_CONSOLE_AND_CHAT_HISTORY);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void friendship_offer_callback(S32 option, void* user_data)
+{
+ LLFriendshipOffer* offer = (LLFriendshipOffer*)user_data;
+ if(!offer) return;
+ LLUUID fid;
+ LLMessageSystem* msg = gMessageSystem;
+ switch(option)
+ {
+ case 0:
+ // accept
+ LLAvatarTracker::formFriendship(offer->mFromID);
+
+ fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+
+ // This will also trigger an onlinenotification if the user is online
+ msg->newMessageFast(_PREHASH_AcceptFriendship);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offer->mTransactionID);
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, fid);
+ msg->sendReliable(offer->mHost);
+ break;
+ case 1:
+ // decline
+ msg->newMessageFast(_PREHASH_DeclineFriendship);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offer->mTransactionID);
+ msg->sendReliable(offer->mHost);
+ break;
+ default:
+ // close button probably, possibly timed out
+ break;
+ }
+
+ delete offer;
+ offer = NULL;
+}
+
+struct LLCallingCardOfferData
+{
+ LLUUID mTransactionID;
+ LLUUID mSourceID;
+ LLHost mHost;
+};
+
+void callingcard_offer_callback(S32 option, void* user_data)
+{
+ LLCallingCardOfferData* offerdata = (LLCallingCardOfferData*)user_data;
+ if(!offerdata) return;
+ LLUUID fid;
+ LLUUID from_id;
+ LLMessageSystem* msg = gMessageSystem;
+ switch(option)
+ {
+ case 0:
+ // accept
+ msg->newMessageFast(_PREHASH_AcceptCallingCard);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offerdata->mTransactionID);
+ fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, fid);
+ msg->sendReliable(offerdata->mHost);
+ break;
+ case 1:
+ // decline
+ msg->newMessageFast(_PREHASH_DeclineCallingCard);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offerdata->mTransactionID);
+ msg->sendReliable(offerdata->mHost);
+ busy_message(msg, offerdata->mSourceID);
+ break;
+ default:
+ // close button probably, possibly timed out
+ break;
+ }
+
+ delete offerdata;
+ offerdata = NULL;
+}
+
+void process_offer_callingcard(LLMessageSystem* msg, void**)
+{
+ // someone has offered to form a friendship
+ lldebugs << "callingcard offer" << llendl;
+
+ LLUUID source_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, source_id);
+ LLUUID tid;
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_TransactionID, tid);
+
+ LLCallingCardOfferData* offerdata = new LLCallingCardOfferData;
+ offerdata->mTransactionID = tid;
+ offerdata->mSourceID = source_id;
+ offerdata->mHost = msg->getSender();
+
+ LLViewerObject* source = gObjectList.findObject(source_id);
+ LLString::format_map_t args;
+ std::string source_name;
+ if(source && source->isAvatar())
+ {
+ LLNameValue* nvfirst = source->getNVPair("FirstName");
+ LLNameValue* nvlast = source->getNVPair("LastName");
+ if (nvfirst && nvlast)
+ {
+ args["[FIRST]"] = nvfirst->getString();
+ args["[LAST]"] = nvlast->getString();
+ source_name = std::string(nvfirst->getString()) + " " + nvlast->getString();
+ }
+ }
+
+ if(!source_name.empty())
+ {
+ if (gAgent.getBusy()
+ || gMuteListp->isMuted(source_id, source_name))
+ {
+ // automatically decline offer
+ callingcard_offer_callback(1, (void*)offerdata);
+ return;
+ }
+
+ LLNotifyBox::showXml("OfferCallingCard", args,
+ &callingcard_offer_callback, (void*)offerdata);
+ }
+ else
+ {
+ llwarns << "Calling card offer from an unknown source." << llendl;
+ }
+}
+
+void process_accept_callingcard(LLMessageSystem* msg, void**)
+{
+ LLNotifyBox::showXml("CallingCardAccepted");
+}
+
+void process_decline_callingcard(LLMessageSystem* msg, void**)
+{
+ LLNotifyBox::showXml("CallingCardDeclined");
+}
+
+
+void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
+{
+ LLChat chat;
+ char mesg[DB_CHAT_MSG_BUF_SIZE];
+ char from_name[DB_FULL_NAME_BUF_SIZE];
+ U8 source_temp;
+ U8 type_temp;
+ U8 audible_temp;
+ LLColor4 color(1.0f, 1.0f, 1.0f, 1.0f);
+ LLUUID from_id;
+ LLUUID owner_id;
+ BOOL is_owned_by_me = FALSE;
+ LLViewerObject* chatter;
+
+ msg->getString("ChatData", "FromName", DB_FULL_NAME_BUF_SIZE, from_name);
+ chat.mFromName = from_name;
+
+ msg->getUUID("ChatData", "SourceID", from_id);
+ chat.mFromID = from_id;
+
+ // Object owner for objects
+ msg->getUUID("ChatData", "OwnerID", owner_id);
+
+ msg->getU8Fast(_PREHASH_ChatData, _PREHASH_SourceType, source_temp);
+ chat.mSourceType = (EChatSourceType)source_temp;
+
+ msg->getU8("ChatData", "ChatType", type_temp);
+ chat.mChatType = (EChatType)type_temp;
+
+ msg->getU8Fast(_PREHASH_ChatData, _PREHASH_Audible, audible_temp);
+ chat.mAudible = (EChatAudible)audible_temp;
+
+ chat.mTime = LLFrameTimer::getElapsedSeconds();
+
+ BOOL is_self = (from_id == gAgent.getID());
+ BOOL is_busy = gAgent.getBusy();
+
+ // Apparently you can receive chat before app is fully initialized, hence
+ // gMuteListp can be null. JC
+ BOOL is_muted = FALSE;
+ BOOL is_linden = FALSE;
+ if (gMuteListp)
+ {
+ is_muted = gMuteListp->isMuted(from_id, from_name)
+ || gMuteListp->isMuted(owner_id);
+ is_linden = gMuteListp->isLinden(from_name);
+ }
+
+ chatter = gObjectList.findObject(from_id);
+ if (chatter)
+ {
+ chat.mPosAgent = chatter->getPositionAgent();
+
+ // Make swirly things only for talking objects. (not script debug messages, though)
+ if (chat.mSourceType == CHAT_SOURCE_OBJECT
+ && chat.mChatType != CHAT_TYPE_DEBUG_MSG)
+ {
+ LLViewerPartSourceChat *psc = new LLViewerPartSourceChat(chatter->getPositionAgent());
+ psc->setSourceObject(chatter);
+ psc->setColor(color);
+ //We set the particles to be owned by the object's owner,
+ //just in case they should be muted by the mute list
+ psc->setOwnerUUID(owner_id);
+ gWorldPointer->mPartSim.addPartSource(psc);
+ }
+
+ // only pay attention to other people chatting
+ if ((is_linden || (!is_muted && !is_busy))
+ && chatter != gAgent.getAvatarObject())
+ {
+ gAgent.heardChat(chat);
+ if (gLindenLabRandomNumber.llrand(2) == 0)
+ {
+ gAgent.setLookAt(LOOKAT_TARGET_AUTO_LISTEN, chatter, LLVector3::zero);
+ }
+ }
+
+ is_owned_by_me = chatter->permYouOwner();
+ }
+
+ BOOL is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible);
+ if (is_audible)
+ {
+ BOOL visible_in_chat_bubble = FALSE;
+ std::string verb;
+
+ color.setVec(1,1,1,1);
+ msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, DB_CHAT_MSG_BUF_SIZE, mesg);
+
+ BOOL ircstyle = FALSE;
+
+ // Look for IRC-style emotes here so chatbubbles work
+ if (!strncmp(mesg, "/me ", 4) || !strncmp(mesg, "/me'", 4))
+ {
+ chat.mText = from_name;
+ chat.mText += (mesg + 3);
+ ircstyle = TRUE;
+ }
+ else
+ {
+ chat.mText = mesg;
+ }
+
+ // Look for the start of typing so we can put "..." in the bubbles.
+ if (CHAT_TYPE_START == chat.mChatType)
+ {
+ // Might not have the avatar constructed yet, eg on login.
+ if (chatter && chatter->isAvatar())
+ {
+ ((LLVOAvatar*)chatter)->startTyping();
+ }
+ return;
+ }
+ else if (CHAT_TYPE_STOP == chat.mChatType)
+ {
+ // Might not have the avatar constructed yet, eg on login.
+ if (chatter && chatter->isAvatar())
+ {
+ ((LLVOAvatar*)chatter)->stopTyping();
+ }
+ return;
+ }
+
+ // We have a real utterance now, so can stop showing "..." and proceed.
+ if (chatter && chatter->isAvatar())
+ {
+ ((LLVOAvatar*)chatter)->stopTyping();
+
+ if (!is_muted && !is_busy)
+ {
+ visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles");
+ ((LLVOAvatar*)chatter)->addChat(chat);
+ }
+ }
+
+ // Look for IRC-style emotes
+ if (ircstyle)
+ {
+ // Do nothing, ircstyle is fixed above for chat bubbles
+ }
+ else
+ {
+ switch(chat.mChatType)
+ {
+ case CHAT_TYPE_WHISPER:
+ if (is_self)
+ {
+ verb = " whisper: ";
+ }
+ else
+ {
+ verb = " whispers: ";
+ }
+ break;
+ case CHAT_TYPE_DEBUG_MSG:
+ case CHAT_TYPE_NORMAL:
+ verb = ": ";
+ break;
+ case CHAT_TYPE_SHOUT:
+ if (is_self)
+ {
+ verb = " shout: ";
+ }
+ else
+ {
+ verb = " shouts: ";
+ }
+ break;
+ case CHAT_TYPE_START:
+ case CHAT_TYPE_STOP:
+ llwarns << "Got chat type start/stop in main chat processing." << llendl;
+ break;
+ default:
+ llwarns << "Unknown type " << chat.mChatType << " in chat!" << llendl;
+ verb = " say, ";
+ break;
+ }
+
+ if (is_self)
+ {
+ chat.mText = "You";
+ }
+ else
+ {
+ chat.mText = from_name;
+ }
+ chat.mText += verb;
+ chat.mText += mesg;
+ }
+
+ if (chatter)
+ {
+ chat.mPosAgent = chatter->getPositionAgent();
+ }
+
+ // truth table:
+ // LINDEN BUSY MUTED OWNED_BY_YOU DISPLAY STORE IN HISTORY
+ // F F F F Yes Yes
+ // F F F T Yes Yes
+ // F F T F No No
+ // F F T T No No
+ // F T F F No Yes
+ // F T F T Yes Yes
+ // F T T F No No
+ // F T T T No No
+ // T * * * Yes Yes
+
+ chat.mMuted = is_muted && !is_linden;
+
+
+ if (!visible_in_chat_bubble
+ && (is_linden || !is_busy || is_owned_by_me))
+ {
+ // show on screen and add to history
+ LLFloaterChat::addChat(chat, FALSE, FALSE);
+ }
+ else
+ {
+ // just add to chat history
+ LLFloaterChat::addChatHistory(chat);
+ }
+ }
+}
+
+/*
+void process_agent_to_new_region(LLMessageSystem *mesgsys, void **user_data)
+{
+// LLFastTimer t(LLFastTimer::FTM_TEMP8);
+
+ U64 handle;
+ U32 ip;
+ U16 port;
+ LLUUID session_id;
+
+ // Actually, the agent itself should process this message.
+ // From a "AgentToNewRegion" message
+ mesgsys->getIPAddrFast(_PREHASH_RegionData, _PREHASH_IP, ip);
+ mesgsys->getIPPortFast(_PREHASH_RegionData, _PREHASH_Port, port);
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_Handle, handle);
+ mesgsys->getUUIDFast(_PREHASH_RegionData, _PREHASH_SessionID, session_id);
+
+ if (gAgent.getSessionID() != session_id)
+ {
+ llwarns << "Got AgentToNewRegion with invalid session ID, ignoring" << llendl;
+ return;
+ }
+
+ LLViewerRegion *regionp;
+
+ F32 x, y;
+ from_region_handle(handle, &x, &y);
+ regionp = gWorldp->getRegionFromHandle(handle);
+ if (!regionp)
+ {
+ if (gAgent.getRegion())
+ {
+ llwarns << "current region " << gAgent.getRegion()->getOriginGlobal() << llendl;
+ }
+
+ llwarns << "Agent being sent to invalid home region: "
+ << x << ":" << y
+ << " current pos " << gAgent.getPositionGlobal()
+ << llendl;
+ do_disconnect("You were sent to an invalid region.");
+ return;
+
+ }
+
+ if (regionp == gAgent.getRegion())
+ {
+ llinfos << "Agent being sent to current home region, skipping." << llendl;
+ return;
+ }
+
+
+ llinfos << "AgentToNewRegion - being sent to " << x << ":" << y
+ << ""
+
+ LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal());
+
+ gAgent.setRegion(regionp);
+
+ gObjectList.shiftObjects(shift_vector);
+
+ llinfos << "Changing home region to " << x << ":" << y << llendl;
+
+ // send camera update to new region
+
+ send_agent_update(TRUE, TRUE);
+
+ // set our upstream asset provider to the new simulator
+ LLHost upstream(ip, port);
+ gAssetStorage->setUpstream(upstream);
+ gCacheName->setUpstream(upstream);
+
+ // Not needed, as simulator will always send request as it creates the new
+ // agent in the new region.
+ // send_current_avatar_info();
+}
+*/
+
+// Simulator we're on is informing the viewer that the agent
+// is starting to teleport (perhaps to another sim, perhaps to the
+// same sim). If we initiated the teleport process by sending some kind
+// of TeleportRequest, then this info is redundant, but if the sim
+// initiated the teleport (via a script call, being killed, etc.)
+// then this info is news to us.
+void process_teleport_start(LLMessageSystem *msg, void**)
+{
+ U32 teleport_flags = 0x0;
+ msg->getU32("Info", "TeleportFlags", teleport_flags);
+
+ if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL)
+ {
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "");
+ }
+ else
+ {
+ gViewerWindow->setProgressCancelButtonVisible(TRUE, "Cancel");
+ }
+
+ // Freeze the UI and show progress bar
+ // Note: could add data here to differentiate between normal teleport and death.
+
+ if( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE )
+ {
+ gTeleportDisplay = TRUE;
+ gAgent.setTeleportState( LLAgent::TELEPORT_START );
+ make_ui_sound("UISndTeleportOut");
+
+ // Don't call LLFirstUse::useTeleport here because this could be
+ // due to being killed, which would send you home, not to a Telehub
+ }
+}
+
+void process_teleport_progress(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ if((gAgent.getID() != agent_id)
+ || (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE))
+ {
+ llwarns << "Unexpected teleport progress message." << llendl;
+ return;
+ }
+ U32 teleport_flags = 0x0;
+ msg->getU32("Info", "TeleportFlags", teleport_flags);
+ if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL)
+ {
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "");
+ }
+ else
+ {
+ gViewerWindow->setProgressCancelButtonVisible(TRUE, "Cancel");
+ }
+ char buffer[MAX_STRING];
+ msg->getString("Info", "Message", MAX_STRING, buffer);
+ lldebugs << "teleport progress: " << buffer << llendl;
+ gAgent.setTeleportMessage(buffer);
+}
+
+class LLFetchInWelcomeArea : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLFetchInWelcomeArea() {}
+ virtual void done()
+ {
+ LLIsType is_landmark(LLAssetType::AT_LANDMARK);
+ LLIsType is_card(LLAssetType::AT_CALLINGCARD);
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ folder_ref_t::iterator it = mCompleteFolders.begin();
+ folder_ref_t::iterator end = mCompleteFolders.end();
+ for(; it != end; ++it)
+ {
+ gInventory.collectDescendentsIf(
+ (*it),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_landmark);
+ gInventory.collectDescendentsIf(
+ (*it),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_card);
+ }
+ S32 count = items.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLString::format_map_t args;
+ args["[NAME]"] = items[i]->getName();
+ switch(items[i]->getType())
+ {
+ case LLAssetType::AT_LANDMARK:
+ LLNotifyBox::showXml("TeleportToLandmark",args);
+ break;
+ case LLAssetType::AT_CALLINGCARD:
+ LLNotifyBox::showXml("TeleportToPerson",args);
+ break;
+ default:
+ break;
+ }
+ }
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+// Teleport notification from the simulator
+// We're going to pretend to be a new agent
+void process_teleport_finish(LLMessageSystem* msg, void**)
+{
+ //llinfos << "Got teleport location message" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Got teleport notification for wrong agent!" << llendl;
+ return;
+ }
+
+ // Do teleport effect for where you're leaving
+ // VEFFECT: TeleportStart
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(gAgent.getPositionGlobal());
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gHUDManager->sendEffects();
+
+ U32 location_id;
+ U32 sim_ip;
+ U16 sim_port;
+ LLVector3 pos, look_at;
+ U64 region_handle;
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id);
+ msg->getIPAddrFast(_PREHASH_Info, _PREHASH_SimIP, sim_ip);
+ msg->getIPPortFast(_PREHASH_Info, _PREHASH_SimPort, sim_port);
+ //msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos);
+ //msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at);
+ msg->getU64Fast(_PREHASH_Info, _PREHASH_RegionHandle, region_handle);
+ U32 teleport_flags;
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags);
+
+
+ char seedCap[STD_STRING_BUF_SIZE];
+ msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability,
+ STD_STRING_BUF_SIZE, seedCap);
+
+ // update home location if we are teleporting out of prelude
+ if((teleport_flags & TELEPORT_FLAGS_SET_HOME_TO_TARGET)
+ && (!gAgent.isGodlike()))
+ {
+ gAgent.setHomePosRegion(region_handle, pos);
+
+ // get callingcards and landmarks available to the user arriving.
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLUUID folder_id;
+ folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+ if(folder_id.notNull()) folders.push_back(folder_id);
+ folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
+ if(folder_id.notNull()) folders.push_back(folder_id);
+ if(!folders.empty())
+ {
+ LLFetchInWelcomeArea* fetcher = new LLFetchInWelcomeArea;
+ fetcher->fetchDescendents(folders);
+ if(fetcher->isEverythingComplete())
+ {
+ fetcher->done();
+ }
+ else
+ {
+ gInventory.addObserver(fetcher);
+ }
+ }
+ }
+
+ LLHost sim_host(sim_ip, sim_port);
+
+ // Viewer trusts the simulator.
+ gMessageSystem->enableCircuit(sim_host, TRUE);
+ LLViewerRegion* regionp = gWorldp->addRegion(region_handle, sim_host);
+
+/*
+ // send camera update to new region
+ gAgent.updateCamera();
+
+ // likewise make sure the camera is behind the avatar
+ gAgent.resetView(TRUE);
+ LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal());
+ gAgent.setRegion(regionp);
+ gObjectList.shiftObjects(shift_vector);
+
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->clearChatText();
+ gAgent.slamLookAt(look_at);
+ }
+ gAgent.setPositionAgent(pos);
+ gAssetStorage->setUpstream(sim);
+ gCacheName->setUpstream(sim);
+*/
+
+ // now, use the circuit info to tell simulator about us!
+ llinfos << "process_teleport_finish() Enabling "
+ << sim_host << " with code " << msg->mOurCircuitCode << llendl;
+ msg->newMessageFast(_PREHASH_UseCircuitCode);
+ msg->nextBlockFast(_PREHASH_CircuitCode);
+ msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_ID, gAgent.getID());
+ msg->sendReliable(sim_host);
+
+ send_complete_agent_movement(sim_host);
+ gAgent.setTeleportState( LLAgent::TELEPORT_MOVING );
+ gAgent.setTeleportMessage("Contacting New Region...");
+
+ regionp->setSeedCapability(std::string(seedCap));
+
+ // Don't send camera updates to the new region until we're
+ // actually there...
+
+
+ // Now do teleport effect for where you're going.
+ // VEFFECT: TeleportEnd
+ effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(gAgent.getPositionGlobal());
+
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gHUDManager->sendEffects();
+
+// gTeleportDisplay = TRUE;
+// gTeleportDisplayTimer.reset();
+// gViewerWindow->setShowProgress(TRUE);
+
+ // This could be first use of teleport, so test for that
+ LLFirstUse::useTeleport();
+}
+
+// stuff we have to do every time we get an AvatarInitComplete from a sim
+/*
+void process_avatar_init_complete(LLMessageSystem* msg, void**)
+{
+ LLVector3 agent_pos;
+ msg->getVector3Fast(_PREHASH_AvatarData, _PREHASH_Position, agent_pos);
+ agent_movement_complete(msg->getSender(), agent_pos);
+}
+*/
+
+void process_agent_movement_complete(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id))
+ {
+ llwarns << "Incorrect id in process_agent_movement_complete()"
+ << llendl;
+ return;
+ }
+
+ llinfos << "process_agent_movement_complete()" << llendl;
+
+ // *FIX: check timestamp.
+ LLVector3 agent_pos;
+ msg->getVector3Fast(_PREHASH_Data, _PREHASH_Position, agent_pos);
+ LLVector3 look_at;
+ msg->getVector3Fast(_PREHASH_Data, _PREHASH_LookAt, look_at);
+ U64 region_handle;
+ msg->getU64Fast(_PREHASH_Data, _PREHASH_RegionHandle, region_handle);
+
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (!avatarp)
+ {
+ // Could happen if you were immediately god-teleported away on login,
+ // maybe other cases. Continue, but warn. JC
+ llwarns << "agent_movement_complete() with NULL avatarp." << llendl;
+ }
+
+ F32 x, y;
+ from_region_handle(region_handle, &x, &y);
+ LLViewerRegion* regionp = gWorldp->getRegionFromHandle(region_handle);
+ if (!regionp)
+ {
+ if (gAgent.getRegion())
+ {
+ llwarns << "current region " << gAgent.getRegion()->getOriginGlobal() << llendl;
+ }
+
+ llwarns << "Agent being sent to invalid home region: "
+ << x << ":" << y
+ << " current pos " << gAgent.getPositionGlobal()
+ << llendl;
+ do_disconnect("You were sent to an invalid region.");
+ return;
+
+ }
+
+ llinfos << "Changing home region to " << x << ":" << y << llendl;
+
+ // set our upstream host the new simulator and shuffle things as
+ // appropriate.
+ LLVector3 shift_vector = regionp->getPosRegionFromGlobal(
+ gAgent.getRegion()->getOriginGlobal());
+ gAgent.setRegion(regionp);
+ gObjectList.shiftObjects(shift_vector);
+ gAssetStorage->setUpstream(msg->getSender());
+ gCacheName->setUpstream(msg->getSender());
+ gViewerThrottle.sendToSim();
+ gViewerWindow->sendShapeToSim();
+
+ bool is_teleport = false;
+
+ if( gAgent.getTeleportState() == LLAgent::TELEPORT_MOVING )
+ {
+ // Force the camera back onto the agent, don't animate. JC
+ gAgent.setFocusOnAvatar(TRUE, FALSE);
+ gAgent.slamLookAt(look_at);
+ gAgent.updateCamera();
+
+ gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL );
+
+ // set the appearance on teleport since the new sim does not
+ // know what you look like.
+ gAgent.sendAgentSetAppearance();
+
+ if (avatarp)
+ {
+ avatarp->setPositionAgent(agent_pos);
+ avatarp->clearChat();
+ avatarp->slamPosition();
+ }
+
+ is_teleport = true;
+ }
+ else
+ {
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+
+ if ( LLTracker::isTracking(NULL) )
+ {
+ // Check distance to beacon, if < 5m, remove beacon
+ LLVector3d beacon_pos = LLTracker::getTrackedPositionGlobal();
+ LLVector3 beacon_dir(agent_pos.mV[VX] - (F32)fmod(beacon_pos.mdV[VX], 256.0), agent_pos.mV[VY] - (F32)fmod(beacon_pos.mdV[VY], 256.0), 0);
+ if (beacon_dir.magVecSquared() < 25.f)
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ else if ( is_teleport )
+ {
+ //look at the beacon
+ LLVector3 global_agent_pos = agent_pos;
+ global_agent_pos[0] += x;
+ global_agent_pos[1] += y;
+ look_at = (LLVector3)beacon_pos - global_agent_pos;
+ look_at.normVec();
+ gAgent.slamLookAt(look_at);
+ }
+ }
+
+ // TODO: Put back a check for flying status! DK 12/19/05
+ // Sim tells us whether the new position is off the ground
+ /*
+ if (teleport_flags & TELEPORT_FLAGS_IS_FLYING)
+ {
+ gAgent.setFlying(TRUE);
+ }
+ else
+ {
+ gAgent.setFlying(FALSE);
+ }
+ */
+
+ send_agent_update(TRUE, TRUE);
+
+ if (gAgent.getRegion()->getBlockFly())
+ {
+ gAgent.setFlying(gAgent.canFly());
+ }
+
+ // force simulator to recognize busy state
+ if (gAgent.getBusy())
+ {
+ gAgent.setBusy();
+ }
+ else
+ {
+ gAgent.clearBusy();
+ }
+
+ if (avatarp)
+ {
+ avatarp->mFootPlane.clearVec();
+ }
+
+ // reset always run status
+ msg->newMessageFast(_PREHASH_SetAlwaysRun);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_AlwaysRun, gAgent.getAlwaysRun());
+ gAgent.sendReliableMessage();
+}
+
+void process_crossed_region(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id))
+ {
+ llwarns << "Incorrect id in process_crossed_region()"
+ << llendl;
+ return;
+ }
+ llinfos << "process_crossed_region()" << llendl;
+
+ U32 sim_ip;
+ msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip);
+ U16 sim_port;
+ msg->getIPPortFast(_PREHASH_RegionData, _PREHASH_SimPort, sim_port);
+ LLHost sim_host(sim_ip, sim_port);
+ U64 region_handle;
+ msg->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+
+ char seedCap[STD_STRING_BUF_SIZE];
+ msg->getStringFast(_PREHASH_RegionData, _PREHASH_SeedCapability, STD_STRING_BUF_SIZE, seedCap);
+
+ send_complete_agent_movement(sim_host);
+
+ LLViewerRegion* regionp = gWorldp->addRegion(region_handle, sim_host);
+ regionp->setSeedCapability(std::string(seedCap));
+}
+
+
+
+// Sends avatar and camera information to simulator.
+// Sent roughly once per frame, or 20 times per second, whichever is less often
+
+const F32 THRESHOLD_HEAD_ROT_QDOT = 0.9997f; // ~= 2.5 degrees -- if its less than this we need to update head_rot
+const F32 MAX_HEAD_ROT_QDOT = 0.99999f; // ~= 0.5 degrees -- if its greater than this then no need to update head_rot
+ // between these values we delay the updates (but no more than one second)
+
+
+void send_agent_update(BOOL force_send, BOOL send_reliable)
+{
+ if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE)
+ {
+ // We don't care if they want to send an agent update, they're not allowed to until the simulator
+ // that's the target is ready to receive them (after avatar_init_complete is received)
+ return;
+ }
+
+ // We have already requested to log out. Don't send agent updates.
+ if(gLogoutRequestSent)
+ {
+ return;
+ }
+
+ const F32 TRANSLATE_THRESHOLD = 0.01f;
+
+ // NOTA BENE: This is (intentionally?) using the small angle sine approximation to test for rotation
+ // Plus, there is an extra 0.5 in the mix since the perpendicular between last_camera_at and getAtAxis() bisects cam_rot_change
+ // Thus, we're actually testing against 0.2 degrees
+ const F32 ROTATION_THRESHOLD = 0.1f * 2.f*F_PI/360.f; // Rotation thresh 0.2 deg, see note above
+
+ const U8 DUP_MSGS = 1; // HACK! number of times to repeat data on motionless agent
+
+ // Store data on last sent update so that if no changes, no send
+ static LLVector3 last_camera_pos_agent,
+ last_camera_at,
+ last_camera_left,
+ last_camera_up;
+
+ static LLVector3 cam_center_chg,
+ cam_rot_chg;
+
+ static LLQuaternion last_head_rot;
+ static U32 last_control_flags = 0;
+ static U8 last_render_state;
+ static U8 duplicate_count = 0;
+ static F32 head_rot_chg = 1.0;
+ static U8 last_flags;
+
+ LLMessageSystem *msg = gMessageSystem;
+ LLVector3 camera_pos_agent; // local to avatar's region
+ U8 render_state;
+
+ LLQuaternion body_rotation = gAgent.getFrameAgent().getQuaternion();
+ LLQuaternion head_rotation = gAgent.getHeadRotation();
+
+ camera_pos_agent = gAgent.getCameraPositionAgent();
+
+ render_state = gAgent.getRenderState();
+
+ U32 control_flag_change = 0;
+ U8 flag_change = 0;
+
+ cam_center_chg = last_camera_pos_agent - camera_pos_agent;
+ cam_rot_chg = last_camera_at - gCamera->getAtAxis();
+
+ // If a modifier key is held down, turn off
+ // LBUTTON and ML_LBUTTON so that using the camera (alt-key) doesn't
+ // trigger a control event.
+ U32 control_flags = gAgent.getControlFlags();
+ MASK key_mask = gKeyboard->currentMask(TRUE);
+ if (key_mask & MASK_ALT || key_mask & MASK_CONTROL)
+ {
+ control_flags &= ~( AGENT_CONTROL_LBUTTON_DOWN |
+ AGENT_CONTROL_ML_LBUTTON_DOWN );
+ control_flags |= AGENT_CONTROL_LBUTTON_UP |
+ AGENT_CONTROL_ML_LBUTTON_UP ;
+ }
+
+ control_flag_change = last_control_flags ^ control_flags;
+
+ U8 flags = AU_FLAGS_NONE;
+ if (gAgent.isGroupTitleHidden())
+ {
+ flags |= AU_FLAGS_HIDETITLE;
+ }
+
+ flag_change = last_flags ^ flags;
+
+ head_rot_chg = dot(last_head_rot, head_rotation);
+
+ if (force_send ||
+ (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) ||
+ (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) ||
+ (last_render_state != render_state) ||
+ (cam_rot_chg.magVec() > ROTATION_THRESHOLD) ||
+ control_flag_change != 0 ||
+ flag_change != 0)
+ {
+/*
+ if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT)
+ {
+ //llinfos << "head rot " << head_rotation << llendl;
+ llinfos << "head_rot_chg = " << head_rot_chg << llendl;
+ }
+ if (cam_rot_chg.magVec() > ROTATION_THRESHOLD)
+ {
+ llinfos << "cam rot " << cam_rot_chg.magVec() << llendl;
+ }
+ if (cam_center_chg.magVec() > TRANSLATE_THRESHOLD)
+ {
+ llinfos << "cam center " << cam_center_chg.magVec() << llendl;
+ }
+// if (drag_delta_chg.magVec() > TRANSLATE_THRESHOLD)
+// {
+// llinfos << "drag delta " << drag_delta_chg.magVec() << llendl;
+// }
+ if (control_flag_change)
+ {
+ llinfos << "dcf = " << control_flag_change << llendl;
+ }
+*/
+
+ duplicate_count = 0;
+ }
+ else
+ {
+ duplicate_count++;
+
+ if (head_rot_chg < MAX_HEAD_ROT_QDOT && duplicate_count < AGENT_UPDATES_PER_SECOND)
+ {
+ // The head_rotation is sent for updating things like attached guns.
+ // We only trigger a new update when head_rotation deviates beyond
+ // some threshold from the last update, however this can break fine
+ // adjustments when trying to aim an attached gun, so what we do here
+ // (where we would normally skip sending an update when nothing has changed)
+ // is gradually reduce the threshold to allow a better update to
+ // eventually get sent... should update to within 0.5 degrees in less
+ // than a second.
+ if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT + (MAX_HEAD_ROT_QDOT - THRESHOLD_HEAD_ROT_QDOT) * duplicate_count / AGENT_UPDATES_PER_SECOND)
+ {
+ duplicate_count = 0;
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ if (duplicate_count < DUP_MSGS && !gDisconnected)
+ {
+ // Build the message
+ msg->newMessageFast(_PREHASH_AgentUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addQuatFast(_PREHASH_BodyRotation, body_rotation);
+ msg->addQuatFast(_PREHASH_HeadRotation, head_rotation);
+ msg->addU8Fast(_PREHASH_State, render_state);
+ msg->addU8Fast(_PREHASH_Flags, flags);
+
+// if (camera_pos_agent.mV[VY] > 255.f)
+// {
+// llinfos << "Sending camera center " << camera_pos_agent << llendl;
+// }
+
+ msg->addVector3Fast(_PREHASH_CameraCenter, camera_pos_agent);
+ msg->addVector3Fast(_PREHASH_CameraAtAxis, gCamera->getAtAxis());
+ msg->addVector3Fast(_PREHASH_CameraLeftAxis, gCamera->getLeftAxis());
+ msg->addVector3Fast(_PREHASH_CameraUpAxis, gCamera->getUpAxis());
+ msg->addF32Fast(_PREHASH_Far, gAgent.mDrawDistance);
+
+ msg->addU32Fast(_PREHASH_ControlFlags, control_flags);
+
+ if (gDebugClicks)
+ {
+ if (control_flags & AGENT_CONTROL_LBUTTON_DOWN)
+ {
+ llinfos << "AgentUpdate left button down" << llendl;
+ }
+
+ if (control_flags & AGENT_CONTROL_LBUTTON_UP)
+ {
+ llinfos << "AgentUpdate left button up" << llendl;
+ }
+ }
+
+ gAgent.enableControlFlagReset();
+
+ if (!send_reliable)
+ {
+ gAgent.sendMessage();
+ }
+ else
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ //llinfos << "agent " << avatar_pos_agent << " cam " << camera_pos_agent << llendl;
+
+ // Copy the old data
+ last_head_rot = head_rotation;
+ last_render_state = render_state;
+ last_camera_pos_agent = camera_pos_agent;
+ last_camera_at = gCamera->getAtAxis();
+ last_camera_left = gCamera->getLeftAxis();
+ last_camera_up = gCamera->getUpAxis();
+ last_control_flags = control_flags;
+ last_flags = flags;
+ }
+}
+
+
+
+// TODO: FIX this dependency
+extern U32 gObjectBits;
+
+void process_object_update(LLMessageSystem *mesgsys, void **user_data)
+{
+ // Update the data counters
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ // Update the object...
+ gObjectList.processObjectUpdate(mesgsys, user_data, OUT_FULL);
+ stop_glerror();
+}
+
+void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data)
+{
+ // Update the data counters
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ // Update the object...
+ gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_FULL_COMPRESSED);
+ stop_glerror();
+}
+
+void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data)
+{
+ // Update the data counters
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ // Update the object...
+ gObjectList.processCachedObjectUpdate(mesgsys, user_data, OUT_FULL_CACHED);
+ stop_glerror();
+}
+
+
+void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data)
+{
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_TERSE_IMPROVED);
+}
+
+
+
+void process_kill_object(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS);
+
+ LLUUID id;
+ U32 local_id;
+ S32 i;
+ S32 num_objects;
+
+ num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData);
+
+ for (i = 0; i < num_objects; i++)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+
+ LLViewerObjectList::getUUIDFromLocal(id,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (id == LLUUID::null)
+ {
+ //llinfos << "Unknown kill for local " << local_id << llendl;
+ gObjectList.mNumUnknownKills++;
+ continue;
+ }
+ else
+ {
+ //llinfos << "Kill message for local " << local_id << llendl;
+ }
+
+ gSelectMgr->selectionRemoveObject(id);
+
+ // ...don't kill the avatar
+ if (!(id == gAgentID))
+ {
+ LLViewerObject *objectp = gObjectList.findObject(id);
+ if (objectp)
+ {
+ // Display green bubble on kill
+ if ( gShowObjectUpdates )
+ {
+ LLViewerObject* newobject;
+ newobject = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, objectp->getRegion());
+
+ LLVOTextBubble* bubble = (LLVOTextBubble*) newobject;
+
+ bubble->mColor.setVec(0.f, 1.f, 0.f, 1.f);
+ bubble->setScale( 2.0f * bubble->getScale() );
+ bubble->setPositionGlobal(objectp->getPositionGlobal());
+ gPipeline.addObject(bubble);
+ }
+
+ // Do the kill
+ gObjectList.killObject(objectp);
+ }
+ else
+ {
+ llwarns << "Object in UUID lookup, but not on object list in kill!" << llendl;
+ gObjectList.mNumUnknownKills++;
+ }
+ }
+ }
+}
+
+void process_time_synch(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLVector3 sun_direction;
+ LLVector3 sun_ang_velocity;
+ F32 phase;
+ U64 space_time_usec;
+
+ // "SimulatorViewerTimeMessage"
+ mesgsys->getU64Fast(_PREHASH_TimeInfo, _PREHASH_UsecSinceStart, space_time_usec);
+ mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerDay, gSecondsPerDay);
+ mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerYear, gSecondsPerYear);
+
+ // This should eventually be moved to an "UpdateHeavenlyBodies" message
+ mesgsys->getF32Fast(_PREHASH_TimeInfo, _PREHASH_SunPhase, phase);
+ mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunDirection, sun_direction);
+ mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunAngVelocity, sun_ang_velocity);
+
+ gWorldp->setSpaceTimeUSec(space_time_usec);
+
+ //lldebugs << "time_synch() - " << sun_direction << ", " << sun_ang_velocity
+ // << ", " << phase << llendl;
+
+ gSky.setSunPhase(phase);
+ gSky.setSunTargetDirection(sun_direction, sun_ang_velocity);
+ if (!gNoRender && !(gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || gSky.getOverrideSun()))
+ {
+ gSky.setSunDirection(sun_direction, sun_ang_velocity);
+ }
+}
+
+void process_sound_trigger(LLMessageSystem *msg, void **)
+{
+ if (!gAudiop) return;
+ if (!gParcelMgr) return;
+ if (!gMuteListp) return;
+
+ U64 region_handle = 0;
+ F32 gain = 0;
+ LLUUID sound_id;
+ LLUUID owner_id;
+ LLUUID object_id;
+ LLUUID parent_id;
+ LLVector3 pos_local;
+
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_SoundID, sound_id);
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_OwnerID, owner_id);
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ParentID, parent_id);
+ msg->getU64Fast(_PREHASH_SoundData, _PREHASH_Handle, region_handle);
+ msg->getVector3Fast(_PREHASH_SoundData, _PREHASH_Position, pos_local);
+ msg->getF32Fast(_PREHASH_SoundData, _PREHASH_Gain, gain);
+
+ // adjust sound location to true global coords
+ LLVector3d pos_global = from_region_handle(region_handle);
+ pos_global.mdV[VX] += pos_local.mV[VX];
+ pos_global.mdV[VY] += pos_local.mV[VY];
+ pos_global.mdV[VZ] += pos_local.mV[VZ];
+
+ // Don't play a trigger sound if you can't hear it due
+ // to parcel "local audio only" settings.
+ if (!gParcelMgr->canHearSound(pos_global)) return;
+
+ // Don't play sounds triggered by someone you muted.
+ if (gMuteListp->isMuted(owner_id)) return;
+
+ // Don't play sounds from an object you muted
+ if (gMuteListp->isMuted(object_id)) return;
+
+ // Don't play sounds from an object whose parent you muted
+ if (parent_id.notNull()
+ && gMuteListp->isMuted(parent_id))
+ {
+ return;
+ }
+
+ gAudiop->triggerSound(sound_id, owner_id, gain, pos_global);
+}
+
+void process_preload_sound(LLMessageSystem *msg, void **user_data)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ LLUUID sound_id;
+ LLUUID object_id;
+ LLUUID owner_id;
+
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id);
+
+ LLViewerObject *objectp = gObjectList.findObject(object_id);
+ if (!objectp) return;
+
+ if (gMuteListp->isMuted(object_id)) return;
+ if (gMuteListp->isMuted(owner_id)) return;
+
+ LLAudioSource *sourcep = objectp->getAudioSource(owner_id);
+ if (!sourcep) return;
+
+ LLAudioData *datap = gAudiop->getAudioData(sound_id);
+
+ // Note that I don't actually do any loading of the
+ // audio data into a buffer at this point, as it won't actually
+ // help us out.
+
+ // Add audioData starts a transfer internally.
+ sourcep->addAudioData(datap, FALSE);
+}
+
+void process_attached_sound(LLMessageSystem *msg, void **user_data)
+{
+ F32 gain = 0;
+ LLUUID sound_id;
+ LLUUID object_id;
+ LLUUID owner_id;
+ U8 flags;
+
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id);
+ msg->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain);
+ msg->getU8Fast(_PREHASH_DataBlock, _PREHASH_Flags, flags);
+
+ LLViewerObject *objectp = gObjectList.findObject(object_id);
+ if (!objectp)
+ {
+ // we don't know about this object, just bail
+ return;
+ }
+
+ if (gMuteListp->isMuted(object_id)) return;
+
+ if (gMuteListp->isMuted(owner_id)) return;
+
+ objectp->setAttachedSound(sound_id, owner_id, gain, flags);
+}
+
+
+void process_attached_sound_gain_change(LLMessageSystem *mesgsys, void **user_data)
+{
+ F32 gain = 0;
+ LLUUID object_guid;
+ LLViewerObject *objectp = NULL;
+
+ mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid);
+
+ if (!((objectp = gObjectList.findObject(object_guid))))
+ {
+ // we don't know about this object, just bail
+ return;
+ }
+
+ mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain);
+
+ objectp->adjustAudioGain(gain);
+}
+
+/* Unused July 2006
+void process_attached_sound_cutoff_radius(LLMessageSystem *mesgsys, void **user_data)
+{
+ F32 radius = 0;
+ LLUUID object_guid;
+ LLViewerObject *objectp = NULL;
+
+ mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid);
+
+ if (!((objectp = gObjectList.findObject(object_guid))))
+ {
+ // we don't know about this object, just bail
+ return;
+ }
+
+ mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Radius, radius);
+
+ if (gAudiop)
+ {
+// gAudiop->attachToObject(sound_guid, object_guid, gain, priority, flags);
+ }
+}
+*/
+
+void process_health_message(LLMessageSystem *mesgsys, void **user_data)
+{
+ F32 health;
+
+ mesgsys->getF32Fast(_PREHASH_HealthData, _PREHASH_Health, health);
+
+ if (gStatusBar)
+ {
+ gStatusBar->setHealth((S32)health);
+ }
+}
+
+
+void process_sim_stats(LLMessageSystem *msg, void **user_data)
+{
+ S32 count = msg->getNumberOfBlocks("Stat");
+ for (S32 i = 0; i < count; ++i)
+ {
+ U32 stat_id;
+ F32 stat_value;
+ msg->getU32("Stat", "StatID", stat_id, i);
+ msg->getF32("Stat", "StatValue", stat_value, i);
+ switch (stat_id)
+ {
+ case LL_SIM_STAT_TIME_DILATION:
+ gViewerStats->mSimTimeDilation.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_FPS:
+ gViewerStats->mSimFPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PHYSFPS:
+ gViewerStats->mSimPhysicsFPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_AGENTUPS:
+ gViewerStats->mSimAgentUPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_FRAMEMS:
+ gViewerStats->mSimFrameMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NETMS:
+ gViewerStats->mSimNetMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_SIMOTHERMS:
+ gViewerStats->mSimSimOtherMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_SIMPHYSICSMS:
+ gViewerStats->mSimSimPhysicsMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_AGENTMS:
+ gViewerStats->mSimAgentMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_IMAGESMS:
+ gViewerStats->mSimImagesMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_SCRIPTMS:
+ gViewerStats->mSimScriptMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMTASKS:
+ gViewerStats->mSimObjects.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMTASKSACTIVE:
+ gViewerStats->mSimActiveObjects.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMAGENTMAIN:
+ gViewerStats->mSimMainAgents.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMAGENTCHILD:
+ gViewerStats->mSimChildAgents.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMSCRIPTSACTIVE:
+ gViewerStats->mSimActiveScripts.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_LSLIPS:
+ gViewerStats->mSimLSLIPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_INPPS:
+ gViewerStats->mSimInPPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_OUTPPS:
+ gViewerStats->mSimOutPPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PENDING_DOWNLOADS:
+ gViewerStats->mSimPendingDownloads.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PENDING_UPLOADS:
+ gViewerStats->mSimPendingUploads.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PENDING_LOCAL_UPLOADS:
+ gViewerStats->mSimPendingLocalUploads.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_TOTAL_UNACKED_BYTES:
+ gViewerStats->mSimTotalUnackedBytes.addValue(stat_value / 1024.f);
+ break;
+ default:
+ llwarns << "Unknown stat id" << stat_id << llendl;
+ }
+ }
+
+ /*
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_PhysicsTimeDilation, time_dilation);
+ gViewerStats->mSimTDStat.addValue(time_dilation);
+
+ // Process information
+ // { CpuUsage F32 }
+ // { SimMemTotal F32 }
+ // { SimMemRSS F32 }
+ // { ProcessUptime F32 }
+ F32 cpu_usage;
+ F32 sim_mem_total;
+ F32 sim_mem_rss;
+ F32 process_uptime;
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_CpuUsage, cpu_usage);
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemTotal, sim_mem_total);
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemRSS, sim_mem_rss);
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_ProcessUptime, process_uptime);
+ gViewerStats->mSimCPUUsageStat.addValue(cpu_usage);
+ gViewerStats->mSimMemTotalStat.addValue(sim_mem_total);
+ gViewerStats->mSimMemRSSStat.addValue(sim_mem_rss);
+ */
+
+ //
+ // Various hacks that aren't statistics, but are being handled here.
+ //
+ U32 max_tasks_per_region;
+ U32 region_flags;
+ msg->getU32("Region", "ObjectCapacity", max_tasks_per_region);
+ msg->getU32("Region", "RegionFlags", region_flags);
+
+ LLViewerRegion* regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ BOOL was_flying = gAgent.getFlying();
+ regionp->setRegionFlags(region_flags);
+ regionp->setMaxTasks(max_tasks_per_region);
+ // HACK: This makes agents drop from the sky if the region is
+ // set to no fly while people are still in the sim.
+ if (was_flying && regionp->getBlockFly())
+ {
+ gAgent.setFlying(gAgent.canFly());
+ }
+ }
+}
+
+
+// This info is requested by the simulator when the agent first logs in
+// or when it moves into a simulator in which it did not already have
+// a child agent.
+void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data)
+{
+ llinfos << "process_avatar_info_request()" << llendl;
+
+ // Send the avatar appearance (parameters and texture entry UUIDs)
+ gAgent.sendAgentSetAppearance();
+ send_agent_update(TRUE, TRUE);
+}
+
+
+void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLUUID animation_id;
+ LLUUID uuid;
+ S32 anim_sequence_id;
+ LLVOAvatar *avatarp;
+
+ mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid);
+
+ //clear animation flags
+ avatarp = (LLVOAvatar *)gObjectList.findObject(uuid);
+
+ if (!avatarp)
+ {
+ // no agent by this ID...error?
+ llwarns << "Received animation state for unknown avatar" << uuid << llendl;
+ return;
+ }
+
+ S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);
+ S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList);
+
+ avatarp->mSignaledAnimations.clear();
+
+ if (avatarp->mIsSelf)
+ {
+ LLUUID object_id;
+
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i);
+ mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i);
+
+ //llinfos << "Anim sequence ID: " << anim_sequence_id << llendl;
+
+ avatarp->mSignaledAnimations[animation_id] = anim_sequence_id;
+
+ if (i < num_source_blocks)
+ {
+ mesgsys->getUUIDFast(_PREHASH_AnimationSourceList, _PREHASH_ObjectID, object_id, i);
+
+ LLViewerObject* object = gObjectList.findObject(object_id);
+ if (object)
+ {
+ object->mFlags |= FLAGS_ANIM_SOURCE;
+
+ BOOL anim_found = FALSE;
+ LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.find(object_id);
+ for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it)
+ {
+ if (anim_it->second == animation_id)
+ {
+ anim_found = TRUE;
+ break;
+ }
+ }
+
+ if (!anim_found)
+ {
+ avatarp->mAnimationSources.insert(LLVOAvatar::AnimationSourceMap::value_type(object_id, animation_id));
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i);
+ mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i);
+ avatarp->mSignaledAnimations[animation_id] = anim_sequence_id;
+ }
+ }
+
+ if (num_blocks)
+ {
+ avatarp->processAnimationStateChanges();
+ }
+}
+
+void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLUUID uuid;
+ mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid);
+
+ LLVOAvatar* avatarp = (LLVOAvatar *)gObjectList.findObject(uuid);
+ if( avatarp )
+ {
+ avatarp->processAvatarAppearance( mesgsys );
+ }
+ else
+ {
+ llwarns << "avatar_appearance sent for unknown avatar " << uuid << llendl;
+ }
+}
+
+void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLVector4 cameraCollidePlane;
+ mesgsys->getVector4Fast(_PREHASH_CameraCollidePlane, _PREHASH_Plane, cameraCollidePlane);
+
+ gAgent.setCameraCollidePlane(cameraCollidePlane);
+}
+
+void near_sit_object(BOOL success, void *data)
+{
+ // Send message to sit on object
+ gMessageSystem->newMessageFast(_PREHASH_AgentSit);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+}
+
+void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLVector3 sitPosition;
+ LLQuaternion sitRotation;
+ LLUUID sitObjectID;
+ BOOL use_autopilot;
+ mesgsys->getUUIDFast(_PREHASH_SitObject, _PREHASH_ID, sitObjectID);
+ mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_AutoPilot, use_autopilot);
+ mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_SitPosition, sitPosition);
+ mesgsys->getQuatFast(_PREHASH_SitTransform, _PREHASH_SitRotation, sitRotation);
+ LLVector3 camera_eye;
+ mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraEyeOffset, camera_eye);
+ LLVector3 camera_at;
+ mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraAtOffset, camera_at);
+ BOOL force_mouselook;
+ mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_ForceMouselook, force_mouselook);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+
+ if (avatar && dist_vec_squared(camera_eye, camera_at) > 0.0001f)
+ {
+ gAgent.setSitCamera(sitObjectID, camera_eye, camera_at);
+ }
+
+ gAgent.mForceMouselook = force_mouselook;
+
+ LLViewerObject* object = gObjectList.findObject(sitObjectID);
+ if (object)
+ {
+ LLVector3 sit_spot = object->getPositionAgent() + (sitPosition * object->getRotation());
+ if (!use_autopilot || (avatar->mIsSitting && avatar->getRoot() == object->getRoot()))
+ {
+ //we're already sitting on this object, so don't autopilot
+ }
+ else
+ {
+ gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(sit_spot), "Sit", &sitRotation, near_sit_object, NULL, 0.5f);
+ }
+
+ // deselect transient selections (pie menu) when sitting
+ gSelectMgr->deselectTransient();
+ }
+ else
+ {
+ llwarns << "Received sit approval for unknown object " << sitObjectID << llendl;
+ }
+}
+
+void process_clear_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLUUID source_id;
+
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id);
+
+ LLFollowCamMgr::removeFollowCamParams(source_id);
+}
+
+void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data)
+{
+ S32 type;
+ F32 value;
+ bool settingPosition = false;
+ bool settingFocus = false;
+ bool settingFocusOffset = false;
+ LLVector3 position;
+ LLVector3 focus;
+ LLVector3 focus_offset;
+
+ LLUUID source_id;
+
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id);
+
+ LLViewerObject* objectp = gObjectList.findObject(source_id);
+ if (objectp)
+ {
+ objectp->mFlags |= FLAGS_CAMERA_SOURCE;
+ }
+
+ S32 num_objects = mesgsys->getNumberOfBlocks("CameraProperty");
+ for (S32 block_index = 0; block_index < num_objects; block_index++)
+ {
+ mesgsys->getS32("CameraProperty", "Type", type, block_index);
+ mesgsys->getF32("CameraProperty", "Value", value, block_index);
+ switch(type)
+ {
+ case FOLLOWCAM_PITCH:
+ LLFollowCamMgr::setPitch(source_id, value);
+ break;
+ case FOLLOWCAM_FOCUS_OFFSET_X:
+ focus_offset.mV[VX] = value;
+ settingFocusOffset = true;
+ break;
+ case FOLLOWCAM_FOCUS_OFFSET_Y:
+ focus_offset.mV[VY] = value;
+ settingFocusOffset = true;
+ break;
+ case FOLLOWCAM_FOCUS_OFFSET_Z:
+ focus_offset.mV[VZ] = value;
+ settingFocusOffset = true;
+ break;
+ case FOLLOWCAM_POSITION_LAG:
+ LLFollowCamMgr::setPositionLag(source_id, value);
+ break;
+ case FOLLOWCAM_FOCUS_LAG:
+ LLFollowCamMgr::setFocusLag(source_id, value);
+ break;
+ case FOLLOWCAM_DISTANCE:
+ LLFollowCamMgr::setDistance(source_id, value);
+ break;
+ case FOLLOWCAM_BEHINDNESS_ANGLE:
+ LLFollowCamMgr::setBehindnessAngle(source_id, value);
+ break;
+ case FOLLOWCAM_BEHINDNESS_LAG:
+ LLFollowCamMgr::setBehindnessLag(source_id, value);
+ break;
+ case FOLLOWCAM_POSITION_THRESHOLD:
+ LLFollowCamMgr::setPositionThreshold(source_id, value);
+ break;
+ case FOLLOWCAM_FOCUS_THRESHOLD:
+ LLFollowCamMgr::setFocusThreshold(source_id, value);
+ break;
+ case FOLLOWCAM_ACTIVE:
+ //if 1, set using followcam,.
+ LLFollowCamMgr::setCameraActive(source_id, value != 0.f);
+ break;
+ case FOLLOWCAM_POSITION_X:
+ settingPosition = true;
+ position.mV[ 0 ] = value;
+ break;
+ case FOLLOWCAM_POSITION_Y:
+ settingPosition = true;
+ position.mV[ 1 ] = value;
+ break;
+ case FOLLOWCAM_POSITION_Z:
+ settingPosition = true;
+ position.mV[ 2 ] = value;
+ break;
+ case FOLLOWCAM_FOCUS_X:
+ settingFocus = true;
+ focus.mV[ 0 ] = value;
+ break;
+ case FOLLOWCAM_FOCUS_Y:
+ settingFocus = true;
+ focus.mV[ 1 ] = value;
+ break;
+ case FOLLOWCAM_FOCUS_Z:
+ settingFocus = true;
+ focus.mV[ 2 ] = value;
+ break;
+ case FOLLOWCAM_POSITION_LOCKED:
+ LLFollowCamMgr::setPositionLocked(source_id, value != 0.f);
+ break;
+ case FOLLOWCAM_FOCUS_LOCKED:
+ LLFollowCamMgr::setFocusLocked(source_id, value != 0.f);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( settingPosition )
+ {
+ LLFollowCamMgr::setPosition(source_id, position);
+ }
+ if ( settingFocus )
+ {
+ LLFollowCamMgr::setFocus(source_id, focus);
+ }
+ if ( settingFocusOffset )
+ {
+ LLFollowCamMgr::setFocusOffset(source_id, focus_offset);
+ }
+}
+//end Ventrella
+
+
+// Culled from newsim lltask.cpp
+void process_name_value(LLMessageSystem *mesgsys, void **user_data)
+{
+ char temp_str[NAME_VALUE_BUF_SIZE];
+ LLUUID id;
+ S32 i, num_blocks;
+
+ mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id);
+
+ LLViewerObject* object = gObjectList.findObject(id);
+
+ if (object)
+ {
+ num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData);
+ for (i = 0; i < num_blocks; i++)
+ {
+ mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, NAME_VALUE_BUF_SIZE, temp_str, i);
+ llinfos << "Added to object Name Value: " << temp_str << llendl;
+ object->addNVPair(temp_str);
+ }
+ }
+ else
+ {
+ llinfos << "Can't find object " << id << " to add name value pair" << llendl;
+ }
+}
+
+void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data)
+{
+ char temp_str[NAME_VALUE_BUF_SIZE];
+ LLUUID id;
+ S32 i, num_blocks;
+
+ mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id);
+
+ LLViewerObject* object = gObjectList.findObject(id);
+
+ if (object)
+ {
+ num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData);
+ for (i = 0; i < num_blocks; i++)
+ {
+ mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, NAME_VALUE_BUF_SIZE, temp_str, i);
+ llinfos << "Removed from object Name Value: " << temp_str << llendl;
+ object->removeNVPair(temp_str);
+ }
+ }
+ else
+ {
+ llinfos << "Can't find object " << id << " to remove name value pair" << llendl;
+ }
+}
+
+void process_kick_user(LLMessageSystem *msg, void** /*user_data*/)
+{
+ char message[2048];
+ message[0] = '\0';
+
+ msg->getStringFast(_PREHASH_UserInfo, _PREHASH_Reason, 2048, message);
+
+ do_disconnect(message);
+}
+
+
+/*
+void process_user_list_reply(LLMessageSystem *msg, void **user_data)
+{
+ LLUserList::processUserListReply(msg, user_data);
+ return;
+ char firstname[MAX_STRING+1];
+ char lastname[MAX_STRING+1];
+ U8 status;
+ S32 user_count;
+
+ user_count = msg->getNumberOfBlocks("UserBlock");
+
+ for (S32 i = 0; i < user_count; i++)
+ {
+ msg->getData("UserBlock", i, "FirstName", firstname);
+ msg->getData("UserBlock", i, "LastName", lastname);
+ msg->getData("UserBlock", i, "Status", &status);
+
+ if (status & 0x01)
+ {
+ dialog_friends_add_friend(buffer, TRUE);
+ }
+ else
+ {
+ dialog_friends_add_friend(buffer, FALSE);
+ }
+ }
+
+ dialog_friends_done_adding();
+}
+*/
+
+// this is not handled in processUpdateMessage
+/*
+void process_time_dilation(LLMessageSystem *msg, void **user_data)
+{
+ // get the time_dilation
+ U16 foo;
+ msg->getData("TimeDilation", "TimeDilation", &foo);
+ F32 time_dilation = ((F32) foo) / 65535.f;
+
+ // get the pointer to the right region
+ U32 ip = msg->getSenderIP();
+ U32 port = msg->getSenderPort();
+ LLViewerRegion *regionp = gWorldp->getRegion(ip, port);
+ if (regionp)
+ {
+ regionp->setTimeDilation(time_dilation);
+ }
+}
+*/
+
+
+
+void process_money_balance_reply( LLMessageSystem* msg, void** )
+{
+ S32 balance = 0;
+ S32 credit = 0;
+ S32 committed = 0;
+ char desc[STD_STRING_BUF_SIZE] = "";
+
+ msg->getS32("MoneyData", "MoneyBalance", balance);
+ msg->getS32("MoneyData", "SquareMetersCredit", credit);
+ msg->getS32("MoneyData", "SquareMetersCommitted", committed);
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, STD_STRING_BUF_SIZE, desc);
+ llinfos << "money, credit, committed: " << balance << " " << credit << " "
+ << committed << llendl;
+
+ if (gStatusBar)
+ {
+ S32 old_balance = gStatusBar->getBalance();
+
+ // This is an update, not the first transmission of balance
+ if (old_balance != 0)
+ {
+ // this is actually an update
+ if (balance > old_balance)
+ {
+ LLFirstUse::useBalanceIncrease(balance - old_balance);
+ }
+ else if (balance < old_balance)
+ {
+ LLFirstUse::useBalanceDecrease(balance - old_balance);
+ }
+ }
+
+ gStatusBar->setBalance(balance);
+ gStatusBar->setLandCredit(credit);
+ gStatusBar->setLandCommitted(committed);
+ }
+
+ LLUUID tid;
+ msg->getUUID("MoneyData", "TransactionID", tid);
+ static std::deque<LLUUID> recent;
+ if(desc[0] && gSavedSettings.getBOOL("NotifyMoneyChange")
+ && (std::find(recent.rbegin(), recent.rend(), tid) == recent.rend()))
+ {
+ // Make the user confirm the transaction, since they might
+ // have missed something during an event.
+ // XUI:translate
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = desc;
+ LLNotifyBox::showXml("SystemMessage", args);
+
+ // Once the 'recent' container gets large enough, chop some
+ // off the beginning.
+ const U32 MAX_LOOKBACK = 30;
+ const S32 POP_FRONT_SIZE = 12;
+ if(recent.size() > MAX_LOOKBACK)
+ {
+ lldebugs << "Removing oldest transaction records" << llendl;
+ recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE);
+ }
+ //lldebugs << "Pushing back transaction " << tid << llendl;
+ recent.push_back(tid);
+ }
+}
+
+void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data)
+{
+ char buffer[MAX_STRING];
+ msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, MAX_STRING, buffer);
+ BOOL modal = FALSE;
+ msgsystem->getBOOL("AlertData", "Modal", modal);
+ process_alert_core(buffer, modal);
+}
+
+void process_alert_message(LLMessageSystem *msgsystem, void **user_data)
+{
+ char buffer[MAX_STRING];
+ msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, MAX_STRING, buffer);
+ BOOL modal = FALSE;
+ process_alert_core(buffer, modal);
+}
+
+void process_alert_core(const char* buffer, BOOL modal)
+{
+ // make sure the cursor is back to the usual default since the
+ // alert is probably due to some kind of error.
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ // HACK -- handle callbacks for specific alerts
+ if( !strcmp( buffer, "You died and have been teleported to your home location" ) )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_KILLED_COUNT);
+ }
+ else if( !strcmp( buffer, "Home position set." ) )
+ {
+ // save the home location image to disk
+ char temp_str[LL_MAX_PATH];
+ strcpy(temp_str, gDirUtilp->getLindenUserDir().c_str());
+ strcat(temp_str, "/");
+ strcat(temp_str,SCREEN_HOME_FILENAME);
+ gViewerWindow->saveSnapshot(temp_str, gViewerWindow->getWindowWidth(), gViewerWindow->getWindowHeight(), FALSE, FALSE);
+ }
+
+ // Translate system messages here.
+ if (buffer[0] == '/')
+ {
+ // System message is important, show in upper-right box not tip
+ LLString text(buffer+1);
+ LLString::format_map_t args;
+ if (text.substr(0,17) == "RESTART_X_MINUTES")
+ {
+ S32 mins = 0;
+ LLString::convertToS32(text.substr(18), mins);
+ args["[MINUTES]"] = llformat("%d",mins);
+ LLNotifyBox::showXml("RegionRestartMinutes", args);
+ }
+ else if (text.substr(0,17) == "RESTART_X_SECONDS")
+ {
+ S32 secs = 0;
+ LLString::convertToS32(text.substr(18), secs);
+ args["[SECONDS]"] = llformat("%d",secs);
+ LLNotifyBox::showXml("RegionRestartSeconds", args);
+ }
+ else
+ {
+ //XUI:translate
+ args["[MESSAGE]"] = text;
+ LLNotifyBox::showXml("SystemMessage", args);
+ }
+ }
+ else if (modal)
+ {
+ //XUI:translate
+ LLString::format_map_t args;
+ args["[BUFFER]"] = buffer;
+ gViewerWindow->alertXml("ErrorMessage", args);
+ }
+ else
+ {
+ //XUI:translate
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = buffer;
+ LLNotifyBox::showXml("SystemMessageTip", args);
+ }
+}
+
+LLLinkedList<LLMeanCollisionData> gMeanCollisionList;
+time_t gLastDisplayedTime = 0;
+
+void handle_show_mean_events(void *)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ LLFloaterBump::show(NULL);
+}
+
+void mean_name_callback(const LLUUID &id, const char *first, const char *last, BOOL always_false, void* data)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ while(gMeanCollisionList.getLength() > 20)
+ {
+ gMeanCollisionList.getLastData();
+ gMeanCollisionList.deleteCurrentData();
+ }
+
+ LLMeanCollisionData *mcd;
+ for (mcd = gMeanCollisionList.getFirstData(); mcd; mcd = gMeanCollisionList.getNextData())
+ {
+ if (mcd->mPerp == id)
+ {
+ strcpy(mcd->mFirstName, first);
+ strcpy(mcd->mLastName, last);
+ }
+ }
+}
+
+void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **user_data)
+{
+ if (gAgent.inPrelude())
+ {
+ // JC: In prelude, bumping is OK. This dialog is rather confusing to
+ // newbies, so we don't show it. Drop the packet on the floor.
+ return;
+ }
+
+ // make sure the cursor is back to the usual default since the
+ // alert is probably due to some kind of error.
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ LLUUID perp;
+ U32 time;
+ U8 u8type;
+ EMeanCollisionType type;
+ F32 mag;
+
+ S32 i, num = msgsystem->getNumberOfBlocks(_PREHASH_MeanCollision);
+
+ for (i = 0; i < num; i++)
+ {
+ msgsystem->getUUIDFast(_PREHASH_MeanCollision, _PREHASH_Perp, perp);
+ msgsystem->getU32Fast(_PREHASH_MeanCollision, _PREHASH_Time, time);
+ msgsystem->getF32Fast(_PREHASH_MeanCollision, _PREHASH_Mag, mag);
+ msgsystem->getU8Fast(_PREHASH_MeanCollision, _PREHASH_Type, u8type);
+
+ type = (EMeanCollisionType)u8type;
+
+ LLMeanCollisionData *mcd;
+
+ BOOL b_found = FALSE;
+
+ for (mcd = gMeanCollisionList.getFirstData(); mcd; mcd = gMeanCollisionList.getNextData())
+ {
+ if ((mcd->mPerp == perp) && (mcd->mType == type))
+ {
+ mcd->mTime = time;
+ mcd->mMag = mag;
+ b_found = TRUE;
+ break;
+ }
+ }
+
+ if (!b_found)
+ {
+ LLMeanCollisionData *mcd = new LLMeanCollisionData(gAgentID, perp, time, type, mag);
+ gMeanCollisionList.addData(mcd);
+ const BOOL is_group = FALSE;
+ gCacheName->get(perp, is_group, mean_name_callback);
+ }
+ }
+}
+
+void process_frozen_message(LLMessageSystem *msgsystem, void **user_data)
+{
+ // make sure the cursor is back to the usual default since the
+ // alert is probably due to some kind of error.
+ gViewerWindow->getWindow()->resetBusyCount();
+ BOOL b_frozen;
+
+ msgsystem->getBOOL("FrozenData", "Data", b_frozen);
+
+ // TODO: make being frozen change view
+ if (b_frozen)
+ {
+ }
+ else
+ {
+ }
+}
+
+// do some extra stuff once we get our economy data
+void process_economy_data(LLMessageSystem *msg, void** /*user_data*/)
+{
+ LLGlobalEconomy::processEconomyData(msg, (void**)gGlobalEconomy);
+
+ S32 upload_cost = gGlobalEconomy->getPriceUpload();
+ LLFloaterImagePreview::setUploadAmount(upload_cost);
+
+ gMenuHolder->childSetLabelArg("Upload Image", "[COST]", llformat("%d", upload_cost));
+ gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", llformat("%d", upload_cost));
+ gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", llformat("%d", upload_cost));
+ gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", llformat("%d", upload_cost));
+}
+
+class LLScriptQuestionCBData
+{
+public:
+ LLScriptQuestionCBData(const LLUUID &taskid, const LLUUID &itemid, const LLHost &sender, S32 questions)
+ : mTaskID(taskid), mItemID(itemid), mSender(sender), mQuestions(questions)
+ {
+ }
+
+ LLUUID mTaskID;
+ LLUUID mItemID;
+ LLHost mSender;
+ S32 mQuestions;
+};
+
+
+void script_question_cb(S32 option, void* user_data)
+{
+ LLMessageSystem *msg = gMessageSystem;
+ LLScriptQuestionCBData *cbdata = (LLScriptQuestionCBData *)user_data;
+ if (option != 0)
+ {
+ cbdata->mQuestions = 0;
+ }
+ msg->newMessageFast(_PREHASH_ScriptAnswerYes);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_TaskID, cbdata->mTaskID);
+ msg->addUUIDFast(_PREHASH_ItemID, cbdata->mItemID);
+ msg->addS32Fast(_PREHASH_Questions, cbdata->mQuestions);
+ msg->sendReliable(cbdata->mSender);
+ delete cbdata;
+}
+
+
+void process_script_question(LLMessageSystem *msg, void **user_data)
+{
+ // XUI:translate owner name -> [FIRST] [LAST]
+ const LLString script_questions[SCRIPT_PERMISSION_EOF] =
+ {
+ "ScriptTakeMoney",
+ "ActOnControlInputs",
+ "RemapControlInputs",
+ "AnimateYourAvatar",
+ "AttachToYourAvatar",
+ "ReleaseOwnership",
+ "LinkAndDelink",
+ "AddAndRemoveJoints",
+ "ChangePermissions",
+ "TrackYourCamera",
+ "ControlYourCamera"
+ };
+
+ LLHost sender = msg->getSender();
+
+ LLUUID taskid;
+ LLUUID itemid;
+ S32 questions;
+ char object_name[255];
+ char owner_name[DB_FULL_NAME_BUF_SIZE];
+
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid );
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ItemID, itemid );
+ msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectName, 255, object_name);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, DB_FULL_NAME_BUF_SIZE, owner_name);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions );
+
+ LLString script_question;
+ if (questions)
+ {
+ S32 count = 0;
+ LLString::format_map_t args;
+ args["[OBJECTNAME]"] = object_name;
+ args["[NAME]"] = owner_name;
+ for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++)
+ {
+ if (questions & LSCRIPTRunTimePermissionBits[i])
+ {
+ count++;
+ script_question += " " + LLNotifyBox::getTemplateMessage(script_questions[i]) + "\n";
+ }
+ }
+ args["[QUESTIONS]"] = script_question;
+
+ LLScriptQuestionCBData *cbdata = new LLScriptQuestionCBData(taskid, itemid, sender, questions);
+
+ LLNotifyBox::showXml("ScriptQuestion", args, script_question_cb, cbdata);
+ }
+}
+
+
+void process_derez_container(LLMessageSystem *msg, void**)
+{
+ llwarns << "call to deprecated process_derez_container" << llendl;
+}
+
+void container_inventory_arrived(LLViewerObject* object,
+ InventoryObjectList* inventory,
+ S32 serial_num,
+ void* data)
+{
+ llinfos << "container_inventory_arrived()" << llendl;
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+
+ if (inventory->size() > 2)
+ {
+ // create a new inventory category to put this in
+ LLUUID cat_id;
+ cat_id = gInventory.createNewCategory(gAgent.getInventoryRootID(),
+ LLAssetType::AT_NONE,
+ "Acquired Items");
+
+ InventoryObjectList::const_iterator it = inventory->begin();
+ InventoryObjectList::const_iterator end = inventory->end();
+ for ( ; it != end; ++it)
+ {
+ if ((*it)->getType() != LLAssetType::AT_CATEGORY &&
+ (*it)->getType() != LLAssetType::AT_ROOT_CATEGORY)
+ {
+ LLInventoryObject* obj = (LLInventoryObject*)(*it);
+ LLInventoryItem* item = (LLInventoryItem*)(obj);
+ LLUUID item_id;
+ item_id.generate();
+ S32 creation_date_utc = time_corrected();
+ LLPointer<LLViewerInventoryItem> new_item
+ = new LLViewerInventoryItem(item_id,
+ cat_id,
+ item->getPermissions(),
+ item->getAssetUUID(),
+ item->getType(),
+ item->getInventoryType(),
+ item->getName(),
+ item->getDescription(),
+ LLSaleInfo::DEFAULT,
+ item->getFlags(),
+ creation_date_utc);
+ new_item->updateServer(TRUE);
+ gInventory.updateItem(new_item);
+ }
+ }
+ gInventory.notifyObservers();
+ if(view)
+ {
+ view->getPanel()->setSelection(cat_id, TAKE_FOCUS_NO);
+ }
+ }
+ else if (inventory->size() == 2)
+ {
+ // we're going to get one fake root category as well as the
+ // one actual object
+ InventoryObjectList::iterator it = inventory->begin();
+
+ if ((*it)->getType() == LLAssetType::AT_CATEGORY ||
+ (*it)->getType() == LLAssetType::AT_ROOT_CATEGORY)
+ {
+ ++it;
+ }
+
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLUUID category = gInventory.findCategoryUUIDForType(item->getType());
+
+ LLUUID item_id;
+ item_id.generate();
+ S32 creation_date_utc = time_corrected();
+ LLPointer<LLViewerInventoryItem> new_item
+ = new LLViewerInventoryItem(item_id, category,
+ item->getPermissions(),
+ item->getAssetUUID(),
+ item->getType(),
+ item->getInventoryType(),
+ item->getName(),
+ item->getDescription(),
+ LLSaleInfo::DEFAULT,
+ item->getFlags(),
+ creation_date_utc);
+ new_item->updateServer(TRUE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ if(view)
+ {
+ view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO);
+ }
+ }
+
+ // we've got the inventory, now delete this object if this was a take
+ BOOL delete_object = (BOOL)(intptr_t)data;
+ LLViewerRegion *region = gAgent.getRegion();
+ if (delete_object && region)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_ObjectDelete);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ const U8 NO_FORCE = 0;
+ gMessageSystem->addU8Fast(_PREHASH_Force, NO_FORCE);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID());
+ gMessageSystem->sendReliable(region->getHost());
+ }
+}
+
+// method to format the time. Buffer should be at least
+// TIME_STR_LENGTH long, and the function returns buffer (for use in
+// sprintf and the like)
+char* formatted_time(const time_t& the_time, char* buffer)
+{
+ LLString::copy(buffer, ctime(&the_time), TIME_STR_LENGTH);
+ buffer[24] = '\0';
+ return buffer;
+}
+
+
+void process_teleport_failed(LLMessageSystem *msg, void**)
+{
+ char reason[STD_STRING_BUF_SIZE];
+ msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, STD_STRING_BUF_SIZE, reason);
+
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = reason;
+ gViewerWindow->alertXml("CouldNotTeleportReason", args);
+
+ if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE )
+ {
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+}
+
+void process_teleport_local(LLMessageSystem *msg,void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Got teleport notification for wrong agent!" << llendl;
+ return;
+ }
+
+ U32 location_id;
+ LLVector3 pos, look_at;
+ U32 teleport_flags;
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id);
+ msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos);
+ msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at);
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags);
+
+ if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE )
+ {
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+
+ // Sim tells us whether the new position is off the ground
+ if (teleport_flags & TELEPORT_FLAGS_IS_FLYING)
+ {
+ gAgent.setFlying(TRUE);
+ }
+ else
+ {
+ gAgent.setFlying(FALSE);
+ }
+
+ gAgent.setPositionAgent(pos);
+ gAgent.slamLookAt(look_at);
+
+ // likewise make sure the camera is behind the avatar
+ gAgent.resetView(TRUE);
+
+ // send camera update to new region
+ gAgent.updateCamera();
+
+ send_agent_update(TRUE, TRUE);
+}
+
+void send_simple_im(const LLUUID& to_id,
+ const char* message,
+ EInstantMessage dialog,
+ const LLUUID& id)
+{
+ std::string my_name;
+ gAgent.buildFullname(my_name);
+ send_improved_im(to_id,
+ my_name.c_str(),
+ message,
+ IM_ONLINE,
+ dialog,
+ id,
+ NO_TIMESTAMP,
+ (U8*)EMPTY_BINARY_BUCKET,
+ EMPTY_BINARY_BUCKET_SIZE);
+}
+
+void send_group_notice(const LLUUID& group_id,
+ const char* subject,
+ const char* message,
+ const LLInventoryItem* item)
+{
+ // Put this notice into an instant message form.
+ // This will mean converting the item to a binary bucket,
+ // and the subject/message into a single field.
+ std::string my_name;
+ gAgent.buildFullname(my_name);
+
+ // Combine subject + message into a single string.
+ std::ostringstream subject_and_message;
+ // TODO: turn all existing |'s into ||'s in subject and message.
+ subject_and_message << subject << "|" << message;
+
+ // Create an empty binary bucket.
+ U8 bin_bucket[MAX_INVENTORY_BUFFER_SIZE];
+ U8* bucket_to_send = bin_bucket;
+ bin_bucket[0] = '\0';
+ S32 bin_bucket_size = EMPTY_BINARY_BUCKET_SIZE;
+ // If there is an item being sent, pack it into the binary bucket.
+ if (item)
+ {
+ LLSD item_def;
+ item_def["item_id"] = item->getUUID();
+ item_def["owner_id"] = item->getPermissions().getOwner();
+ std::ostringstream ostr;
+ LLSDSerialize::serialize(item_def, ostr, LLSDSerialize::LLSD_XML);
+ bin_bucket_size = ostr.str().copy(
+ (char*)bin_bucket, ostr.str().size());
+ bin_bucket[bin_bucket_size] = '\0';
+ }
+ else
+ {
+ bucket_to_send = (U8*) EMPTY_BINARY_BUCKET;
+ }
+
+
+ send_improved_im(
+ group_id,
+ my_name.c_str(),
+ subject_and_message.str().c_str(),
+ IM_ONLINE,
+ IM_GROUP_NOTICE,
+ LLUUID::null,
+ NO_TIMESTAMP,
+ bucket_to_send,
+ bin_bucket_size);
+}
+
+void handle_lure_callback(S32 option, const LLString& text, void* userdata)
+{
+ LLDynamicArray<LLUUID>* invitees = (LLDynamicArray<LLUUID>*)userdata;
+
+ if(0 == option)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_StartLure);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
+ msg->addStringFast(_PREHASH_Message, text.c_str());
+ for(LLDynamicArray<LLUUID>::iterator itr = invitees->begin(); itr != invitees->end(); ++itr)
+ {
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_TargetID, *itr);
+ }
+ gAgent.sendReliableMessage();
+ }
+
+ delete invitees;
+ invitees = NULL;
+}
+
+void handle_lure_callback_godlike(S32 option, void* userdata)
+{
+ handle_lure_callback(option, LLString::null, userdata);
+}
+
+void send_lure_911(void** user_data, S32 result)
+{
+ LLUUID im_session_id(*((LLUUID*)user_data));
+ LLString name;
+ gAgent.getName(name);
+ LLString text = name + " needs help"; // this text is ignored for 911 lures
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_StartLure);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU8Fast(_PREHASH_LureType, (U8)IM_LURE_911);
+ msg->addStringFast(_PREHASH_Message, text.c_str());
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_TargetID, im_session_id);
+ gAgent.sendReliableMessage();
+}
+
+void handle_lure(const LLUUID& invitee)
+{
+ LLDynamicArray<LLUUID> ids;
+ ids.push_back(invitee);
+ handle_lure(ids);
+}
+
+// Prompt for a message to the invited user.
+void handle_lure(LLDynamicArray<LLUUID>& ids)
+{
+ LLDynamicArray<LLUUID>* userdata = new LLDynamicArray<LLUUID>(ids);
+
+ LLString::format_map_t edit_args;
+ edit_args["[REGION]"] = gAgent.getRegion()->getName();
+ if (gAgent.isGodlike())
+ {
+ gViewerWindow->alertXmlEditText("OfferTeleportFromGod", edit_args,
+ &handle_lure_callback_godlike, userdata,
+ NULL, NULL, edit_args);
+ }
+ else
+ {
+ gViewerWindow->alertXmlEditText("OfferTeleport", edit_args,
+ NULL, NULL,
+ handle_lure_callback, userdata, edit_args);
+ }
+}
+
+
+void send_improved_im(const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline,
+ EInstantMessage dialog,
+ const LLUUID& id,
+ U32 timestamp,
+ const U8* binary_bucket,
+ S32 binary_bucket_size)
+{
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ to_id,
+ name,
+ message,
+ offline,
+ dialog,
+ id,
+ 0,
+ LLUUID::null,
+ gAgent.getPositionAgent(),
+ timestamp,
+ binary_bucket,
+ binary_bucket_size);
+ gAgent.sendReliableMessage();
+}
+
+
+void send_places_query(const LLUUID& query_id,
+ const LLUUID& trans_id,
+ const char* query_text,
+ U32 query_flags,
+ S32 category,
+ const char* sim_name)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessage("PlacesQuery");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addUUID("QueryID", query_id);
+ msg->nextBlock("TransactionData");
+ msg->addUUID("TransactionID", trans_id);
+ msg->nextBlock("QueryData");
+ msg->addString("QueryText", query_text);
+ msg->addU32("QueryFlags", query_flags);
+ msg->addS8("Category", (S8)category);
+ msg->addString("SimName", sim_name);
+ gAgent.sendReliableMessage();
+}
+
+
+void process_user_info_reply(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "process_user_info_reply - "
+ << "wrong agent id." << llendl;
+ }
+
+ BOOL im_via_email;
+ msg->getBOOLFast(_PREHASH_UserData, _PREHASH_IMViaEMail, im_via_email);
+ char email[DB_USER_EMAIL_ADDR_BUF_SIZE];
+ msg->getStringFast(_PREHASH_UserData, _PREHASH_EMail, DB_USER_EMAIL_ADDR_BUF_SIZE,
+ email);
+ char dir_visibility[MAX_STRING];
+ msg->getString(
+ "UserData", "DirectoryVisibility", MAX_STRING, dir_visibility);
+
+ LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email);
+ LLFloaterPostcard::updateUserInfo(email);
+}
+
+
+//---------------------------------------------------------------------------
+// Script Dialog
+//---------------------------------------------------------------------------
+
+const S32 SCRIPT_DIALOG_MAX_BUTTONS = 12;
+const S32 SCRIPT_DIALOG_BUTTON_STR_SIZE = 24;
+const S32 SCRIPT_DIALOG_MAX_MESSAGE_SIZE = 512;
+const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n";
+
+struct ScriptDialogInfo
+{
+ LLHost mSender;
+ LLUUID mObjectID;
+ S32 mChatChannel;
+ std::vector<LLString> mButtons;
+};
+
+void callback_script_dialog(S32 option, void* data)
+{
+ ScriptDialogInfo* info = (ScriptDialogInfo*)data;
+ if (!info) return;
+
+ // Didn't click "Ignore"
+ if (0 != option)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ScriptDialogReply");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("ObjectID", info->mObjectID);
+ msg->addS32("ChatChannel", info->mChatChannel);
+ msg->addS32("ButtonIndex", option);
+ msg->addString("ButtonLabel", info->mButtons[option-1]);
+ msg->sendReliable(info->mSender);
+ }
+
+ delete info;
+}
+
+void process_script_dialog(LLMessageSystem* msg, void**)
+{
+ S32 i;
+
+ ScriptDialogInfo* info = new ScriptDialogInfo;
+
+ const S32 messageLength = SCRIPT_DIALOG_MAX_MESSAGE_SIZE + sizeof(SCRIPT_DIALOG_HEADER);
+ char message[messageLength]; // Account for size of "Script Dialog:\n"
+
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_GROUP_NAME_BUF_SIZE];
+ char title[DB_INV_ITEM_NAME_BUF_SIZE];
+ info->mSender = msg->getSender();
+
+ msg->getUUID("Data", "ObjectID", info->mObjectID);
+ msg->getString("Data", "FirstName", DB_FIRST_NAME_BUF_SIZE, first_name);
+ msg->getString("Data", "LastName", DB_LAST_NAME_BUF_SIZE, last_name);
+ msg->getString("Data", "ObjectName", DB_INV_ITEM_NAME_BUF_SIZE, title);
+ msg->getString("Data", "Message", SCRIPT_DIALOG_MAX_MESSAGE_SIZE, message);
+ msg->getS32("Data", "ChatChannel", info->mChatChannel);
+
+ // unused for now
+ LLUUID image_id;
+ msg->getUUID("Data", "ImageID", image_id);
+
+ S32 button_count = msg->getNumberOfBlocks("Buttons");
+ if (button_count > SCRIPT_DIALOG_MAX_BUTTONS)
+ {
+ button_count = SCRIPT_DIALOG_MAX_BUTTONS;
+ }
+
+ for (i = 0; i < button_count; i++)
+ {
+ char tdesc[SCRIPT_DIALOG_BUTTON_STR_SIZE+1];
+ msg->getString("Buttons", "ButtonLabel", SCRIPT_DIALOG_BUTTON_STR_SIZE + 1, tdesc, i);
+ info->mButtons.push_back(LLString(tdesc));
+ }
+
+ LLStringBase<char>::format_map_t args;
+ args["[TITLE]"] = title;
+ args["[MESSAGE]"] = message;
+ if (strlen(first_name) > 0)
+ {
+ args["[FIRST]"] = first_name;
+ args["[LAST]"] = last_name;
+ LLNotifyBox::showXml("ScriptDialog", args,
+ callback_script_dialog, info,
+ info->mButtons,
+ TRUE);
+ }
+ else
+ {
+ args["[GROUPNAME]"] = last_name;
+ LLNotifyBox::showXml("ScriptDialogGroup", args,
+ callback_script_dialog, info,
+ info->mButtons,
+ TRUE);
+ }
+}
+
+//---------------------------------------------------------------------------
+
+struct LoadUrlInfo
+{
+ LLUUID mObjectID;
+ LLUUID mOwnerID;
+ BOOL mOwnerIsGroup;
+ char mObjectName[256];
+ char mMessage[256];
+ char mUrl[256];
+};
+
+std::vector<LoadUrlInfo*> gLoadUrlList;
+
+void callback_load_url(S32 option, void* data)
+{
+ LoadUrlInfo* infop = (LoadUrlInfo*)data;
+ if (!infop) return;
+
+ if (0 == option)
+ {
+ LLWeb::loadURL(infop->mUrl);
+ }
+
+ delete infop;
+ infop = NULL;
+}
+
+
+// We've got the name of the person who owns the object hurling the url.
+// Display confirmation dialog.
+void callback_load_url_name(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* data)
+{
+ std::vector<LoadUrlInfo*>::iterator it;
+ for (it = gLoadUrlList.begin(); it != gLoadUrlList.end(); )
+ {
+ LoadUrlInfo* infop = *it;
+ if (infop->mOwnerID == id)
+ {
+ it = gLoadUrlList.erase(it);
+
+ std::string owner_name(first);
+ if (is_group)
+ {
+ owner_name += " (group)";
+ }
+ else
+ {
+ owner_name += " ";
+ owner_name += last;
+ }
+
+ // TODO: Talk to james about using an id instead of a name for this.
+ if (gMuteListp->isMuted(LLUUID::null, owner_name))
+ {
+ delete infop;
+ infop = NULL;
+ continue;
+ }
+ LLString::format_map_t args;
+ args["[URL]"] = infop->mUrl;
+ args["[MESSAGE]"] = infop->mMessage;
+ args["[OBJECTNAME]"] = infop->mObjectName;
+ args["[NAME]"] = owner_name;
+ LLNotifyBox::showXml("LoadWebPage", args, callback_load_url, infop);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+void process_load_url(LLMessageSystem* msg, void**)
+{
+ LoadUrlInfo* infop = new LoadUrlInfo;
+
+ msg->getString("Data", "ObjectName", 256, infop->mObjectName);
+ msg->getUUID( "Data", "ObjectID", infop->mObjectID);
+ msg->getUUID( "Data", "OwnerID", infop->mOwnerID);
+ msg->getBOOL( "Data", "OwnerIsGroup", infop->mOwnerIsGroup);
+ msg->getString("Data", "Message", 256, infop->mMessage);
+ msg->getString("Data", "URL", 256, infop->mUrl);
+
+ // URL is safety checked in load_url above
+
+ // Check if object or owner is muted
+ if (gMuteListp->isMuted(infop->mObjectID, infop->mObjectName))
+ {
+ delete infop;
+ infop = NULL;
+ return;
+ }
+
+ // Add to list of pending name lookups
+ gLoadUrlList.push_back(infop);
+
+ gCacheName->get(infop->mOwnerID, infop->mOwnerIsGroup, callback_load_url_name);
+}
+
+
+void callback_download_complete(void** data, S32 result)
+{
+ LLString* filepath = (LLString*)data;
+ LLString::format_map_t args;
+ args["[DOWNLOAD_PATH]"] = *filepath;
+ gViewerWindow->alertXml("FinishedRawDownload", args);
+ delete filepath;
+}
+
+
+void process_initiate_download(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Initiate download for wrong agent" << llendl;
+ return;
+ }
+
+ char sim_filename[MAX_PATH];
+ char viewer_filename[MAX_PATH];
+ msg->getString("FileData", "SimFilename", MAX_PATH, sim_filename);
+ msg->getString("FileData", "ViewerFilename", MAX_PATH, viewer_filename);
+
+ gXferManager->requestFile(viewer_filename,
+ sim_filename,
+ LL_PATH_NONE,
+ msg->getSender(),
+ FALSE, // don't delete remote
+ callback_download_complete,
+ (void**)new LLString(viewer_filename));
+}
+
+
+void process_script_teleport_request(LLMessageSystem* msg, void**)
+{
+ char object_name[256];
+ char sim_name[256];
+ LLVector3 pos;
+ LLVector3 look_at;
+
+ msg->getString("Data", "ObjectName", 255, object_name);
+ msg->getString( "Data", "SimName", 255, sim_name);
+ msg->getVector3("Data", "SimPosition", pos);
+ msg->getVector3("Data", "LookAt", look_at);
+
+ gFloaterWorldMap->trackURL(sim_name, (U32)pos.mV[VX], (U32)pos.mV[VY], (U32)pos.mV[VZ]);
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+void process_covenant_reply(LLMessageSystem* msg, void**)
+{
+ LLUUID covenant_id, estate_owner_id;
+ char estate_name[MAX_STRING];
+ U32 covenant_timestamp;
+ msg->getUUID("Data", "CovenantID", covenant_id);
+ msg->getU32("Data", "CovenantTimestamp", covenant_timestamp);
+ msg->getString("Data", "EstateName", MAX_STRING, estate_name);
+ msg->getUUID("Data", "EstateOwnerID", estate_owner_id);
+
+ LLPanelEstateCovenant::updateEstateName(estate_name);
+ LLPanelLandCovenant::updateEstateName(estate_name);
+ LLFloaterBuyLand::updateEstateName(estate_name);
+
+ // standard message, not from system
+ char last_modified[MAX_STRING];
+ last_modified[0] = '\0';
+ char time_buf[TIME_STR_LENGTH];
+ sprintf(last_modified, "Last Modified %s",
+ formatted_time((time_t)covenant_timestamp, time_buf));
+
+ LLPanelEstateCovenant::updateLastModified(last_modified);
+ LLPanelLandCovenant::updateLastModified(last_modified);
+ LLFloaterBuyLand::updateLastModified(last_modified);
+
+ gCacheName->getName(estate_owner_id, callbackCacheEstateOwnerName);
+
+ // load the actual covenant asset data
+ const BOOL high_priority = TRUE;
+ if (covenant_id.notNull())
+ {
+ gAssetStorage->getEstateAsset(gAgent.getRegionHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ covenant_id,
+ LLAssetType::AT_NOTECARD,
+ ET_Covenant,
+ onCovenantLoadComplete,
+ NULL,
+ high_priority);
+ }
+ else
+ {
+ std::string covenant_text;
+ if (estate_owner_id.isNull())
+ {
+ // mainland
+ covenant_text = "There is no Covenant provided for this Estate.";
+ }
+ else
+ {
+ covenant_text = "There is no Covenant provided for this Estate. The land on this estate is being sold by the Estate owner, not Linden Lab. Please contact the Estate Owner for sales details.";
+ }
+ LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id);
+ LLPanelLandCovenant::updateCovenantText(covenant_text);
+ LLFloaterBuyLand::updateCovenantText(covenant_text, covenant_id);
+ }
+}
+
+void callbackCacheEstateOwnerName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*)
+{
+ std::string name;
+
+ if (id.isNull())
+ {
+ name = "(none)";
+ }
+ else
+ {
+ name = first;
+ name += " ";
+ name += last;
+ }
+ LLPanelEstateCovenant::updateEstateOwnerName(name);
+ LLPanelLandCovenant::updateEstateOwnerName(name);
+ LLFloaterBuyLand::updateEstateOwnerName(name);
+}
+
+void onCovenantLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "onCovenantLoadComplete()" << llendl;
+ std::string covenant_text;
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+
+ if( (file_length > 19) && !strncmp( buffer, "Linden text version", 19 ) )
+ {
+ LLViewerTextEditor* editor = new LLViewerTextEditor("temp",
+ LLRect(0,0,0,0),
+ file_length+1);
+ if( !editor->importBuffer( buffer ) )
+ {
+ llwarns << "Problem importing estate covenant." << llendl;
+ covenant_text = "Problem importing estate covenant.";
+ }
+ else
+ {
+ // Version 0 (just text, doesn't include version number)
+ covenant_text = editor->getText();
+ }
+ delete[] buffer;
+ delete editor;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ covenant_text = "Estate covenant notecard is missing from database.";
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ covenant_text = "Insufficient permissions to view estate covenant.";
+ }
+ else
+ {
+ covenant_text = "Unable to load estate covenant at this time.";
+ }
+
+ llwarns << "Problem loading notecard: " << status << llendl;
+ }
+ }
+ LLPanelEstateCovenant::updateCovenantText(covenant_text, asset_uuid);
+ LLPanelLandCovenant::updateCovenantText(covenant_text);
+ LLFloaterBuyLand::updateCovenantText(covenant_text, asset_uuid);
+}
+
+void send_generic_message(const char* method,
+ const std::vector<std::string>& strings,
+ const LLUUID& invoice)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GenericMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", method);
+ msg->addUUID("Invoice", invoice);
+ if(strings.empty())
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", NULL);
+ }
+ else
+ {
+ std::vector<std::string>::const_iterator it = strings.begin();
+ std::vector<std::string>::const_iterator end = strings.end();
+ for(; it != end; ++it)
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", (*it).c_str());
+ }
+ }
+ gAgent.sendReliableMessage();
+}
+
+
+void process_generic_message(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "GenericMessage for wrong agent" << llendl;
+ return;
+ }
+
+ std::string request;
+ LLUUID invoice;
+ LLDispatcher::sparam_t strings;
+ LLDispatcher::unpackMessage(msg, request, invoice, strings);
+
+ if(!gGenericDispatcher.dispatch(request, invoice, strings))
+ {
+ llwarns << "GenericMessage " << request << " failed to dispatch"
+ << llendl;
+ }
+}
+
+void process_feature_disabled_message(LLMessageSystem* msg, void**)
+{
+ // Handle Blacklisted feature simulator response...
+ LLUUID agentID;
+ LLUUID transactionID;
+ char messageText[MAX_STRING];
+ msg->getStringFast(_PREHASH_FailureInfo,_PREHASH_ErrorMessage,MAX_STRING,&messageText[0],0);
+ msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_AgentID,agentID);
+ msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_TransactionID,transactionID);
+
+ llwarns << "Blacklisted Feature Response:" << &messageText[0] << llendl;
+}
+
+// ------------------------------------------------------------
+// Message system exception callbacks
+// ------------------------------------------------------------
+
+void invalid_message_callback(LLMessageSystem* msg,
+ void*,
+ EMessageException exception)
+{
+ bad_network_handler();
+}
diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h
new file mode 100644
index 0000000000..c7d22656c7
--- /dev/null
+++ b/indra/newview/llviewermessage.h
@@ -0,0 +1,203 @@
+/**
+ * @file llviewermessage.h
+ * @brief Message system callbacks for viewer.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERMESSAGE_H
+#define LL_LLVIEWERMESSAGE_H
+
+//#include "linked_lists.h"
+#include "llinstantmessage.h"
+#include "lltransactiontypes.h"
+#include "lluuid.h"
+#include "stdenums.h"
+#include "message.h"
+
+//
+// Forward declarations
+//
+class LLColor4;
+class LLViewerObject;
+class LLInventoryObject;
+class LLInventoryItem;
+class LLViewerRegion;
+
+//
+// Prototypes
+//
+
+BOOL can_afford_transaction(S32 cost);
+void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group = FALSE,
+ S32 trx_type = TRANS_GIFT, const LLString& desc = LLString::null);
+void busy_message (LLMessageSystem* msg, LLUUID from_id);
+
+void process_logout_reply(LLMessageSystem* msg, void**);
+void process_layer_data(LLMessageSystem *mesgsys, void **user_data);
+void process_derez_ack(LLMessageSystem*, void**);
+void process_places_reply(LLMessageSystem* msg, void** data);
+void send_sound_trigger(const LLUUID& sound_id, F32 gain);
+void process_improved_im(LLMessageSystem *msg, void **user_data);
+void process_script_question(LLMessageSystem *msg, void **user_data);
+void process_chat_from_simulator(LLMessageSystem *mesgsys, void **user_data);
+
+//void process_agent_to_new_region(LLMessageSystem *mesgsys, void **user_data);
+void send_agent_update(BOOL force_send, BOOL send_reliable = FALSE);
+void process_object_update(LLMessageSystem *mesgsys, void **user_data);
+void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data);
+void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data);
+void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data);
+
+void send_simulator_throttle_settings(const LLHost &host);
+void process_kill_object( LLMessageSystem *mesgsys, void **user_data);
+void process_time_synch( LLMessageSystem *mesgsys, void **user_data);
+void process_sound_trigger(LLMessageSystem *mesgsys, void **user_data);
+void process_preload_sound( LLMessageSystem *mesgsys, void **user_data);
+void process_attached_sound( LLMessageSystem *mesgsys, void **user_data);
+void process_attached_sound_gain_change( LLMessageSystem *mesgsys, void **user_data);
+//void process_attached_sound_cutoff_radius( LLMessageSystem *mesgsys, void **user_data);
+void process_energy_statistics(LLMessageSystem *mesgsys, void **user_data);
+void process_health_message(LLMessageSystem *mesgsys, void **user_data);
+void process_sim_stats(LLMessageSystem *mesgsys, void **user_data);
+void process_shooter_agent_hit(LLMessageSystem* msg, void** user_data);
+void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data);
+void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data);
+void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data);
+void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data);
+void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data);
+void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data);
+void process_clear_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data);
+void process_name_value(LLMessageSystem *mesgsys, void **user_data);
+void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data);
+void process_kick_user(LLMessageSystem *msg, void** /*user_data*/);
+//void process_avatar_init_complete(LLMessageSystem *msg, void** /*user_data*/);
+
+void process_economy_data(LLMessageSystem *msg, void** /*user_data*/);
+void process_money_balance_reply(LLMessageSystem* msg_system, void**);
+void process_adjust_balance(LLMessageSystem* msg_system, void**);
+
+void process_alert_message(LLMessageSystem* msg, void**);
+void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data);
+void process_alert_core(const char* buffer, BOOL modal);
+
+// "Mean" or player-vs-player abuse
+void handle_show_mean_events(void *);
+void process_mean_collision_alert_message(LLMessageSystem* msg, void**);
+
+void process_frozen_message(LLMessageSystem* msg, void**);
+
+void process_derez_container(LLMessageSystem *msg, void**);
+void container_inventory_arrived(LLViewerObject* object,
+ std::list<LLPointer<LLInventoryObject> >* inventory, //InventoryObjectList
+ S32 serial_num,
+ void* data);
+
+// agent movement
+void send_complete_agent_movement(const LLHost& sim_host);
+void process_agent_movement_complete(LLMessageSystem* msg, void**);
+void process_crossed_region(LLMessageSystem* msg, void**);
+void process_teleport_start(LLMessageSystem* msg, void**);
+void process_teleport_progress(LLMessageSystem* msg, void**);
+void process_teleport_failed(LLMessageSystem *msg,void**);
+void process_teleport_finish(LLMessageSystem *msg, void**);
+
+//void process_user_sim_location_reply(LLMessageSystem *msg,void**);
+void process_teleport_local(LLMessageSystem *msg,void**);
+void process_user_sim_location_reply(LLMessageSystem *msg,void**);
+
+void send_simple_im(const LLUUID& to_id,
+ const char* message,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null);
+
+void send_group_notice(const LLUUID& group_id,
+ const char* subject,
+ const char* message,
+ const LLInventoryItem* item);
+
+void handle_lure(const LLUUID& invitee);
+void handle_lure(LLDynamicArray<LLUUID>& ids);
+
+// always from gAgent and
+// routes through the gAgent's current simulator
+void send_improved_im(const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline = IM_ONLINE,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null,
+ U32 timestamp = NO_TIMESTAMP,
+ const U8* binary_bucket = (U8*)EMPTY_BINARY_BUCKET,
+ S32 binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE);
+
+void process_user_info_reply(LLMessageSystem* msg, void**);
+
+// method to format the time. Buffer should be at least
+// TIME_STR_LENGTH long, and the function reutnrs buffer (for use in
+// sprintf and the like)
+const S32 TIME_STR_LENGTH = 30;
+char* formatted_time(const time_t& the_time, char* buffer);
+
+void send_places_query(const LLUUID& query_id,
+ const LLUUID& trans_id,
+ const char* query_text,
+ U32 query_flags,
+ S32 category,
+ const char* sim_name);
+void process_script_dialog(LLMessageSystem* msg, void**);
+void process_load_url(LLMessageSystem* msg, void**);
+void process_script_teleport_request(LLMessageSystem* msg, void**);
+void process_covenant_reply(LLMessageSystem* msg, void**);
+void onCovenantLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+void callbackCacheEstateOwnerName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*);
+
+// calling cards
+void process_offer_callingcard(LLMessageSystem* msg, void**);
+void process_accept_callingcard(LLMessageSystem* msg, void**);
+void process_decline_callingcard(LLMessageSystem* msg, void**);
+
+// Message system exception prototypes
+void invalid_message_callback(LLMessageSystem*, void*, EMessageException);
+
+void send_lure_911(void** user_data, S32 result);
+
+void process_initiate_download(LLMessageSystem* msg, void**);
+void inventory_offer_callback(S32 option, void* user_data);
+
+struct LLOfferInfo
+{
+ EInstantMessage mIM;
+ LLUUID mFromID;
+ BOOL mFromGroup;
+ BOOL mFromObject;
+ LLUUID mTransactionID;
+ LLUUID mFolderID;
+ LLUUID mObjectID;
+ LLAssetType::EType mType;
+ LLString mFromName;
+ LLString mDesc;
+ LLHost mHost;
+};
+
+void send_generic_message(const char* method,
+ const std::vector<std::string>& strings,
+ const LLUUID& invoice = LLUUID::null);
+
+void process_generic_message(LLMessageSystem* msg, void**);
+
+void process_feature_disabled_message(LLMessageSystem* msg, void**);
+
+extern LLDispatcher gGenericDispatcher;
+
+#endif
+
diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp
new file mode 100644
index 0000000000..aecc015e0e
--- /dev/null
+++ b/indra/newview/llviewernetwork.cpp
@@ -0,0 +1,70 @@
+/**
+ * @file llviewernetwork.cpp
+ * @author James Cook, Richard Nelson
+ * @brief Networking constants and globals for viewer.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewernetwork.h"
+
+LLUserServerData gUserServerDomainName[USERSERVER_COUNT] =
+{
+ { "None", "", "", ""},
+ { "Aditi",
+ "userserver.aditi.lindenlab.com",
+ "https://login.aditi.lindenlab.com/cgi-bin/login.cgi",
+ "http://aditi-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Agni",
+ "userserver.agni.lindenlab.com",
+ "https://login.agni.lindenlab.com/cgi-bin/login.cgi",
+ "https://secondlife.com/helpers/" },
+ { "DMZ",
+ "userserver.dmz.lindenlab.com",
+ "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+ "http://dmz-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Siva",
+ "userserver.siva.lindenlab.com",
+ "https://login.siva.lindenlab.com/cgi-bin/login.cgi",
+ "http://siva-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Durga",
+ "userserver.durga.lindenlab.com",
+ "https://login.durga.lindenlab.com/cgi-bin/login.cgi",
+ "http://durga-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Shakti",
+ "userserver.shakti.lindenlab.com",
+ "https://login.shakti.lindenlab.com/cgi-bin/login.cgi",
+ "http://shakti-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Soma",
+ "userserver.soma.lindenlab.com",
+ "https://login.soma.lindenlab.com/cgi-bin/login.cgi",
+ "http://soma-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Ganga",
+ "userserver.ganga.lindenlab.com",
+ "https://login.ganga.lindenlab.com/cgi-bin/login.cgi",
+ "http://ganga-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Local",
+ "localhost",
+ "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+ "" },
+ { "Other",
+ "",
+ "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+ "" }
+};
+
+// Use this to figure out which domain name and login URI to use.
+
+EUserServerDomain gUserServerChoice = USERSERVER_NONE;
+char gUserServerName[MAX_STRING];
+
+LLHost gUserServer;
+
+F32 gPacketDropPercentage = 0.f;
+F32 gInBandwidth = 0.f;
+F32 gOutBandwidth = 0.f;
+
+unsigned char gMACAddress[MAC_ADDRESS_BYTES];
diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h
new file mode 100644
index 0000000000..acb1b53525
--- /dev/null
+++ b/indra/newview/llviewernetwork.h
@@ -0,0 +1,52 @@
+/**
+ * @file llviewernetwork.h
+ * @author James Cook
+ * @brief Networking constants and globals for viewer.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERNETWORK_H
+#define LL_LLVIEWERNETWORK_H
+
+class LLHost;
+
+enum EUserServerDomain
+{
+ USERSERVER_NONE,
+ USERSERVER_ADITI,
+ USERSERVER_AGNI,
+ USERSERVER_DMZ,
+ USERSERVER_SIVA,
+ USERSERVER_DURGA,
+ USERSERVER_SHAKTI,
+ USERSERVER_SOMA,
+ USERSERVER_GANGA,
+ USERSERVER_LOCAL,
+ USERSERVER_OTHER, // IP address set via -user or other command line option
+ USERSERVER_COUNT
+};
+
+
+struct LLUserServerData
+{
+ const char* mLabel;
+ const char* mName;
+ const char* mLoginURI;
+ const char* mHelperURI;
+};
+
+extern LLHost gUserServer;
+
+extern F32 gPacketDropPercentage;
+extern F32 gInBandwidth;
+extern F32 gOutBandwidth;
+extern EUserServerDomain gUserServerChoice;
+extern LLUserServerData gUserServerDomainName[];
+extern char gUserServerName[MAX_STRING];
+
+const S32 MAC_ADDRESS_BYTES = 6;
+extern unsigned char gMACAddress[MAC_ADDRESS_BYTES];
+
+#endif
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
new file mode 100644
index 0000000000..b1e4ee1947
--- /dev/null
+++ b/indra/newview/llviewerobject.cpp
@@ -0,0 +1,4683 @@
+/**
+ * @file llviewerobject.cpp
+ * @brief Base class for viewer objects
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerobject.h"
+
+#include "audioengine.h"
+#include "imageids.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llflexibleobject.h"
+#include "llviewercontrol.h"
+#include "lldatapacker.h"
+#include "llfasttimer.h"
+#include "llfontgl.h"
+#include "llframetimer.h"
+#include "llinventory.h"
+#include "llmaterialtable.h"
+#include "llmutelist.h"
+#include "llnamevalue.h"
+#include "llprimitive.h"
+#include "llquantize.h"
+#include "llregionhandle.h"
+#include "lltree_common.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "object_flags.h"
+#include "timing.h"
+
+#include "llaudiosourcevo.h"
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbox.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llfloaterproperties.h"
+#include "llfollowcam.h"
+#include "llnetmap.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "lltooldraganddrop.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermedialist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llviewertextureanim.h"
+#include "llviewerwindow.h" // For getSpinAxis
+#include "llvoavatar.h"
+#include "llvoclouds.h"
+#include "llvograss.h"
+#include "llvoground.h"
+#include "llvolume.h"
+#include "llvolumemessage.h"
+#include "llvopart.h"
+#include "llvopartgroup.h"
+#include "llvosky.h"
+#include "llvostars.h"
+#include "llvosurfacepatch.h"
+#include "llvotextbubble.h"
+#include "llvotree.h"
+#include "llvotreenew.h"
+#include "llvovolume.h"
+#include "llvowater.h"
+#include "llworld.h"
+#include "llui.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+//#define DEBUG_UPDATE_TYPE
+
+extern BOOL gVelocityInterpolate;
+extern BOOL gPingInterpolate;
+extern U32 gFrameCount;
+
+U32 LLViewerObject::sNumZombieObjects = 0;
+S32 LLViewerObject::sNumObjects = 0;
+BOOL LLViewerObject::sMapDebug = TRUE;
+LLColor4 LLViewerObject::sEditSelectColor( 1.0f, 1.f, 0.f, 0.3f); // Edit OK
+LLColor4 LLViewerObject::sNoEditSelectColor( 1.0f, 0.f, 0.f, 0.3f); // Can't edit
+S32 LLViewerObject::sAxisArrowLength(50);
+BOOL LLViewerObject::sPulseEnabled(FALSE);
+BOOL LLViewerObject::sUseSharedDrawables(FALSE); // TRUE
+
+// static
+LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLViewerObject *res = NULL;
+ LLFastTimer t1(LLFastTimer::FTM_CREATE_OBJECT);
+
+ switch (pcode)
+ {
+ case LL_PCODE_VOLUME:
+ res = new LLVOVolume(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_AVATAR:
+ res = new LLVOAvatar(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_GRASS:
+ res = new LLVOGrass(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_PART_SYS:
+ res = new LLVOPart(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_TREE:
+ res = new LLVOTree(id, pcode, regionp); break;
+ case LL_PCODE_TREE_NEW:
+ llwarns << "Creating new tree!" << llendl;
+// res = new LLVOTree(id, pcode, regionp); break;
+// res = new LLVOTreeNew(id, pcode, regionp); break;
+ res = NULL; break;
+ case LL_PCODE_LEGACY_TEXT_BUBBLE:
+ res = new LLVOTextBubble(id, pcode, regionp); break;
+ case LL_VO_CLOUDS:
+ res = new LLVOClouds(id, pcode, regionp); break;
+ case LL_VO_SURFACE_PATCH:
+ res = new LLVOSurfacePatch(id, pcode, regionp); break;
+ case LL_VO_SKY:
+ res = new LLVOSky(id, pcode, regionp); break;
+ case LL_VO_STARS:
+ res = new LLVOStars(id, pcode, regionp); break;
+ case LL_VO_WATER:
+ res = new LLVOWater(id, pcode, regionp); break;
+ case LL_VO_GROUND:
+ res = new LLVOGround(id, pcode, regionp); break;
+ case LL_VO_PART_GROUP:
+ res = new LLVOPartGroup(id, pcode, regionp); break;
+ default:
+ llwarns << "Unknown object pcode " << (S32)pcode << llendl;
+ res = NULL; break;
+ }
+ return res;
+}
+
+LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLPrimitive(),
+ mChildList(),
+ mID(id),
+ mLocalID(0),
+ mTotalCRC(0),
+ mTEImages(NULL),
+ mGLName(0),
+ mbCanSelect(TRUE),
+ mFlags(0),
+ mDrawable(),
+ mCreateSelected(FALSE),
+ mRenderMedia(FALSE),
+ mBestUpdatePrecision(0),
+ mText(),
+ mLastInterpUpdateSecs(0.f),
+ mLastMessageUpdateSecs(0.f),
+ mData(NULL),
+ mAudioSourcep(NULL),
+ mAppAngle(0.f),
+ mPixelArea(0.f),
+ mInventory(NULL),
+ mInventorySerialNum(0),
+ mRegionp( regionp ),
+ mInventoryPending(FALSE),
+ mInventoryDirty(FALSE),
+ mDead(FALSE),
+ mOrphaned(FALSE),
+ mUserSelected(FALSE),
+ mOnActiveList(FALSE),
+ mOnMap(FALSE),
+ mStatic(FALSE),
+ mFaceIndexOffset(0),
+ mNumFaces(0),
+ mLastUpdateFrame(0),
+ mTimeDilation(1.f),
+ mRotTime(0.f),
+ mJointInfo(NULL),
+ mState(0),
+ mMedia(NULL),
+ mClickAction(0)
+{
+ llassert(mRegionp);
+
+ LLPrimitive::init(pcode);
+
+ // CP: added 12/2/2005 - this was being initialised to 0, not the current frame time
+ mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
+
+ mPositionRegion = LLVector3(0.f, 0.f, 0.f);
+ mPositionAgent = mRegionp->getOriginAgent();
+
+ LLViewerObject::sNumObjects++;
+}
+
+LLViewerObject::~LLViewerObject()
+{
+ deleteTEImages();
+
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete entries
+ delete mInventory;
+ mInventory = NULL;
+ }
+
+ if (mJointInfo)
+ {
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ // Delete memory associated with extra parameters.
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ delete iter->second->data;
+ delete iter->second;
+ }
+ mExtraParameterList.clear();
+
+ for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ;
+ mNameValuePairs.clear();
+
+ delete[] mData;
+ mData = NULL;
+
+ delete mMedia;
+ mMedia = NULL;
+
+ sNumObjects--;
+ sNumZombieObjects--;
+ llassert(mChildList.size() == 0);
+
+ clearInventoryListeners();
+}
+
+void LLViewerObject::deleteTEImages()
+{
+ delete[] mTEImages;
+ mTEImages = NULL;
+}
+
+void LLViewerObject::markDead()
+{
+ if (!mDead)
+ {
+ //llinfos << "Marking self " << mLocalID << " as dead." << llendl;
+
+ // Root object of this hierarchy unlinks itself.
+ if (getParent())
+ {
+ ((LLViewerObject *)getParent())->removeChild(this);
+ // go ahead and delete any jointinfo's that we find
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ // Mark itself as dead
+ mDead = TRUE;
+ gObjectList.cleanupReferences(this);
+
+ LLViewerObject *childp;
+ while (mChildList.size() > 0)
+ {
+ childp = mChildList[0];
+ if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR)
+ {
+ //llinfos << "Marking child " << childp->getLocalID() << " as dead." << llendl;
+ childp->setParent(NULL); // LLViewerObject::markDead 1
+ childp->markDead();
+ }
+ else
+ {
+ // make sure avatar is no longer parented,
+ // so we can properly set it's position
+ childp->setDrawableParent(NULL);
+ ((LLVOAvatar*)childp)->getOffObject();
+ childp->setParent(NULL); // LLViewerObject::markDead 2
+ }
+ mChildList.erase(mChildList.begin());
+ }
+
+ if (mDrawable.notNull())
+ {
+ // Drawables are reference counted, mark as dead, then nuke the pointer.
+ mDrawable->markDead();
+ mDrawable = NULL;
+ }
+
+ if (mText)
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ if (mIcon)
+ {
+ mIcon->markDead();
+ mIcon = NULL;
+ }
+
+ if (mPartSourcep)
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+
+ if (mAudioSourcep)
+ {
+ // Do some cleanup
+ if (gAudiop)
+ {
+ gAudiop->cleanupAudioSource(mAudioSourcep);
+ }
+ mAudioSourcep = NULL;
+ }
+
+ if (flagAnimSource())
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp && !avatarp->isDead())
+ {
+ // stop motions associated with this object
+ avatarp->stopMotionFromSource(mID);
+ }
+ }
+
+ if (flagCameraSource())
+ {
+ LLFollowCamMgr::removeFollowCamParams(mID);
+ }
+
+ sNumZombieObjects++;
+ }
+}
+
+void LLViewerObject::dump() const
+{
+ llinfos << "Type: " << pCodeToString(mPrimitiveCode) << llendl;
+ llinfos << "Drawable: " << (LLDrawable *)mDrawable << llendl;
+ llinfos << "Update Age: " << LLFrameTimer::getElapsedSeconds() - mLastMessageUpdateSecs << llendl;
+
+ llinfos << "Parent: " << getParent() << llendl;
+ llinfos << "ID: " << mID << llendl;
+ llinfos << "LocalID: " << mLocalID << llendl;
+ llinfos << "PositionRegion: " << getPositionRegion() << llendl;
+ llinfos << "PositionAgent: " << getPositionAgent() << llendl;
+ llinfos << "PositionGlobal: " << getPositionGlobal() << llendl;
+ llinfos << "Velocity: " << getVelocity() << llendl;
+ if (mDrawable.notNull() && mDrawable->getNumFaces())
+ {
+ LLDrawPool *poolp = mDrawable->getFace(0)->getPool();
+ llinfos << "Pool: " << poolp << llendl;
+ llinfos << "Pool reference count: " << poolp->mReferences.size() << llendl;
+ llinfos << "Pool vertex count: " << poolp->getVertexCount() << llendl;
+ }
+ //llinfos << "BoxTree Min: " << mDrawable->getBox()->getMin() << llendl;
+ //llinfos << "BoxTree Max: " << mDrawable->getBox()->getMin() << llendl;
+ /*
+ llinfos << "Velocity: " << getVelocity() << llendl;
+ llinfos << "AnyOwner: " << permAnyOwner() << " YouOwner: " << permYouOwner() << " Edit: " << mPermEdit << llendl;
+ llinfos << "UsePhysics: " << usePhysics() << " CanSelect " << mbCanSelect << " UserSelected " << mUserSelected << llendl;
+ llinfos << "AppAngle: " << mAppAngle << llendl;
+ llinfos << "PixelArea: " << mPixelArea << llendl;
+
+ char buffer[1000];
+ char *key;
+ for (key = mNameValuePairs.getFirstKey(); key; key = mNameValuePairs.getNextKey() )
+ {
+ mNameValuePairs[key]->printNameValue(buffer);
+ llinfos << buffer << llendl;
+ }
+
+ S32 i;
+
+ LLViewerObject *child;
+ for (i = 0; i < mChildList.size(); i++)
+ {
+ child = mChildList[i];
+ llinfos << " child " << child->getID() << llendl;
+ }
+ */
+}
+
+void LLViewerObject::printNameValuePairs() const
+{
+ for (name_value_map_t::const_iterator iter = mNameValuePairs.begin();
+ iter != mNameValuePairs.end(); iter++)
+ {
+ LLNameValue* nv = iter->second;
+ llinfos << nv->printNameValue() << llendl;
+ }
+}
+
+void LLViewerObject::initVOClasses()
+{
+ // Initialized shared class stuff first.
+ LLVOAvatar::initClass();
+ LLVOTree::initClass();
+ if (gNoRender)
+ {
+ // Don't init anything else in drone mode
+ return;
+ }
+ llinfos << "Viewer Object size: " << sizeof(LLViewerObject) << llendl;
+ LLVOGrass::initClass();
+ LLVOPart::initClass();
+ LLVOWater::initClass();
+ LLVOSky::initClass();
+ LLVOVolume::initClass();
+}
+
+void LLViewerObject::cleanupVOClasses()
+{
+ LLVOGrass::cleanupClass();
+ LLVOWater::cleanupClass();
+ LLVOTree::cleanupClass();
+ LLVOAvatar::cleanupClass();
+}
+
+// Replaces all name value pairs with data from \n delimited list
+// Does not update server
+void LLViewerObject::setNameValueList(char* name_value_list)
+{
+ // Clear out the old
+ for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ;
+ mNameValuePairs.clear();
+
+ // Bring in the new
+ char* token_start = name_value_list;
+ char* scan = name_value_list;
+
+ if (*scan == '\0') return;
+
+ BOOL done = FALSE;
+ while (!done)
+ {
+ while ( (*scan != '\0') && (*scan != '\n') )
+ {
+ scan++;
+ }
+
+ if (*scan == '\n')
+ {
+ *scan = '\0';
+ addNVPair(token_start);
+ scan++;
+ token_start = scan;
+ }
+ else
+ {
+ addNVPair(token_start);
+ done = TRUE;
+ }
+ }
+}
+
+
+// This method returns true if the object is over land owned by the
+// agent.
+BOOL LLViewerObject::isOverAgentOwnedLand() const
+{
+ return mRegionp
+ && mRegionp->getParcelOverlay()
+ && mRegionp->getParcelOverlay()->isOwnedSelf(getPositionRegion());
+}
+
+// This method returns true if the object is over land owned by the
+// agent.
+BOOL LLViewerObject::isOverGroupOwnedLand() const
+{
+ return mRegionp
+ && mRegionp->getParcelOverlay()
+ && mRegionp->getParcelOverlay()->isOwnedGroup(getPositionRegion());
+}
+
+void LLViewerObject::setParent(LLViewerObject* parent)
+{
+ LLPrimitive::setParent(parent);
+}
+
+void LLViewerObject::addChild(LLViewerObject *childp)
+{
+ BOOL result = TRUE;
+
+ for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i)
+ {
+ if (*i == childp)
+ { //already has child
+ return;
+ }
+ }
+
+ if (!isAvatar())
+ {
+ // propagate selection properties
+ childp->mbCanSelect = mbCanSelect;
+ }
+
+ childp->setParent(this);
+ mChildList.push_back(childp);
+
+ if (!result)
+ {
+ llwarns << "Failed to attach child " << childp->getID() << " to object " << getID() << llendl;
+ removeChild(childp);
+ if (mJointInfo)
+ {
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+ }
+}
+
+void LLViewerObject::removeChild(LLViewerObject *childp)
+{
+ for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i)
+ {
+ if (*i == childp)
+ {
+ if (!childp->isAvatar() && mDrawable.notNull() && mDrawable->isActive() && childp->mDrawable.notNull() && !isAvatar())
+ {
+ gPipeline.markRebuild(childp->mDrawable, LLDrawable::REBUILD_VOLUME);
+ }
+
+ mChildList.erase(i);
+ childp->setParent(NULL);
+ break;
+ }
+ }
+
+ if (childp->isSelected())
+ {
+ gSelectMgr->deselectObjectAndFamily(childp);
+ BOOL add_to_end = TRUE;
+ gSelectMgr->selectObjectAndFamily(childp, add_to_end);
+ }
+}
+
+LLViewerObject::child_list_t& LLViewerObject::getChildren()
+{
+ return mChildList;
+}
+
+void LLViewerObject::addThisAndAllChildren(LLDynamicArray<LLViewerObject*>& objects)
+{
+ objects.put(this);
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ if (!mChildList[i]->isAvatar())
+ {
+ (mChildList[i])->addThisAndAllChildren(objects);
+ }
+ }
+}
+
+void LLViewerObject::addThisAndNonJointChildren(LLDynamicArray<LLViewerObject*>& objects)
+{
+ objects.put(this);
+ // don't add any attachments when temporarily selecting avatar
+ if (isAvatar())
+ {
+ return;
+ }
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ if ( (!mChildList[i]->isAvatar())
+ && (!mChildList[i]->isJointChild()))
+ {
+ (mChildList[i])->addThisAndNonJointChildren(objects);
+ }
+ }
+}
+
+BOOL LLViewerObject::isChild(LLViewerObject *childp) const
+{
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLViewerObject *testChildp = &(*mChildList[i]);
+ if (testChildp == childp) return TRUE;
+ }
+ return FALSE;
+}
+
+
+// returns TRUE if at least one avatar is sitting on this object
+BOOL LLViewerObject::isSeat() const
+{
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLViewerObject *childp = &(*mChildList[i]);
+ if (childp->isAvatar())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+
+}
+
+BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp)
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ mDrawable->mParent = parentp;
+
+ BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ gPipeline.markMoved(mDrawable, FALSE);
+
+ return ret;
+}
+
+U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ U32 retval = 0x0;
+
+ // Coordinates of objects on simulators are region-local.
+ U64 region_handle;
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+ mRegionp = gWorldPointer->getRegionFromHandle(region_handle);
+ if (!mRegionp)
+ {
+ U32 x, y;
+ from_region_handle(region_handle, &x, &y);
+
+ llerrs << "Object has invalid region " << x << ":" << y << "!" << llendl;
+ }
+
+ U16 time_dilation16;
+ mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16);
+ F32 time_dilation = ((F32) time_dilation16) / 65535.f;
+ mTimeDilation = time_dilation;
+ mRegionp->setTimeDilation(time_dilation);
+
+ // this will be used to determine if we've really changed position
+ // Use getPosition, not getPositionRegion, since this is what we're comparing directly against.
+ LLVector3 test_pos_parent = getPosition();
+
+ U8 data[60+16]; // This needs to match the largest size below.
+#ifdef LL_BIG_ENDIAN
+ U16 valswizzle[4];
+#endif
+ U16 *val;
+ const F32 size = gWorldPointer->getRegionWidthInMeters();
+ const F32 MAX_HEIGHT = gWorldPointer->getRegionMaxHeight();
+ const F32 MIN_HEIGHT = gWorldPointer->getRegionMinHeight();
+ S32 length;
+ S32 count;
+ S32 this_update_precision = 32; // in bits
+
+ // Temporaries, because we need to compare w/ previous to set dirty flags...
+ LLVector3 new_pos_parent;
+ LLVector3 new_vel;
+ LLVector3 new_acc;
+ LLVector3 new_angv;
+ LLQuaternion new_rot;
+ LLVector3 new_scale;
+
+ U32 parent_id = 0;
+ U8 material = 0;
+ U8 click_action = 0;
+ U32 crc = 0;
+
+ bool old_special_hover_cursor = specialHoverCursor();
+
+ LLViewerObject *cur_parentp = (LLViewerObject *)getParent();
+
+ if (cur_parentp)
+ {
+ parent_id = cur_parentp->mLocalID;
+ }
+
+ if (!dp)
+ {
+ switch(update_type)
+ {
+ case OUT_FULL:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "Full:" << getID() << llendl;
+#endif
+ LLUUID audio_uuid;
+ LLUUID owner_id; // only valid if audio_uuid or particle system is not null
+ F32 gain;
+ U8 sound_flags;
+
+ mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_CRC, crc, block_num);
+ mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_ParentID, parent_id, block_num);
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_Sound, audio_uuid, block_num );
+ // HACK: Owner id only valid if non-null sound id or particle system
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, block_num );
+ mesgsys->getF32Fast( _PREHASH_ObjectData, _PREHASH_Gain, gain, block_num );
+ mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Flags, sound_flags, block_num );
+ mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Material, material, block_num );
+ mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_ClickAction, click_action, block_num);
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_Scale, new_scale, block_num );
+ length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num);
+
+ mTotalCRC = crc;
+
+ // Owner ID used for sound muting or particle system muting
+ setAttachedSound(audio_uuid, owner_id, gain, sound_flags);
+
+ U8 old_material = getMaterial();
+ if (old_material != material)
+ {
+ setMaterial(material);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ setClickAction(click_action);
+
+ count = 0;
+ LLVector4 collision_plane;
+
+ switch(length)
+ {
+ case (60 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 60:
+ this_update_precision = 32;
+ // this is a terse update
+ // pos
+ htonmemcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // vel
+ htonmemcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // acc
+ htonmemcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // theta
+ {
+ LLVector3 vec;
+ htonmemcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ new_rot.unpackFromVector3(vec);
+ }
+ count += sizeof(LLVector3);
+ // omega
+ htonmemcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+#if LL_DARWIN
+ if (length == 76)
+ {
+ setAngularVelocity(LLVector3::zero);
+ }
+#endif
+ break;
+ case(32 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 32:
+ this_update_precision = 16;
+ test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+
+ // This is a terse 16 update, so treat data as an array of U16's.
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setVelocity(LLVector3(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size)));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setAcceleration(LLVector3(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size)));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Quat, 4);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*4;
+ new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f);
+ new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f);
+ new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f);
+ new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ new_angv.setVec(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+ break;
+
+ case 16:
+ this_update_precision = 8;
+ test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+ // this is a terse 8 update
+ new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT);
+
+ setVelocity(U8_to_F32(data[3], -size, size),
+ U8_to_F32(data[4], -size, size),
+ U8_to_F32(data[5], -size, size) );
+
+ setAcceleration(U8_to_F32(data[6], -size, size),
+ U8_to_F32(data[7], -size, size),
+ U8_to_F32(data[8], -size, size) );
+
+ new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f);
+ new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f);
+ new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f);
+ new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f);
+
+ new_angv.setVec(U8_to_F32(data[13], -size, size),
+ U8_to_F32(data[14], -size, size),
+ U8_to_F32(data[15], -size, size) );
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+ break;
+ }
+
+ ////////////////////////////////////////////////////
+ //
+ // Here we handle data specific to the full message.
+ //
+
+ U32 flags;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num);
+ // clear all but local flags
+ mFlags &= FLAGS_LOCAL;
+ mFlags |= flags;
+
+ U8 state;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
+ mState = state;
+
+ // ...new objects that should come in selected need to be added to the selected list
+ mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
+
+ // Set the change flags for scale
+ if (new_scale != getScale())
+ {
+ setChanged(SCALED | SILHOUETTE);
+ setScale(new_scale); // Must follow setting permYouOwner()
+ }
+
+ // Set all name value pairs
+ S32 nv_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_NameValue);
+ if (nv_size > 0)
+ {
+ char* name_value_list = new char[nv_size];
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_NameValue, nv_size, name_value_list, block_num);
+
+ setNameValueList(name_value_list);
+
+ delete [] name_value_list;
+ }
+
+ // Clear out any existing generic data
+ if (mData)
+ {
+ delete [] mData;
+ }
+
+ // Check for appended generic data
+ S32 data_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data);
+ if (data_size == 0)
+ {
+ mData = NULL;
+ }
+ else
+ {
+ // ...has generic data
+ mData = new U8[data_size];
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, mData, data_size, block_num);
+ }
+
+ S32 text_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Text);
+ if (text_size > 1)
+ {
+ // Setup object text
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1);
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+
+ char temp_string[256]; // not MAX_STRING, must hold 255 chars + \0
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, 256, temp_string, block_num );
+
+ LLColor4U coloru;
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num);
+
+ // alpha was flipped so that it zero encoded better
+ coloru.mV[3] = 255 - coloru.mV[3];
+ mText->setColor(LLColor4(coloru));
+ mText->setStringUTF8(temp_string);
+
+ if (mDrawable.notNull())
+ {
+ setChanged(MOVED | SILHOUETTE);
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ else if (mText.notNull())
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ char media_url[MAX_STRING+1];
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_MediaURL, MAX_STRING+1, media_url, block_num);
+ //if (media_url[0])
+ //{
+ // llinfos << "WEBONPRIM media_url " << media_url << llendl;
+ //}
+ if (!mMedia && media_url[0] != '\0')
+ {
+ retval |= MEDIA_URL_ADDED;
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mMediaType = LLViewerObject::MEDIA_TYPE_WEB_PAGE;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ else if (mMedia)
+ {
+ if (media_url[0] == '\0')
+ {
+ retval |= MEDIA_URL_REMOVED;
+ delete mMedia;
+ mMedia = NULL;
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ // We just added or changed a web page.
+ retval |= MEDIA_URL_UPDATED;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ }
+
+ //
+ // Unpack particle system data
+ //
+ unpackParticleSource(block_num, owner_id);
+
+ // Mark all extra parameters not used
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ iter->second->in_use = FALSE;
+ }
+
+ // Unpack extra parameters
+ S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ExtraParams);
+ if (size > 0)
+ {
+ U8 *buffer = new U8[size];
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ExtraParams, buffer, size, block_num);
+ LLDataPackerBinaryBuffer dp(buffer, size);
+
+ U8 num_parameters;
+ dp.unpackU8(num_parameters, "num_params");
+ U8 param_block[MAX_OBJECT_PARAMS_SIZE];
+ for (U8 param=0; param<num_parameters; ++param)
+ {
+ U16 param_type;
+ S32 param_size;
+ dp.unpackU16(param_type, "param_type");
+ dp.unpackBinaryData(param_block, param_size, "param_data");
+ //llinfos << "Param type: " << param_type << ", Size: " << param_size << llendl;
+ LLDataPackerBinaryBuffer dp2(param_block, param_size);
+ unpackParameterEntry(param_type, &dp2);
+ }
+ delete[] buffer;
+ }
+
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ if (!iter->second->in_use)
+ {
+ // Send an update message in case it was formerly in use
+ parameterChanged(iter->first, iter->second->data, FALSE, false);
+ }
+ }
+
+ U8 joint_type = 0;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_JointType, joint_type, block_num);
+ if (joint_type)
+ {
+ // create new joint info
+ if (!mJointInfo)
+ {
+ mJointInfo = new LLVOJointInfo;
+ }
+ mJointInfo->mJointType = (EHavokJointType) joint_type;
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointPivot, mJointInfo->mPivot, block_num);
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointAxisOrAnchor, mJointInfo->mAxisOrAnchor, block_num);
+ }
+ else if (mJointInfo)
+ {
+ // this joint info is no longer needed
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ break;
+ }
+
+ case OUT_TERSE_IMPROVED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "TI:" << getID() << llendl;
+#endif
+ length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num);
+ count = 0;
+ LLVector4 collision_plane;
+
+ switch(length)
+ {
+ case(60 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 60:
+ // this is a terse 32 update
+ // pos
+ this_update_precision = 32;
+ htonmemcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // vel
+ htonmemcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // acc
+ htonmemcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // theta
+ {
+ LLVector3 vec;
+ htonmemcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ new_rot.unpackFromVector3(vec);
+ }
+ count += sizeof(LLVector3);
+ // omega
+ htonmemcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+#if LL_DARWIN
+ if (length == 76)
+ {
+ setAngularVelocity(LLVector3::zero);
+ }
+#endif
+ break;
+ case(32 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 32:
+ // this is a terse 16 update
+ this_update_precision = 16;
+ test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setVelocity(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setAcceleration(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Quat, 8);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*4;
+ new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f);
+ new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f);
+ new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f);
+ new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ setAngularVelocity( U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+ break;
+
+ case 16:
+ // this is a terse 8 update
+ this_update_precision = 8;
+ test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+ new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT);
+
+ setVelocity(U8_to_F32(data[3], -size, size),
+ U8_to_F32(data[4], -size, size),
+ U8_to_F32(data[5], -size, size) );
+
+ setAcceleration(U8_to_F32(data[6], -size, size),
+ U8_to_F32(data[7], -size, size),
+ U8_to_F32(data[8], -size, size) );
+
+ new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f);
+ new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f);
+ new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f);
+ new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f);
+
+ setAngularVelocity( U8_to_F32(data[13], -size, size),
+ U8_to_F32(data[14], -size, size),
+ U8_to_F32(data[15], -size, size) );
+ break;
+ }
+
+ U8 state;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
+ mState = state;
+ break;
+ }
+
+ default:
+ break;
+
+ }
+ }
+ else
+ {
+ // handle the compressed case
+ LLUUID sound_uuid;
+ LLUUID owner_id;
+ F32 gain = 0;
+ U8 sound_flags = 0;
+ F32 cutoff = 0;
+
+ U16 val[4];
+
+ U8 state;
+
+ dp->unpackU8(state, "State");
+ mState = state;
+
+ switch(update_type)
+ {
+ case OUT_TERSE_IMPROVED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "CompTI:" << getID() << llendl;
+#endif
+ U8 value;
+ dp->unpackU8(value, "agent");
+ if (value)
+ {
+ LLVector4 collision_plane;
+ dp->unpackVector4(collision_plane, "Plane");
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ }
+ test_pos_parent = getPosition();
+ dp->unpackVector3(new_pos_parent, "Pos");
+ dp->unpackU16(val[VX], "VelX");
+ dp->unpackU16(val[VY], "VelY");
+ dp->unpackU16(val[VZ], "VelZ");
+ setVelocity(U16_to_F32(val[VX], -128.f, 128.f),
+ U16_to_F32(val[VY], -128.f, 128.f),
+ U16_to_F32(val[VZ], -128.f, 128.f));
+ dp->unpackU16(val[VX], "AccX");
+ dp->unpackU16(val[VY], "AccY");
+ dp->unpackU16(val[VZ], "AccZ");
+ setAcceleration(U16_to_F32(val[VX], -64.f, 64.f),
+ U16_to_F32(val[VY], -64.f, 64.f),
+ U16_to_F32(val[VZ], -64.f, 64.f));
+
+ dp->unpackU16(val[VX], "ThetaX");
+ dp->unpackU16(val[VY], "ThetaY");
+ dp->unpackU16(val[VZ], "ThetaZ");
+ dp->unpackU16(val[VS], "ThetaS");
+ new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f);
+ new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f);
+ new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f);
+ new_rot.mQ[VS] = U16_to_F32(val[VS], -1.f, 1.f);
+ dp->unpackU16(val[VX], "AccX");
+ dp->unpackU16(val[VY], "AccY");
+ dp->unpackU16(val[VZ], "AccZ");
+ setAngularVelocity( U16_to_F32(val[VX], -64.f, 64.f),
+ U16_to_F32(val[VY], -64.f, 64.f),
+ U16_to_F32(val[VZ], -64.f, 64.f));
+ }
+ break;
+ case OUT_FULL_COMPRESSED:
+ case OUT_FULL_CACHED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "CompFull:" << getID() << llendl;
+#endif
+ dp->unpackU32(crc, "CRC");
+ mTotalCRC = crc;
+ dp->unpackU8(material, "Material");
+ U8 old_material = getMaterial();
+ if (old_material != material)
+ {
+ setMaterial(material);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ dp->unpackU8(click_action, "ClickAction");
+ setClickAction(click_action);
+ dp->unpackVector3(new_scale, "Scale");
+ dp->unpackVector3(new_pos_parent, "Pos");
+ LLVector3 vec;
+ dp->unpackVector3(vec, "Rot");
+ new_rot.unpackFromVector3(vec);
+ setAcceleration(LLVector3::zero);
+
+ U32 value;
+ dp->unpackU32(value, "SpecialCode");
+
+ dp->setPassFlags(value);
+
+
+ if (value & 0x80)
+ {
+ dp->unpackVector3(vec, "Omega");
+ setAngularVelocity(vec);
+ }
+
+ if (value & 0x20)
+ {
+ dp->unpackU32(parent_id, "ParentID");
+ }
+ else
+ {
+ parent_id = 0;
+ }
+
+ S32 sp_size;
+ U32 size;
+ if (value & 0x2)
+ {
+ sp_size = 1;
+ delete [] mData;
+ mData = new U8[1];
+ dp->unpackU8(((U8*)mData)[0], "TreeData");
+ }
+ else if (value & 0x1)
+ {
+ dp->unpackU32(size, "ScratchPadSize");
+ delete [] mData;
+ mData = new U8[size];
+ dp->unpackBinaryData((U8 *)mData, sp_size, "PartData");
+ }
+ else
+ {
+ mData = NULL;
+ }
+
+ // Setup object text
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1); // Set to match current agni behavior.
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+
+ if (value & 0x4)
+ {
+ char temp_string[256]; // not MAX_STRING, must hold 255 chars + \0
+ dp->unpackString(temp_string, "Text");
+ LLColor4U coloru;
+ dp->unpackBinaryDataFixed(coloru.mV, 4, "Color");
+ coloru.mV[3] = 255 - coloru.mV[3];
+ mText->setColor(LLColor4(coloru));
+ mText->setStringUTF8(temp_string);
+
+ setChanged(TEXTURE);
+ }
+ else
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ if (value & 0x200)
+ {
+ char media_url[MAX_STRING+1];
+ dp->unpackString(media_url, "MediaURL");
+ //if (media_url[0])
+ //{
+ // llinfos << "WEBONPRIM media_url " << media_url << llendl;
+ //}
+ if (!mMedia)
+ {
+ retval |= MEDIA_URL_ADDED;
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mMediaType = LLViewerObject::MEDIA_TYPE_WEB_PAGE;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ retval |= MEDIA_URL_UPDATED;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ }
+ else if (mMedia)
+ {
+ retval |= MEDIA_URL_REMOVED;
+ delete mMedia;
+ mMedia = NULL;
+ }
+
+ //
+ // Unpack particle system data
+ //
+ if (value & 0x8)
+ unpackParticleSource(*dp, owner_id);
+
+ // Mark all extra parameters not used
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ iter->second->in_use = FALSE;
+ }
+
+ // Unpack extra params
+ U8 num_parameters;
+ dp->unpackU8(num_parameters, "num_params");
+ U8 param_block[MAX_OBJECT_PARAMS_SIZE];
+ for (U8 param=0; param<num_parameters; ++param)
+ {
+ U16 param_type;
+ S32 param_size;
+ dp->unpackU16(param_type, "param_type");
+ dp->unpackBinaryData(param_block, param_size, "param_data");
+ //llinfos << "Param type: " << param_type << ", Size: " << param_size << llendl;
+ LLDataPackerBinaryBuffer dp2(param_block, param_size);
+ unpackParameterEntry(param_type, &dp2);
+ }
+
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ if (!iter->second->in_use)
+ {
+ // Send an update message in case it was formerly in use
+ parameterChanged(iter->first, iter->second->data, FALSE, false);
+ }
+ }
+
+ if (value & 0x10)
+ {
+ dp->unpackUUID(sound_uuid, "SoundUUID");
+ dp->unpackUUID(owner_id, "OwnerID");
+ dp->unpackF32(gain, "SoundGain");
+ dp->unpackU8(sound_flags, "SoundFlags");
+ dp->unpackF32(cutoff, "SoundRadius");
+ }
+
+ if (value & 0x100)
+ {
+ char name_value_list[2048];
+ dp->unpackString(name_value_list, "NV");
+
+ setNameValueList(name_value_list);
+ }
+
+ mTotalCRC = crc;
+
+ setAttachedSound(sound_uuid, owner_id, gain, sound_flags);
+
+ // only get these flags on updates from sim, not cached ones
+ // Preload these five flags for every object.
+ // Finer shades require the object to be selected, and the selection manager
+ // stores the extended permission info.
+ U32 flags;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num);
+ // keep local flags and overwrite remote-controlled flags
+ mFlags = (mFlags & FLAGS_LOCAL) | flags;
+
+ // ...new objects that should come in selected need to be added to the selected list
+ mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
+
+ // Set the change flags for scale
+ if (new_scale != getScale())
+ {
+ setChanged(SCALED | SILHOUETTE);
+ setScale(new_scale); // Must follow setting permYouOwner()
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ //
+ // Fix object parenting.
+ //
+ BOOL b_changed_status = FALSE;
+
+ if (OUT_TERSE_IMPROVED != update_type)
+ {
+ // We only need to update parenting on full updates, terse updates
+ // don't send parenting information.
+ if (!cur_parentp)
+ {
+ if (parent_id == 0)
+ {
+ // No parent now, no parent in message -> do nothing
+ }
+ else
+ {
+ // No parent now, new parent in message -> attach to that parent if possible
+ LLUUID parent_uuid;
+ LLViewerObjectList::getUUIDFromLocal(parent_uuid,
+ parent_id,
+ mesgsys->getSenderIP(),
+ mesgsys->getSenderPort());
+
+ LLViewerObject *sent_parentp = gObjectList.findObject(parent_uuid);
+
+ //
+ // Check to see if we have the corresponding viewer object for the parent.
+ //
+ if (sent_parentp && sent_parentp->getParent() == this)
+ {
+ // Try to recover if we attempt to attach a parent to its child
+ llwarns << "Attempt to attach a parent to it's child: " << this->getID() << " to " << sent_parentp->getID() << llendl;
+ this->removeChild(sent_parentp);
+ sent_parentp->setDrawableParent(NULL);
+ }
+
+ if (sent_parentp && (sent_parentp != this) && !sent_parentp->isDead())
+ {
+ //
+ // We have a viewer object for the parent, and it's not dead.
+ // Do the actual reparenting here.
+ //
+
+ // new parent is valid
+ b_changed_status = TRUE;
+ // ...no current parent, so don't try to remove child
+ if (mDrawable.notNull())
+ {
+ if (mDrawable->isDead() || !mDrawable->getVObj())
+ {
+ llwarns << "Drawable is dead or no VObj!" << llendl;
+ sent_parentp->addChild(this);
+ }
+ else
+ {
+ if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 1
+ {
+ // Bad, we got a cycle somehow.
+ // Kill both the parent and the child, and
+ // set cache misses for both of them.
+ llwarns << "Attempting to recover from parenting cycle!" << llendl
+ llwarns << "Killing " << sent_parentp->getID() << " and " << getID() << llendl;
+ llwarns << "Adding to cache miss list" << llendl;
+ setParent(NULL);
+ sent_parentp->setParent(NULL);
+ getRegion()->addCacheMissFull(getLocalID());
+ getRegion()->addCacheMissFull(sent_parentp->getLocalID());
+ gObjectList.killObject(sent_parentp);
+ gObjectList.killObject(this);
+ return retval;
+ }
+ sent_parentp->addChild(this);
+ // make sure this object gets a non-damped update
+ if (sent_parentp->mDrawable.notNull())
+ {
+ gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped
+ }
+ }
+ }
+ else
+ {
+ sent_parentp->addChild(this);
+ }
+
+ setChanged(MOVED | SILHOUETTE);
+ }
+ else
+ {
+ //
+ // No corresponding viewer object for the parent, put the various
+ // pieces on the orphan list.
+ //
+
+ //parent_id
+ U32 ip = mesgsys->getSenderIP();
+ U32 port = mesgsys->getSenderPort();
+
+ gObjectList.orphanize(this, parent_id, ip, port);
+ }
+ }
+ }
+ else
+ {
+ // BUG: this is a bad assumption once border crossing is alowed
+ if ( (parent_id == cur_parentp->mLocalID)
+ &&(update_type == OUT_TERSE_IMPROVED))
+ {
+ // Parent now, same parent in message -> do nothing
+
+ // Debugging for suspected problems with local ids.
+ //LLUUID parent_uuid;
+ //LLViewerObjectList::getUUIDFromLocal(parent_uuid, parent_id, mesgsys->getSenderIP(), mesgsys->getSenderPort() );
+ //if (parent_uuid != cur_parentp->getID() )
+ //{
+ // llerrs << "Local ID match but UUID mismatch of viewer object" << llendl;
+ //}
+ }
+ else
+ {
+ // Parented now, different parent in message
+ LLViewerObject *sent_parentp;
+ if (parent_id == 0)
+ {
+ //
+ // This object is no longer parented, we sent in a zero parent ID.
+ //
+ sent_parentp = NULL;
+ }
+ else
+ {
+ LLUUID parent_uuid;
+ LLViewerObjectList::getUUIDFromLocal(parent_uuid,
+ parent_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ sent_parentp = gObjectList.findObject(parent_uuid);
+
+ if (isAvatar())
+ {
+ // This logic is meant to handle the case where a sitting avatar has reached a new sim
+ // ahead of the object she was sitting on (which is common as objects are transfered through
+ // a slower route than agents)...
+ // In this case, the local id for the object will not be valid, since the viewer has not received
+ // a full update for the object from that sim yet, so we assume that the agent is still sitting
+ // where she was originally. --RN
+ if (!sent_parentp)
+ {
+ sent_parentp = cur_parentp;
+ }
+ }
+ else if (!sent_parentp)
+ {
+ //
+ // Switching parents, but we don't know the new parent.
+ //
+ U32 ip = mesgsys->getSenderIP();
+ U32 port = mesgsys->getSenderPort();
+
+ // We're an orphan, flag things appropriately.
+ gObjectList.orphanize(this, parent_id, ip, port);
+ }
+ }
+
+ // Reattach if possible.
+ if (sent_parentp && sent_parentp != cur_parentp && sent_parentp != this)
+ {
+ // New parent is valid, detach and reattach
+ b_changed_status = TRUE;
+ if (mDrawable.notNull())
+ {
+ if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 2
+ {
+ // Bad, we got a cycle somehow.
+ // Kill both the parent and the child, and
+ // set cache misses for both of them.
+ llwarns << "Attempting to recover from parenting cycle!" << llendl
+ llwarns << "Killing " << sent_parentp->getID() << " and " << getID() << llendl;
+ llwarns << "Adding to cache miss list" << llendl;
+ setParent(NULL);
+ sent_parentp->setParent(NULL);
+ getRegion()->addCacheMissFull(getLocalID());
+ getRegion()->addCacheMissFull(sent_parentp->getLocalID());
+ gObjectList.killObject(sent_parentp);
+ gObjectList.killObject(this);
+ return retval;
+ }
+ // make sure this object gets a non-damped update
+ }
+ cur_parentp->removeChild(this);
+ sent_parentp->addChild(this);
+ setChanged(MOVED | SILHOUETTE);
+ sent_parentp->setChanged(MOVED | SILHOUETTE);
+ if (sent_parentp->mDrawable.notNull())
+ {
+ gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped
+ }
+ }
+ else if (!sent_parentp)
+ {
+ bool remove_parent = true;
+ // No new parent, or the parent that we sent doesn't exist on the viewer.
+ LLViewerObject *parentp = (LLViewerObject *)getParent();
+ if (parentp)
+ {
+ if (parentp->getRegion() != getRegion())
+ {
+ // This is probably an object flying across a region boundary, the
+ // object probably ISN'T being reparented, but just got an object
+ // update out of order (child update before parent).
+ //llinfos << "Don't reparent object handoffs!" << llendl;
+ remove_parent = false;
+ }
+ }
+
+ if (remove_parent)
+ {
+ b_changed_status = TRUE;
+ if (mDrawable.notNull())
+ {
+ // clear parent to removeChild can put the drawable on the damped list
+ setDrawableParent(NULL); // LLViewerObject::processUpdateMessage 3
+ }
+
+ cur_parentp->removeChild(this);
+
+ if (mJointInfo && !parent_id)
+ {
+ // since this object is no longer parent-relative
+ // we make sure we delete any joint info
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ setChanged(MOVED | SILHOUETTE);
+
+ if (mDrawable.notNull())
+ {
+ // make sure this object gets a non-damped update
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ }
+ }
+ }
+ }
+
+ new_rot.normQuat();
+
+ if (gPingInterpolate)
+ {
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender());
+ F32 ping_delay = 0.5f * mTimeDilation * ( ((F32)cdp->getPingDelay()) * 0.001f + gFrameDTClamped);
+ LLVector3 diff = getVelocity() * (0.5f*mTimeDilation*(gFrameDTClamped + ((F32)ping_delay)*0.001f));
+ new_pos_parent += diff;
+ }
+
+ //////////////////////////
+ //
+ // Set the generic change flags...
+ //
+ //
+
+ // first, let's see if the new position is actually a change
+
+ //static S32 counter = 0;
+
+ F32 vel_mag_sq = getVelocity().magVecSquared();
+ F32 accel_mag_sq = getAcceleration().magVecSquared();
+
+ if ( ((b_changed_status)||(test_pos_parent != new_pos_parent))
+ ||( (!isSelected())
+ &&( (vel_mag_sq != 0.f)
+ ||(accel_mag_sq != 0.f)
+ ||(this_update_precision > mBestUpdatePrecision))))
+ {
+ mBestUpdatePrecision = this_update_precision;
+ setPositionParent(new_pos_parent);
+
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+
+ if (new_rot != mLastRot)
+ {
+ // if (getAngularVelocity().isExactlyZero() ||
+ // new_angv != getAngularVelocity())
+ {
+ mLastRot = new_rot;
+ setChanged(ROTATED | SILHOUETTE);
+ setRotation(new_rot);
+ resetRot();
+ }
+ }
+
+
+ if ( gShowObjectUpdates )
+ {
+ if (!((mPrimitiveCode == LL_PCODE_LEGACY_AVATAR) && (((LLVOAvatar *) this)->mIsSelf))
+ && mRegionp)
+ {
+ LLViewerObject* object = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, mRegionp);
+ LLVOTextBubble* bubble = (LLVOTextBubble*) object;
+
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ bubble->mColor.setVec(0.f, 0.f, 1.f, 1.f);
+ }
+ else
+ {
+ bubble->mColor.setVec(1.f, 0.f, 0.f, 1.f);
+ }
+ object->setPositionGlobal(getPositionGlobal());
+ gPipeline.addObject(object);
+ }
+ }
+
+ if ((0.0f == vel_mag_sq) &&
+ (0.0f == accel_mag_sq) &&
+ (0.0f == getAngularVelocity().magVecSquared()))
+ {
+ mStatic = TRUE; // This object doesn't move!
+ }
+ else
+ {
+ mStatic = FALSE;
+ }
+
+// BUG: This code leads to problems during group rotate and any scale operation.
+// Small discepencies between the simulator and viewer representations cause the
+// selection center to creep, leading to objects moving around the wrong center.
+//
+// Removing this, however, means that if someone else drags an object you have
+// selected, your selection center and dialog boxes will be wrong. It also means
+// that higher precision information on selected objects will be ignored.
+//
+// I believe the group rotation problem is fixed. JNC 1.21.2002
+//
+ // Additionally, if any child is selected, need to update the dialogs and selection
+ // center.
+ BOOL needs_refresh = mUserSelected;
+ LLViewerObject *childp;
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ childp = mChildList[i];
+ needs_refresh = needs_refresh || childp->mUserSelected;
+ }
+
+ if (needs_refresh)
+ {
+ gSelectMgr->updateSelectionCenter();
+ dialog_refresh_all();
+ }
+
+
+ // Mark update time as approx. now, with the ping delay.
+ // Ping delay is off because it's not set for velocity interpolation, causing
+ // much jumping and hopping around...
+
+// U32 ping_delay = mesgsys->mCircuitInfo.getPingDelay();
+ mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
+ mLastMessageUpdateSecs = LLFrameTimer::getElapsedSeconds();
+ if (mDrawable.notNull())
+ {
+ // Don't clear invisibility flag on update if still orphaned!
+ if (mDrawable->isState(LLDrawable::FORCE_INVISIBLE) && !mOrphaned)
+ {
+// lldebugs << "Clearing force invisible: " << mID << ":" << getPCodeString() << ":" << getPositionAgent() << llendl;
+ mDrawable->setState(LLDrawable::CLEAR_INVISIBLE);
+ }
+ }
+
+ // Update special hover cursor status
+ bool special_hover_cursor = specialHoverCursor();
+ if (old_special_hover_cursor != special_hover_cursor
+ && mDrawable.notNull())
+ {
+ mDrawable->updateSpecialHoverCursor(special_hover_cursor);
+ }
+
+ return retval;
+}
+
+BOOL LLViewerObject::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead)
+ {
+ // It's dead. Don't update it.
+ return TRUE;
+ }
+
+ // CRO - don't velocity interp linked objects!
+ // Leviathan - but DO velocity interp joints
+ if (!mStatic && gVelocityInterpolate && !isSelected())
+ {
+ // calculate dt from last update
+ F32 dt_raw = (F32)(time - mLastInterpUpdateSecs);
+ F32 dt = mTimeDilation * dt_raw;
+
+ if (!mUserSelected && !mJointInfo)
+ {
+ applyAngularVelocity(dt);
+ }
+
+ LLViewerObject *parentp = (LLViewerObject *) getParent();
+ if (mJointInfo)
+ {
+ if (parentp)
+ {
+ // do parent-relative stuff
+ LLVector3 ang_vel = getAngularVelocity();
+ F32 omega = ang_vel.magVecSquared();
+ F32 angle = 0.0f;
+ LLQuaternion dQ;
+ if (omega > 0.00001f)
+ {
+ omega = sqrt(omega);
+ angle = omega * dt;
+ dQ.setQuat(angle, ang_vel);
+ }
+ LLVector3 pos = getPosition();
+
+ if (HJT_HINGE == mJointInfo->mJointType)
+ {
+ // hinge = uniform circular motion
+ LLVector3 parent_pivot = getVelocity();
+ LLVector3 parent_axis = getAcceleration();
+
+ angle = dt * (ang_vel * mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis
+ dQ.setQuat(angle, mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis
+ LLVector3 pivot_offset = pos - mJointInfo->mPivot; // pos in pivot-frame
+ pivot_offset = pivot_offset * dQ; // new rotated pivot-frame pos
+ pos = mJointInfo->mPivot + pivot_offset; // parent-frame
+ LLViewerObject::setPosition(pos);
+ LLQuaternion Q_PC = getRotation();
+ setRotation(Q_PC * dQ);
+ mLastInterpUpdateSecs = time;
+ }
+ else if (HJT_POINT == mJointInfo->mJointType)
+ // || HJT_LPOINT == mJointInfo->mJointType)
+ {
+ // point-to-point = spin about axis and uniform circular motion
+ // of axis about the pivot point
+ //
+ // NOTE: this interpolation scheme is not quite good enough to
+ // reduce the bandwidth -- needs a gravitational correction.
+ // Similarly for hinges with axes that deviate from vertical.
+
+ LLQuaternion Q_PC = getRotation();
+ Q_PC = Q_PC * dQ;
+ setRotation(Q_PC);
+
+ LLVector3 pivot_to_child = - mJointInfo->mAxisOrAnchor; // AxisOrAnchor = anchor
+ pos = mJointInfo->mPivot + pivot_to_child * Q_PC;
+ LLViewerObject::setPosition(pos);
+ mLastInterpUpdateSecs = time;
+ }
+ /* else if (HJT_WHEEL == mJointInfo->mJointInfo)
+ {
+ // wheel = uniform rotation about axis, with linear
+ // velocity interpolation (if any)
+ LLVector3 parent_axis = getAcceleration(); // HACK -- accel stores the parent-axis (parent-frame)
+
+ LLQuaternion Q_PC = getRotation();
+
+ angle = dt * (parent_axis * ang_vel);
+ dQ.setQuat(angle, parent_axis);
+
+ Q_PC = Q_PC * dQ;
+ setRotation(Q_PC);
+
+ pos = getPosition() + dt * getVelocity();
+ LLViewerObject::setPosition(pos);
+ mLastInterpUpdateSecs = time;
+ }*/
+ }
+ }
+ else if (isAttachment())
+ {
+ mLastInterpUpdateSecs = time;
+ return TRUE;
+ }
+ else
+ {
+ // linear motion
+ // HAVOK_TIMESTEP is used below to correct for the fact that the velocity in object
+ // updates represents the average velocity of the last timestep, rather than the final velocity.
+ // the time dilation above should guarrantee that dt is never less than HAVOK_TIMESTEP, theoretically
+ //
+ // There is a problem here if dt is negative. . .
+
+ //FIXME: should also wrap linear accel/velocity in check
+ // to see if object is selected, instead of explicitly
+ // zeroing it out
+ LLVector3 accel = getAcceleration();
+ LLVector3 vel = getVelocity();
+
+ if (!(accel.isExactlyZero() && vel.isExactlyZero()))
+ {
+ LLVector3 pos = (vel + (0.5f * (dt-HAVOK_TIMESTEP)) * accel) * dt;
+
+ // region local
+ setPositionRegion(pos + getPositionRegion());
+ setVelocity(vel + accel*dt);
+
+ // for objects that are spinning but not translating, make sure to flag them as having moved
+ setChanged(MOVED | SILHOUETTE);
+ }
+
+ mLastInterpUpdateSecs = time;
+ }
+ }
+
+ if (gNoRender)
+ {
+ // Skip drawable stuff if not rendering.
+ return TRUE;
+ }
+
+ updateDrawable(FALSE);
+
+ return TRUE;
+}
+
+
+BOOL LLViewerObject::setData(const U8 *datap, const U32 data_size)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ delete [] mData;
+
+ if (datap)
+ {
+ mData = new U8[data_size];
+ if (!mData)
+ {
+ return FALSE;
+ }
+ memcpy(mData, datap, data_size);
+ }
+ return TRUE;
+}
+
+// delete an item in the inventory, but don't tell the server. This is
+// used internally by remove, update, and savescript.
+// This will only delete the first item with an item_id in the list
+void LLViewerObject::deleteInventoryItem(const LLUUID& item_id)
+{
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it )
+ {
+ if((*it)->getUUID() == item_id)
+ {
+ // This is safe only because we return immediatly.
+ mInventory->erase(it); // will deref and delete it
+ return;
+ }
+ }
+ doInventoryCallback();
+ }
+}
+
+void LLViewerObject::doUpdateInventory(
+ LLViewerInventoryItem* item,
+ U8 key,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLViewerInventoryItem* old_item = NULL;
+ if(TASK_INVENTORY_ITEM_KEY == key)
+ {
+ old_item = (LLViewerInventoryItem*)getInventoryObject(item->getUUID());
+ }
+ else if(TASK_INVENTORY_ASSET_KEY == key)
+ {
+ old_item = getInventoryItemByAsset(item->getAssetUUID());
+ }
+ LLUUID item_id;
+ LLUUID new_owner;
+ LLUUID new_group;
+ BOOL group_owned = FALSE;
+ if(old_item)
+ {
+ item_id = old_item->getUUID();
+ new_owner = old_item->getPermissions().getOwner();
+ new_group = old_item->getPermissions().getGroup();
+ group_owned = old_item->getPermissions().isGroupOwned();
+ old_item = NULL;
+ }
+ else
+ {
+ item_id = item->getUUID();
+ }
+ if(!is_new && mInventory)
+ {
+ // Attempt to update the local inventory. If we can get the
+ // object perm, we have perfect visibility, so we want the
+ // serial number to match. Otherwise, take our best guess and
+ // make sure that the serial number does not match.
+ deleteInventoryItem(item_id);
+ LLPermissions perm(item->getPermissions());
+ LLPermissions* obj_perm = gSelectMgr->findObjectPermissions(this);
+ bool is_atomic = ((S32)LLAssetType::AT_OBJECT == item->getType()) ? false : true;
+ if(obj_perm)
+ {
+ perm.setOwnerAndGroup(LLUUID::null, obj_perm->getOwner(), obj_perm->getGroup(), is_atomic);
+ }
+ else
+ {
+ if(group_owned)
+ {
+ perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic);
+ }
+ else if(!new_owner.isNull())
+ {
+ // The object used to be in inventory, so we can
+ // assume the owner and group will match what they are
+ // there.
+ perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic);
+ }
+ // *FIX: can make an even better guess by using the mPermGroup flags
+ else if(permYouOwner())
+ {
+ // best guess.
+ perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), item->getPermissions().getGroup(), is_atomic);
+ --mInventorySerialNum;
+ }
+ else
+ {
+ // dummy it up.
+ perm.setOwnerAndGroup(LLUUID::null, LLUUID::null, LLUUID::null, is_atomic);
+ --mInventorySerialNum;
+ }
+ }
+ LLViewerInventoryItem* new_item = new LLViewerInventoryItem(item);
+ new_item->setPermissions(perm);
+ mInventory->push_front(new_item);
+ doInventoryCallback();
+ ++mInventorySerialNum;
+ }
+}
+
+// save a script, which involves removing the old one, and rezzing
+// in the new one. This method should be called with the asset id
+// of the new and old script AFTER the bytecode has been saved.
+void LLViewerObject::saveScript(
+ const LLViewerInventoryItem* item,
+ BOOL active,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ /*
+ * XXXPAM Investigate not making this copy. Seems unecessary, but I'm unsure about the
+ * interaction with doUpdateInventory() called below.
+ */
+ lldebugs << "LLViewerObject::saveScript() " << item->getUUID() << " " << item->getAssetUUID() << llendl;
+ LLPointer<LLViewerInventoryItem> task_item =
+ new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
+ item->getAssetUUID(), item->getType(),
+ item->getInventoryType(),
+ item->getName(), item->getDescription(),
+ item->getSaleInfo(), item->getFlags(),
+ item->getCreationDate());
+ task_item->setTransactionID(item->getTransactionID());
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RezScript);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ msg->nextBlockFast(_PREHASH_UpdateBlock);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, (mLocalID));
+ U8 enabled = active;
+ msg->addBOOLFast(_PREHASH_Enabled, enabled);
+ msg->nextBlockFast(_PREHASH_InventoryBlock);
+ task_item->packMessage(msg);
+ msg->sendReliable(mRegionp->getHost());
+
+ // do the internal logic
+ doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, is_new);
+}
+
+void LLViewerObject::moveInventory(const LLUUID& folder_id,
+ const LLUUID& item_id)
+{
+ lldebugs << "LLViewerObject::moveInventory " << item_id << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_FolderID, folder_id);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->sendReliable(mRegionp->getHost());
+
+ LLInventoryObject* inv_obj = getInventoryObject(item_id);
+ if(inv_obj)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_obj;
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ deleteInventoryItem(item_id);
+ ++mInventorySerialNum;
+ }
+ }
+}
+
+void LLViewerObject::dirtyInventory()
+{
+ // If there aren't any LLVOInventoryListeners, we won't be
+ // able to update our mInventory when it comes back from the
+ // simulator, so we should not clear the inventory either.
+ if(mInventory && !mInventoryCallbacks.isEmpty())
+ {
+ mInventory->clear(); // will deref and delete entries
+ delete mInventory;
+ mInventory = NULL;
+ mInventoryDirty = TRUE;
+ }
+}
+
+void LLViewerObject::registerInventoryListener(LLVOInventoryListener* listener, void* user_data)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLInventoryCallbackInfo* info = new LLInventoryCallbackInfo;
+ info->mListener = listener;
+ info->mInventoryData = user_data;
+ mInventoryCallbacks.addData(info);
+}
+
+void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener)
+{
+ if (listener == NULL) return;
+ LLInventoryCallbackInfo* info;
+ for (info = mInventoryCallbacks.getFirstData();
+ info;
+ info = mInventoryCallbacks.getNextData() )
+ {
+ if (info->mListener == listener)
+ {
+ mInventoryCallbacks.deleteCurrentData();
+ break;
+ }
+ }
+}
+
+void LLViewerObject::clearInventoryListeners()
+{
+ mInventoryCallbacks.deleteAllData();
+}
+
+void LLViewerObject::requestInventory()
+{
+ mInventoryDirty = FALSE;
+ if(mInventory)
+ {
+ //mInventory->clear() // will deref and delete it
+ //delete mInventory;
+ //mInventory = NULL;
+ doInventoryCallback();
+ }
+ // throw away duplicate requests
+ else if (! mInventoryPending)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->sendReliable(mRegionp->getHost());
+
+ // this will get reset by dirtyInventory or doInventoryCallback
+ mInventoryPending = TRUE;
+ }
+}
+
+struct LLFilenameAndTask
+{
+ LLUUID mTaskID;
+ char mFilename[MAX_STRING]; // Just the filename, not the path
+#ifdef _DEBUG
+ static S32 sCount;
+ LLFilenameAndTask()
+ {
+ ++sCount;
+ lldebugs << "Constructing LLFilenameAndTask: " << sCount << llendl;
+ }
+ ~LLFilenameAndTask()
+ {
+ --sCount;
+ lldebugs << "Destroying LLFilenameAndTask: " << sCount << llendl;
+ }
+private:
+ LLFilenameAndTask(const LLFilenameAndTask& rhs);
+ const LLFilenameAndTask& operator=(const LLFilenameAndTask& rhs) const;
+#endif
+};
+
+#ifdef _DEBUG
+S32 LLFilenameAndTask::sCount = 0;
+#endif
+
+// static
+void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLUUID task_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_TaskID, task_id);
+ LLViewerObject* object = gObjectList.findObject(task_id);
+ if(object)
+ {
+ msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, object->mInventorySerialNum);
+ LLFilenameAndTask* ft = new LLFilenameAndTask;
+ ft->mTaskID = task_id;
+ msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, MAX_STRING, ft->mFilename);
+ if(!ft->mFilename[0])
+ {
+ lldebugs << "Task has no inventory" << llendl;
+ // mock up some inventory to make a drop target.
+ if(object->mInventory)
+ {
+ object->mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ object->mInventory = new InventoryObjectList();
+ }
+ LLPointer<LLInventoryObject> obj;
+ obj = new LLInventoryObject(object->mID, LLUUID::null,
+ LLAssetType::AT_CATEGORY,
+ "Contents");
+ object->mInventory->push_front(obj);
+ object->doInventoryCallback();
+ delete ft;
+ return;
+ }
+ gXferManager->requestFile(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ft->mFilename).c_str(),
+ ft->mFilename, LL_PATH_CACHE,
+ object->mRegionp->getHost(),
+ TRUE,
+ &LLViewerObject::processTaskInvFile,
+ (void**)ft,
+ LLXferManager::HIGH_PRIORITY);
+ }
+}
+
+void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code)
+{
+ LLFilenameAndTask* ft = (LLFilenameAndTask*)user_data;
+ LLViewerObject* object = NULL;
+ if(ft && (0 == error_code) &&
+ (object = gObjectList.findObject(ft->mTaskID)))
+ {
+ object->loadTaskInvFile(ft->mFilename);
+ }
+ else
+ {
+ // This Occurs When to requests were made, and the first one
+ // has already handled it.
+ lldebugs << "Problem loading task inventory. Return code: "
+ << error_code << llendl;
+ }
+ delete ft;
+}
+
+void LLViewerObject::loadTaskInvFile(const char* filename)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename);
+ llifstream ifs(filename_and_local_path.c_str());
+ if(ifs.good())
+ {
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ mInventory = new InventoryObjectList;
+ }
+ while(ifs.good())
+ {
+ ifs.getline(buffer, MAX_STRING);
+ sscanf(buffer, " %s", keyword);
+ if(0 == strcmp("inv_item", keyword))
+ {
+ LLPointer<LLInventoryObject> inv = new LLViewerInventoryItem;
+ inv->importLegacyStream(ifs);
+ mInventory->push_front(inv);
+ }
+ else if(0 == strcmp("inv_object", keyword))
+ {
+ LLPointer<LLInventoryObject> inv = new LLInventoryObject;
+ inv->importLegacyStream(ifs);
+ mInventory->push_front(inv);
+ }
+ else
+ {
+ llwarns << "Unknown token in inventory file '"
+ << keyword << "'" << llendl;
+ }
+ }
+ ifs.close();
+ LLFile::remove(filename_and_local_path.c_str());
+ }
+ else
+ {
+ llwarns << "unable to load task inventory: " << filename_and_local_path
+ << llendl;
+ }
+ doInventoryCallback();
+}
+
+void LLViewerObject::doInventoryCallback()
+{
+ for(LLInventoryCallbackInfo* info = mInventoryCallbacks.getFirstData();
+ info != NULL;
+ info = mInventoryCallbacks.getNextData())
+ {
+ if (info->mListener != NULL)
+ {
+ info->mListener->inventoryChanged(this,
+ mInventory,
+ mInventorySerialNum,
+ info->mInventoryData);
+ }
+ else
+ {
+ llinfos << "LLViewerObject::doInventoryCallback() deleting bad listener entry." << llendl;
+ mInventoryCallbacks.deleteCurrentData();
+ }
+ }
+ mInventoryPending = FALSE;
+}
+
+void LLViewerObject::removeInventory(const LLUUID& item_id)
+{
+ // close any associated floater properties
+ LLFloaterProperties::closeByID(item_id, mID);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->sendReliable(mRegionp->getHost());
+ deleteInventoryItem(item_id);
+ ++mInventorySerialNum;
+
+ // The viewer object should not refresh UI since this is a utility
+ // function. The UI functionality that called this method should
+ // refresh the views if necessary.
+ //gBuildView->refresh();
+}
+
+void LLViewerObject::updateInventory(
+ LLViewerInventoryItem* item,
+ U8 key,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ // This slices the object into what we're concerned about on the
+ // viewer. The simulator will take the permissions and transfer
+ // ownership.
+ LLPointer<LLViewerInventoryItem> task_item =
+ new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
+ item->getAssetUUID(), item->getType(),
+ item->getInventoryType(),
+ item->getName(), item->getDescription(),
+ item->getSaleInfo(),
+ item->getFlags(),
+ item->getCreationDate());
+ task_item->setTransactionID(item->getTransactionID());
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_UpdateData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addU8Fast(_PREHASH_Key, key);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ task_item->packMessage(msg);
+ msg->sendReliable(mRegionp->getHost());
+
+ // do the internal logic
+ doUpdateInventory(task_item, key, is_new);
+}
+
+LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id)
+{
+ LLInventoryObject* rv = NULL;
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getUUID() == item_id)
+ {
+ rv = *it;
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+void LLViewerObject::getInventoryContents(InventoryObjectList& objects)
+{
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it)
+ {
+ if ((*it)->getType() != LLAssetType::AT_CATEGORY)
+ {
+ objects.push_back(*it);
+ }
+ }
+ }
+}
+
+LLInventoryObject* LLViewerObject::getInventoryRoot()
+{
+ if (!mInventory || !mInventory->size())
+ {
+ return NULL;
+ }
+ return mInventory->back();
+}
+
+LLViewerInventoryItem* LLViewerObject::getInventoryItemByAsset(const LLUUID& asset_id)
+{
+ LLViewerInventoryItem* rv = NULL;
+ if(mInventory)
+ {
+ LLViewerInventoryItem* item = NULL;
+
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it)
+ {
+ LLInventoryObject* obj = *it;
+ if(obj->getType() != LLAssetType::AT_CATEGORY)
+ {
+ // *FIX: gank-ass down cast!
+ item = (LLViewerInventoryItem*)obj;
+ if(item->getAssetUUID() == asset_id)
+ {
+ rv = item;
+ break;
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent)
+{
+ if (getVolume())
+ { //volumes calculate pixel area and angle per face
+ return;
+ }
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 max_scale = getMaxScale();
+ F32 mid_scale = getMidScale();
+ F32 min_scale = getMinScale();
+
+ // IW: esitmate - when close to large objects, computing range based on distance from center is no good
+ // to try to get a min distance from face, subtract min_scale/2 from the range.
+ // This means we'll load too much detail sometimes, but that's better than not enough
+ // I don't think there's a better way to do this without calculating distance per-poly
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2;
+
+ if (range < 0.001f || isHUDAttachment()) // range == zero
+ {
+ mAppAngle = 180.f;
+ mPixelArea = (F32)gCamera->getScreenPixelArea();
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ F32 pixels_per_meter = gCamera->getPixelMeterRatio() / range;
+
+ mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale);
+ if (mPixelArea > gCamera->getScreenPixelArea())
+ {
+ mAppAngle = 180.f;
+ mPixelArea = (F32)gCamera->getScreenPixelArea();
+ }
+ }
+}
+
+BOOL LLViewerObject::updateLOD()
+{
+ return FALSE;
+}
+
+BOOL LLViewerObject::updateGeometry(LLDrawable *drawable)
+{
+ return TRUE;
+}
+
+LLDrawable* LLViewerObject::createDrawable(LLPipeline *pipeline)
+{
+ return NULL;
+}
+
+void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped)
+{
+ LLPrimitive::setScale(scale);
+ if (mDrawable.notNull())
+ {
+ //encompass completely sheared objects by taking
+ //the most extreme point possible (<1,1,0.5>)
+ mDrawable->setRadius(LLVector3(1,1,0.5f).scaleVec(scale).magVec());
+ updateDrawable(damped);
+ }
+
+ if( (LL_PCODE_VOLUME == getPCode()) && !isDead() )
+ {
+ if (permYouOwner() || (scale.magVecSquared() > (7.5f * 7.5f)) )
+ {
+ if (!mOnMap)
+ {
+ gObjectList.addToMap(this);
+ mOnMap = TRUE;
+ }
+ }
+ else
+ {
+ if (mOnMap)
+ {
+ gObjectList.removeFromMap(this);
+ mOnMap = FALSE;
+ }
+ }
+ }
+}
+
+void LLViewerObject::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+{
+ LLVector3 center = getRenderPosition();
+ F32 sz = llmin(mDrawable->getRadius(), 256.f);
+ LLVector3 size = LLVector3(sz,sz,sz);
+ newMin.setVec(center-size);
+ newMax.setVec(center+size);
+ mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
+}
+
+F32 LLViewerObject::getMaxScale() const
+{
+ return llmax(getScale().mV[VX],getScale().mV[VY], getScale().mV[VZ]);
+}
+
+F32 LLViewerObject::getMinScale() const
+{
+ return llmin(getScale().mV[0],getScale().mV[1],getScale().mV[2]);
+}
+
+F32 LLViewerObject::getMidScale() const
+{
+ if (getScale().mV[VX] < getScale().mV[VY])
+ {
+ if (getScale().mV[VY] < getScale().mV[VZ])
+ {
+ return getScale().mV[VY];
+ }
+ else if (getScale().mV[VX] < getScale().mV[VZ])
+ {
+ return getScale().mV[VZ];
+ }
+ else
+ {
+ return getScale().mV[VX];
+ }
+ }
+ else if (getScale().mV[VX] < getScale().mV[VZ])
+ {
+ return getScale().mV[VX];
+ }
+ else if (getScale().mV[VY] < getScale().mV[VZ])
+ {
+ return getScale().mV[VZ];
+ }
+ else
+ {
+ return getScale().mV[VY];
+ }
+}
+
+
+void LLViewerObject::updateTextures(LLAgent &agent)
+{
+}
+
+void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)
+{
+ if (isDead())
+ {
+ return;
+ }
+
+ S32 i;
+ S32 tex_count = getNumTEs();
+ for (i = 0; i < tex_count; i++)
+ {
+ getTEImage(i)->setBoostLevel(LLViewerImage::BOOST_SELECTED);
+ }
+
+ if (boost_children)
+ {
+ S32 num_children = mChildList.size();
+ for (i = 0; i < num_children; i++)
+ {
+ LLViewerObject *childp = mChildList[i];
+ childp->boostTexturePriority();
+ }
+ }
+}
+
+
+void LLViewerObject::setLineWidthForWindowSize(S32 window_width)
+{
+ if (window_width < 700)
+ {
+ LLUI::setLineWidth(2.0f);
+ }
+ else if (window_width < 1100)
+ {
+ LLUI::setLineWidth(3.0f);
+ }
+ else if (window_width < 2000)
+ {
+ LLUI::setLineWidth(4.0f);
+ }
+ else
+ {
+ // _damn_, what a nice monitor!
+ LLUI::setLineWidth(5.0f);
+ }
+}
+
+void LLViewerObject::increaseArrowLength()
+{
+/* ???
+ if (mAxisArrowLength == 50)
+ {
+ mAxisArrowLength = 100;
+ }
+ else
+ {
+ mAxisArrowLength = 150;
+ }
+*/
+}
+
+
+void LLViewerObject::decreaseArrowLength()
+{
+/* ???
+ if (mAxisArrowLength == 150)
+ {
+ mAxisArrowLength = 100;
+ }
+ else
+ {
+ mAxisArrowLength = 50;
+ }
+*/
+}
+
+// Culled from newsim LLTask::addNVPair
+void LLViewerObject::addNVPair(const char* data)
+{
+ // cout << "LLViewerObject::addNVPair() with ---" << data << "---" << endl;
+ LLNameValue *nv = new LLNameValue(data);
+
+// char splat[MAX_STRING];
+// temp->printNameValue(splat);
+// llinfos << "addNVPair " << splat << llendl;
+
+ name_value_map_t::iterator iter = mNameValuePairs.find(nv->mName);
+ if (iter != mNameValuePairs.end())
+ {
+ LLNameValue* foundnv = iter->second;
+ if (foundnv->mClass != NVC_READ_ONLY)
+ {
+ delete foundnv;
+ mNameValuePairs.erase(iter);
+ }
+ else
+ {
+ delete nv;
+// llinfos << "Trying to write to Read Only NVPair " << temp->mName << " in addNVPair()" << llendl;
+ return;
+ }
+ }
+ mNameValuePairs[nv->mName] = nv;
+}
+
+BOOL LLViewerObject::removeNVPair(const char *name)
+{
+ char* canonical_name = gNVNameTable.addString(name);
+
+ lldebugs << "LLViewerObject::removeNVPair(): " << name << llendl;
+
+ name_value_map_t::iterator iter = mNameValuePairs.find(canonical_name);
+ if (iter != mNameValuePairs.end())
+ {
+ if( mRegionp )
+ {
+ LLNameValue* nv = iter->second;
+/*
+ std::string buffer = nv->printNameValue();
+ gMessageSystem->newMessageFast(_PREHASH_RemoveNameValuePair);
+ gMessageSystem->nextBlockFast(_PREHASH_TaskData);
+ gMessageSystem->addUUIDFast(_PREHASH_ID, mID);
+
+ gMessageSystem->nextBlockFast(_PREHASH_NameValueData);
+ gMessageSystem->addStringFast(_PREHASH_NVPair, buffer.c_str());
+
+ gMessageSystem->sendReliable( mRegionp->getHost() );
+*/
+ // Remove the NV pair from the local list.
+ delete nv;
+ mNameValuePairs.erase(iter);
+ return TRUE;
+ }
+ else
+ {
+ lldebugs << "removeNVPair - No region for object" << llendl;
+ }
+ }
+ return FALSE;
+}
+
+
+LLNameValue *LLViewerObject::getNVPair(const char *name) const
+{
+ char *canonical_name;
+
+ canonical_name = gNVNameTable.addString(name);
+
+ // If you access a map with a name that isn't in it, it will add the name and a null pointer.
+ // So first check if the data is in the map.
+ name_value_map_t::const_iterator iter = mNameValuePairs.find(canonical_name);
+ if (iter != mNameValuePairs.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void LLViewerObject::updatePositionCaches() const
+{
+ if (!isRoot())
+ {
+ mPositionRegion = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+ else
+ {
+ mPositionRegion = getPosition();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+}
+
+const LLVector3d LLViewerObject::getPositionGlobal() const
+{
+ LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion());;
+
+ if (isAttachment())
+ {
+ position_global = gAgent.getPosGlobalFromAgent(getRenderPosition());
+ }
+
+ return position_global;
+}
+
+const LLVector3 &LLViewerObject::getPositionAgent() const
+{
+ if (mRegionp)
+ {
+ if (mDrawable.notNull() && (!mDrawable->isRoot() && getParent()))
+ {
+ // Don't return cached position if you have a parent, recalc (until all dirtying is done correctly.
+ LLVector3 position_region;
+ position_region = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(position_region);
+ }
+ else
+ {
+ mPositionAgent = mRegionp->getPosAgentFromRegion(getPosition());
+ }
+ }
+ return mPositionAgent;
+}
+
+const LLVector3 &LLViewerObject::getPositionRegion() const
+{
+ if (!isRoot())
+ {
+ LLViewerObject *parent = (LLViewerObject *)getParent();
+ mPositionRegion = parent->getPositionRegion() + (getPosition() * parent->getRotation());
+ }
+ return mPositionRegion;
+}
+
+const LLVector3 LLViewerObject::getPositionEdit() const
+{
+ if (isRootEdit())
+ {
+ return getPosition();
+ }
+ else
+ {
+ LLViewerObject *parent = (LLViewerObject *)getParent();
+ LLVector3 position_edit = parent->getPositionEdit() + getPosition() * parent->getRotationEdit();
+ return position_edit;
+ }
+}
+
+const LLVector3 LLViewerObject::getRenderPosition() const
+{
+ if (mDrawable.isNull() || mDrawable->getGeneration() < 0)
+ {
+ return getPositionAgent();
+ }
+ else
+ {
+ if (isAvatar())
+ {
+ if (isRoot())
+ {
+ return mDrawable->getPositionAgent();
+ }
+ else
+ {
+ return getPosition() * mDrawable->getParent()->getRenderMatrix();
+ }
+ }
+
+ return mDrawable->getPositionAgent();
+ }
+}
+
+const LLVector3 LLViewerObject::getPivotPositionAgent() const
+{
+ return getRenderPosition();
+}
+
+const LLQuaternion LLViewerObject::getRenderRotation() const
+{
+ LLQuaternion ret;
+ if (mDrawable.isNull() || mDrawable->isStatic())
+ {
+ ret = getRotationEdit();
+ }
+ else
+ {
+ if (!mDrawable->isRoot())
+ {
+ ret = getRotation() * LLQuaternion(mDrawable->getParent()->getWorldMatrix());
+ }
+ else
+ {
+ ret = LLQuaternion(mDrawable->getWorldMatrix());
+ }
+ }
+
+ return ret;
+}
+
+const LLMatrix4 LLViewerObject::getRenderMatrix() const
+{
+ return mDrawable->getWorldMatrix();
+}
+
+const LLQuaternion LLViewerObject::getRotationRegion() const
+{
+ LLQuaternion global_rotation = getRotation();
+ if (!((LLXform *)this)->isRoot())
+ {
+ global_rotation = global_rotation * getParent()->getRotation();
+ }
+ return global_rotation;
+}
+
+const LLQuaternion LLViewerObject::getRotationEdit() const
+{
+ LLQuaternion global_rotation = getRotation();
+ if (!((LLXform *)this)->isRootEdit())
+ {
+ global_rotation = global_rotation * getParent()->getRotation();
+ }
+ return global_rotation;
+}
+
+void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, BOOL damped )
+{
+ if (isAttachment())
+ {
+ LLVector3 new_pos = mRegionp->getPosRegionFromGlobal(pos_global);
+ if (isRootEdit())
+ {
+ new_pos -= mDrawable->mXform.getParent()->getWorldPosition();
+ LLQuaternion world_rotation = mDrawable->mXform.getParent()->getWorldRotation();
+ new_pos = new_pos * ~world_rotation;
+ }
+ else
+ {
+ LLViewerObject* parentp = (LLViewerObject*)getParent();
+ new_pos -= parentp->getPositionAgent();
+ new_pos = new_pos * ~parentp->getRotationRegion();
+ }
+ LLViewerObject::setPosition(new_pos);
+
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+ else
+ {
+ if( isRoot() )
+ {
+ setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global));
+ }
+ else
+ {
+ // the relative position with the parent is not constant
+ LLViewerObject* parent = (LLViewerObject *)getParent();
+ //RN: this assumes we are only calling this function from the edit tools
+ gPipeline.updateMoveNormalAsync(parent->mDrawable);
+
+ LLVector3 pos_local = mRegionp->getPosRegionFromGlobal(pos_global) - parent->getPositionRegion();
+ pos_local = pos_local * ~parent->getRotationRegion();
+ LLViewerObject::setPosition( pos_local );
+ }
+ }
+ //RN: assumes we always want to snap the object when calling this function
+ gPipeline.updateMoveNormalAsync(mDrawable);
+}
+
+void LLViewerObject::setPosition(const LLVector3 &pos, BOOL damped)
+{
+ if (getPosition() != pos)
+ {
+ setChanged(TRANSLATED | SILHOUETTE);
+ }
+
+ LLXform::setPosition(pos);
+ updateDrawable(damped);
+ if (isRoot())
+ {
+ // position caches need to be up to date on root objects
+ updatePositionCaches();
+ }
+}
+
+void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, BOOL damped)
+{
+ if (isAttachment())
+ {
+ if (isRootEdit())
+ {
+ LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global);
+ newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition();
+
+ LLQuaternion invWorldRotation = mDrawable->mXform.getParent()->getWorldRotation();
+ invWorldRotation.transQuat();
+
+ newPos = newPos * invWorldRotation;
+ LLViewerObject::setPosition(newPos);
+ }
+ else
+ {
+ // assumes parent is root editable (root of attachment)
+ LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global);
+ newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition();
+ LLVector3 delta_pos = newPos - getPosition();
+
+ LLQuaternion invRotation = mDrawable->getRotation();
+ invRotation.transQuat();
+
+ delta_pos = delta_pos * invRotation;
+
+ //FIXME: is this right? Shouldn't we be calling the LLViewerObject version of setPosition?
+ LLVector3 old_pos = mDrawable->mXform.getParent()->getPosition();
+ mDrawable->mXform.getParent()->setPosition(old_pos + delta_pos);
+ setChanged(TRANSLATED | SILHOUETTE);
+ }
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+ else
+ {
+ if (isRoot())
+ {
+ setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global));
+ }
+ else
+ {
+ // the relative position with the parent is constant, but the parent's position needs to be changed
+ LLVector3d position_offset;
+ position_offset.setVec(getPosition()*getParent()->getRotation());
+ LLVector3d new_pos_global = pos_global - position_offset;
+ ((LLViewerObject *)getParent())->setPositionGlobal(new_pos_global);
+ }
+ }
+ updateDrawable(damped);
+}
+
+
+void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, BOOL damped)
+{
+ // Set position relative to parent, if no parent, relative to region
+ if (!isRoot())
+ {
+ LLViewerObject::setPosition(pos_parent);
+ updateDrawable(damped);
+ }
+ else
+ {
+ setPositionRegion(pos_parent, damped);
+ }
+}
+
+void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, BOOL damped)
+{
+ if (!isRootEdit())
+ {
+ LLViewerObject* parent = (LLViewerObject*) getParent();
+ LLViewerObject::setPosition((pos_region-parent->getPositionRegion())*~parent->getRotationRegion());
+ }
+ else
+ {
+ LLViewerObject::setPosition(pos_region);
+ mPositionRegion = pos_region;
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+}
+
+void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, BOOL damped)
+{
+ LLVector3 pos_region = getRegion()->getPosRegionFromAgent(pos_agent);
+ setPositionRegion(pos_region, damped);
+}
+
+// identical to setPositionRegion() except it checks for child-joints
+// and doesn't also move the joint-parent
+// TODO -- implement similar intelligence for joint-parents toward
+// their joint-children
+void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped)
+{
+ if (!isRootEdit())
+ {
+ // the relative position with the parent is constant, but the parent's position needs to be changed
+ LLVector3 position_offset = getPosition() * getParent()->getRotation();
+
+ ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset);
+ }
+ else if (isJointChild())
+ {
+ // compute new parent-relative position
+ LLViewerObject *parent = (LLViewerObject *) getParent();
+ LLQuaternion inv_parent_rot = parent->getRotation();
+ inv_parent_rot.transQuat();
+ LLVector3 pos_parent = (pos_edit - parent->getPositionRegion()) * inv_parent_rot;
+ LLViewerObject::setPosition(pos_parent);
+ }
+ else
+ {
+ LLViewerObject::setPosition(pos_edit);
+ mPositionRegion = pos_edit;
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+ updateDrawable(damped);
+}
+
+
+LLViewerObject* LLViewerObject::getRootEdit() const
+{
+ const LLViewerObject* root = this;
+ while (root->mParent
+ && !(root->mJointInfo
+ || ((LLViewerObject*)root->mParent)->isAvatar()) )
+ {
+ root = (LLViewerObject*)root->mParent;
+ }
+ return (LLViewerObject*)root;
+}
+
+U8 LLViewerObject::getMediaType() const
+{
+ if (mMedia)
+ {
+ return mMedia->mMediaType;
+ }
+ else
+ {
+ return LLViewerObject::MEDIA_TYPE_NONE;
+ }
+}
+
+void LLViewerObject::setMediaType(U8 media_type)
+{
+ if (!mMedia)
+ {
+ // JAMESDEBUG TODO what if we don't have a media pointer?
+ }
+ else if (mMedia->mMediaType != media_type)
+ {
+ mMedia->mMediaType = media_type;
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ gMediaList->updatedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+const LLString& LLViewerObject::getMediaURL() const
+{
+ if (mMedia)
+ {
+ return mMedia->mMediaURL;
+ }
+ else
+ {
+ return LLString::null;
+ }
+}
+
+void LLViewerObject::setMediaURL(const LLString& media_url)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ if (!mMedia)
+ {
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ if (gMediaList)
+ {
+ gMediaList->addedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ gMediaList->updatedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+BOOL LLViewerObject::getMediaPassedWhitelist() const
+{
+ if (mMedia)
+ {
+ return mMedia->mPassedWhitelist;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLViewerObject::setMediaPassedWhitelist(BOOL passed)
+{
+ if (mMedia)
+ {
+ mMedia->mPassedWhitelist = passed;
+ }
+}
+
+BOOL LLViewerObject::setMaterial(const U8 material)
+{
+ BOOL res = LLPrimitive::setMaterial(material);
+ if (res)
+ {
+ setChanged(TEXTURE);
+ }
+ return res;
+}
+
+void LLViewerObject::setNumTEs(const U8 num_tes)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ U32 i;
+ if (num_tes != getNumTEs())
+ {
+ if (num_tes)
+ {
+ LLPointer<LLViewerImage> *new_images;
+ new_images = new LLPointer<LLViewerImage>[num_tes];
+ for (i = 0; i < num_tes; i++)
+ {
+ if (i < getNumTEs())
+ {
+ new_images[i] = mTEImages[i];
+ }
+ else if (getNumTEs())
+ {
+ new_images[i] = mTEImages[getNumTEs()-1];
+ }
+ else
+ {
+ new_images[i] = NULL;
+ }
+ }
+
+ deleteTEImages();
+
+ mTEImages = new_images;
+ }
+ else
+ {
+ deleteTEImages();
+ }
+ LLPrimitive::setNumTEs(num_tes);
+ setChanged(TEXTURE);
+
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+void LLViewerObject::sendMaterialUpdate() const
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessageFast(_PREHASH_ObjectMaterial);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addU8Fast(_PREHASH_Material, getMaterial() );
+ gMessageSystem->sendReliable( regionp->getHost() );
+
+}
+
+// formerly send_object_rotation
+void LLViewerObject::sendRotationUpdate() const
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessageFast(_PREHASH_ObjectRotation);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID);
+ gMessageSystem->addQuatFast(_PREHASH_Rotation, getRotationEdit());
+ //llinfos << "Sent rotation " << getRotationEdit() << llendl;
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+// formerly send_object_position_global
+void LLViewerObject::sendPositionUpdate() const
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectPosition);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addVector3Fast(_PREHASH_Position, getPositionRegion());
+ LLViewerRegion* regionp = getRegion();
+ gMessageSystem->sendReliable(regionp->getHost());
+}
+
+
+//formerly send_object_scale
+void LLViewerObject::sendScaleUpdate()
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectScale);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addVector3Fast(_PREHASH_Scale, (getScale()));
+
+ LLViewerRegion *regionp = getRegion();
+ gMessageSystem->sendReliable(regionp->getHost() );
+}
+
+
+//formerly send_object_shape(LLViewerObject *object)
+void LLViewerObject::sendShapeUpdate()
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectShape);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+
+ LLVolumeMessage::packVolumeParams(&getVolume()->getParams(), gMessageSystem);
+
+ LLViewerRegion *regionp = getRegion();
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+
+void LLViewerObject::sendTEUpdate() const
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectImage);
+
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ if (mMedia)
+ {
+ msg->addString("MediaURL", mMedia->mMediaURL);
+ }
+ else
+ {
+ msg->addString("MediaURL", NULL);
+ }
+
+ // JAMESDEBUG TODO send media type
+
+ packTEMessage(msg);
+
+ LLViewerRegion *regionp = getRegion();
+ msg->sendReliable( regionp->getHost() );
+}
+
+void LLViewerObject::setTE(const U8 te, const LLTextureEntry &texture_entry)
+{
+ LLPrimitive::setTE(te, texture_entry);
+// JAMESDEBUG This doesn't work, don't get any textures.
+// if (mDrawable.notNull() && mDrawable->isVisible())
+// {
+ const LLUUID& image_id = getTE(te)->getID();
+ mTEImages[te] = gImageList.getImage(image_id);
+// }
+}
+
+void LLViewerObject::setTEImage(const U8 te, LLViewerImage *imagep)
+{
+ if (mTEImages[te] != imagep)
+ {
+ mTEImages[te] = imagep;
+ LLPrimitive::setTETexture(te, imagep->getID());
+ setChanged(TEXTURE);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+
+S32 LLViewerObject::setTETextureCore(const U8 te, const LLUUID& uuid, LLHost host)
+{
+ S32 retval = 0;
+ if (uuid != getTE(te)->getID() ||
+ uuid == LLUUID::null)
+ {
+ retval = LLPrimitive::setTETexture(te, uuid);
+ mTEImages[te] = gImageList.getImageFromHost(uuid, host);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid)
+{
+ // Invalid host == get from the agent's sim
+ return setTETextureCore(te, uuid, LLHost::invalid);
+}
+
+
+S32 LLViewerObject::setTEColor(const U8 te, const LLColor4 &color)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (color != tep->getColor())
+ {
+ retval = LLPrimitive::setTEColor(te, color);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ // These should only happen on updates which are not the initial update.
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEBumpmap(const U8 te, const U8 bump)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (bump != tep->getBumpmap())
+ {
+ retval = LLPrimitive::setTEBumpmap(te, bump);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTETexGen(const U8 te, const U8 texgen)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (texgen != tep->getTexGen())
+ {
+ retval = LLPrimitive::setTETexGen(te, texgen);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEShiny(const U8 te, const U8 shiny)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (shiny != tep->getShiny())
+ {
+ retval = LLPrimitive::setTEShiny(te, shiny);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (fullbright != tep->getFullbright())
+ {
+ retval = LLPrimitive::setTEFullbright(te, fullbright);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ // JAMESDEBUG this might need work for media type
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (media_flags != tep->getMediaFlags())
+ {
+ retval = LLPrimitive::setTEMediaFlags(te, media_flags);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, TRUE);
+ gPipeline.markTextured(mDrawable);
+ // JC - probably only need this if changes texture coords
+ //gPipeline.markRebuild(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ S32 retval = 0;
+ retval = LLPrimitive::setTEScale(te, s, t);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEScaleS(const U8 te, const F32 s)
+{
+ S32 retval = LLPrimitive::setTEScaleS(te, s);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEScaleT(const U8 te, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEScaleT(te, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffset(const U8 te, const F32 s, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEOffset(te, s, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffsetS(const U8 te, const F32 s)
+{
+ S32 retval = LLPrimitive::setTEOffsetS(te, s);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffsetT(const U8 te, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEOffsetT(te, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTERotation(const U8 te, const F32 r)
+{
+ S32 retval = LLPrimitive::setTERotation(te, r);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+
+LLViewerImage *LLViewerObject::getTEImage(const U8 face) const
+{
+// llassert(mTEImages);
+
+ if (face < getNumTEs())
+ {
+ LLViewerImage* image = mTEImages[face];
+ if (image)
+ {
+ return image;
+ }
+ else
+ {
+ return (LLViewerImage*)((LLImageGL*)LLViewerImage::sDefaultImagep);
+ }
+ }
+
+ llerrs << "Requested invalid face!" << llendl;
+
+ return NULL;
+}
+
+
+void LLViewerObject::fitFaceTexture(const U8 face)
+{
+ llinfos << "fitFaceTexture not implemented" << llendl;
+}
+
+
+LLBBox LLViewerObject::getBoundingBoxAgent() const
+{
+ LLVector3 position_agent;
+ LLQuaternion rot;
+ LLViewerObject* root_edit = (LLViewerObject*)getRootEdit();
+ LLViewerObject* avatar_parent = (LLViewerObject*)root_edit->getParent();
+ if (avatar_parent && avatar_parent->isAvatar() && root_edit->mDrawable.notNull())
+ {
+ LLXform* parent_xform = root_edit->mDrawable->getXform()->getParent();
+ position_agent = (getPositionEdit() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ rot = getRotationEdit() * parent_xform->getWorldRotation();
+ }
+ else
+ {
+ position_agent = getPositionAgent();
+ rot = getRotationRegion();
+ }
+
+ return LLBBox( position_agent, rot, getScale() * -0.5f, getScale() * 0.5f );
+}
+
+U32 LLViewerObject::getNumVertices() const
+{
+ U32 num_vertices = 0;
+ if (mDrawable.notNull())
+ {
+ S32 i, num_faces;
+ num_faces = mDrawable->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ num_vertices += mDrawable->getFace(i)->getGeomCount();
+ }
+ }
+ return num_vertices;
+}
+
+U32 LLViewerObject::getNumIndices() const
+{
+ U32 num_indices = 0;
+ if (mDrawable.notNull())
+ {
+ S32 i, num_faces;
+ num_faces = mDrawable->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ num_indices += mDrawable->getFace(i)->getIndicesCount();
+ }
+ }
+ return num_indices;
+}
+
+// Find the number of instances of this object's inventory that are of the given type
+S32 LLViewerObject::countInventoryContents(LLAssetType::EType type)
+{
+ S32 count = 0;
+ if( mInventory )
+ {
+ InventoryObjectList::const_iterator it = mInventory->begin();
+ InventoryObjectList::const_iterator end = mInventory->end();
+ for( ; it != end ; ++it )
+ {
+ if( (*it)->getType() == type )
+ {
+ ++count;
+ }
+ }
+ }
+ return count;
+}
+
+
+void LLViewerObject::setCanSelect(BOOL canSelect)
+{
+ mbCanSelect = canSelect;
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->mbCanSelect = canSelect;
+ }
+}
+
+void LLViewerObject::setDebugText(const std::string &utf8text)
+{
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1);
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+ mText->setColor(LLColor4::white);
+ mText->setStringUTF8(utf8text);
+ mText->setZCompare(FALSE);
+ mText->setDoFade(FALSE);
+ updateText();
+}
+
+void LLViewerObject::setIcon(LLViewerImage* icon_image)
+{
+ if (!mIcon)
+ {
+ mIcon = (LLHUDIcon *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_ICON);
+ mIcon->setSourceObject(this);
+ mIcon->setImage(icon_image);
+ //FIXME: make this user configurable
+ mIcon->setScale(0.03f);
+ }
+ else
+ {
+ mIcon->restartLifeTimer();
+ }
+}
+
+void LLViewerObject::clearIcon()
+{
+ if (mIcon)
+ {
+ mIcon = NULL;
+ }
+}
+
+LLViewerObject* LLViewerObject::getSubParent()
+{
+ if (isJointChild())
+ {
+ return this;
+ }
+ return (LLViewerObject*) getParent();
+}
+
+const LLViewerObject* LLViewerObject::getSubParent() const
+{
+ if (isJointChild())
+ {
+ return this;
+ }
+ return (const LLViewerObject*) getParent();
+}
+
+BOOL LLViewerObject::isOnMap()
+{
+ return mOnMap;
+}
+
+
+void LLViewerObject::updateText()
+{
+ if (!isDead())
+ {
+ if (mText.notNull())
+ {
+ LLVector3 up_offset(0,0,0);
+ up_offset.mV[2] = getScale().mV[VZ]*0.6f;
+
+ if (mDrawable.notNull())
+ {
+ mText->setPositionAgent(getRenderPosition() + up_offset);
+ }
+ else
+ {
+ mText->setPositionAgent(getPositionAgent() + up_offset);
+ }
+ }
+ }
+}
+
+BOOL LLViewerObject::isParticleSource() const
+{
+ return !mPartSourcep.isNull() && !mPartSourcep->isDead();
+}
+
+void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& owner_id)
+{
+ if (!mPartSourcep.isNull() && mPartSourcep->isDead())
+ {
+ mPartSourcep = NULL;
+ }
+ if (mPartSourcep)
+ {
+ // If we've got one already, just update the existing source (or remove it)
+ if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, block_num))
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ }
+ else
+ {
+ //If the owner is muted, don't create the system
+ if(gMuteListp->isMuted(owner_id)) return;
+ LLViewerPartSourceScript *pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num);
+
+ // We need to be able to deal with a particle source that hasn't changed, but still got an update!
+ if (pss)
+ {
+// llinfos << "Making particle system with owner " << owner_id << llendl;
+ pss->setOwnerUUID(owner_id);
+ mPartSourcep = pss;
+ gWorldPointer->mPartSim.addPartSource(pss);
+ }
+ }
+ if (mPartSourcep)
+ {
+ if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID)
+ {
+ LLViewerImage* image;
+ if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null)
+ {
+ LLUUID id(gViewerArt.getString("pixiesmall.tga"));
+ image = gImageList.getImage(id);
+ }
+ else
+ {
+ image = gImageList.getImage(mPartSourcep->mPartSysData.mPartImageID);
+ }
+ mPartSourcep->setImage(image);
+ }
+ }
+}
+
+void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id)
+{
+ if (!mPartSourcep.isNull() && mPartSourcep->isDead())
+ {
+ mPartSourcep = NULL;
+ }
+ if (mPartSourcep)
+ {
+ // If we've got one already, just update the existing source (or remove it)
+ if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, dp))
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ }
+ else
+ {
+ //If the owner is muted, don't create the system
+ if(gMuteListp->isMuted(owner_id)) return;
+ LLViewerPartSourceScript *pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp);
+
+ // We need to be able to deal with a particle source that hasn't changed, but still got an update!
+ if (pss)
+ {
+// llinfos << "Making particle system with owner " << owner_id << llendl;
+ pss->setOwnerUUID(owner_id);
+ mPartSourcep = pss;
+ gWorldPointer->mPartSim.addPartSource(pss);
+ }
+ }
+ if (mPartSourcep)
+ {
+ if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID)
+ {
+ LLViewerImage* image;
+ if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null)
+ {
+ LLUUID id(gViewerArt.getString("pixiesmall.tga"));
+ image = gImageList.getImage(id);
+ }
+ else
+ {
+ image = gImageList.getImage(mPartSourcep->mPartSysData.mPartImageID);
+ }
+ mPartSourcep->setImage(image);
+ }
+ }
+}
+
+// virtual
+void LLViewerObject::updateDrawable(BOOL force_damped)
+{
+ if (mDrawable.notNull() &&
+ !mDrawable->isState(LLDrawable::ON_MOVE_LIST) &&
+ isChanged(MOVED) &&
+ !isAvatar())
+ {
+ mLastUpdateFrame = LLFrameTimer::getFrameCount();
+ BOOL damped_motion =
+ !isChanged(SHIFTED) && // not shifted between regions this frame and...
+ (force_damped || // ...forced into damped motion by application logic or...
+ (!isSelected() && // ...not selected and...
+ (mDrawable->isRoot() || // ... is root or ...
+ !((LLViewerObject*)getParent())->isSelected()) && // ... parent is not selected and ...
+ getPCode() == LL_PCODE_VOLUME && // ...is a volume object and...
+ getVelocity().isExactlyZero() && // ...is not moving physically and...
+ mDrawable->getGeneration() != -1) // ...was not created this frame.
+ );
+ gPipeline.markMoved(mDrawable, damped_motion);
+ }
+ clearChanged(SHIFTED);
+}
+
+// virtual, overridden by LLVOVolume
+F32 LLViewerObject::getVObjRadius() const
+{
+ return mDrawable.notNull() ? mDrawable->getRadius() : 0.f;
+}
+
+void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const U8 flags)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ if (audio_uuid.isNull())
+ {
+ if (mAudioSourcep && mAudioSourcep->isLoop() && !mAudioSourcep->hasPendingPreloads())
+ {
+ // We don't clear the sound if it's a loop, it'll go away on its own.
+ // At least, this appears to be how the scripts work.
+ // The attached sound ID is set to NULL to avoid it playing back when the
+ // object rezzes in on non-looping sounds.
+ //llinfos << "Clearing attached sound " << mAudioSourcep->getCurrentData()->getID() << llendl;
+ gAudiop->cleanupAudioSource(mAudioSourcep);
+ mAudioSourcep = NULL;
+ }
+ else if (mAudioSourcep)
+ {
+ if (mAudioSourcep->isLoop())
+ {
+ // Just shut off the sound
+ mAudioSourcep->play(LLUUID::null);
+ }
+ }
+ return;
+ }
+ if (flags & LL_SOUND_FLAG_LOOP)
+ {
+ if (mAudioSourcep && mAudioSourcep->isLoop() && mAudioSourcep->getCurrentData())
+ {
+ if (mAudioSourcep->getCurrentData()->getID() == audio_uuid)
+ {
+ //llinfos << "Already playing this sound on a loop, ignoring" << llendl;
+ return;
+ }
+ }
+ }
+
+ getAudioSource(owner_id);
+
+ if (mAudioSourcep)
+ {
+ mAudioSourcep->setGain(gain);
+ mAudioSourcep->setLoop((flags & LL_SOUND_FLAG_LOOP) ? TRUE : FALSE);
+ mAudioSourcep->setSyncMaster((flags & LL_SOUND_FLAG_SYNC_MASTER) ? TRUE : FALSE);
+ mAudioSourcep->setSyncSlave((flags & LL_SOUND_FLAG_SYNC_SLAVE) ? TRUE : FALSE);
+ mAudioSourcep->setQueueSounds((flags & LL_SOUND_FLAG_QUEUE) ? TRUE : FALSE);
+ //llinfos << "Playing attached sound " << audio_uuid << llendl;
+ mAudioSourcep->play(audio_uuid);
+ }
+}
+
+LLAudioSource *LLViewerObject::getAudioSource(const LLUUID& owner_id)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ if (!mAudioSourcep)
+ {
+ // Arbitrary low gain for a sound that's not playing.
+ // This is used for sound preloads, for example.
+ LLAudioSourceVO *asvop = new LLAudioSourceVO(mID, owner_id, 0.01f, this);
+
+ mAudioSourcep = asvop;
+ if(gAudiop) gAudiop->addAudioSource(asvop);
+ }
+
+ return mAudioSourcep;
+}
+
+void LLViewerObject::adjustAudioGain(const F32 gain)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ if (!mAudioSourcep)
+ {
+ return;
+ }
+ mAudioSourcep->setGain(gain);
+}
+
+//----------------------------------------------------------------------------
+
+bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param)
+ {
+ param->data->unpack(*dp);
+ param->in_use = TRUE;
+ parameterChanged(param_type, param->data, TRUE, false);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 param_type)
+{
+ LLNetworkData* new_block = NULL;
+ switch (param_type)
+ {
+ case LLNetworkData::PARAMS_FLEXIBLE:
+ {
+ new_block = new LLFlexibleObjectData();
+ break;
+ }
+ case LLNetworkData::PARAMS_LIGHT:
+ {
+ new_block = new LLLightParams();
+ break;
+ }
+ default:
+ {
+ llinfos << "Unknown param type." << llendl;
+ break;
+ }
+ };
+
+ if (new_block)
+ {
+ ExtraParameter* new_entry = new ExtraParameter;
+ new_entry->data = new_block;
+ new_entry->in_use = false; // not in use yet
+ mExtraParameterList[param_type] = new_entry;
+ return new_entry;
+ }
+ return NULL;
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntry(U16 param_type) const
+{
+ std::map<U16, ExtraParameter*>::const_iterator itor = mExtraParameterList.find(param_type);
+ if (itor != mExtraParameterList.end())
+ {
+ return itor->second;
+ }
+ return NULL;
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntryCreate(U16 param_type)
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (!param)
+ {
+ param = createNewParameterEntry(param_type);
+ }
+ return param;
+}
+
+LLNetworkData* LLViewerObject::getParameterEntry(U16 param_type) const
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ return param->data;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+BOOL LLViewerObject::getParameterEntryInUse(U16 param_type) const
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ return param->in_use;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+bool LLViewerObject::setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param)
+ {
+ if (param->in_use && new_value == *(param->data))
+ {
+ return false;
+ }
+ param->in_use = true;
+ param->data->copy(new_value);
+ parameterChanged(param_type, param->data, TRUE, local_origin);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Assumed to be called locally
+// If in_use is TRUE, will crate a new extra parameter if none exists.
+// Should always return true.
+bool LLViewerObject::setParameterEntryInUse(U16 param_type, BOOL in_use, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param->in_use != in_use)
+ {
+ param->in_use = in_use;
+ parameterChanged(param_type, param->data, in_use, local_origin);
+ return true;
+ }
+ return false;
+}
+
+void LLViewerObject::parameterChanged(U16 param_type, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ parameterChanged(param_type, param->data, param->in_use, local_origin);
+ }
+}
+
+void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin)
+{
+ if (local_origin)
+ {
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+
+ // Change happened on the viewer. Send the change up
+ U8 tmp[MAX_OBJECT_PARAMS_SIZE];
+ LLDataPackerBinaryBuffer dpb(tmp, MAX_OBJECT_PARAMS_SIZE);
+ if (data->pack(dpb))
+ {
+ U32 datasize = (U32)dpb.getCurrentSize();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectExtraParams);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+
+ msg->addU16Fast(_PREHASH_ParamType, param_type);
+ msg->addBOOLFast(_PREHASH_ParamInUse, in_use);
+
+ msg->addU32Fast(_PREHASH_ParamSize, datasize);
+ msg->addBinaryDataFast(_PREHASH_ParamData, tmp, datasize);
+
+ msg->sendReliable( regionp->getHost() );
+ }
+ else
+ {
+ llwarns << "Failed to send object extra parameters: " << param_type << llendl;
+ }
+ }
+}
+
+void LLViewerObject::setDrawableState(U32 state, BOOL recursive)
+{
+ if (mDrawable)
+ {
+ mDrawable->setState(state);
+ }
+ if (recursive)
+ {
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->setDrawableState(state, recursive);
+ }
+ }
+}
+
+void LLViewerObject::clearDrawableState(U32 state, BOOL recursive)
+{
+ if (mDrawable)
+ {
+ mDrawable->clearState(state);
+ }
+ if (recursive)
+ {
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->clearDrawableState(state, recursive);
+ }
+ }
+}
+
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// RN: these functions assume a 2-level hierarchy
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+// Owned by anyone?
+BOOL LLViewerObject::permAnyOwner() const
+{
+ if (isRootEdit())
+ {
+ return ((mFlags & FLAGS_OBJECT_ANY_OWNER) != 0);
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permAnyOwner();
+ }
+}
+// Owned by this viewer?
+BOOL LLViewerObject::permYouOwner() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_YOU_OWNER) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permYouOwner();
+ }
+}
+
+// Owned by a group?
+BOOL LLViewerObject::permGroupOwner() const
+{
+ if (isRootEdit())
+ {
+ return ((mFlags & FLAGS_OBJECT_GROUP_OWNED) != 0);
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permGroupOwner();
+ }
+}
+
+// Can the owner edit
+BOOL LLViewerObject::permOwnerModify() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_OWNER_MODIFY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permOwnerModify();
+ }
+}
+
+// Can edit
+BOOL LLViewerObject::permModify() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_MODIFY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permModify();
+ }
+}
+
+// Can copy
+BOOL LLViewerObject::permCopy() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_COPY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permCopy();
+ }
+}
+
+// Can move
+BOOL LLViewerObject::permMove() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_MOVE) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permMove();
+ }
+}
+
+// Can be transferred
+BOOL LLViewerObject::permTransfer() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_TRANSFER) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permTransfer();
+ }
+}
+
+// Can only open objects that you own, or that someone has
+// given you modify rights to. JC
+BOOL LLViewerObject::allowOpen() const
+{
+ return !flagInventoryEmpty() && (permYouOwner() || permModify());
+}
+
+LLViewerObject::LLInventoryCallbackInfo::~LLInventoryCallbackInfo()
+{
+ if (mListener)
+ {
+ mListener->clearVOInventoryListener();
+ }
+}
+
+void LLViewerObject::updateVolume(const LLVolumeParams& volume_params)
+{
+ if (setVolume(volume_params, 1)) // FIXME: magic number, ack!
+ {
+ // Transmit the update to the simulator
+ sendShapeUpdate();
+ markForUpdate(TRUE);
+ }
+}
+
+void LLViewerObject::markForUpdate(BOOL priority)
+{
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, priority);
+ }
+}
+
+void LLViewerObject::setRegion(LLViewerRegion *regionp)
+{
+ llassert(regionp);
+ mRegionp = regionp;
+ setChanged(MOVED | SILHOUETTE);
+ updateDrawable(FALSE);
+}
+
+bool LLViewerObject::specialHoverCursor() const
+{
+ return (mFlags & FLAGS_USE_PHYSICS)
+ || (mFlags & FLAGS_HANDLE_TOUCH)
+ || (mClickAction != 0);
+}
+
+void LLViewerObject::updateFlags()
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessage("ObjectFlagUpdate");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, getLocalID() );
+ gMessageSystem->addBOOLFast(_PREHASH_UsePhysics, usePhysics() );
+ gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() );
+ gMessageSystem->addBOOL("IsPhantom", flagPhantom() );
+ gMessageSystem->addBOOL("CastsShadows", flagCastShadows() );
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+BOOL LLViewerObject::setFlags(U32 flags, BOOL state)
+{
+ BOOL setit = FALSE;
+ if (state)
+ {
+ if ((mFlags & flags) != flags)
+ {
+ mFlags |= flags;
+ setit = TRUE;
+ }
+ }
+ else
+ {
+ if ((mFlags & flags) != 0)
+ {
+ mFlags &= ~flags;
+ setit = TRUE;
+ }
+ }
+
+ // BUG: Sometimes viewer physics and simulator physics get
+ // out of sync. To fix this, always send update to simulator.
+// if (setit)
+ {
+ updateFlags();
+ }
+ return setit;
+}
+
+BOOL LLViewerObject::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ return FALSE;
+}
+
+void LLViewerObject::applyAngularVelocity(F32 dt)
+{
+ //do target omega here
+ mRotTime += dt;
+ LLVector3 ang_vel = getAngularVelocity();
+ F32 omega = ang_vel.magVecSquared();
+ F32 angle = 0.0f;
+ LLQuaternion dQ;
+ if (omega > 0.00001f)
+ {
+ omega = sqrt(omega);
+ angle = omega * dt;
+
+ ang_vel *= 1.f/omega;
+
+ dQ.setQuat(angle, ang_vel);
+
+ setRotation(getRotation()*dQ);
+ setChanged(MOVED | SILHOUETTE);
+ }
+}
+
+void LLViewerObject::resetRot()
+{
+ mRotTime = 0.0f;
+}
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
new file mode 100644
index 0000000000..959c44abaa
--- /dev/null
+++ b/indra/newview/llviewerobject.h
@@ -0,0 +1,626 @@
+/**
+ * @file llviewerobject.h
+ * @brief Description of LLViewerObject class, which is the base class for most objects in the viewer.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWEROBJECT_H
+#define LL_LLVIEWEROBJECT_H
+
+#include <map>
+
+#include "linked_lists.h"
+#include "llassetstorage.h"
+#include "lldarrayptr.h"
+#include "llhudtext.h"
+#include "llhudicon.h"
+#include "llinventory.h"
+#include "llmemory.h"
+#include "llprimitive.h"
+#include "lluuid.h"
+#include "llvoinventorylistener.h"
+#include "object_flags.h"
+#include "llquaternion.h"
+#include "v3dmath.h"
+#include "v3math.h"
+
+class LLAgent; // TODO: Get rid of this.
+class LLAudioSource;
+class LLAudioSourceVO;
+class LLBBox;
+class LLDataPacker;
+class LLColor4;
+class LLFrameTimer;
+class LLDrawable;
+class LLHost;
+class LLWorld;
+class LLNameValue;
+class LLNetMap;
+class LLMessageSystem;
+class LLPrimitive;
+class LLPipeline;
+class LLTextureEntry;
+class LLViewerImage;
+class LLViewerInventoryItem;
+class LLViewerObject;
+class LLViewerPartSourceScript;
+class LLViewerRegion;
+class LLViewerObjectMedia;
+class LLVOInventoryListener;
+
+typedef enum e_object_update_type
+{
+ OUT_FULL,
+ OUT_TERSE_IMPROVED,
+ OUT_FULL_COMPRESSED,
+ OUT_FULL_CACHED,
+} EObjectUpdateType;
+
+
+// callback typedef for inventory
+typedef void (*inventory_callback)(LLViewerObject*,
+ InventoryObjectList*,
+ S32 serial_num,
+ void*);
+
+// a small struct for keeping track of joints
+struct LLVOJointInfo
+{
+ EHavokJointType mJointType;
+ LLVector3 mPivot; // parent-frame
+ // whether the below an axis or anchor (and thus its frame)
+ // depends on the joint type:
+ // HINGE ==> axis=parent-frame
+ // P2P ==> anchor=child-frame
+ LLVector3 mAxisOrAnchor;
+};
+
+// for exporting textured materials from SL
+struct LLMaterialExportInfo
+{
+public:
+ LLMaterialExportInfo(S32 mat_index, S32 texture_index, LLColor4 color) :
+ mMaterialIndex(mat_index), mTextureIndex(texture_index), mColor(color) {};
+
+ S32 mMaterialIndex;
+ S32 mTextureIndex;
+ LLColor4 mColor;
+};
+
+//============================================================================
+
+class LLViewerObject : public LLPrimitive, public LLRefCount
+{
+protected:
+ virtual ~LLViewerObject(); // use unref()
+
+ // TomY: Provide for a list of extra parameter structures, mapped by structure name
+ struct ExtraParameter
+ {
+ BOOL in_use;
+ LLNetworkData *data;
+ };
+ std::map<U16, ExtraParameter*> mExtraParameterList;
+
+public:
+ typedef std::vector<LLPointer<LLViewerObject> > child_list_t;
+
+ LLViewerObject(const LLUUID &id, const LLPCode type, LLViewerRegion *regionp);
+ MEM_TYPE_NEW(LLMemType::MTYPE_OBJECT);
+
+ virtual void markDead(); // Mark this object as dead, and clean up its references
+ BOOL isDead() const {return mDead;}
+ BOOL isOrphaned() const { return mOrphaned; }
+ BOOL isParticleSource() const;
+
+ static void initVOClasses();
+ static void cleanupVOClasses();
+
+ void addNVPair(const char* data);
+ BOOL removeNVPair(const char *name);
+ LLNameValue *getNVPair(const char *name) const; // null if no name value pair by that name
+
+ // Object create and update functions
+ virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ // Types of media we can associate
+ enum { MEDIA_TYPE_NONE = 0, MEDIA_TYPE_WEB_PAGE = 1 };
+
+ // Return codes for processUpdateMessage
+ enum { MEDIA_URL_REMOVED = 0x1, MEDIA_URL_ADDED = 0x2, MEDIA_URL_UPDATED = 0x4 };
+
+ enum { CLICK_ACTION_TOUCH = 0, CLICK_ACTION_SIT = 1, CLICK_ACTION_BUY = 2 };
+
+ virtual U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+
+
+ virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ BOOL onActiveList() const {return mOnActiveList;}
+ void setOnActiveList(BOOL on_active) { mOnActiveList = on_active; }
+
+ virtual BOOL isAttachment() const { return FALSE; }
+ virtual BOOL isHUDAttachment() const { return FALSE; }
+ virtual void updateRadius() {};
+ virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius()
+
+ BOOL isJointChild() const { return mJointInfo ? TRUE : FALSE; }
+ EHavokJointType getJointType() const { return mJointInfo ? mJointInfo->mJointType : HJT_INVALID; }
+ // for jointed and other parent-relative hacks
+ LLViewerObject* getSubParent();
+ const LLViewerObject* getSubParent() const;
+
+ // Object visiblility and GPW functions
+ virtual void setPixelAreaAndAngle(LLAgent &agent); // Override to generate accurate apparent angle and area
+
+ virtual U32 getNumVertices() const;
+ virtual U32 getNumIndices() const;
+ S32 getNumFaces() const { return mNumFaces; }
+
+ // Graphical stuff for objects - maybe broken out into render class later?
+ virtual void updateTextures(LLAgent &agent);
+ virtual void boostTexturePriority(BOOL boost_children = TRUE); // When you just want to boost priority of this object
+
+ virtual LLDrawable* createDrawable(LLPipeline *pipeline);
+ virtual BOOL updateGeometry(LLDrawable *drawable);
+ virtual BOOL updateLOD();
+ virtual BOOL setDrawableParent(LLDrawable* parentp);
+ virtual BOOL updateLighting(BOOL do_lighting) { return TRUE; };
+ F32 getRotTime() { return mRotTime; }
+ void resetRot();
+ void applyAngularVelocity(F32 dt);
+
+ void setLineWidthForWindowSize(S32 window_width);
+
+ static void increaseArrowLength(); // makes axis arrows for selections longer
+ static void decreaseArrowLength(); // makes axis arrows for selections shorter
+
+ // Accessor functions
+ LLViewerRegion* getRegion() const { return mRegionp; }
+
+ BOOL isSelected() const { return mUserSelected; }
+ void setSelected(BOOL sel) { mUserSelected = sel; mRotTime = 0.f;}
+
+ const LLUUID &getID() const { return mID; }
+ U32 getLocalID() const { return mLocalID; }
+ U32 getCRC() const { return mTotalCRC; }
+
+ virtual BOOL isFlexible() const { return false; }
+
+ // This method returns true if the object is over land owned by
+ // the agent.
+ BOOL isOverAgentOwnedLand() const;
+
+ // True if over land owned by group of which the agent is
+ // either officer or member.
+ BOOL isOverGroupOwnedLand() const;
+
+ /*
+ // This method will scan through this object, and then query the
+ // selection manager to see if the local agent probably has the
+ // ability to modify the object. Since this calls into the
+ // selection manager, you should avoid calling this method from
+ // there.
+ BOOL isProbablyModifiable() const;
+ */
+
+ virtual void setParent(LLViewerObject* parent);
+ virtual void addChild(LLViewerObject *childp);
+ virtual void removeChild(LLViewerObject *childp);
+ child_list_t& getChildren();
+ void addThisAndAllChildren(LLDynamicArray<LLViewerObject*>& objects);
+ void addThisAndNonJointChildren(LLDynamicArray<LLViewerObject*>& objects);
+ BOOL isChild(LLViewerObject *childp) const;
+ BOOL isSeat() const;
+
+
+ //detect if given line segment (in agent space) intersects with this viewer object.
+ //returns TRUE if intersection detected and moves end to the point of intersection
+ //closest to start.
+ virtual BOOL lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+
+ const LLVector3d getPositionGlobal() const;
+ const LLVector3 &getPositionRegion() const;
+ const LLVector3 getPositionEdit() const;
+ const LLVector3 &getPositionAgent() const;
+ const LLVector3 getRenderPosition() const;
+
+ virtual const LLVector3 getPivotPositionAgent() const; // Usually = to getPositionAgent, unless like flex objects it's not
+
+ LLViewerObject* getRootEdit() const;
+
+ const LLQuaternion getRotationRegion() const;
+ const LLQuaternion getRotationEdit() const;
+ const LLQuaternion getRenderRotation() const;
+ virtual const LLMatrix4 getRenderMatrix() const;
+
+ void setPosition(const LLVector3 &pos, BOOL damped = FALSE);
+ void setPositionGlobal(const LLVector3d &position, BOOL damped = FALSE);
+ void setPositionRegion(const LLVector3 &position, BOOL damped = FALSE);
+ void setPositionEdit(const LLVector3 &position, BOOL damped = FALSE);
+ void setPositionAgent(const LLVector3 &pos_agent, BOOL damped = FALSE);
+ void setPositionParent(const LLVector3 &pos_parent, BOOL damped = FALSE);
+ void setPositionAbsoluteGlobal( const LLVector3d &pos_global, BOOL damped = FALSE );
+ void sendPositionUpdate() const;
+
+ virtual const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const { return xform->getWorldMatrix(); }
+
+ inline void setRotation(const F32 x, const F32 y, const F32 z, BOOL damped = FALSE);
+ inline void setRotation(const LLQuaternion& quat, BOOL damped = FALSE);
+ void sendRotationUpdate() const;
+
+ /*virtual*/ void setNumTEs(const U8 num_tes);
+ /*virtual*/ void setTE(const U8 te, const LLTextureEntry &texture_entry);
+ /*virtual*/ S32 setTETexture(const U8 te, const LLUUID &uuid);
+ S32 setTETextureCore(const U8 te, const LLUUID& uuid, LLHost host);
+ /*virtual*/ S32 setTEColor(const U8 te, const LLColor4 &color);
+ /*virtual*/ S32 setTEScale(const U8 te, const F32 s, const F32 t);
+ /*virtual*/ S32 setTEScaleS(const U8 te, const F32 s);
+ /*virtual*/ S32 setTEScaleT(const U8 te, const F32 t);
+ /*virtual*/ S32 setTEOffset(const U8 te, const F32 s, const F32 t);
+ /*virtual*/ S32 setTEOffsetS(const U8 te, const F32 s);
+ /*virtual*/ S32 setTEOffsetT(const U8 te, const F32 t);
+ /*virtual*/ S32 setTERotation(const U8 te, const F32 r);
+ /*virtual*/ S32 setTEBumpmap(const U8 te, const U8 bump );
+ /*virtual*/ S32 setTETexGen(const U8 te, const U8 texgen );
+ /*virtual*/ S32 setTEShiny(const U8 te, const U8 shiny );
+ /*virtual*/ S32 setTEFullbright(const U8 te, const U8 fullbright );
+ /*virtual*/ S32 setTEMediaFlags(const U8 te, const U8 media_flags );
+ /*virtual*/ BOOL setMaterial(const U8 material);
+ virtual void setTEImage(const U8 te, LLViewerImage *imagep); // Not derived from LLPrimitive
+ LLViewerImage *getTEImage(const U8 te) const;
+
+ S32 getFaceIndexOffset() { return mFaceIndexOffset; }
+
+ void fitFaceTexture(const U8 face);
+ void sendTEUpdate() const; // Sends packed representation of all texture entry information
+
+ virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE);
+ void sendScaleUpdate();
+
+ void sendShapeUpdate();
+
+ U8 getState() { return mState; }
+
+ F32 getAppAngle() const { return mAppAngle; }
+ F32 getMaxScale() const;
+ F32 getMidScale() const;
+ F32 getMinScale() const;
+
+ // Owner id is this object's owner
+ void setAttachedSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const U8 flags);
+ void adjustAudioGain(const F32 gain);
+ void clearAttachedSound() { mAudioSourcep = NULL; }
+
+ // Create if necessary
+ LLAudioSource *getAudioSource(const LLUUID& owner_id);
+
+ U8 getMediaType() const;
+ void setMediaType(U8 media_type);
+
+ const LLString& getMediaURL() const;
+ void setMediaURL(const LLString& media_url);
+
+ BOOL getMediaPassedWhitelist() const;
+ void setMediaPassedWhitelist(BOOL passed);
+
+ void sendMaterialUpdate() const;
+
+ void setCanSelect(BOOL canSelect);
+
+ void setDebugText(const std::string &utf8text);
+ void setIcon(LLViewerImage* icon_image);
+ void clearIcon();
+
+ void markForUpdate(BOOL priority);
+ void updateVolume(const LLVolumeParams& volume_params);
+ virtual void updateSpatialExtents(LLVector3& min, LLVector3& max);
+
+ LLBBox getBoundingBoxAgent() const;
+
+ void updatePositionCaches() const; // Update the global and region position caches from the object (and parent's) xform.
+ void updateText(); // update text label position
+ virtual void updateDrawable(BOOL force_damped); // force updates on static objects
+
+ void setDrawableState(U32 state, BOOL recursive = TRUE);
+ void clearDrawableState(U32 state, BOOL recursive = TRUE);
+
+ // Called when the drawable shifts
+ virtual void onShift(const LLVector3 &shift_vector) { }
+
+ //////////////////////////////////////
+ //
+ // Inventory methods
+ //
+
+ // This function is called when someone is interested in a viewer
+ // object's inventory. The callback is called as soon as the
+ // viewer object has the inventory stored locally.
+ void registerInventoryListener(LLVOInventoryListener* listener, void* user_data);
+ void removeInventoryListener(LLVOInventoryListener* listener);
+ BOOL isInventoryPending() { return mInventoryPending; }
+ void clearInventoryListeners();
+ void requestInventory();
+ static void processTaskInv(LLMessageSystem* msg, void** user_data);
+ void removeInventory(const LLUUID& item_id);
+
+ // The updateInventory() call potentially calls into the selection
+ // manager, so do no call updateInventory() from the selection
+ // manager until we have better iterators.
+ void updateInventory(LLViewerInventoryItem* item, U8 key, bool is_new);
+ LLInventoryObject* getInventoryObject(const LLUUID& item_id);
+ void getInventoryContents(InventoryObjectList& objects);
+ LLInventoryObject* getInventoryRoot();
+ LLViewerInventoryItem* getInventoryItemByAsset(const LLUUID& asset_id);
+ S16 getInventorySerial() const { return mInventorySerialNum; }
+
+ // This function will make sure that we refresh the inventory.
+ void dirtyInventory();
+ BOOL isInventoryDirty() { return mInventoryDirty; }
+
+ // save a script, which involves removing the old one, and rezzing
+ // in the new one. This method should be called with the asset id
+ // of the new and old script AFTER the bytecode has been saved.
+ void saveScript(const LLViewerInventoryItem* item, BOOL active, bool is_new);
+
+ // move an inventory item out of the task and into agent
+ // inventory. This operation is based on messaging. No permissions
+ // checks are made on the viewer - the server will double check.
+ void moveInventory(const LLUUID& agent_folder, const LLUUID& item_id);
+
+ // Find the number of instances of this object's inventory that are of the given type
+ S32 countInventoryContents( LLAssetType::EType type );
+
+ BOOL permAnyOwner() const;
+ BOOL permYouOwner() const;
+ BOOL permGroupOwner() const;
+ BOOL permOwnerModify() const;
+ BOOL permModify() const;
+ BOOL permCopy() const;
+ BOOL permMove() const;
+ BOOL permTransfer() const;
+ inline BOOL usePhysics() const { return ((mFlags & FLAGS_USE_PHYSICS) != 0); }
+ inline BOOL flagScripted() const { return ((mFlags & FLAGS_SCRIPTED) != 0); }
+ inline BOOL flagHandleTouch() const { return ((mFlags & FLAGS_HANDLE_TOUCH) != 0); }
+ inline BOOL flagTakesMoney() const { return ((mFlags & FLAGS_TAKES_MONEY) != 0); }
+ inline BOOL flagPhantom() const { return ((mFlags & FLAGS_PHANTOM) != 0); }
+ inline BOOL flagInventoryEmpty() const { return ((mFlags & FLAGS_INVENTORY_EMPTY) != 0); }
+ inline BOOL flagCastShadows() const { return ((mFlags & FLAGS_CAST_SHADOWS) != 0); }
+ inline BOOL flagAllowInventoryAdd() const { return ((mFlags & FLAGS_ALLOW_INVENTORY_DROP) != 0); }
+ inline BOOL flagTemporary() const { return ((mFlags & FLAGS_TEMPORARY) != 0); }
+ inline BOOL flagTemporaryOnRez() const { return ((mFlags & FLAGS_TEMPORARY_ON_REZ) != 0); }
+ inline BOOL flagAnimSource() const { return ((mFlags & FLAGS_ANIM_SOURCE) != 0); }
+ inline BOOL flagCameraSource() const { return ((mFlags & FLAGS_CAMERA_SOURCE) != 0); }
+ inline BOOL flagCameraDecoupled() const { return ((mFlags & FLAGS_CAMERA_DECOUPLED) != 0); }
+
+ // Does "open" object menu item apply?
+ BOOL allowOpen() const;
+
+ void setClickAction(U8 action) { mClickAction = action; }
+ U8 getClickAction() const { return mClickAction; }
+ bool specialHoverCursor() const; // does it have a special hover cursor?
+
+ void setRegion(LLViewerRegion *regionp);
+ virtual void updateRegion(LLViewerRegion *regionp) {}
+
+ void updateFlags();
+ BOOL setFlags(U32 flag, BOOL state);
+
+ virtual void dump() const;
+ static U32 getNumZombieObjects() { return sNumZombieObjects; }
+
+ void printNameValuePairs() const;
+
+ virtual S32 getLOD() const { return 3; }
+
+ virtual LLNetworkData* getParameterEntry(U16 param_type) const;
+ virtual bool setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin);
+ virtual BOOL getParameterEntryInUse(U16 param_type) const;
+ virtual bool setParameterEntryInUse(U16 param_type, BOOL in_use, bool local_origin);
+ // Called when a parameter is changed
+ virtual void parameterChanged(U16 param_type, bool local_origin);
+ virtual void parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin);
+
+ friend class LLViewerObjectList;
+ friend class LLViewerMediaList;
+
+private:
+ ExtraParameter* createNewParameterEntry(U16 param_type);
+ ExtraParameter* getExtraParameterEntry(U16 param_type) const;
+ ExtraParameter* getExtraParameterEntryCreate(U16 param_type);
+ bool unpackParameterEntry(U16 param_type, LLDataPacker *dp);
+
+public:
+ //
+ // Viewer-side only types - use the LL_PCODE_APP mask.
+ //
+ typedef enum e_vo_types
+ {
+ LL_VO_CLOUDS = LL_PCODE_APP | 0x20,
+ LL_VO_SURFACE_PATCH = LL_PCODE_APP | 0x30,
+ LL_VO_STARS = LL_PCODE_APP | 0x40,
+ LL_VO_SQUARE_TORUS = LL_PCODE_APP | 0x50,
+ LL_VO_SKY = LL_PCODE_APP | 0x60,
+ LL_VO_WATER = LL_PCODE_APP | 0x70,
+ LL_VO_GROUND = LL_PCODE_APP | 0x80,
+ LL_VO_PART_GROUP = LL_PCODE_APP | 0x90,
+ LL_VO_TRIANGLE_TORUS = LL_PCODE_APP | 0xa0,
+ } EVOType;
+
+ child_list_t mChildList;
+ LLUUID mID;
+
+ // unique within region, not unique across regions
+ // Local ID = 0 is not used
+ U32 mLocalID;
+
+ // Last total CRC received from sim, used for caching
+ U32 mTotalCRC;
+
+ LLPointer<LLViewerImage> *mTEImages;
+
+ // Selection, picking and rendering variables
+ U32 mGLName; // GL "name" used by selection code
+ BOOL mbCanSelect; // true if user can select this object by clicking
+
+ // Grabbed from UPDATE_FLAGS
+ U32 mFlags;
+
+ // Pipeline classes
+ LLPointer<LLDrawable> mDrawable;
+
+ // Band-aid to select object after all creation initialization is done
+ BOOL mCreateSelected;
+
+ // Replace textures with web pages on this object while drawing
+ BOOL mRenderMedia;
+
+ // In bits
+ S32 mBestUpdatePrecision;
+
+ // TODO: Make all this stuff private. JC
+ LLPointer<LLHUDText> mText;
+ LLPointer<LLHUDIcon> mIcon;
+
+ static BOOL sUseSharedDrawables;
+
+protected:
+ // delete an item in the inventory, but don't tell the
+ // server. This is used internally by remove, update, and
+ // savescript.
+ void deleteInventoryItem(const LLUUID& item_id);
+
+ // do the update/caching logic. called by saveScript and
+ // updateInventory.
+ void doUpdateInventory(LLViewerInventoryItem* item, U8 key, bool is_new);
+
+
+ static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp);
+
+ BOOL setData(const U8 *datap, const U32 data_size);
+
+ //////////////////////////
+ //
+ // inventory functionality
+ //
+
+ static void processTaskInvFile(void** user_data, S32 error_code);
+ void loadTaskInvFile(const char* filename);
+ void doInventoryCallback();
+
+ BOOL isOnMap();
+
+ void unpackParticleSource(const S32 block_num, const LLUUID& owner_id);
+ void unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id);
+
+private:
+ void setNameValueList(char* list); // clears nv pairs and then individually adds \n separated NV pairs from \0 terminated string
+ void deleteTEImages(); // correctly deletes list of images
+
+protected:
+ typedef std::map<char *, LLNameValue *> name_value_map_t;
+ name_value_map_t mNameValuePairs; // Any name-value pairs stored by script
+
+ F64 mLastInterpUpdateSecs; // Last update for purposes of interpolation
+ F64 mLastMessageUpdateSecs; // Last update from a message from the simulator
+
+ // extra data sent from the sim...currently only used for tree species info
+ U8* mData;
+
+ LLPointer<LLViewerPartSourceScript> mPartSourcep; // Particle source associated with this object.
+ LLAudioSourceVO *mAudioSourcep;
+
+ F32 mAppAngle; // Apparent visual arc in degrees
+ F32 mPixelArea; // Apparent area in pixels
+
+ // This is the object's inventory from the viewer's perspective.
+ InventoryObjectList* mInventory;
+ class LLInventoryCallbackInfo
+ {
+ public:
+ ~LLInventoryCallbackInfo();
+ LLVOInventoryListener* mListener;
+ void* mInventoryData;
+ };
+ LLLinkedList<LLInventoryCallbackInfo> mInventoryCallbacks;
+ S16 mInventorySerialNum;
+
+ LLViewerRegion *mRegionp; // Region that this object belongs to.
+ BOOL mInventoryPending;
+ BOOL mInventoryDirty;
+ BOOL mDead;
+ BOOL mOrphaned; // This is an orphaned child
+ BOOL mUserSelected; // Cached user select information
+ BOOL mOnActiveList;
+ BOOL mOnMap; // On the map.
+ BOOL mStatic; // Object doesn't move.
+ S32 mFaceIndexOffset; // offset into drawable's faces, zero except in special cases
+ S32 mNumFaces;
+
+ S32 mLastUpdateFrame; // frames in which an object had last moved for smart coalescing of drawables
+ // (child objects not moving relative to parent)
+
+ F32 mTimeDilation; // Time dilation sent with the object.
+ F32 mRotTime; // Amount (in seconds) that object has rotated according to angular velocity (llSetTargetOmega)
+ LLQuaternion mLastRot; // last rotation received from the simulator
+
+ LLVOJointInfo* mJointInfo;
+ U8 mState; // legacy
+ LLViewerObjectMedia* mMedia; // NULL if no media associated
+ U8 mClickAction;
+
+ static U32 sNumZombieObjects; // Objects which are dead, but not deleted
+
+ static BOOL sMapDebug; // Map render mode
+ static LLColor4 sEditSelectColor;
+ static LLColor4 sNoEditSelectColor;
+ static F32 sCurrentPulse;
+ static BOOL sPulseEnabled;
+
+ static S32 sAxisArrowLength;
+
+ // These two caches are only correct for non-parented objects right now!
+ mutable LLVector3 mPositionRegion;
+ mutable LLVector3 mPositionAgent;
+
+private:
+ static S32 sNumObjects;
+};
+
+
+///////////////////
+//
+// Inlines
+//
+//
+
+inline void LLViewerObject::setRotation(const LLQuaternion& quat, BOOL damped)
+{
+ LLPrimitive::setRotation(quat);
+ setChanged(ROTATED | SILHOUETTE);
+ updateDrawable(damped);
+}
+
+inline void LLViewerObject::setRotation(const F32 x, const F32 y, const F32 z, BOOL damped)
+{
+ LLPrimitive::setRotation(x, y, z);
+ setChanged(ROTATED | SILHOUETTE);
+ updateDrawable(damped);
+}
+
+class LLViewerObjectMedia
+{
+public:
+ LLViewerObjectMedia() : mMediaURL(), mPassedWhitelist(FALSE), mMediaType(0) { }
+
+ LLString mMediaURL; // for web pages on surfaces, one per prim
+ BOOL mPassedWhitelist; // user has OK'd display
+ U8 mMediaType; // see LLTextureEntry::WEB_PAGE, etc.
+};
+
+#endif
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
new file mode 100644
index 0000000000..e1fed12563
--- /dev/null
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -0,0 +1,1484 @@
+/**
+ * @file llviewerobjectlist.cpp
+ * @brief Implementation of LLViewerObjectList class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerobjectlist.h"
+
+#include "gpw.h"
+#include "message.h"
+#include "timing.h"
+#include "llfasttimer.h"
+
+#include "llviewercontrol.h"
+#include "llface.h"
+#include "llvoavatar.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llnetmap.h"
+#include "llagent.h"
+#include "pipeline.h"
+#include "llhoverview.h"
+#include "llworld.h"
+#include "llstring.h"
+#include "llhudtext.h"
+#include "lldrawable.h"
+#include "xform.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llselectmgr.h"
+#include "llresmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "llkeyboard.h"
+#include "u64.h"
+#include "llviewerimagelist.h"
+#include "lldatapacker.h"
+#include <zlib/zlib.h>
+#include "object_flags.h"
+#include "llviewermedialist.h"
+
+extern BOOL gVelocityInterpolate;
+extern BOOL gPingInterpolate;
+extern F32 gMinObjectDistance;
+extern U32 gFrameCount;
+extern LLTimer gRenderStartTime;
+extern BOOL gAnimateTextures;
+
+void dialog_refresh_all();
+
+#define CULL_VIS
+//#define ORPHAN_SPAM
+//#define IGNORE_DEAD
+
+// Global lists of objects - should go away soon.
+LLViewerObjectList gObjectList;
+
+extern LLPipeline gPipeline;
+
+// Statics for object lookup tables.
+U32 LLViewerObjectList::sSimulatorMachineIndex = 1; // Not zero deliberately, to speed up index check.
+LLMap<U64, U32> LLViewerObjectList::sIPAndPortToIndex;
+std::map<U64, LLUUID> LLViewerObjectList::sIndexAndLocalIDToUUID;
+
+LLViewerObjectList::LLViewerObjectList()
+{
+ mNumVisCulled = 0;
+ mNumSizeCulled = 0;
+ mCurLazyUpdateIndex = 0;
+ mCurBin = 0;
+ mNumDeadObjects = 0;
+ mNumOrphans = 0;
+ mNumNewObjects = 0;
+ mWasPaused = FALSE;
+ mNumDeadObjectUpdates = 0;
+ mNumUnknownKills = 0;
+ mNumUnknownUpdates = 0;
+}
+
+LLViewerObjectList::~LLViewerObjectList()
+{
+ destroy();
+}
+
+void LLViewerObjectList::destroy()
+{
+ killAllObjects();
+
+ resetObjectBeacons();
+ mActiveObjects.clear();
+ mDeadObjects.clear();
+ mMapObjects.clear();
+ mUUIDObjectMap.clear();
+}
+
+
+void LLViewerObjectList::getUUIDFromLocal(LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ index = sSimulatorMachineIndex++;
+ sIPAndPortToIndex[ipport] = index;
+ }
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+
+ id = get_if_there(sIndexAndLocalIDToUUID, indexid, LLUUID::null);
+}
+
+U64 LLViewerObjectList::getIndex(const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ return 0;
+ }
+
+ return (((U64)index) << 32) | (U64)local_id;
+}
+
+BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object)
+{
+ U32 local_id = object.mLocalID;
+ LLHost region_host = object.getRegion()->getHost();
+ U32 ip = region_host.getAddress();
+ U32 port = region_host.getPort();
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+ U32 index = sIPAndPortToIndex[ipport];
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+ return sIndexAndLocalIDToUUID.erase(indexid) > 0 ? TRUE : FALSE;
+}
+
+void LLViewerObjectList::setUUIDAndLocal(const LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ index = sSimulatorMachineIndex++;
+ sIPAndPortToIndex[ipport] = index;
+ }
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+
+ sIndexAndLocalIDToUUID[indexid] = id;
+}
+
+S32 gFullObjectUpdates = 0;
+S32 gTerseObjectUpdates = 0;
+
+void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp,
+ void** user_data,
+ U32 i,
+ const EObjectUpdateType update_type,
+ LLDataPacker* dpp,
+ BOOL justCreated)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ U32 pum_flags = objectp->processUpdateMessage(msg, user_data, i, update_type, dpp);
+ if (objectp->isDead())
+ {
+ // The update failed
+ return;
+ }
+ updateActive(objectp);
+
+ // Also sets the approx. pixel area
+ objectp->setPixelAreaAndAngle(gAgent);
+
+ // Update the image levels of textures for this object.
+ objectp->updateTextures(gAgent);
+
+ if (justCreated)
+ {
+ gPipeline.addObject(objectp);
+ }
+
+ // RN: this must be called after we have a drawable
+ // (from gPipeline.addObject)
+ // so that the drawable parent is set properly
+ findOrphans(objectp, msg->getSenderIP(), msg->getSenderPort());
+
+ // If we're just wandering around, don't create new objects selected.
+ if (justCreated
+ && update_type != OUT_TERSE_IMPROVED
+ && objectp->mCreateSelected)
+ {
+ if ( gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ) != gToolPie )
+ {
+ //llinfos << "DEBUG selecting " << objectp->mID << " "
+ // << objectp->mLocalID << llendl;
+ gSelectMgr->selectObjectAndFamily(objectp);
+ dialog_refresh_all();
+ }
+
+ objectp->mCreateSelected = false;
+ gViewerWindow->getWindow()->decBusyCount();
+ gViewerWindow->getWindow()->setCursor( UI_CURSOR_ARROW );
+ }
+
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ if (pum_flags & LLViewerObject::MEDIA_URL_ADDED)
+ {
+ //llwarns << "WEBONPRIM media url added " << objectp->getMediaURL() << llendl;
+ gMediaList->addedMediaURL(objectp);
+ }
+
+ if (pum_flags & LLViewerObject::MEDIA_URL_UPDATED)
+ {
+ //llwarns << "WEBONPRIM media url updated " << objectp->getMediaURL() << llendl;
+ gMediaList->updatedMediaURL(objectp);
+ }
+
+ if (pum_flags & LLViewerObject::MEDIA_URL_REMOVED)
+ {
+ //llwarns << "WEBONPRIM media url removed " << objectp->getMediaURL() << llendl;
+ gMediaList->removedMediaURL(objectp);
+ }
+
+ // Make sure we get moved in or out of LLDrawPoolMedia, as needed
+ if (pum_flags & (LLViewerObject::MEDIA_URL_ADDED | LLViewerObject::MEDIA_URL_REMOVED | LLViewerObject::MEDIA_URL_UPDATED))
+ {
+ if (objectp->mDrawable.notNull())
+ {
+ gPipeline.markTextured(objectp->mDrawable);
+ }
+ }
+ }
+}
+
+void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type,
+ bool cached, bool compressed)
+{
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS);
+
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+ LLViewerObject *objectp;
+ S32 num_objects;
+ U32 local_id;
+ LLPCode pcode = 0;
+ LLUUID fullid;
+ S32 i;
+
+ // figure out which simulator these are from and get it's index
+ // Coordinates in simulators are region-local
+ // Until we get region-locality working on viewer we
+ // have to transform to absolute coordinates.
+ num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData);
+
+ if (!cached && !compressed && update_type != OUT_FULL)
+ {
+ gTerseObjectUpdates += num_objects;
+ S32 size;
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ size = mesgsys->getReceiveCompressedSize();
+ }
+ else
+ {
+ size = mesgsys->getReceiveSize();
+ }
+// llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
+ }
+ else
+ {
+ S32 size;
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ size = mesgsys->getReceiveCompressedSize();
+ }
+ else
+ {
+ size = mesgsys->getReceiveSize();
+ }
+
+// llinfos << "Received " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
+ gFullObjectUpdates += num_objects;
+ }
+
+ U64 region_handle;
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromHandle(region_handle);
+
+ if (!regionp)
+ {
+ llwarns << "Object update from unknown region!" << llendl;
+ return;
+ }
+
+ U8 compressed_dpbuffer[2048];
+ LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048);
+ LLDataPacker *cached_dpp = NULL;
+
+ for (i = 0; i < num_objects; i++)
+ {
+ LLTimer update_timer;
+ BOOL justCreated = FALSE;
+
+ if (cached)
+ {
+ U32 id;
+ U32 crc;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, id, i);
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);
+
+ // Lookup data packer and add this id to cache miss lists if necessary.
+ cached_dpp = regionp->getDP(id, crc);
+ if (cached_dpp)
+ {
+ cached_dpp->reset();
+ cached_dpp->unpackUUID(fullid, "ID");
+ cached_dpp->unpackU32(local_id, "LocalID");
+ cached_dpp->unpackU8(pcode, "PCode");
+ }
+ else
+ {
+ continue; // no data packer, skip this object
+ }
+ }
+ else if (compressed)
+ {
+ U8 compbuffer[2048];
+ S32 uncompressed_length = 2048;
+ S32 compressed_length;
+
+ compressed_dp.reset();
+
+ U32 flags = 0;
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);
+ }
+
+ if (flags & FLAGS_ZLIB_COMPRESSED)
+ {
+ compressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compbuffer, 0, i);
+ uncompressed_length = 2048;
+ uncompress(compressed_dpbuffer, (unsigned long *)&uncompressed_length,
+ compbuffer, compressed_length);
+ compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
+ }
+ else
+ {
+ uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i);
+ compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
+ }
+
+
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ compressed_dp.unpackUUID(fullid, "ID");
+ compressed_dp.unpackU32(local_id, "LocalID");
+ compressed_dp.unpackU8(pcode, "PCode");
+ }
+ else
+ {
+ compressed_dp.unpackU32(local_id, "LocalID");
+ getUUIDFromLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (fullid.isNull())
+ {
+ //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
+ mNumUnknownUpdates++;
+ }
+ }
+ }
+ else if (update_type != OUT_FULL)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+ getUUIDFromLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (fullid.isNull())
+ {
+ //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
+ mNumUnknownUpdates++;
+ }
+ }
+ else
+ {
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i);
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+ // llinfos << "Full Update, obj " << local_id << ", global ID" << fullid << "from " << mesgsys->getSender() << llendl;
+ }
+ objectp = findObject(fullid);
+
+ // This looks like it will break if the local_id of the object doesn't change
+ // upon boundary crossing, but we check for region id matching later...
+ if (objectp && (objectp->mLocalID != local_id))
+ {
+ removeFromLocalIDTable(*objectp);
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ }
+
+ if (!objectp)
+ {
+ if (compressed)
+ {
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ // llinfos << "terse update for an unknown object:" << fullid << llendl;
+ continue;
+ }
+ }
+ else if (cached)
+ {
+ }
+ else
+ {
+ if (update_type != OUT_FULL)
+ {
+// llinfos << "terse update for an unknown object:" << fullid << llendl;
+ continue;
+ }
+
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_PCode, pcode, i);
+ }
+#ifdef IGNORE_DEAD
+ if (mDeadObjects.find(fullid) != mDeadObjects.end())
+ {
+ mNumDeadObjectUpdates++;
+ //llinfos << "update for a dead object:" << fullid << llendl;
+ continue;
+ }
+#endif
+
+ objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender());
+ if (!objectp)
+ {
+ continue;
+ }
+ justCreated = TRUE;
+ mNumNewObjects++;
+ }
+ else
+ {
+ if (objectp->getRegion() != regionp)
+ {
+ // Object has changed region! Update lookup tables, set region pointer.
+ removeFromLocalIDTable(*objectp);
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ objectp->setRegion(regionp);
+ }
+ objectp->updateRegion(regionp); // for LLVOAvatar
+ }
+
+
+ if (objectp->isDead())
+ {
+ llwarns << "Dead object " << objectp->mID << " in UUID map 1!" << llendl;
+ }
+
+ if (compressed)
+ {
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ objectp->mLocalID = local_id;
+ }
+ processUpdateCore(objectp, user_data, i, update_type, &compressed_dp, justCreated);
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp);
+ }
+ }
+ else if (cached)
+ {
+ objectp->mLocalID = local_id;
+ processUpdateCore(objectp, user_data, i, update_type, cached_dpp, justCreated);
+ }
+ else
+ {
+ if (update_type == OUT_FULL)
+ {
+ objectp->mLocalID = local_id;
+ }
+ processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated);
+ }
+ }
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+}
+
+void LLViewerObjectList::processCompressedObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type)
+{
+ processObjectUpdate(mesgsys, user_data, update_type, false, true);
+}
+
+void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type)
+{
+ processObjectUpdate(mesgsys, user_data, update_type, true, false);
+}
+
+void LLViewerObjectList::relightAllObjects()
+{
+ for (S32 i = 0; i < mObjects.count(); i++)
+ {
+ LLDrawable *drawable = mObjects[i]->mDrawable;
+ if (drawable)
+ {
+ gPipeline.markRelight(drawable);
+ }
+ }
+}
+
+void LLViewerObjectList::dirtyAllObjectInventory()
+{
+ S32 count = mObjects.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ mObjects[i]->dirtyInventory();
+ }
+}
+
+void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
+{
+ S32 i;
+ S32 num_objects = 0;
+ LLViewerObject *objectp;
+
+ S32 num_updates, max_value;
+ if (NUM_BINS - 1 == mCurBin)
+ {
+ num_updates = mObjects.count() - mCurLazyUpdateIndex;
+ max_value = mObjects.count();
+ gImageList.setUpdateStats(TRUE);
+ }
+ else
+ {
+ num_updates = (mObjects.count() / NUM_BINS) + 1;
+ max_value = llmin(mObjects.count(), mCurLazyUpdateIndex + num_updates);
+ }
+
+
+ if (!gNoRender)
+ {
+ // Slam priorities for textures that we care about (hovered, selected, and focused)
+ // Hovered
+ // Assumes only one level deep of parenting
+ objectp = gHoverView->getLastHoverObject();
+ if (objectp)
+ {
+ objectp->boostTexturePriority();
+ }
+ }
+
+ // Focused
+ objectp = gAgent.getFocusObject();
+ if (objectp)
+ {
+ objectp->boostTexturePriority();
+ }
+
+ // Selected
+ for (objectp = gSelectMgr->getFirstRootObject(); objectp; objectp = gSelectMgr->getNextRootObject())
+ {
+ objectp->boostTexturePriority();
+ }
+
+
+ // Iterate through some of the objects and lazy update their texture priorities
+ for (i = mCurLazyUpdateIndex; i < max_value; i++)
+ {
+ objectp = mObjects[i];
+ if (!objectp->isDead())
+ {
+ num_objects++;
+
+ // Update distance & gpw
+ objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area
+ objectp->updateTextures(agent); // Update the image levels of textures for this object.
+ }
+ }
+
+ mCurLazyUpdateIndex = max_value;
+ if (mCurLazyUpdateIndex == mObjects.count())
+ {
+ mCurLazyUpdateIndex = 0;
+ }
+
+ mCurBin = (++mCurBin) % NUM_BINS;
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+}
+
+
+void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ // Update globals
+ gVelocityInterpolate = gSavedSettings.getBOOL("VelocityInterpolate");
+ gPingInterpolate = gSavedSettings.getBOOL("PingInterpolate");
+ gAnimateTextures = gSavedSettings.getBOOL("AnimateTextures");
+
+ // update global timer
+ F32 last_time = gFrameTimeSeconds;
+ U64 time = totalTime(); // this will become the new gFrameTime when the update is done
+ // Time _can_ go backwards, for example if the user changes the system clock.
+ // It doesn't cause any fatal problems (just some oddness with stats), so we shouldn't assert here.
+// llassert(time > gFrameTime);
+ F64 time_diff = U64_to_F64(time - gFrameTime)/(F64)SEC_TO_MICROSEC;
+ gFrameTime = time;
+ F64 time_since_start = U64_to_F64(gFrameTime - gStartTime)/(F64)SEC_TO_MICROSEC;
+ gFrameTimeSeconds = (F32)time_since_start;
+
+ gFrameIntervalSeconds = gFrameTimeSeconds - last_time;
+ if (gFrameIntervalSeconds < 0.f)
+ {
+ gFrameIntervalSeconds = 0.f;
+ }
+
+ //clear avatar LOD change counter
+ LLVOAvatar::sNumLODChangesThisFrame = 0;
+
+ const F64 frame_time = LLFrameTimer::getElapsedSeconds();
+
+ std::vector<LLViewerObject*> kill_list;
+ S32 num_active_objects = 0;
+
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
+ iter != mActiveObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (objectp->getPCode() == LLViewerObject::LL_VO_CLOUDS ||
+ objectp->isAvatar())
+ {
+ objectp->idleUpdate(agent, world, frame_time);
+ }
+ }
+ }
+ else
+ {
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
+ iter != mActiveObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (!objectp->idleUpdate(agent, world, frame_time))
+ {
+ // If Idle Update returns false, kill object!
+ kill_list.push_back(objectp);
+ }
+ else
+ {
+ num_active_objects++;
+ }
+ }
+ for (std::vector<LLViewerObject*>::iterator iter = kill_list.begin();
+ iter != kill_list.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ killObject(objectp);
+ }
+ }
+
+ mNumSizeCulled = 0;
+ mNumVisCulled = 0;
+
+ // compute all sorts of time-based stats
+ // don't factor frames that were paused into the stats
+ if (! mWasPaused)
+ {
+ gViewerStats->updateFrameStats(time_diff);
+ }
+
+ /*
+ // Debugging code for viewing orphans, and orphaned parents
+ LLUUID id;
+ char id_str[UUID_STR_LENGTH + 20];
+ for (i = 0; i < mOrphanParents.count(); i++)
+ {
+ id = sIndexAndLocalIDToUUID[mOrphanParents[i]];
+ LLViewerObject *objectp = findObject(id);
+ if (objectp)
+ {
+ sprintf(id_str, "Par: ");
+ objectp->mID.toString(id_str + 5);
+ addDebugBeacon(objectp->getPositionAgent(),
+ id_str,
+ LLColor4(1.f,0.f,0.f,1.f),
+ LLColor4(1.f,1.f,1.f,1.f));
+ }
+ }
+
+ LLColor4 text_color;
+ for (i = 0; i < mOrphanChildren.count(); i++)
+ {
+ OrphanInfo oi = mOrphanChildren[i];
+ LLViewerObject *objectp = findObject(oi.mChildInfo);
+ if (objectp)
+ {
+ if (objectp->getParent())
+ {
+ sprintf(id_str, "ChP: ");
+ text_color = LLColor4(0.f, 1.f, 0.f, 1.f);
+ }
+ else
+ {
+ sprintf(id_str, "ChNoP: ");
+ text_color = LLColor4(1.f, 0.f, 0.f, 1.f);
+ }
+ id = sIndexAndLocalIDToUUID[oi.mParentInfo];
+ objectp->mID.toString(id_str + 8);
+ addDebugBeacon(objectp->getPositionAgent() + LLVector3(0.f, 0.f, -0.25f),
+ id_str,
+ LLColor4(0.25f,0.25f,0.25f,1.f),
+ text_color);
+ }
+ i++;
+ }
+ */
+
+ mNumObjectsStat.addValue(mObjects.count());
+ mNumActiveObjectsStat.addValue(num_active_objects);
+ mNumSizeCulledStat.addValue(mNumSizeCulled);
+ mNumVisCulledStat.addValue(mNumVisCulled);
+}
+
+void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ if (mDeadObjects.count(objectp->mID))
+ {
+ llinfos << "Object " << objectp->mID << " already on dead list, ignoring cleanup!" << llendl;
+ return;
+ }
+
+ mDeadObjects.insert(std::pair<LLUUID, LLPointer<LLViewerObject> >(objectp->mID, objectp));
+
+ // Cleanup any references we have to this object
+ // Remove from object map so noone can look it up.
+
+ mUUIDObjectMap.erase(objectp->mID);
+ removeFromLocalIDTable(*objectp);
+
+ if (objectp->onActiveList())
+ {
+ //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list in cleanupReferences." << llendl;
+ objectp->setOnActiveList(FALSE);
+ mActiveObjects.erase(objectp);
+ }
+
+ if (objectp->isOnMap())
+ {
+ mMapObjects.removeObj(objectp);
+ }
+
+ // Don't clean up mObject references, these will be cleaned up more efficiently later!
+ // Also, not cleaned up
+ removeDrawable(objectp->mDrawable);
+
+ mNumDeadObjects++;
+}
+
+void LLViewerObjectList::removeDrawable(LLDrawable* drawablep)
+{
+ if (!drawablep)
+ {
+ return;
+ }
+
+ for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+ {
+ LLViewerObject* objectp = drawablep->getFace(i)->getViewerObject();
+ mSelectPickList.erase(objectp);
+ }
+}
+
+BOOL LLViewerObjectList::killObject(LLViewerObject *objectp)
+{
+ // When we're killing objects, all we do is mark them as dead.
+ // We clean up the dead objects later.
+
+ if (objectp)
+ {
+ if (objectp->isDead())
+ {
+ // This object is already dead. Don't need to do more.
+ return TRUE;
+ }
+ else
+ {
+ objectp->markDead();
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLViewerObjectList::killObjects(LLViewerRegion *regionp)
+{
+ LLViewerObject *objectp;
+
+ S32 i;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+
+ if (objectp->mRegionp == regionp)
+ {
+ killObject(objectp);
+ }
+ }
+
+ // Have to clean right away because the region is becoming invalid.
+ cleanDeadObjects(FALSE);
+}
+
+void LLViewerObjectList::killAllObjects()
+{
+ // Used only on global destruction.
+ LLViewerObject *objectp;
+
+ for (S32 i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+
+ killObject(objectp);
+ llassert(objectp->isDead());
+ }
+
+ cleanDeadObjects(FALSE);
+
+ if(!mObjects.empty())
+ {
+ llwarns << "LLViewerObjectList::killAllObjects still has entries in mObjects: " << mObjects.count() << llendl;
+ mObjects.clear();
+ }
+
+ if (!mActiveObjects.empty())
+ {
+ llwarns << "Some objects still on active object list!" << llendl;
+ mActiveObjects.clear();
+ }
+
+ if (!mMapObjects.empty())
+ {
+ llwarns << "Some objects still on map object list!" << llendl;
+ mActiveObjects.clear();
+ }
+}
+
+void LLViewerObjectList::cleanDeadObjects(BOOL use_timer)
+{
+ if (!mNumDeadObjects)
+ {
+ // No dead objects, don't need to scan object list.
+ return;
+ }
+
+ S32 i = 0;
+ S32 num_removed = 0;
+ LLViewerObject *objectp;
+ while (i < mObjects.count())
+ {
+ // Scan for all of the dead objects and remove any "global" references to them.
+ objectp = mObjects[i];
+ if (objectp->isDead())
+ {
+ mObjects.remove(i);
+ num_removed++;
+
+ if (num_removed == mNumDeadObjects)
+ {
+ // We've cleaned up all of the dead objects.
+ break;
+ }
+ }
+ else
+ {
+ // iterate, this isn't a dead object.
+ i++;
+ }
+ }
+
+ // We've cleaned the global object list, now let's do some paranoia testing on objects
+ // before blowing away the dead list.
+ mDeadObjects.clear();
+ mNumDeadObjects = 0;
+}
+
+void LLViewerObjectList::updateActive(LLViewerObject *objectp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ if (objectp->isDead())
+ {
+ return; // We don't update dead objects!
+ }
+
+ BOOL active = objectp->isActive();
+ if (active != objectp->onActiveList())
+ {
+ if (active)
+ {
+ //llinfos << "Adding " << objectp->mID << " " << objectp->getPCodeString() << " to active list." << llendl;
+ mActiveObjects.insert(objectp);
+ objectp->setOnActiveList(TRUE);
+ }
+ else
+ {
+ //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list." << llendl;
+ mActiveObjects.erase(objectp);
+ objectp->setOnActiveList(FALSE);
+ }
+ }
+}
+
+
+
+void LLViewerObjectList::shiftObjects(const LLVector3 &offset)
+{
+ // This is called when we shift our origin when we cross region boundaries...
+ // We need to update many object caches, I'll document this more as I dig through the code
+ // cleaning things out...
+
+ if (gNoRender || 0 == offset.magVecSquared())
+ {
+ return;
+ }
+
+ LLViewerObject *objectp;
+ S32 i;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = getObject(i);
+ // There could be dead objects on the object list, so don't update stuff if the object is dead.
+ if (objectp)
+ {
+ objectp->updatePositionCaches();
+
+ if (objectp->mDrawable.notNull() && !objectp->mDrawable->isDead())
+ {
+ gPipeline.markShift(objectp->mDrawable);
+ }
+ }
+ }
+
+ gPipeline.shiftObjects(offset);
+ gWorldPointer->mPartSim.shift(offset);
+}
+
+void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
+{
+ LLColor4 above_water_color = gColors.getColor( "NetMapOtherOwnAboveWater" );
+ LLColor4 below_water_color = gColors.getColor( "NetMapOtherOwnBelowWater" );
+ LLColor4 you_own_above_water_color =
+ gColors.getColor( "NetMapYouOwnAboveWater" );
+ LLColor4 you_own_below_water_color =
+ gColors.getColor( "NetMapYouOwnBelowWater" );
+ LLColor4 group_own_above_water_color =
+ gColors.getColor( "NetMapGroupOwnAboveWater" );
+ LLColor4 group_own_below_water_color =
+ gColors.getColor( "NetMapGroupOwnBelowWater" );
+
+
+ for (S32 i = 0; i < mMapObjects.count(); i++)
+ {
+ LLViewerObject* objectp = mMapObjects[i];
+ if (objectp->isOrphaned() || objectp->isAttachment())
+ {
+ continue;
+ }
+ const LLVector3& scale = objectp->getScale();
+ const LLVector3d pos = objectp->getPositionGlobal();
+ const F64 water_height = F64( objectp->getRegion()->getWaterHeight() );
+ // gWorldPointer->getWaterHeight();
+
+ F32 approx_radius = (scale.mV[VX] + scale.mV[VY]) * 0.5f * 0.5f * 1.3f; // 1.3 is a fudge
+
+ LLColor4U color = above_water_color;
+ if( objectp->permYouOwner() )
+ {
+ const F32 MIN_RADIUS_FOR_OWNED_OBJECTS = 2.f;
+ if( approx_radius < MIN_RADIUS_FOR_OWNED_OBJECTS )
+ {
+ approx_radius = MIN_RADIUS_FOR_OWNED_OBJECTS;
+ }
+
+ if( pos.mdV[VZ] >= water_height )
+ {
+ if ( objectp->permGroupOwner() )
+ {
+ color = group_own_above_water_color;
+ }
+ else
+ {
+ color = you_own_above_water_color;
+ }
+ }
+ else
+ {
+ if ( objectp->permGroupOwner() )
+ {
+ color = group_own_below_water_color;
+ }
+ else
+ {
+ color = you_own_below_water_color;
+ }
+ }
+ }
+ else
+ if( pos.mdV[VZ] < water_height )
+ {
+ color = below_water_color;
+ }
+
+ netmap.renderScaledPointGlobal(
+ pos,
+ color,
+ approx_radius );
+ }
+}
+
+void LLViewerObjectList::renderObjectBounds(const LLVector3 &center)
+{
+}
+
+
+U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall, BOOL keep_pick_list)
+{
+ gRenderForSelect = TRUE;
+
+ // LLTimer pick_timer;
+ if (!keep_pick_list)
+ {
+ LLViewerObject *objectp;
+ S32 i;
+ // Reset all of the GL names to zero.
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+ objectp->mGLName = 0;
+ }
+
+ mSelectPickList.clear();
+
+ std::vector<LLDrawable*> pick_drawables;
+ gPipeline.mObjectPartition->cull(camera, &pick_drawables, TRUE);
+
+ for (std::vector<LLDrawable*>::iterator iter = pick_drawables.begin();
+ iter != pick_drawables.end(); iter++)
+ {
+ LLDrawable* drawablep = *iter;
+
+ LLViewerObject* last_objectp = NULL;
+ for (S32 face_num = 0; face_num < drawablep->getNumFaces(); face_num++)
+ {
+ LLViewerObject* objectp = drawablep->getFace(face_num)->getViewerObject();
+
+ if (objectp && objectp != last_objectp)
+ {
+ mSelectPickList.insert(objectp);
+ last_objectp = objectp;
+ }
+ }
+ }
+
+ LLHUDText::addPickable(mSelectPickList);
+
+ for (objectp = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ objectp;
+ objectp = (LLVOAvatar*)LLCharacter::sInstances.getNextData())
+ {
+ if (!objectp->isDead())
+ {
+ if (objectp->mDrawable.notNull() && objectp->mDrawable->isVisible())
+ {
+ mSelectPickList.insert(objectp);
+ }
+ }
+ }
+
+ // add all hud objects to pick list
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp)
+ {
+ LLViewerJointAttachment* attachmentp;
+ for (attachmentp = avatarp->mAttachmentPoints.getFirstData();
+ attachmentp;
+ attachmentp = avatarp->mAttachmentPoints.getNextData())
+ {
+ if (attachmentp->getIsHUDAttachment())
+ {
+ LLViewerObject* objectp = attachmentp->getObject(0);
+ if (objectp)
+ {
+ mSelectPickList.insert(objectp);
+ for (U32 i = 0; i < objectp->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = objectp->mChildList[i];
+ if (childp)
+ {
+ mSelectPickList.insert(childp);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ S32 num_pickables = (S32)mSelectPickList.size() + LLHUDIcon::getNumInstances();
+
+ S32 step = (0x000fffff - GL_NAME_INDEX_OFFSET) / num_pickables;
+
+ std::set<LLViewerObject*>::iterator pick_it;
+ i = 0;
+ for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end();)
+ {
+ LLViewerObject* objp = (*pick_it);
+ if (!objp || objp->isDead() || !objp->mbCanSelect)
+ {
+ mSelectPickList.erase(pick_it++);
+ continue;
+ }
+
+ objp->mGLName = (i * step) + GL_NAME_INDEX_OFFSET;
+ i++;
+ ++pick_it;
+ }
+
+ LLHUDIcon::generatePickIDs(i * step, step);
+ }
+
+ // At this point, we should only have live drawables/viewer objects
+ gPipeline.renderForSelect();
+ //
+ // Render pass for selected objects
+ //
+ gViewerWindow->renderSelections( TRUE, pick_parcel_wall, FALSE );
+
+ // render pickable ui elements, like names, etc.
+ LLHUDObject::renderAllForSelect();
+
+ gRenderForSelect = FALSE;
+
+ //llinfos << "Rendered " << count << " for select" << llendl;
+ //llinfos << "Took " << pick_timer.getElapsedTimeF32()*1000.f << "ms to pick" << llendl;
+ return 0;
+}
+
+LLViewerObject *LLViewerObjectList::getSelectedObject(const U32 object_id)
+{
+ std::set<LLViewerObject*>::iterator pick_it;
+ for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end(); ++pick_it)
+ {
+ if ((*pick_it)->mGLName == object_id)
+ {
+ return (*pick_it);
+ }
+ }
+ return NULL;
+}
+
+void LLViewerObjectList::addDebugBeacon(const LLVector3 &pos_agent,
+ const LLString &string,
+ const LLColor4 &color,
+ const LLColor4 &text_color,
+ S32 line_width)
+{
+ LLDebugBeacon *beaconp = mDebugBeacons.reserve_block(1);
+ beaconp->mPositionAgent = pos_agent;
+ beaconp->mString = string;
+ beaconp->mColor = color;
+ beaconp->mTextColor = text_color;
+ beaconp->mLineWidth = line_width;
+}
+
+void LLViewerObjectList::resetObjectBeacons()
+{
+ mDebugBeacons.reset();
+}
+
+LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ LLUUID fullid;
+ fullid.generate();
+
+ LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
+ if (!objectp)
+ {
+ llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << llendl;
+ return NULL;
+ }
+
+ mUUIDObjectMap[fullid] = objectp;
+
+ mObjects.put(objectp);
+
+ updateActive(objectp);
+
+ return objectp;
+}
+
+
+
+LLViewerObject *LLViewerObjectList::createObject(const LLPCode pcode, LLViewerRegion *regionp,
+ const LLUUID &uuid, const U32 local_id, const LLHost &sender)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ LLFastTimer t(LLFastTimer::FTM_CREATE_OBJECT);
+
+ LLUUID fullid;
+ if (uuid == LLUUID::null)
+ {
+ fullid.generate();
+ }
+ else
+ {
+ fullid = uuid;
+ }
+
+ LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
+ if (!objectp)
+ {
+ llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << " id:" << fullid << llendl;
+ return NULL;
+ }
+
+ mUUIDObjectMap[fullid] = objectp;
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+
+ mObjects.put(objectp);
+
+ updateActive(objectp);
+
+ return objectp;
+}
+
+LLViewerObject *LLViewerObjectList::replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLViewerObject *old_instance = findObject(id);
+ if (old_instance)
+ {
+ cleanupReferences(old_instance);
+ old_instance->markDead();
+
+ return createObject(pcode, regionp, id, old_instance->getLocalID(), LLHost());
+ }
+ return NULL;
+}
+
+S32 LLViewerObjectList::findReferences(LLDrawable *drawablep) const
+{
+ LLViewerObject *objectp;
+ S32 i;
+ S32 num_refs = 0;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+ if (objectp->mDrawable.notNull())
+ {
+ num_refs += objectp->mDrawable->findReferences(drawablep);
+ }
+ }
+ return num_refs;
+}
+
+
+void LLViewerObjectList::orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+#ifdef ORPHAN_SPAM
+ llinfos << "Orphaning object " << childp->getID() << " with parent " << parent_id << llendl;
+#endif
+
+ // We're an orphan, flag things appropriately.
+ childp->mOrphaned = TRUE;
+ if (childp->mDrawable.notNull())
+ {
+ bool make_invisible = true;
+ LLViewerObject *parentp = (LLViewerObject *)childp->getParent();
+ if (parentp)
+ {
+ if (parentp->getRegion() != childp->getRegion())
+ {
+ // This is probably an object flying across a region boundary, the
+ // object probably ISN'T being reparented, but just got an object
+ // update out of order (child update before parent).
+ make_invisible = false;
+ //llinfos << "Don't make object handoffs invisible!" << llendl;
+ }
+ }
+
+ if (make_invisible)
+ {
+ // Make sure that this object becomes invisible if it's an orphan
+ childp->mDrawable->setState(LLDrawable::FORCE_INVISIBLE);
+ }
+ }
+
+ // Unknown parent, add to orpaned child list
+ U64 parent_info = getIndex(parent_id, ip, port);
+
+ if (-1 == mOrphanParents.find(parent_info))
+ {
+ mOrphanParents.put(parent_info);
+ }
+
+ LLViewerObjectList::OrphanInfo oi(parent_info, childp->mID);
+ if (-1 == mOrphanChildren.find(oi))
+ {
+ mOrphanChildren.put(oi);
+ mNumOrphans++;
+ }
+}
+
+
+void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (objectp->isDead())
+ {
+ llwarns << "Trying to find orphans for dead obj " << objectp->mID
+ << ":" << objectp->getPCodeString() << llendl;
+ return;
+ }
+
+ // See if we are a parent of an orphan.
+ // Note: This code is fairly inefficient but it should happen very rarely.
+ // It can be sped up if this is somehow a performance issue...
+ if (0 == mOrphanParents.count())
+ {
+ // no known orphan parents
+ return;
+ }
+ if (-1 == mOrphanParents.find(getIndex(objectp->mLocalID, ip, port)))
+ {
+ // did not find objectp in OrphanParent list
+ return;
+ }
+
+ S32 i;
+ U64 parent_info = getIndex(objectp->mLocalID, ip, port);
+ BOOL orphans_found = FALSE;
+ // Iterate through the orphan list, and set parents of matching children.
+ for (i = 0; i < mOrphanChildren.count(); i++)
+ {
+ if (mOrphanChildren[i].mParentInfo != parent_info)
+ {
+ continue;
+ }
+ LLViewerObject *childp = findObject(mOrphanChildren[i].mChildInfo);
+ if (childp)
+ {
+ if (childp == objectp)
+ {
+ llwarns << objectp->mID << " has self as parent, skipping!"
+ << llendl;
+ continue;
+ }
+
+#ifdef ORPHAN_SPAM
+ llinfos << "Reunited parent " << objectp->mID
+ << " with child " << childp->mID << llendl;
+ llinfos << "Glob: " << objectp->getPositionGlobal() << llendl;
+ llinfos << "Agent: " << objectp->getPositionAgent() << llendl;
+ addDebugBeacon(objectp->getPositionAgent(),"");
+#endif
+ gPipeline.markMoved(objectp->mDrawable);
+ objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE);
+
+ // Flag the object as no longer orphaned
+ childp->mOrphaned = FALSE;
+ if (childp->mDrawable.notNull())
+ {
+ // Make the drawable visible again and set the drawable parent
+ childp->mDrawable->setState(LLDrawable::CLEAR_INVISIBLE);
+ childp->setDrawableParent(objectp->mDrawable); // LLViewerObjectList::findOrphans()
+ }
+ objectp->addChild(childp);
+ orphans_found = TRUE;
+ }
+ else
+ {
+ llinfos << "Missing orphan child, removing from list" << llendl;
+ mOrphanChildren.remove(i);
+ i--;
+ }
+ }
+
+ // Remove orphan parent and children from lists now that they've been found
+ mOrphanParents.remove(mOrphanParents.find(parent_info));
+
+ i = 0;
+ while (i < mOrphanChildren.count())
+ {
+ if (mOrphanChildren[i].mParentInfo == parent_info)
+ {
+ mOrphanChildren.remove(i);
+ mNumOrphans--;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ if (orphans_found && objectp->isSelected())
+ {
+ LLSelectNode* nodep = gSelectMgr->findSelectNode(objectp);
+ if (nodep && !nodep->mIndividualSelection)
+ {
+ // rebuild selection with orphans
+ gSelectMgr->deselectObjectAndFamily(objectp);
+ gSelectMgr->selectObjectAndFamily(objectp);
+ }
+ }
+}
+
+
+LLViewerObjectList::OrphanInfo::OrphanInfo()
+{
+}
+
+LLViewerObjectList::OrphanInfo::OrphanInfo(const U64 parent_info, const LLUUID child_info)
+ : mParentInfo(parent_info), mChildInfo(child_info)
+{
+}
+
+bool LLViewerObjectList::OrphanInfo::operator==(const OrphanInfo &rhs) const
+{
+ return (mParentInfo == rhs.mParentInfo) && (mChildInfo == rhs.mChildInfo);
+}
+
+bool LLViewerObjectList::OrphanInfo::operator!=(const OrphanInfo &rhs) const
+{
+ return !operator==(rhs);
+}
+
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
new file mode 100644
index 0000000000..a2893c4b7d
--- /dev/null
+++ b/indra/newview/llviewerobjectlist.h
@@ -0,0 +1,256 @@
+/**
+ * @file llviewerobjectlist.h
+ * @brief Description of LLViewerObjectList class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWEROBJECTLIST_H
+#define LL_LLVIEWEROBJECTLIST_H
+
+#include <map>
+#include <set>
+
+// common includes
+#include "doublelinkedlist.h"
+#include "llstat.h"
+#include "lldarrayptr.h"
+#include "llskipmap.h"
+#include "llstring.h"
+
+// project includes
+#include "llviewerobject.h"
+
+class LLNetMap;
+class LLDebugBeacon;
+
+const U32 CLOSE_BIN_SIZE = 10;
+const U32 NUM_BINS = 16;
+
+// GL name = position in object list + GL_NAME_INDEX_OFFSET so that
+// we can have special numbers like zero.
+const U32 GL_NAME_LAND = 0;
+const U32 GL_NAME_PARCEL_WALL = 1;
+
+const U32 GL_NAME_INDEX_OFFSET = 10;
+
+class LLViewerObjectList
+{
+public:
+ LLViewerObjectList();
+ ~LLViewerObjectList();
+
+ void destroy();
+
+ // For internal use only. Does NOT take a local id, takes an index into
+ // an internal dynamic array.
+ inline LLViewerObject *getObject(const S32 index);
+
+ inline LLViewerObject *findObject(const LLUUID &id);
+ LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp); // Create a viewer-side object
+ LLViewerObject *createObject(const LLPCode pcode, LLViewerRegion *regionp,
+ const LLUUID &uuid, const U32 local_id, const LLHost &sender);
+
+ LLViewerObject *replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); // TomY: hack to switch VO instances on the fly
+
+ BOOL killObject(LLViewerObject *objectp);
+ void killObjects(LLViewerRegion *regionp); // Kill all objects owned by a particular region.
+ void killAllObjects();
+ void removeDrawable(LLDrawable* drawablep);
+
+ void cleanDeadObjects(const BOOL use_timer = TRUE); // Clean up the dead object list.
+
+ // Simulator and viewer side object updates...
+ void processUpdateCore(LLViewerObject* objectp, void** data, U32 block, const EObjectUpdateType update_type, LLDataPacker* dpp, BOOL justCreated);
+ void processObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type, bool cached=false, bool compressed=false);
+ void processCompressedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type);
+ void processCachedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type);
+ void updateApparentAngles(LLAgent &agent);
+ void update(LLAgent &agent, LLWorld &world);
+
+ void shiftObjects(const LLVector3 &offset);
+
+ void renderObjectsForMap(LLNetMap &netmap);
+ void renderObjectBounds(const LLVector3 &center);
+
+ void addDebugBeacon(const LLVector3 &pos_agent, const LLString &string,
+ const LLColor4 &color=LLColor4(1.f, 0.f, 0.f, 0.5f),
+ const LLColor4 &text_color=LLColor4(1.f, 1.f, 1.f, 1.f),
+ S32 line_width = 1);
+ void renderObjectBeacons();
+ void resetObjectBeacons();
+
+ void relightAllObjects();
+ void dirtyAllObjectInventory();
+
+ void updateActive(LLViewerObject *objectp);
+ void updateAvatarVisibility();
+
+ // Selection related stuff
+ U32 renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall = FALSE, BOOL keep_pick_list = FALSE);
+ LLViewerObject *getSelectedObject(const U32 object_id);
+
+ inline S32 getNumObjects() { return mObjects.count(); }
+
+ void addToMap(LLViewerObject *objectp);
+ void removeFromMap(LLViewerObject *objectp);
+
+
+ ////////////////////////////////////////////
+ //
+ // Only accessed by markDead in LLViewerObject
+ void cleanupReferences(LLViewerObject *objectp);
+
+ S32 findReferences(LLDrawable *drawablep) const; // Find references to drawable in all objects, and return value.
+
+ S32 getOrphanParentCount() const { return mOrphanParents.count(); }
+ S32 getOrphanCount() const { return mNumOrphans; }
+ void orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port);
+ void findOrphans(LLViewerObject* objectp, U32 ip, U32 port);
+
+public:
+ // Class for keeping track of orphaned objects
+ class OrphanInfo
+ {
+ public:
+ OrphanInfo();
+ OrphanInfo(const U64 parent_info, const LLUUID child_info);
+ bool operator==(const OrphanInfo &rhs) const;
+ bool operator!=(const OrphanInfo &rhs) const;
+ U64 mParentInfo;
+ LLUUID mChildInfo;
+ };
+
+
+ U32 mCurBin; // Current bin we're working on...
+
+ //////////////////////
+ //
+ // Statistics data
+ //
+ //
+ LLStat mNumObjectsStat;
+ LLStat mNumActiveObjectsStat;
+ LLStat mNumNewObjectsStat;
+ LLStat mNumSizeCulledStat;
+ LLStat mNumVisCulledStat;
+
+ S32 mNumNewObjects;
+
+ S32 mNumSizeCulled;
+ S32 mNumVisCulled;
+
+ // if we paused in the last frame
+ // used to discount stats from this frame
+ BOOL mWasPaused;
+
+ static void getUUIDFromLocal(LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port);
+ static void setUUIDAndLocal(const LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port); // Requires knowledge of message system info!
+
+ static BOOL removeFromLocalIDTable(const LLViewerObject &object);
+ // Used ONLY by the orphaned object code.
+ static U64 getIndex(const U32 local_id, const U32 ip, const U32 port);
+
+ S32 mNumUnknownUpdates;
+ S32 mNumDeadObjectUpdates;
+ S32 mNumUnknownKills;
+ S32 mNumDeadObjects;
+protected:
+ LLDynamicArray<U64> mOrphanParents; // LocalID/ip,port of orphaned objects
+ LLDynamicArray<OrphanInfo> mOrphanChildren; // UUID's of orphaned objects
+ S32 mNumOrphans;
+
+ LLDynamicArrayPtr<LLPointer<LLViewerObject>, 256> mObjects;
+ std::set<LLPointer<LLViewerObject> > mActiveObjects;
+
+ LLDynamicArrayPtr<LLPointer<LLViewerObject> > mMapObjects;
+
+ typedef std::map<LLUUID, LLPointer<LLViewerObject> > vo_map;
+ vo_map mDeadObjects; // Need to keep multiple entries per UUID
+
+ std::map<LLUUID, LLPointer<LLViewerObject> > mUUIDObjectMap;
+
+ LLDynamicArray<LLDebugBeacon> mDebugBeacons;
+
+ S32 mCurLazyUpdateIndex;
+
+ static U32 sSimulatorMachineIndex;
+ static LLMap<U64, U32> sIPAndPortToIndex;
+
+ static std::map<U64, LLUUID> sIndexAndLocalIDToUUID;
+
+ std::set<LLViewerObject *> mSelectPickList;
+
+ friend class LLViewerObject;
+};
+
+
+class LLDebugBeacon
+{
+public:
+ ~LLDebugBeacon()
+ {
+ if (mHUDObject.notNull())
+ {
+ mHUDObject->markDead();
+ }
+ }
+
+ LLVector3 mPositionAgent;
+ LLString mString;
+ LLColor4 mColor;
+ LLColor4 mTextColor;
+ S32 mLineWidth;
+ LLPointer<LLHUDObject> mHUDObject;
+};
+
+
+
+// Global object list
+extern LLViewerObjectList gObjectList;
+
+// Inlines
+inline LLViewerObject *LLViewerObjectList::findObject(const LLUUID &id)
+{
+ std::map<LLUUID, LLPointer<LLViewerObject> >::iterator iter = mUUIDObjectMap.find(id);
+ if(iter != mUUIDObjectMap.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+inline LLViewerObject *LLViewerObjectList::getObject(const S32 index)
+{
+ LLViewerObject *objectp;
+ objectp = mObjects[index];
+ if (objectp->isDead())
+ {
+ //llwarns << "Dead object " << objectp->mID << " in getObject" << llendl;
+ return NULL;
+ }
+ return objectp;
+}
+
+inline void LLViewerObjectList::addToMap(LLViewerObject *objectp)
+{
+ mMapObjects.put(objectp);
+}
+
+inline void LLViewerObjectList::removeFromMap(LLViewerObject *objectp)
+{
+ mMapObjects.removeObj(objectp);
+}
+
+
+#endif // LL_VIEWER_OBJECT_LIST_H
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
new file mode 100644
index 0000000000..2b3c364454
--- /dev/null
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -0,0 +1,2568 @@
+/**
+ * @file llviewerparcelmgr.cpp
+ * @brief Viewer-side representation of owned land
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerparcelmgr.h"
+
+// Library includes
+#include "audioengine.h"
+#include "indra_constants.h"
+#include "llcachename.h"
+#include "llgl.h"
+#include "llmediaengine.h"
+#include "llparcel.h"
+#include "llsecondlifeurls.h"
+#include "message.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llfloatergroupinfo.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloaterbuyland.h"
+#include "llfloatergroups.h"
+#include "llfloaterhtml.h"
+#include "llfloatersellland.h"
+#include "llfloatertools.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+//#include "llwebbrowserctrl.h"
+#include "llworld.h"
+#include "lloverlaybar.h"
+#include "roles_constants.h"
+
+const F32 PARCEL_COLLISION_DRAW_SECS = 1.f;
+
+// Globals
+LLViewerParcelMgr *gParcelMgr = NULL;
+
+U8* LLViewerParcelMgr::sPackedOverlay = NULL;
+
+LLUUID gCurrentMovieID = LLUUID::null;
+
+// Local functions
+void optionally_start_music(const LLString& music_url);
+void callback_start_music(S32 option, void* data);
+void optionally_prepare_video(const LLParcel *parcelp);
+void callback_prepare_video(S32 option, void* data);
+void prepare_video(const LLParcel *parcelp);
+void start_video(const LLParcel *parcelp);
+void stop_video();
+void callback_god_force_owner(S32 option, void* user_data);
+
+struct LLGodForceOwnerData
+{
+ LLUUID mOwnerID;
+ S32 mLocalID;
+ LLHost mHost;
+
+ LLGodForceOwnerData(
+ const LLUUID& owner_id,
+ S32 local_parcel_id,
+ const LLHost& host) :
+ mOwnerID(owner_id),
+ mLocalID(local_parcel_id),
+ mHost(host) {}
+};
+
+//
+// Methods
+//
+LLViewerParcelMgr::LLViewerParcelMgr()
+: mSelected(FALSE),
+ mSelectedMultipleOwners(FALSE),
+ mWholeParcelSelected(FALSE),
+ mWestSouth(),
+ mEastNorth(),
+ mSelectedDwell(0.f),
+ mAgentParcelSequenceID(-1),
+ mHoverWestSouth(),
+ mHoverEastNorth(),
+ mRenderCollision(FALSE),
+ mRenderSelection(TRUE),
+ mCollisionBanned(0),
+ mCollisionTimer(),
+ mMediaParcelId(0),
+ mMediaRegionId(0)
+{
+ mParcel = new LLParcel();
+ mAgentParcel = new LLParcel();
+ mHoverParcel = new LLParcel();
+ mCollisionParcel = new LLParcel();
+
+ mParcelsPerEdge = S32( REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS );
+ mHighlightSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
+ resetSegments(mHighlightSegments);
+
+ mCollisionSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
+ resetSegments(mCollisionSegments);
+
+ mBlockedImageID.set(gViewerArt.getString("noentrylines.tga"));
+ mBlockedImage = gImageList.getImage(mBlockedImageID, TRUE, TRUE);
+
+ mPassImageID.set(gViewerArt.getString("noentrypasslines.tga"));
+ mPassImage = gImageList.getImage(mPassImageID, TRUE, TRUE);
+
+ S32 overlay_size = mParcelsPerEdge * mParcelsPerEdge / PARCEL_OVERLAY_CHUNKS;
+ sPackedOverlay = new U8[overlay_size];
+
+ mAgentParcelOverlay = new U8[mParcelsPerEdge * mParcelsPerEdge];
+ S32 i;
+ for (i = 0; i < mParcelsPerEdge * mParcelsPerEdge; i++)
+ {
+ mAgentParcelOverlay[i] = 0;
+ }
+}
+
+
+LLViewerParcelMgr::~LLViewerParcelMgr()
+{
+ delete mParcel;
+ mParcel = NULL;
+
+ delete mAgentParcel;
+ mAgentParcel = NULL;
+
+ delete mCollisionParcel;
+ mCollisionParcel = NULL;
+
+ delete mHoverParcel;
+ mHoverParcel = NULL;
+
+ delete[] mHighlightSegments;
+ mHighlightSegments = NULL;
+
+ delete[] mCollisionSegments;
+ mCollisionSegments = NULL;
+
+ // weird, this crashes if I use an array delete on it!
+ delete sPackedOverlay;
+ sPackedOverlay = NULL;
+
+ delete[] mAgentParcelOverlay;
+ mAgentParcelOverlay = NULL;
+}
+
+
+void LLViewerParcelMgr::destroyGL()
+{
+ mBlockedImage = NULL;
+ mPassImage = NULL;
+}
+
+
+void LLViewerParcelMgr::restoreGL()
+{
+ mBlockedImage = gImageList.getImage(mBlockedImageID, TRUE, TRUE);
+ mPassImage = gImageList.getImage(mPassImageID, TRUE, TRUE);
+}
+
+
+void LLViewerParcelMgr::dump()
+{
+ llinfos << "Parcel Manager Dump" << llendl;
+ llinfos << "mSelected " << S32(mSelected) << llendl;
+ llinfos << "Selected parcel: " << llendl;
+ llinfos << mWestSouth << " to " << mEastNorth << llendl;
+ mParcel->dump();
+ llinfos << "banning " << mParcel->mBanList.size() << llendl;
+
+ access_map_const_iterator cit = mParcel->mBanList.begin();
+ access_map_const_iterator end = mParcel->mBanList.end();
+ for ( ; cit != end; ++cit)
+ {
+ llinfos << "ban id " << (*cit).first << llendl;
+ }
+ llinfos << "Hover parcel:" << llendl;
+ mHoverParcel->dump();
+ llinfos << "Agent parcel:" << llendl;
+ mAgentParcel->dump();
+}
+
+
+LLViewerRegion* LLViewerParcelMgr::getSelectionRegion()
+{
+ if (!gWorldp) return NULL;
+
+ return gWorldp->getRegionFromPosGlobal( mWestSouth );
+}
+
+
+void LLViewerParcelMgr::getDisplayInfo(S32* area_out, S32* claim_out,
+ S32* rent_out,
+ BOOL* for_sale_out,
+ F32* dwell_out)
+{
+ S32 area = 0;
+ S32 price = 0;
+ S32 rent = 0;
+ BOOL for_sale = FALSE;
+ F32 dwell = 0.f;
+
+ if (mSelected)
+ {
+ if (mSelectedMultipleOwners)
+ {
+ area = getClaimableArea();
+ }
+ else
+ {
+ area = getSelectedArea();
+ }
+
+ if (mParcel->getForSale())
+ {
+ price = mParcel->getSalePrice();
+ for_sale = TRUE;
+ }
+ else
+ {
+ price = area * mParcel->getClaimPricePerMeter();
+ for_sale = FALSE;
+ }
+
+ rent = mParcel->getTotalRent();
+
+ dwell = mSelectedDwell;
+ }
+
+ *area_out = area;
+ *claim_out = price;
+ *rent_out = rent;
+ *for_sale_out = for_sale;
+ *dwell_out = dwell;
+}
+
+void LLViewerParcelMgr::getPrimInfo(S32 &sw_max, S32 &sw_total, S32 &max, S32 &total, S32 &owner, S32 &group, S32 &other, S32& selected, F32 &parcel_object_bonus, S32 &other_clean)
+{
+ if (mSelected && mParcel)
+ {
+ sw_max = mParcel->getSimWideMaxPrimCapacity();
+ sw_total = mParcel->getSimWidePrimCount();
+ max = llround(mParcel->getMaxPrimCapacity()*mParcel->getParcelPrimBonus());
+ total = mParcel->getPrimCount();
+ owner = mParcel->getOwnerPrimCount();
+ group = mParcel->getGroupPrimCount();
+ other = mParcel->getOtherPrimCount();
+ selected = mParcel->getSelectedPrimCount();
+ parcel_object_bonus = mParcel->getParcelPrimBonus();
+ other_clean = mParcel->getCleanOtherTime();
+ }
+}
+
+BOOL LLViewerParcelMgr::getMultipleOwners() const
+{
+ return mSelectedMultipleOwners;
+}
+
+
+BOOL LLViewerParcelMgr::getWholeParcelSelected() const
+{
+ return mWholeParcelSelected;
+}
+
+
+S32 LLViewerParcelMgr::getClaimableArea() const
+{
+ const S32 UNIT_AREA = S32( PARCEL_GRID_STEP_METERS * PARCEL_GRID_STEP_METERS );
+ return mSelectedPublicCount * UNIT_AREA;
+}
+
+bool LLViewerParcelMgr::hasOthersSelected() const
+{
+ return mSelectedOtherCount != 0;
+}
+
+
+S32 LLViewerParcelMgr::getSelectedArea() const
+{
+ S32 rv = 0;
+ if(mSelected && mParcel && mWholeParcelSelected)
+ {
+ rv = mParcel->getArea();
+ }
+ else if(mSelected)
+ {
+ F64 width = mEastNorth.mdV[VX] - mWestSouth.mdV[VX];
+ F64 height = mEastNorth.mdV[VY] - mWestSouth.mdV[VY];
+ F32 area = (F32)(width * height);
+ rv = llround(area);
+ }
+ return rv;
+}
+
+void LLViewerParcelMgr::resetSegments(U8* segments)
+{
+ S32 i;
+ S32 count = (mParcelsPerEdge+1)*(mParcelsPerEdge+1);
+ for (i = 0; i < count; i++)
+ {
+ segments[i] = 0x0;
+ }
+}
+
+
+void LLViewerParcelMgr::writeHighlightSegments(F32 west, F32 south, F32 east,
+ F32 north)
+{
+ S32 x, y;
+ S32 min_x = llround( west / PARCEL_GRID_STEP_METERS );
+ S32 max_x = llround( east / PARCEL_GRID_STEP_METERS );
+ S32 min_y = llround( south / PARCEL_GRID_STEP_METERS );
+ S32 max_y = llround( north / PARCEL_GRID_STEP_METERS );
+
+ const S32 STRIDE = mParcelsPerEdge+1;
+
+ // south edge
+ y = min_y;
+ for (x = min_x; x < max_x; x++)
+ {
+ // exclusive OR means that writing to this segment twice
+ // will turn it off
+ mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
+ }
+
+ // west edge
+ x = min_x;
+ for (y = min_y; y < max_y; y++)
+ {
+ mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
+ }
+
+ // north edge - draw the south border on the y+1'th cell,
+ // which given C-style arrays, is item foo[max_y]
+ y = max_y;
+ for (x = min_x; x < max_x; x++)
+ {
+ mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
+ }
+
+ // east edge - draw west border on x+1'th cell
+ x = max_x;
+ for (y = min_y; y < max_y; y++)
+ {
+ mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
+ }
+}
+
+
+void LLViewerParcelMgr::writeSegmentsFromBitmap(U8* bitmap, U8* segments)
+{
+ S32 x;
+ S32 y;
+ const S32 IN_STRIDE = mParcelsPerEdge;
+ const S32 OUT_STRIDE = mParcelsPerEdge+1;
+
+ for (y = 0; y < IN_STRIDE; y++)
+ {
+ x = 0;
+ while( x < IN_STRIDE )
+ {
+ U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];
+
+ S32 bit;
+ for (bit = 0; bit < 8; bit++)
+ {
+ if (byte & (1 << bit) )
+ {
+ S32 out = x+y*OUT_STRIDE;
+
+ // This and one above it
+ segments[out] ^= SOUTH_MASK;
+ segments[out+OUT_STRIDE] ^= SOUTH_MASK;
+
+ // This and one to the right
+ segments[out] ^= WEST_MASK;
+ segments[out+1] ^= WEST_MASK;
+ }
+ x++;
+ }
+ }
+ }
+}
+
+
+void LLViewerParcelMgr::writeAgentParcelFromBitmap(U8* bitmap)
+{
+ S32 x;
+ S32 y;
+ const S32 IN_STRIDE = mParcelsPerEdge;
+
+ for (y = 0; y < IN_STRIDE; y++)
+ {
+ x = 0;
+ while( x < IN_STRIDE )
+ {
+ U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];
+
+ S32 bit;
+ for (bit = 0; bit < 8; bit++)
+ {
+ if (byte & (1 << bit) )
+ {
+ mAgentParcelOverlay[x+y*IN_STRIDE] = 1;
+ }
+ else
+ {
+ mAgentParcelOverlay[x+y*IN_STRIDE] = 0;
+ }
+ x++;
+ }
+ }
+ }
+}
+
+
+// Given a point, find the PARCEL_GRID_STEP x PARCEL_GRID_STEP block
+// containing it and select that.
+void LLViewerParcelMgr::selectParcelAt(const LLVector3d& pos_global)
+{
+ LLVector3d southwest = pos_global;
+ LLVector3d northeast = pos_global;
+
+ southwest -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ southwest.mdV[VX] = llround( southwest.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
+ southwest.mdV[VY] = llround( southwest.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );
+
+ northeast += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ northeast.mdV[VX] = llround( northeast.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
+ northeast.mdV[VY] = llround( northeast.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );
+
+ // Snap to parcel
+ selectLand( southwest, northeast, TRUE );
+}
+
+
+// Tries to select the parcel inside the rectangle
+void LLViewerParcelMgr::selectParcelInRectangle()
+{
+ selectLand(mWestSouth, mEastNorth, TRUE);
+}
+
+
+void LLViewerParcelMgr::selectCollisionParcel()
+{
+ if (!gWorldp)
+ {
+ return;
+ }
+
+ // BUG: Claim to be in the agent's region
+ mWestSouth = gAgent.getRegion()->getOriginGlobal();
+ mEastNorth = mWestSouth;
+ mEastNorth += LLVector3d(PARCEL_GRID_STEP_METERS, PARCEL_GRID_STEP_METERS, 0.0);
+
+ // BUG: must be in the sim you are in
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelPropertiesRequestByID);
+ msg->nextBlockFast(_PREHASH_AgentID);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
+ msg->addS32Fast(_PREHASH_LocalID, mCollisionParcel->getLocalID() );
+ gAgent.sendReliableMessage();
+
+ mRequestResult = PARCEL_RESULT_NO_DATA;
+
+ // Hack: Copy some data over temporarily
+ mParcel->setName( mCollisionParcel->getName() );
+ mParcel->setDesc( mCollisionParcel->getDesc() );
+ mParcel->setPassPrice(mCollisionParcel->getPassPrice());
+ mParcel->setPassHours(mCollisionParcel->getPassHours());
+
+ // clear the list of segments to prevent flashing
+ resetSegments(mHighlightSegments);
+
+ mSelected = TRUE;
+ mWholeParcelSelected = TRUE;
+ notifyObservers();
+ return;
+}
+
+
+// snap_selection = auto-select the hit parcel, if there is exactly one
+void LLViewerParcelMgr::selectLand(const LLVector3d &corner1, const LLVector3d &corner2,
+ BOOL snap_selection)
+{
+ if (!gWorldp)
+ {
+ return;
+ }
+
+ sanitize_corners( corner1, corner2, mWestSouth, mEastNorth );
+
+ // ...x isn't more than one meter away
+ F32 delta_x = getSelectionWidth();
+ if (delta_x * delta_x <= 1.f * 1.f)
+ {
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+
+ // ...y isn't more than one meter away
+ F32 delta_y = getSelectionHeight();
+ if (delta_y * delta_y <= 1.f * 1.f)
+ {
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+
+ // Can't select across region boundary
+ // We need to pull in the upper right corner by a little bit to allow
+ // selection up to the x = 256 or y = 256 edge.
+ LLVector3d east_north_region_check( mEastNorth );
+ east_north_region_check.mdV[VX] -= 0.5;
+ east_north_region_check.mdV[VY] -= 0.5;
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal(mWestSouth);
+ LLViewerRegion *region_other = gWorldp->getRegionFromPosGlobal( east_north_region_check );
+
+ if(!region)
+ {
+ // just in case they somehow selected no land.
+ mSelected = FALSE;
+ return;
+ }
+
+ if (region != region_other)
+ {
+ LLNotifyBox::showXml("CantSelectLandFromMultipleRegions");
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+
+ // Build region global copies of corners
+ LLVector3 wsb_region = region->getPosRegionFromGlobal( mWestSouth );
+ LLVector3 ent_region = region->getPosRegionFromGlobal( mEastNorth );
+
+ /*
+ // Check land to make sure all is either public, owned, or self
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay)
+ {
+ llerrs << "No overlay in LLViewerParcelMgr::selectLand" << llendl;
+ return;
+ }
+
+ U8 start_ownership = overlay->ownership( wsb_region );
+ BOOL identical = TRUE;
+ S32 x_steps = S32( getSelectionWidth() / PARCEL_GRID_STEP_METERS );
+ S32 y_steps = S32( getSelectionHeight() / PARCEL_GRID_STEP_METERS );
+
+ for (S32 x = 0; x < x_steps && identical; x++ )
+ {
+ for (S32 y = 0; y < y_steps && identical; y++ )
+ {
+ // strange recomputation each time to avoid error accumulation
+ LLVector3 check = wsb_region;
+ check.mV[VX] += x * PARCEL_GRID_STEP_METERS;
+ check.mV[VY] += y * PARCEL_GRID_STEP_METERS;
+
+ identical = (start_ownership == overlay->ownership(check));
+ }
+ }
+
+ if (!identical)
+ {
+ add_chat("Can't select mix of your own, other people's and public land.", FALSE, "", FALSE, CHAT_SOURCE_SYSTEM);
+ add_chat("Try selecting a smaller piece of land.", FALSE, "", FALSE, CHAT_SOURCE_SYSTEM);
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+ */
+
+ // Send request message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelPropertiesRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
+ msg->addF32Fast(_PREHASH_West, wsb_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, wsb_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, ent_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, ent_region.mV[VY] );
+ msg->addBOOL("SnapSelection", snap_selection);
+ msg->sendReliable( region->getHost() );
+
+ mRequestResult = PARCEL_RESULT_NO_DATA;
+
+ // clear the list of segments to prevent flashing
+ resetSegments(mHighlightSegments);
+
+ mSelected = TRUE;
+ mWholeParcelSelected = snap_selection;
+ notifyObservers();
+ return;
+}
+
+
+void LLViewerParcelMgr::deselectLand()
+{
+ if (mSelected)
+ {
+ mSelected = FALSE;
+
+ // Invalidate the selected parcel
+ mParcel->setLocalID(-1);
+ mParcel->mAccessList.clear();
+ mParcel->mBanList.clear();
+ //mParcel->mRenterList.reset();
+
+ mSelectedDwell = 0.f;
+
+ notifyObservers();
+ }
+}
+
+
+void LLViewerParcelMgr::addObserver(LLParcelObserver* observer)
+{
+ mObservers.put(observer);
+}
+
+
+void LLViewerParcelMgr::removeObserver(LLParcelObserver* observer)
+{
+ mObservers.removeObj(observer);
+}
+
+
+// Call this method when it's time to update everyone on a new state.
+// Copy the list because an observer could respond by removing itself
+// from the list.
+void LLViewerParcelMgr::notifyObservers()
+{
+ LLDynamicArray<LLParcelObserver*> observers;
+ S32 count = mObservers.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ observers.put(mObservers.get(i));
+ }
+ for(i = 0; i < count; ++i)
+ {
+ observers.get(i)->changed();
+ }
+}
+
+
+//
+// ACCESSORS
+//
+BOOL LLViewerParcelMgr::selectionEmpty() const
+{
+ return !mSelected;
+}
+
+
+LLParcel *LLViewerParcelMgr::getSelectedParcel() const
+{
+ if (mSelected)
+ {
+ return mParcel;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+LLParcel *LLViewerParcelMgr::getAgentParcel() const
+{
+ return mAgentParcel;
+}
+
+// Return whether the agent can build on the land they are on
+BOOL LLViewerParcelMgr::agentCanBuild() const
+{
+ if (mAgentParcel)
+ {
+ return gAgent.isGodlike()
+ || (mAgentParcel->getOwnerID() == gAgent.getID())
+ || (mAgentParcel->getAllowModify());
+ }
+ else
+ {
+ return gAgent.isGodlike();
+ }
+}
+
+BOOL LLViewerParcelMgr::agentCanTakeDamage() const
+{
+ return mAgentParcel->getAllowDamage();
+}
+
+BOOL LLViewerParcelMgr::agentCanFly() const
+{
+ return TRUE;
+}
+
+F32 LLViewerParcelMgr::agentDrawDistance() const
+{
+ return 512.f;
+}
+
+BOOL LLViewerParcelMgr::isOwnedAt(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isOwned( pos_region );
+}
+
+BOOL LLViewerParcelMgr::isOwnedSelfAt(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isOwnedSelf( pos_region );
+}
+
+BOOL LLViewerParcelMgr::isOwnedOtherAt(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isOwnedOther( pos_region );
+}
+
+BOOL LLViewerParcelMgr::isSoundLocal(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isSoundLocal( pos_region );
+}
+
+BOOL LLViewerParcelMgr::canHearSound(const LLVector3d &pos_global) const
+{
+ BOOL in_agent_parcel = inAgentParcel(pos_global);
+
+ if (in_agent_parcel)
+ {
+ // In same parcel as the agent
+ return TRUE;
+ }
+ else
+ {
+ if (gParcelMgr->getAgentParcel()->getSoundLocal())
+ {
+ // Not in same parcel, and agent parcel only has local sound
+ return FALSE;
+ }
+ else if (gParcelMgr->isSoundLocal(pos_global))
+ {
+ // Not in same parcel, and target parcel only has local sound
+ return FALSE;
+ }
+ else
+ {
+ // Not in same parcel, but neither are local sound
+ return TRUE;
+ }
+ }
+}
+
+
+BOOL LLViewerParcelMgr::inAgentParcel(const LLVector3d &pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(pos_global);
+ if (region != gAgent.getRegion())
+ {
+ // Can't be in the agent parcel if you're not in the same region.
+ return FALSE;
+ }
+
+ LLVector3 pos_region = gAgent.getRegion()->getPosRegionFromGlobal(pos_global);
+ S32 row = S32(pos_region.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos_region.mV[VX] / PARCEL_GRID_STEP_METERS);
+
+ if (mAgentParcelOverlay[row*mParcelsPerEdge + column])
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// Returns NULL when there is no valid data.
+LLParcel* LLViewerParcelMgr::getHoverParcel() const
+{
+ if (mHoverRequestResult == PARCEL_RESULT_SUCCESS)
+ {
+ return mHoverParcel;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// Returns NULL when there is no valid data.
+LLParcel* LLViewerParcelMgr::getCollisionParcel() const
+{
+ if (mRenderCollision)
+ {
+ return mCollisionParcel;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//
+// UTILITIES
+//
+
+void LLViewerParcelMgr::render()
+{
+ if (mSelected && mRenderSelection)
+ {
+ // Rendering is done in agent-coordinates, so need to supply
+ // an appropriate offset to the render code.
+ LLViewerRegion *regionp = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!regionp) return;
+
+ renderHighlightSegments(mHighlightSegments, regionp);
+ }
+}
+
+
+void LLViewerParcelMgr::renderParcelCollision()
+{
+ // check for expiration
+ if (mCollisionTimer.getElapsedTimeF32() > PARCEL_COLLISION_DRAW_SECS)
+ {
+ mRenderCollision = FALSE;
+ }
+
+ if (mRenderCollision)
+ {
+ LLViewerRegion* regionp = gAgent.getRegion();
+ BOOL use_pass = mCollisionParcel->getParcelFlag(PF_USE_PASS_LIST);
+ renderCollisionSegments(mCollisionSegments, use_pass, regionp);
+ }
+}
+
+
+void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags)
+{
+ if (!mSelected)
+ {
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+
+ if (flags & AL_BAN)
+ {
+ mParcel->mBanList.clear();
+ }
+ if (flags & AL_ACCESS)
+ {
+ mParcel->mAccessList.clear();
+ }
+
+ // Only the headers differ
+ msg->newMessageFast(_PREHASH_ParcelAccessListRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addS32Fast(_PREHASH_SequenceID, 0);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ msg->addS32("LocalID", mParcel->getLocalID() );
+ msg->sendReliable( region->getHost() );
+}
+
+
+void LLViewerParcelMgr::sendParcelDwellRequest()
+{
+ if (!mSelected)
+ {
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ // Only the headers differ
+ msg->newMessage("ParcelDwellRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addS32("LocalID", mParcel->getLocalID());
+ msg->addUUID("ParcelID", LLUUID::null); // filled in on simulator
+ msg->sendReliable( region->getHost() );
+}
+
+
+void LLViewerParcelMgr::sendParcelGodForceOwner(const LLUUID& owner_id)
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotSetLandOwnerNothingSelected");
+ return;
+ }
+
+ llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;
+
+ // BUG: Only works for the region containing mWestSouthBottom
+ LLVector3d east_north_region_check( mEastNorth );
+ east_north_region_check.mdV[VX] -= 0.5;
+ east_north_region_check.mdV[VY] -= 0.5;
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ // TODO: Add a force owner version of this alert.
+ gViewerWindow->alertXml("CannotContentifyNoRegion");
+ return;
+ }
+
+ // BUG: Make work for cross-region selections
+ LLViewerRegion *region2 = gWorldp->getRegionFromPosGlobal( east_north_region_check );
+ if (region != region2)
+ {
+ gViewerWindow->alertXml("CannotSetLandOwnerMultipleRegions");
+ return;
+ }
+
+ llinfos << "Region " << region->getOriginGlobal() << llendl;
+
+ LLGodForceOwnerData* data = new LLGodForceOwnerData(owner_id, mParcel->getLocalID(), region->getHost());
+ if(mParcel->getAuctionID())
+ {
+ gViewerWindow->alertXml("ForceOwnerAuctionWarning",
+ callback_god_force_owner,
+ (void*)data);
+ }
+ else
+ {
+ callback_god_force_owner(0, (void*)data);
+ }
+}
+
+void callback_god_force_owner(S32 option, void* user_data)
+{
+ LLGodForceOwnerData* data = (LLGodForceOwnerData*)user_data;
+ if(data && (0 == option))
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelGodForceOwner");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("OwnerID", data->mOwnerID);
+ msg->addS32( "LocalID", data->mLocalID);
+ msg->sendReliable(data->mHost);
+ }
+ delete data;
+}
+
+void LLViewerParcelMgr::sendParcelGodForceToContent()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotContentifyNothingSelected");
+ return;
+ }
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotContentifyNoRegion");
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelGodMarkAsContent");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addS32("LocalID", mParcel->getLocalID());
+ msg->sendReliable(region->getHost());
+}
+
+void LLViewerParcelMgr::sendParcelRelease()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandNothingSelected");
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandNoRegion");
+ return;
+ }
+
+ //U32 flags = PR_NONE;
+ //if (god_force) flags |= PR_GOD_FORCE;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelRelease");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID() );
+ msg->nextBlock("Data");
+ msg->addS32("LocalID", mParcel->getLocalID() );
+ //msg->addU32("Flags", flags);
+ msg->sendReliable( region->getHost() );
+
+ // Blitz selection, since the parcel might be non-rectangular, and
+ // we won't have appropriate parcel information.
+ deselectLand();
+}
+
+class LLViewerParcelMgr::ParcelBuyInfo
+{
+public:
+ LLUUID mAgent;
+ LLUUID mSession;
+ LLUUID mGroup;
+ BOOL mIsGroupOwned;
+ BOOL mRemoveContribution;
+ BOOL mIsClaim;
+ LLHost mHost;
+
+ // for parcel buys
+ S32 mParcelID;
+
+ // for land claims
+ F32 mWest;
+ F32 mSouth;
+ F32 mEast;
+ F32 mNorth;
+};
+
+LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy(
+ const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& group_id,
+ BOOL is_group_owned,
+ BOOL is_claim,
+ BOOL remove_contribution)
+{
+ if (!mSelected || !mParcel)
+ {
+ gViewerWindow->alertXml("CannotBuyLandNothingSelected");
+ return NULL;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotBuyLandNoRegion");
+ return NULL;
+ }
+
+ if (is_claim)
+ {
+ llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;
+ llinfos << "Region " << region->getOriginGlobal() << llendl;
+
+ // BUG: Only works for the region containing mWestSouthBottom
+ LLVector3d east_north_region_check( mEastNorth );
+ east_north_region_check.mdV[VX] -= 0.5;
+ east_north_region_check.mdV[VY] -= 0.5;
+
+ LLViewerRegion *region2 = gWorldp->getRegionFromPosGlobal( east_north_region_check );
+
+ if (region != region2)
+ {
+ gViewerWindow->alertXml("CantBuyLandAcrossMultipleRegions");
+ return NULL;
+ }
+ }
+
+
+ ParcelBuyInfo* info = new ParcelBuyInfo;
+
+ info->mAgent = agent_id;
+ info->mSession = session_id;
+ info->mGroup = group_id;
+ info->mIsGroupOwned = is_group_owned;
+ info->mIsClaim = is_claim;
+ info->mRemoveContribution = remove_contribution;
+ info->mHost = region->getHost();
+
+ if (!is_claim)
+ {
+ info->mParcelID = mParcel->getLocalID();
+ }
+ else
+ {
+ // BUG: Make work for cross-region selections
+ LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
+ LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );
+
+ info->mWest = west_south_bottom_region.mV[VX];
+ info->mSouth = west_south_bottom_region.mV[VY];
+ info->mEast = east_north_top_region.mV[VX];
+ info->mNorth = east_north_top_region.mV[VY];
+ }
+
+ return info;
+}
+
+void LLViewerParcelMgr::sendParcelBuy(ParcelBuyInfo* info)
+{
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage(info->mIsClaim ? "ParcelClaim" : "ParcelBuy");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", info->mAgent);
+ msg->addUUID("SessionID", info->mSession);
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", info->mGroup);
+ msg->addBOOL("IsGroupOwned", info->mIsGroupOwned);
+ if (!info->mIsClaim)
+ {
+ msg->addBOOL("RemoveContribution", info->mRemoveContribution);
+ msg->addS32("LocalID", info->mParcelID);
+ }
+ msg->addBOOL("Final", TRUE); // don't allow escrow buys
+ if (info->mIsClaim)
+ {
+ msg->nextBlock("ParcelData");
+ msg->addF32("West", info->mWest);
+ msg->addF32("South", info->mSouth);
+ msg->addF32("East", info->mEast);
+ msg->addF32("North", info->mNorth);
+ }
+ msg->sendReliable(info->mHost);
+}
+
+void LLViewerParcelMgr::deleteParcelBuy(ParcelBuyInfo*& info)
+{
+ delete info;
+ info = NULL;
+}
+
+void LLViewerParcelMgr::sendParcelDeed(const LLUUID& group_id)
+{
+ if (!mSelected || !mParcel)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNothingSelected");
+ return;
+ }
+ if(group_id.isNull())
+ {
+ gViewerWindow->alertXml("CannotDeedLandNoGroup");
+ return;
+ }
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNoRegion");
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelDeedToGroup");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID() );
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", group_id );
+ msg->addS32("LocalID", mParcel->getLocalID() );
+ //msg->addU32("JoinNeighbors", join);
+ msg->sendReliable( region->getHost() );
+}
+
+
+/*
+// *NOTE: We cannot easily make landmarks at global positions because
+// global positions probably refer to a sim/local combination which
+// can move over time. We could implement this by looking up the
+// region global x,y, but it's easier to take it out for now.
+void LLViewerParcelMgr::makeLandmarkAtSelection()
+{
+ // Don't create for parcels you don't own
+ if (gAgent.getID() != mParcel->getOwnerID())
+ {
+ return;
+ }
+
+ LLVector3d global_center(mWestSouth);
+ global_center += mEastNorth;
+ global_center *= 0.5f;
+
+ LLViewerRegion* region;
+ region = gWorldp->getRegionFromPosGlobal(global_center);
+
+ LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
+ LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );
+
+ LLString name("My Land");
+ char buffer[MAX_STRING];
+ S32 pos_x = (S32)floor((west_south_bottom_region.mV[VX] + east_north_top_region.mV[VX]) / 2.0f);
+ S32 pos_y = (S32)floor((west_south_bottom_region.mV[VY] + east_north_top_region.mV[VY]) / 2.0f);
+ sprintf(buffer, "%s in %s (%d, %d)",
+ name.c_str(),
+ region->getName().c_str(),
+ pos_x, pos_y);
+ name.assign(buffer);
+
+ const char* desc = "Claimed land";
+ create_landmark(name.c_str(), desc, global_center);
+}
+*/
+
+const char* LLViewerParcelMgr::getAgentParcelName() const
+{
+ return mAgentParcel->getName();
+}
+
+
+void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, BOOL want_reply_to_update)
+{
+ if (!parcel) return;
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ParcelPropertiesUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );
+
+ U32 flags = 0x0;
+ if (want_reply_to_update) flags |= 0x01;
+ msg->addU32("Flags", flags);
+
+ parcel->packMessage(msg);
+
+ msg->sendReliable( region->getHost() );
+}
+
+
+void LLViewerParcelMgr::requestHoverParcelProperties(const LLVector3d& pos)
+{
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( pos );
+ if (!region)
+ {
+ return;
+ }
+
+ // Send a rectangle around the point.
+ // This means the parcel sent back is at least a rectangle around the point,
+ // which is more efficient for public land. Fewer requests are sent. JC
+ LLVector3 wsb_region = region->getPosRegionFromGlobal( pos );
+
+ F32 west = PARCEL_GRID_STEP_METERS * floor( wsb_region.mV[VX] / PARCEL_GRID_STEP_METERS );
+ F32 south = PARCEL_GRID_STEP_METERS * floor( wsb_region.mV[VY] / PARCEL_GRID_STEP_METERS );
+
+ F32 east = west + PARCEL_GRID_STEP_METERS;
+ F32 north = south + PARCEL_GRID_STEP_METERS;
+
+ // Send request message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelPropertiesRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_SequenceID, HOVERED_PARCEL_SEQ_ID );
+ msg->addF32Fast(_PREHASH_West, west );
+ msg->addF32Fast(_PREHASH_South, south );
+ msg->addF32Fast(_PREHASH_East, east );
+ msg->addF32Fast(_PREHASH_North, north );
+ msg->addBOOL("SnapSelection", FALSE );
+ msg->sendReliable( region->getHost() );
+
+ mHoverRequestResult = PARCEL_RESULT_NO_DATA;
+}
+
+
+// static
+void LLViewerParcelMgr::processParcelOverlay(LLMessageSystem *msg, void **user)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ // Extract the packed overlay information
+ S32 packed_overlay_size = msg->getSizeFast(_PREHASH_ParcelData, _PREHASH_Data);
+
+ if (packed_overlay_size == 0)
+ {
+ llwarns << "Overlay size 0" << llendl;
+ return;
+ }
+
+ S32 parcels_per_edge = gParcelMgr->mParcelsPerEdge;
+ S32 expected_size = parcels_per_edge * parcels_per_edge / PARCEL_OVERLAY_CHUNKS;
+ if (packed_overlay_size != expected_size)
+ {
+ llwarns << "Got parcel overlay size " << packed_overlay_size
+ << " expecting " << expected_size << llendl;
+ return;
+ }
+
+ S32 sequence_id;
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id);
+ msg->getBinaryDataFast(
+ _PREHASH_ParcelData,
+ _PREHASH_Data,
+ sPackedOverlay,
+ expected_size);
+
+ LLHost host = msg->getSender();
+ LLViewerRegion *region = gWorldp->getRegion(host);
+ if (region)
+ {
+ region->mParcelOverlay->uncompressLandOverlay( sequence_id, sPackedOverlay );
+ }
+}
+
+
+// static
+void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **user)
+{
+ S32 request_result;
+ S32 sequence_id;
+ BOOL snap_selection = FALSE;
+ S32 self_count = 0;
+ S32 other_count = 0;
+ S32 public_count = 0;
+ S32 local_id;
+ LLUUID owner_id;
+ BOOL is_group_owned;
+ U32 auction_id = 0;
+ BOOL is_reserved = FALSE;
+ S32 claim_price_per_meter = 0;
+ S32 rent_price_per_meter = 0;
+ S32 claim_date = 0;
+ LLVector3 aabb_min;
+ LLVector3 aabb_max;
+ S32 area = 0;
+ S32 sw_max_prims = 0;
+ S32 sw_total_prims = 0;
+ //LLUUID buyer_id;
+ U8 status = 0;
+ S32 max_prims = 0;
+ S32 total_prims = 0;
+ S32 owner_prims = 0;
+ S32 group_prims = 0;
+ S32 other_prims = 0;
+ S32 selected_prims = 0;
+ F32 parcel_prim_bonus = 1.f;
+ BOOL region_push_override = false;
+ BOOL region_deny_anonymous_override = false;
+ BOOL region_deny_identified_override = false;
+ BOOL region_deny_transacted_override = false;
+
+ S32 other_clean_time = 0;
+
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_RequestResult, request_result );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id );
+
+ if (request_result == PARCEL_RESULT_NO_DATA)
+ {
+ // no valid parcel data
+ llinfos << "no valid parcel data" << llendl;
+ return;
+ }
+
+ // Decide where the data will go.
+ LLParcel* parcel = NULL;
+ if (sequence_id == SELECTED_PARCEL_SEQ_ID)
+ {
+ // ...selected parcels report this sequence id
+ gParcelMgr->mRequestResult = PARCEL_RESULT_SUCCESS;
+ parcel = gParcelMgr->mParcel;
+ }
+ else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
+ parcel = gParcelMgr->mHoverParcel;
+ }
+ else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
+ parcel = gParcelMgr->mCollisionParcel;
+ }
+ else if (sequence_id == 0 || sequence_id > gParcelMgr->mAgentParcelSequenceID)
+ {
+ // new agent parcel
+ gParcelMgr->mAgentParcelSequenceID = sequence_id;
+ parcel = gParcelMgr->mAgentParcel;
+ }
+ else
+ {
+ llinfos << "out of order agent parcel sequence id " << sequence_id
+ << " last good " << gParcelMgr->mAgentParcelSequenceID
+ << llendl;
+ return;
+ }
+
+ msg->getBOOL("ParcelData", "SnapSelection", snap_selection);
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelfCount, self_count);
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherCount, other_count);
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_PublicCount, public_count);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_LocalID, local_id );
+ msg->getUUIDFast(_PREHASH_ParcelData, _PREHASH_OwnerID, owner_id);
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_IsGroupOwned, is_group_owned);
+ msg->getU32Fast(_PREHASH_ParcelData, _PREHASH_AuctionID, auction_id);
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_ReservedNewbie, is_reserved);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_ClaimDate, claim_date);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_ClaimPrice, claim_price_per_meter);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_RentPrice, rent_price_per_meter);
+ msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMin, aabb_min);
+ msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMax, aabb_max);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_Area, area );
+ //msg->getUUIDFast( _PREHASH_ParcelData, _PREHASH_BuyerID, buyer_id);
+ msg->getU8("ParcelData", "Status", status);
+ msg->getS32("ParcelData", "SimWideMaxPrims", sw_max_prims );
+ msg->getS32("ParcelData", "SimWideTotalPrims", sw_total_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_MaxPrims, max_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_TotalPrims, total_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OwnerPrims, owner_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_GroupPrims, group_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherPrims, other_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelectedPrims, selected_prims );
+ msg->getF32Fast(_PREHASH_ParcelData, _PREHASH_ParcelPrimBonus, parcel_prim_bonus );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionPushOverride, region_push_override );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyAnonymous, region_deny_anonymous_override );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyIdentified, region_deny_identified_override );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyTransacted, region_deny_transacted_override );
+
+ msg->getS32("ParcelData", "OtherCleanTime", other_clean_time );
+
+ // Actually extract the data.
+ if (parcel)
+ {
+ parcel->init(owner_id,
+ FALSE, FALSE, FALSE,
+ claim_date, claim_price_per_meter, rent_price_per_meter,
+ area, other_prims, parcel_prim_bonus, is_group_owned);
+ parcel->setLocalID(local_id);
+ parcel->setAABBMin(aabb_min);
+ parcel->setAABBMax(aabb_max);
+
+ parcel->setAuctionID(auction_id);
+ parcel->setReservedForNewbie(is_reserved);
+ parcel->setOwnershipStatus((LLParcel::EOwnershipStatus)status);
+
+ parcel->setSimWideMaxPrimCapacity(sw_max_prims);
+ parcel->setSimWidePrimCount(sw_total_prims);
+ parcel->setMaxPrimCapacity(max_prims);
+ parcel->setOwnerPrimCount(owner_prims);
+ parcel->setGroupPrimCount(group_prims);
+ parcel->setOtherPrimCount(other_prims);
+ parcel->setSelectedPrimCount(selected_prims);
+ parcel->setParcelPrimBonus(parcel_prim_bonus);
+
+ parcel->setCleanOtherTime(other_clean_time);
+ parcel->setRegionPushOverride(region_push_override);
+ parcel->setRegionDenyAnonymousOverride(region_deny_anonymous_override);
+ parcel->setRegionDenyIdentifiedOverride(region_deny_identified_override);
+ parcel->setRegionDenyTransactedOverride(region_deny_transacted_override);
+ parcel->unpackMessage(msg);
+
+ if (parcel == gParcelMgr->mAgentParcel)
+ {
+ S32 bitmap_size = gParcelMgr->mParcelsPerEdge
+ * gParcelMgr->mParcelsPerEdge
+ / 8;
+ U8* bitmap = new U8[ bitmap_size ];
+ msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
+
+ gParcelMgr->writeAgentParcelFromBitmap(bitmap);
+ delete[] bitmap;
+ }
+ }
+
+ // Handle updating selections, if necessary.
+ if (sequence_id == SELECTED_PARCEL_SEQ_ID)
+ {
+ // Update selected counts
+ gParcelMgr->mSelectedSelfCount = self_count;
+ gParcelMgr->mSelectedOtherCount = other_count;
+ gParcelMgr->mSelectedPublicCount = public_count;
+
+ gParcelMgr->mSelectedMultipleOwners =
+ (request_result == PARCEL_RESULT_MULTIPLE);
+
+ // Select the whole parcel
+ LLViewerRegion *region = gWorldp->getRegion( msg->getSender() );
+ if (region)
+ {
+ if (!snap_selection)
+ {
+ // don't muck with the westsouth and eastnorth.
+ // just highlight it
+ LLVector3 west_south = region->getPosRegionFromGlobal(gParcelMgr->mWestSouth);
+ LLVector3 east_north = region->getPosRegionFromGlobal(gParcelMgr->mEastNorth);
+
+ gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments);
+ gParcelMgr->writeHighlightSegments(
+ west_south.mV[VX],
+ west_south.mV[VY],
+ east_north.mV[VX],
+ east_north.mV[VY] );
+ gParcelMgr->mWholeParcelSelected = FALSE;
+ }
+ else if (0 == local_id)
+ {
+ // this is public land, just highlight the selection
+ gParcelMgr->mWestSouth = region->getPosGlobalFromRegion( aabb_min );
+ gParcelMgr->mEastNorth = region->getPosGlobalFromRegion( aabb_max );
+
+ gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments);
+ gParcelMgr->writeHighlightSegments(
+ aabb_min.mV[VX],
+ aabb_min.mV[VY],
+ aabb_max.mV[VX],
+ aabb_max.mV[VY] );
+ gParcelMgr->mWholeParcelSelected = TRUE;
+ }
+ else
+ {
+ gParcelMgr->mWestSouth = region->getPosGlobalFromRegion( aabb_min );
+ gParcelMgr->mEastNorth = region->getPosGlobalFromRegion( aabb_max );
+
+ // Owned land, highlight the boundaries
+ S32 bitmap_size = gParcelMgr->mParcelsPerEdge
+ * gParcelMgr->mParcelsPerEdge
+ / 8;
+ U8* bitmap = new U8[ bitmap_size ];
+ msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
+
+ gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments);
+ gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mHighlightSegments );
+
+ delete bitmap;
+ bitmap = NULL;
+
+ gParcelMgr->mWholeParcelSelected = TRUE;
+ }
+
+ // Request access list information for this land
+ gParcelMgr->sendParcelAccessListRequest(AL_ACCESS | AL_BAN);
+
+ // Request dwell for this land, if it's not public land.
+ gParcelMgr->mSelectedDwell = 0.f;
+ if (0 != local_id)
+ {
+ gParcelMgr->sendParcelDwellRequest();
+ }
+
+ gParcelMgr->mSelected = TRUE;
+ gParcelMgr->notifyObservers();
+ }
+ }
+ else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
+ {
+ // We're about to collide with this parcel
+ gParcelMgr->mRenderCollision = TRUE;
+ gParcelMgr->mCollisionTimer.reset();
+
+ // Differentiate this parcel if we are banned from it.
+ if (sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mCollisionBanned = BA_BANNED;
+ }
+ else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mCollisionBanned = BA_NOT_IN_GROUP;
+ }
+ else
+ {
+ gParcelMgr->mCollisionBanned = BA_NOT_ON_LIST;
+
+ }
+
+ S32 bitmap_size = gParcelMgr->mParcelsPerEdge
+ * gParcelMgr->mParcelsPerEdge
+ / 8;
+ U8* bitmap = new U8[ bitmap_size ];
+ msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
+
+ gParcelMgr->resetSegments(gParcelMgr->mCollisionSegments);
+ gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mCollisionSegments );
+
+ delete bitmap;
+ bitmap = NULL;
+
+ }
+ else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
+ {
+ LLViewerRegion *region = gWorldp->getRegion( msg->getSender() );
+ if (region)
+ {
+ gParcelMgr->mHoverWestSouth = region->getPosGlobalFromRegion( aabb_min );
+ gParcelMgr->mHoverEastNorth = region->getPosGlobalFromRegion( aabb_max );
+ }
+ else
+ {
+ gParcelMgr->mHoverWestSouth.clearVec();
+ gParcelMgr->mHoverEastNorth.clearVec();
+ }
+ }
+ else
+ {
+ // It's the agent parcel
+ BOOL new_parcel = parcel ? FALSE : TRUE;
+ if (parcel)
+ {
+ S32 parcelid = parcel->getLocalID();
+ U64 regionid = gAgent.getRegion()->getHandle();
+ if (parcelid != gParcelMgr->mMediaParcelId || regionid != gParcelMgr->mMediaRegionId)
+ {
+ gParcelMgr->mMediaParcelId = parcelid;
+ gParcelMgr->mMediaRegionId = regionid;
+ new_parcel = TRUE;
+ }
+ }
+ // look for music.
+ if (gAudiop)
+ {
+ if (parcel)
+ {
+ LLString music_url_raw = parcel->getMusicURL();
+
+ // Trim off whitespace from front and back
+ LLString music_url = music_url_raw;
+ LLString::trim(music_url);
+
+ // On entering a new parcel, stop the last stream if the
+ // new parcel has a different music url. (Empty URL counts
+ // as different.)
+ const char* stream_url = gAudiop->getInternetStreamURL();
+
+ if (music_url.empty() || music_url != stream_url)
+ {
+ // URL is different from one currently playing.
+ gAudiop->stopInternetStream();
+
+ // If there is a new music URL and it's valid, play it.
+ if (music_url.size() > 12)
+ {
+ if (music_url.substr(0,7) == "http://")
+ {
+ optionally_start_music(music_url);
+ }
+ }
+ else if (gAudiop->getInternetStreamURL()[0])
+ {
+ llinfos << "Stopping parcel music" << llendl;
+ gAudiop->startInternetStream(NULL);
+ }
+ }
+ }
+ else
+ {
+ // Public land has no music
+ gAudiop->stopInternetStream();
+ }
+ }//if gAudiop
+
+ // now check for video
+ if (LLMediaEngine::getInstance ()->isAvailable ())
+ {
+ // we have a player
+ if (parcel)
+ {
+ // we're in a parcel
+ std::string mediaUrl = std::string ( parcel->getMediaURL () );
+ LLString::trim(mediaUrl);
+
+ // something changed
+ LLMediaEngine* me = LLMediaEngine::getInstance();
+ if ( ( me->getUrl () != mediaUrl )
+ || ( me->getImageUUID () != parcel->getMediaID () )
+ || ( me->isAutoScaled () != parcel->getMediaAutoScale () ) )
+ {
+ BOOL video_was_playing = FALSE;
+ LLMediaBase* renderer = me->getMediaRenderer();
+ if (renderer && (renderer->isPlaying() || renderer->isLooping()))
+ {
+ video_was_playing = TRUE;
+ }
+
+ stop_video();
+
+ if ( !mediaUrl.empty () )
+ {
+ // Someone has "changed the channel", changing the URL of a video
+ // you were already watching. Do we want to automatically start playing? JC
+ if (!new_parcel
+ && gSavedSettings.getBOOL("AudioStreamingVideo")
+ && video_was_playing)
+ {
+ start_video(parcel);
+ }
+ else
+ {
+ // "Prepare" the media engine, but don't auto-play. JC
+ optionally_prepare_video(parcel);
+ }
+ }
+ }
+ }
+ else
+ {
+ stop_video();
+ }
+ }
+ else
+ {
+ // no audio player, do a first use dialog if their is media here
+ if (parcel)
+ {
+ std::string mediaUrl = std::string ( parcel->getMediaURL () );
+ if (!mediaUrl.empty ())
+ {
+ if (gSavedSettings.getWarning("QuickTimeInstalled"))
+ {
+ gSavedSettings.setWarning("QuickTimeInstalled", FALSE);
+
+ LLNotifyBox::showXml("NoQuickTime" );
+ };
+ }
+ }
+ }
+
+ };
+}
+
+void optionally_start_music(const LLString& music_url)
+{
+ if (gSavedSettings.getWarning("FirstStreamingMusic"))
+ {
+ void* data = (void*)strdup(music_url.c_str());
+ gViewerWindow->alertXml("ParcelCanPlayMusic",
+ callback_start_music,
+ (void*)data);
+
+ }
+ else if (gSavedSettings.getBOOL("AudioStreamingMusic"))
+ {
+ // Make the user click the start button on the overlay bar. JC
+ // llinfos << "Starting parcel music " << music_url << llendl;
+
+ // now only play music when you enter a new parcel if the control is in PLAY state
+ // changed as part of SL-4878
+ if ( gOverlayBar->getMusicRemoteControl ()->getTransportState () == LLMediaRemoteCtrl::Play )
+ {
+ if (gAudiop)
+ {
+ gAudiop->startInternetStream(music_url.c_str());
+ }
+ };
+ }
+}
+
+
+void callback_start_music(S32 option, void* data)
+{
+ const char* music_url = (const char*)data;
+
+ if (0 == option)
+ {
+ gSavedSettings.setBOOL("AudioStreamingMusic", TRUE);
+ llinfos << "Starting first parcel music " << music_url << llendl;
+ if (gAudiop)
+ {
+ gAudiop->startInternetStream(music_url);
+ LLMediaRemoteCtrl* ctrl = gOverlayBar->getMusicRemoteControl();
+ ctrl->setTransportState( LLMediaRemoteCtrl::Play, FALSE );
+ }
+ }
+ else
+ {
+ gSavedSettings.setBOOL("AudioStreamingMusic", FALSE);
+ }
+
+ gSavedSettings.setWarning("FirstStreamingMusic", FALSE);
+
+ delete [] music_url;
+ music_url = NULL;
+}
+
+void prepare_video(const LLParcel *parcel)
+{
+ std::string mediaUrl;
+ if (parcel->getParcelFlag(PF_URL_RAW_HTML))
+ {
+ mediaUrl = std::string("data:");
+ mediaUrl.append(parcel->getMediaURL());
+ }
+ else
+ {
+ mediaUrl = std::string ( parcel->getMediaURL () );
+ }
+ LLMediaEngine::getInstance ()->setUrl ( mediaUrl );
+ LLMediaEngine::getInstance ()->setImageUUID ( parcel->getMediaID () );
+ LLMediaEngine::getInstance ()->setAutoScaled ( parcel->getMediaAutoScale () ? TRUE : FALSE ); // (U8 instead of BOOL for future expansion)
+}
+
+void start_video(const LLParcel *parcel)
+{
+ prepare_video(parcel);
+ bool web_url = (parcel->getParcelFlag(PF_URL_WEB_PAGE) || parcel->getParcelFlag(PF_URL_RAW_HTML));
+ std::string path( "" );
+ #if LL_MOZILLA_ENABLED
+ if (web_url)
+ {
+ path = get_mozilla_path();
+ }
+ #endif
+ LLMediaEngine::getInstance ()->convertImageAndLoadUrl ( true, web_url, path);
+}
+
+void stop_video()
+{
+ // set up remote control so stop is selected
+ LLMediaEngine::getInstance ()->stop ();
+ if (gOverlayBar)
+ {
+ gOverlayBar->refresh ();
+ }
+
+ if (LLMediaEngine::getInstance ()->isLoaded())
+ {
+ LLMediaEngine::getInstance ()->unload ();
+
+ gImageList.updateMovieImage(LLUUID::null, FALSE);
+ gCurrentMovieID.setNull();
+ }
+
+ LLMediaEngine::getInstance ()->setUrl ( "" );
+ LLMediaEngine::getInstance ()->setImageUUID ( LLUUID::null );
+
+}
+
+void optionally_prepare_video(const LLParcel *parcelp)
+{
+ if (gSavedSettings.getWarning("FirstStreamingVideo"))
+ {
+ gViewerWindow->alertXml("ParcelCanPlayMedia",
+ callback_prepare_video,
+ (void*)parcelp);
+ }
+ else
+ {
+ llinfos << "Entering parcel " << parcelp->getLocalID() << " with video " << parcelp->getMediaURL() << llendl;
+ prepare_video(parcelp);
+ }
+}
+
+
+void callback_prepare_video(S32 option, void* data)
+{
+ const LLParcel *parcelp = (const LLParcel *)data;
+
+ if (0 == option)
+ {
+ gSavedSettings.setBOOL("AudioStreamingVideo", TRUE);
+ llinfos << "Starting parcel video " << parcelp->getMediaURL() << " on parcel " << parcelp->getLocalID() << llendl;
+ gMessageSystem->setHandlerFunc("ParcelMediaCommandMessage", LLMediaEngine::process_parcel_media);
+ gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", LLMediaEngine::process_parcel_media_update );
+ prepare_video(parcelp);
+ }
+ else
+ {
+ gMessageSystem->setHandlerFunc("ParcelMediaCommandMessage", null_message_callback);
+ gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", null_message_callback );
+ gSavedSettings.setBOOL("AudioStreamingVideo", FALSE);
+ }
+
+ gSavedSettings.setWarning("FirstStreamingVideo", FALSE);
+}
+
+// static
+void LLViewerParcelMgr::processParcelAccessListReply(LLMessageSystem *msg, void **user)
+{
+ LLUUID agent_id;
+ S32 sequence_id = 0;
+ U32 message_flags = 0x0;
+ S32 parcel_id = -1;
+
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_AgentID, agent_id);
+ msg->getS32Fast( _PREHASH_Data, _PREHASH_SequenceID, sequence_id ); //ignored
+ msg->getU32Fast( _PREHASH_Data, _PREHASH_Flags, message_flags);
+ msg->getS32Fast( _PREHASH_Data, _PREHASH_LocalID, parcel_id);
+
+ LLParcel* parcel = gParcelMgr->mParcel;
+ if (!parcel) return;
+
+ if (parcel_id != parcel->getLocalID())
+ {
+ llwarns << "processParcelAccessListReply for parcel " << parcel_id
+ << " which isn't the selected parcel" << llendl;
+ return;
+ }
+
+ if (message_flags & AL_ACCESS)
+ {
+ parcel->unpackAccessEntries(msg, &(parcel->mAccessList) );
+ }
+ else if (message_flags & AL_BAN)
+ {
+ parcel->unpackAccessEntries(msg, &(parcel->mBanList) );
+ }
+ /*else if (message_flags & AL_RENTER)
+ {
+ parcel->unpackAccessEntries(msg, &(parcel->mRenterList) );
+ }*/
+
+ gParcelMgr->notifyObservers();
+}
+
+
+// static
+void LLViewerParcelMgr::processParcelDwellReply(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+
+ S32 local_id;
+ msg->getS32("Data", "LocalID", local_id);
+
+ LLUUID parcel_id;
+ msg->getUUID("Data", "ParcelID", parcel_id);
+
+ F32 dwell;
+ msg->getF32("Data", "Dwell", dwell);
+
+ if (local_id == gParcelMgr->mParcel->getLocalID())
+ {
+ gParcelMgr->mSelectedDwell = dwell;
+ gParcelMgr->notifyObservers();
+ }
+}
+
+
+void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which)
+{
+
+ LLUUID transactionUUID;
+ transactionUUID.generate();
+
+ if (!mSelected)
+ {
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ LLParcel* parcel = mParcel;
+ if (!parcel) return;
+
+ if (which & AL_ACCESS)
+ {
+ S32 count = parcel->mAccessList.size();
+ S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
+ S32 sequence_id = 1;
+ BOOL start_message = TRUE;
+ BOOL initial = TRUE;
+
+ access_map_const_iterator cit = parcel->mAccessList.begin();
+ access_map_const_iterator end = parcel->mAccessList.end();
+ while ( (cit != end) || initial )
+ {
+ if (start_message)
+ {
+ msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addU32Fast(_PREHASH_Flags, AL_ACCESS);
+ msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
+ msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
+ msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
+ msg->addS32Fast(_PREHASH_Sections, num_sections);
+ start_message = FALSE;
+
+ if (initial && (cit == end))
+ {
+ // pack an empty block if there will be no data
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
+ msg->addS32Fast(_PREHASH_Time, 0 );
+ msg->addU32Fast(_PREHASH_Flags, 0 );
+ }
+
+ initial = FALSE;
+ sequence_id++;
+
+ }
+
+ while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
+ {
+
+ const LLAccessEntry& entry = (*cit).second;
+
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, entry.mID );
+ msg->addS32Fast(_PREHASH_Time, entry.mTime );
+ msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
+ ++cit;
+ }
+
+ start_message = TRUE;
+ msg->sendReliable( region->getHost() );
+ }
+ }
+
+ if (which & AL_BAN)
+ {
+ S32 count = parcel->mBanList.size();
+ S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
+ S32 sequence_id = 1;
+ BOOL start_message = TRUE;
+ BOOL initial = TRUE;
+
+ access_map_const_iterator cit = parcel->mBanList.begin();
+ access_map_const_iterator end = parcel->mBanList.end();
+ while ( (cit != end) || initial )
+ {
+ if (start_message)
+ {
+ msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addU32Fast(_PREHASH_Flags, AL_BAN);
+ msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
+ msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
+ msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
+ msg->addS32Fast(_PREHASH_Sections, num_sections);
+ start_message = FALSE;
+
+ if (initial && (cit == end))
+ {
+ // pack an empty block if there will be no data
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
+ msg->addS32Fast(_PREHASH_Time, 0 );
+ msg->addU32Fast(_PREHASH_Flags, 0 );
+ }
+
+ initial = FALSE;
+ sequence_id++;
+
+ }
+
+ while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
+ {
+ const LLAccessEntry& entry = (*cit).second;
+
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, entry.mID );
+ msg->addS32Fast(_PREHASH_Time, entry.mTime );
+ msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
+ ++cit;
+ }
+
+ start_message = TRUE;
+ msg->sendReliable( region->getHost() );
+ }
+ }
+}
+
+
+void LLViewerParcelMgr::deedLandToGroup()
+{
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(mParcel->getGroupID(), group_name);
+ LLString::format_map_t args;
+ args["[AREA]"] = llformat("%d", mParcel->getArea());
+ args["[GROUP_NAME]"] = group_name;
+ if(mParcel->getContributeWithDeed())
+ {
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ first_name[0] = '\0';
+ char last_name[DB_FIRST_NAME_BUF_SIZE];
+ last_name[0] = '\0';
+ gCacheName->getName(mParcel->getOwnerID(), first_name, last_name);
+ args["[FIRST_NAME]"] = first_name;
+ args["[LAST_NAME]"] = last_name;
+ gViewerWindow->alertXml("DeedLandToGroupWithContribution",args, deedAlertCB, NULL);
+ }
+ else
+ {
+ gViewerWindow->alertXml("DeedLandToGroup",args, deedAlertCB, NULL);
+ }
+}
+
+// static
+void LLViewerParcelMgr::deedAlertCB(S32 option, void*)
+{
+ if (option == 0)
+ {
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ LLUUID group_id;
+ if(parcel)
+ {
+ group_id = parcel->getGroupID();
+ }
+ gParcelMgr->sendParcelDeed(group_id);
+ }
+}
+
+
+void LLViewerParcelMgr::startReleaseLand()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandNothingSelected");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_NO_DATA)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandWatingForServer");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_MULTIPLE)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandSelected");
+ return;
+ }
+
+ if (!isParcelOwnedByAgent(mParcel, GP_LAND_RELEASE)
+ && !(gAgent.canManageEstate()))
+ {
+ gViewerWindow->alertXml("CannotReleaseLandDontOwn");
+ return;
+ }
+
+ LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandRegionNotFound");
+ return;
+ }
+/*
+ if ((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ && !gAgent.isGodlike())
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REGION]"] = region->getName();
+ gViewerWindow->alertXml("CannotReleaseLandNoTransfer", args);
+ return;
+ }
+*/
+
+ if (!mWholeParcelSelected)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandPartialSelection");
+ return;
+ }
+
+ // Compute claim price
+ LLStringBase<char>::format_map_t args;
+ args["[AREA]"] = llformat("%d",mParcel->getArea());
+ gViewerWindow->alertXml("ReleaseLandWarning", args,
+ releaseAlertCB, this);
+}
+
+bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const
+{
+ if (!parcel)
+ {
+ return false;
+ }
+
+ if (mSelected && parcel == mParcel)
+ {
+ if (mRequestResult == PARCEL_RESULT_NO_DATA)
+ {
+ return false;
+ }
+ }
+
+ const LLUUID& parcelOwner = parcel->getOwnerID();
+ const LLUUID& authorizeBuyer = parcel->getAuthorizedBuyerID();
+
+ if (parcel->isPublic())
+ {
+ return true; // change this if want to make it gods only
+ }
+
+ bool isForSale = parcel->getForSale()
+ && ((parcel->getSalePrice() > 0) || (authorizeBuyer.notNull()));
+
+ bool isEmpowered
+ = forGroup ? gAgent.hasPowerInActiveGroup(GP_LAND_DEED) == TRUE : true;
+
+ bool isOwner
+ = parcelOwner == (forGroup ? gAgent.getGroupID() : gAgent.getID());
+
+ bool isAvailable
+ = parcel->getReservedForNewbie()
+ ? (!forGroup && gStatusBar->getSquareMetersCommitted() == 0)
+ : true;
+ //FIXME: should be based on never_owned_land, see SL-10728
+
+ bool isAuthorized
+ = (authorizeBuyer.isNull() || (gAgent.getID() == authorizeBuyer));
+
+ return isForSale && !isOwner && isAuthorized && isAvailable && isEmpowered;
+}
+
+
+void LLViewerParcelMgr::startBuyLand(BOOL is_for_group)
+{
+ LLFloaterBuyLand::buyLand(getSelectionRegion(), mParcel, is_for_group == TRUE);
+}
+
+void LLViewerParcelMgr::startSellLand()
+{
+ LLFloaterSellLand::sellLand(getSelectionRegion(), mParcel);
+}
+
+void LLViewerParcelMgr::startDivideLand()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotDivideLandNothingSelected");
+ return;
+ }
+
+ if (mWholeParcelSelected)
+ {
+ gViewerWindow->alertXml("CannotDivideLandPartialSelection");
+ return;
+ }
+
+ gViewerWindow->alertXml("LandDivideWarning",
+ callbackDivideLand,
+ this);
+}
+
+// static
+void LLViewerParcelMgr::callbackDivideLand(S32 option, void* data)
+{
+ LLViewerParcelMgr* self = (LLViewerParcelMgr*)data;
+
+ LLVector3d parcel_center = (self->mWestSouth + self->mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotDivideLandNoRegion");
+ return;
+ }
+
+ if (0 == option)
+ {
+ LLVector3 west_south = region->getPosRegionFromGlobal(self->mWestSouth);
+ LLVector3 east_north = region->getPosRegionFromGlobal(self->mEastNorth);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelDivide");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addF32("West", west_south.mV[VX]);
+ msg->addF32("South", west_south.mV[VY]);
+ msg->addF32("East", east_north.mV[VX]);
+ msg->addF32("North", east_north.mV[VY]);
+ msg->sendReliable(region->getHost());
+ }
+}
+
+
+void LLViewerParcelMgr::startJoinLand()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotJoinLandNothingSelected");
+ return;
+ }
+
+ if (mWholeParcelSelected)
+ {
+ gViewerWindow->alertXml("CannotJoinLandEntireParcelSelected");
+ return;
+ }
+
+ if (!mSelectedMultipleOwners)
+ {
+ gViewerWindow->alertXml("CannotJoinLandSelection");
+ return;
+ }
+
+ gViewerWindow->alertXml("JoinLandWarning",
+ callbackJoinLand,
+ this);
+}
+
+// static
+void LLViewerParcelMgr::callbackJoinLand(S32 option, void* data)
+{
+ LLViewerParcelMgr* self = (LLViewerParcelMgr*)data;
+
+ LLVector3d parcel_center = (self->mWestSouth + self->mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotJoinLandNoRegion");
+ return;
+ }
+
+ if (0 == option)
+ {
+ LLVector3 west_south = region->getPosRegionFromGlobal(self->mWestSouth);
+ LLVector3 east_north = region->getPosRegionFromGlobal(self->mEastNorth);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelJoin");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addF32("West", west_south.mV[VX]);
+ msg->addF32("South", west_south.mV[VY]);
+ msg->addF32("East", east_north.mV[VX]);
+ msg->addF32("North", east_north.mV[VY]);
+ msg->sendReliable(region->getHost());
+ }
+}
+
+
+void LLViewerParcelMgr::startDeedLandToGroup()
+{
+ if (!mSelected || !mParcel)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNothingSelected");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_NO_DATA)
+ {
+ gViewerWindow->alertXml("CannotDeedLandWaitingForServer");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_MULTIPLE)
+ {
+ gViewerWindow->alertXml("CannotDeedLandMultipleSelected");
+ return;
+ }
+
+ LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNoRegion");
+ return;
+ }
+
+ /*
+ if(!gAgent.isGodlike())
+ {
+ if((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ && (mParcel->getOwnerID() != region->getOwner()))
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REGION]"] = region->getName();
+ gViewerWindow->alertXml("CannotDeedLandNoTransfer", args);
+ return;
+ }
+ }
+ */
+
+ deedLandToGroup();
+}
+void LLViewerParcelMgr::reclaimParcel()
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ LLViewerRegion* regionp = gParcelMgr->getSelectionRegion();
+ if(parcel && parcel->getOwnerID().notNull()
+ && (parcel->getOwnerID() != gAgent.getID())
+ && regionp && (regionp->getOwner() == gAgent.getID()))
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelReclaim");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addS32("LocalID", parcel->getLocalID());
+ msg->sendReliable(regionp->getHost());
+ }
+}
+
+// static
+void LLViewerParcelMgr::releaseAlertCB(S32 option, void *)
+{
+ if (option == 0)
+ {
+ // Send the release message, not a force
+ gParcelMgr->sendParcelRelease();
+ }
+}
+
+void LLViewerParcelMgr::buyPass()
+{
+ LLParcel* parcel = getSelectedParcel();
+ if (!parcel) return;
+
+ LLViewerRegion* region = getSelectionRegion();
+ if (!region) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelBuyPass);
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );
+ msg->sendReliable( region->getHost() );
+}
+
+//Tells whether we are allowed to buy a pass or not
+BOOL LLViewerParcelMgr::isCollisionBanned()
+{
+ if ((mCollisionBanned == BA_ALLOWED) || (mCollisionBanned == BA_NOT_ON_LIST))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+// This implementation should mirror LLSimParcelMgr::isParcelOwnedBy
+// static
+BOOL LLViewerParcelMgr::isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power)
+{
+ if (!parcelp)
+ {
+ return FALSE;
+ }
+
+ // Gods can always assume ownership.
+ if (gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+
+ // The owner of a parcel automatically gets all powersr.
+ if (parcelp->getOwnerID() == gAgent.getID())
+ {
+ return TRUE;
+ }
+
+ // Only gods can assume 'ownership' of public land.
+ if (parcelp->isPublic())
+ {
+ return FALSE;
+ }
+
+ // Return whether or not the agent has group_proxy_power powers in the
+ // parcel's group.
+ return gAgent.hasPowerInGroup(parcelp->getOwnerID(), group_proxy_power);
+}
+
+// This implementation should mirror llSimParcelMgr::isParcelModifiableBy
+// static
+BOOL LLViewerParcelMgr::isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power)
+{
+ // If the agent can assume ownership, it is probably modifiable.
+ BOOL rv = FALSE;
+ if (parcelp)
+ {
+ // *FIX: This should only work for leased parcels, but group owned
+ // parcels cannot be OS_LEASED yet. Phoenix 2003-12-15.
+ rv = isParcelOwnedByAgent(parcelp, group_proxy_power);
+
+ // ... except for the case that the parcel is not OS_LEASED for agent-owned parcels.
+ if( (gAgent.getID() == parcelp->getOwnerID())
+ && !gAgent.isGodlike()
+ && (parcelp->getOwnershipStatus() != LLParcel::OS_LEASED) )
+ {
+ rv = FALSE;
+ }
+ }
+ return rv;
+}
+
+void sanitize_corners(const LLVector3d &corner1,
+ const LLVector3d &corner2,
+ LLVector3d &west_south_bottom,
+ LLVector3d &east_north_top)
+{
+ west_south_bottom.mdV[VX] = llmin( corner1.mdV[VX], corner2.mdV[VX] );
+ west_south_bottom.mdV[VY] = llmin( corner1.mdV[VY], corner2.mdV[VY] );
+ west_south_bottom.mdV[VZ] = llmin( corner1.mdV[VZ], corner2.mdV[VZ] );
+
+ east_north_top.mdV[VX] = llmax( corner1.mdV[VX], corner2.mdV[VX] );
+ east_north_top.mdV[VY] = llmax( corner1.mdV[VY], corner2.mdV[VY] );
+ east_north_top.mdV[VZ] = llmax( corner1.mdV[VZ], corner2.mdV[VZ] );
+}
+
diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h
new file mode 100644
index 0000000000..d9823c82a0
--- /dev/null
+++ b/indra/newview/llviewerparcelmgr.h
@@ -0,0 +1,327 @@
+/**
+ * @file llviewerparcelmgr.h
+ * @brief Viewer-side representation of owned land
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARCELMGR_H
+#define LL_LLVIEWERPARCELMGR_H
+
+#include "v3dmath.h"
+#include "lldarray.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+
+class LLUUID;
+class LLMessageSystem;
+class LLParcel;
+class LLViewerRegion;
+
+// Constants for sendLandOwner
+//const U32 NO_NEIGHBOR_JOIN = 0x0;
+//const U32 ALL_NEIGHBOR_JOIN = U32( NORTH_MASK
+// | SOUTH_MASK
+// | EAST_MASK
+// | WEST_MASK);
+
+const F32 PARCEL_POST_HEIGHT = 0.666f;
+//const F32 PARCEL_POST_HEIGHT = 20.f;
+
+// Specify the type of land transfer taking place
+//enum ELandTransferType
+//{
+// LTT_RELEASE_LAND = 0x1,
+// LTT_CLAIM_LAND = 0x2,
+// LTT_BUY_LAND = 0x4,
+// LTT_DEED_LAND = 0x8,
+// LTT_FOR_GROUP = 0x16
+//};
+
+// Base class for people who want to "observe" changes in the viewer
+// parcel selection.
+class LLParcelObserver
+{
+public:
+ virtual ~LLParcelObserver() {};
+ virtual void changed() = 0;
+};
+
+class LLViewerParcelMgr
+{
+public:
+ LLViewerParcelMgr();
+ ~LLViewerParcelMgr();
+
+ void destroyGL();
+ void restoreGL();
+
+ BOOL selectionEmpty() const;
+ F32 getSelectionWidth() const { return F32(mEastNorth.mdV[VX] - mWestSouth.mdV[VX]); }
+ F32 getSelectionHeight() const { return F32(mEastNorth.mdV[VY] - mWestSouth.mdV[VY]); }
+ BOOL getSelection(LLVector3d &min, LLVector3d &max) { min = mWestSouth; max = mEastNorth; return !selectionEmpty();}
+ LLViewerRegion* getSelectionRegion();
+
+ void getDisplayInfo(S32* area, S32* claim, S32* rent, BOOL* for_sale, F32* dwell);
+
+ void getPrimInfo(S32 &sw_max, S32 &sw_total, S32 &max, S32 &total, S32 &owner, S32 &group, S32 &other, S32& selected, F32 &parcel_object_bonus, S32 &other_clean);
+
+ // Does the selection have multiple land owners in it?
+ BOOL getMultipleOwners() const;
+
+ // Is the entire parcel selected, or just a part?
+ BOOL getWholeParcelSelected() const;
+
+ // Returns area that will actually be claimed in meters squared.
+ S32 getClaimableArea() const;
+ bool hasOthersSelected() const;
+
+ // Returns selected area
+ S32 getSelectedArea() const;
+
+ void resetSegments(U8* segments);
+
+ // write a rectangle's worth of line segments into the highlight array
+ void writeHighlightSegments(F32 west, F32 south, F32 east, F32 north);
+
+ // Write highlight segments from a packed bitmap of the appropriate
+ // parcel.
+ void writeSegmentsFromBitmap(U8* bitmap, U8* segments);
+
+ void writeAgentParcelFromBitmap(U8* bitmap);
+
+ // Select the collision parcel
+ void selectCollisionParcel();
+
+ // Select the parcel at a specific point
+ void selectParcelAt(const LLVector3d& pos_global);
+
+ // Take the current rectangle select, and select the parcel contained
+ // within it.
+ void selectParcelInRectangle();
+
+ // Select a piece of land
+ void selectLand(const LLVector3d &corner1, const LLVector3d &corner2,
+ BOOL snap_to_parcel);
+
+ // Clear the selection, and stop drawing the highlight.
+ void deselectLand();
+
+ void addObserver(LLParcelObserver* observer);
+ void removeObserver(LLParcelObserver* observer);
+ void notifyObservers();
+
+ void setSelectionVisible(BOOL visible) { mRenderSelection = visible; }
+
+ BOOL isOwnedAt(const LLVector3d& pos_global) const;
+ BOOL isOwnedSelfAt(const LLVector3d& pos_global) const;
+ BOOL isOwnedOtherAt(const LLVector3d& pos_global) const;
+ BOOL isSoundLocal(const LLVector3d &pos_global) const;
+
+ BOOL canHearSound(const LLVector3d &pos_global) const;
+
+ LLParcel *getSelectedParcel() const;
+ LLParcel *getAgentParcel() const;
+
+ BOOL inAgentParcel(const LLVector3d &pos_global) const;
+
+ // Return the number of grid units that are owned by you within
+ // the selection (computed by server).
+ S32 getSelfCount() const { return mSelectedSelfCount; }
+
+ // Returns a pointer only when it has valid data.
+ LLParcel* getHoverParcel() const;
+
+ LLParcel* getCollisionParcel() const;
+
+ BOOL agentCanTakeDamage() const;
+ BOOL agentCanFly() const;
+ F32 agentDrawDistance() const;
+ BOOL agentCanBuild() const;
+
+ F32 getHoverParcelWidth() const
+ { return F32(mHoverEastNorth.mdV[VX] - mHoverWestSouth.mdV[VX]); }
+
+ F32 getHoverParcelHeight() const
+ { return F32(mHoverEastNorth.mdV[VY] - mHoverWestSouth.mdV[VY]); }
+
+ // UTILITIES
+ void render();
+ void renderParcelCollision();
+
+ void renderRect( const LLVector3d &west_south_bottom,
+ const LLVector3d &east_north_top );
+ void renderOneSegment(F32 x1, F32 y1, F32 x2, F32 y2, F32 height, U8 direction, LLViewerRegion* regionp);
+ void renderHighlightSegments(const U8* segments, LLViewerRegion* regionp);
+ void renderCollisionSegments(U8* segments, BOOL use_pass, LLViewerRegion* regionp);
+
+ void sendParcelGodForceOwner(const LLUUID& owner_id);
+
+ // make the selected parcel a content parcel.
+ void sendParcelGodForceToContent();
+
+ // Take the selected parcel, and toggle it's 'reserved for newbie'
+ // status.
+ // *NOTE: There is no longer a newbie toggle. It is a linden sale
+ // for newbie now.
+ //void toggleParcelGodReserveForNewbie();
+
+ // Pack information about this parcel and send it to the region
+ // containing the southwest corner of the selection.
+ // If want_reply_to_update, simulator will send back a ParcelProperties
+ // message.
+ void sendParcelPropertiesUpdate(LLParcel* parcel, BOOL want_reply_to_update);
+
+ // Takes an Access List flag, like AL_ACCESS or AL_BAN
+ void sendParcelAccessListUpdate(U32 which);
+
+ // Takes an Access List flag, like AL_ACCESS or AL_BAN
+ void sendParcelAccessListRequest(U32 flags);
+
+ // Dwell is not part of the usual parcel update information because the
+ // simulator doesn't actually know the per-parcel dwell. Ack! We have
+ // to get it out of the database.
+ void sendParcelDwellRequest();
+
+ // If the point is outside the current hover parcel, request more data
+ void requestHoverParcelProperties(const LLVector3d& pos_global);
+
+ bool canAgentBuyParcel(LLParcel*, bool forGroup) const;
+
+// void startClaimLand(BOOL is_for_group = FALSE);
+ void startBuyLand(BOOL is_for_group = FALSE);
+ void startSellLand();
+ void startReleaseLand();
+ void startDivideLand();
+ void startJoinLand();
+ void startDeedLandToGroup();
+ void reclaimParcel();
+
+ void buyPass();
+
+ // Buying Land
+
+ class ParcelBuyInfo;
+ ParcelBuyInfo* setupParcelBuy(const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& group_id,
+ BOOL is_group_owned,
+ BOOL is_claim,
+ BOOL remove_contribution);
+ // callers responsibility to call deleteParcelBuy() on return value
+ void sendParcelBuy(ParcelBuyInfo*);
+ void deleteParcelBuy(ParcelBuyInfo*&);
+
+ void sendParcelDeed(const LLUUID& group_id);
+
+ // Send the ParcelRelease message
+ void sendParcelRelease();
+
+ // accessors for mAgentParcel
+ const char *getAgentParcelName() const;
+
+ // Create a landmark at the "appropriate" location for the
+ // currently selected parcel.
+ // *NOTE: Taken out 2005-03-21. Phoenix.
+ //void makeLandmarkAtSelection();
+
+ static void processParcelOverlay(LLMessageSystem *msg, void **user_data);
+ static void processParcelProperties(LLMessageSystem *msg, void **user_data);
+ static void processParcelAccessListReply(LLMessageSystem *msg, void **user);
+ static void processParcelDwellReply(LLMessageSystem *msg, void **user);
+
+ void dump();
+
+ // Whether or not the collision border around the parcel is there because
+ // the agent is banned or not in the allowed group
+ BOOL isCollisionBanned();
+
+ static BOOL isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power);
+ static BOOL isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power);
+
+protected:
+ static void releaseAlertCB(S32 option, void *data);
+
+ // If the user is claiming land and the current selection
+ // borders a piece of land the user already owns, ask if he
+ // wants to join this land to the other piece.
+ //void askJoinIfNecessary(ELandTransferType land_transfer_type);
+ //static void joinAlertCB(S32 option, void* data);
+
+ //void buyAskMoney(ELandTransferType land_transfer_type);
+
+ // move land from current owner to it's group.
+ void deedLandToGroup();
+
+ static void claimAlertCB(S32 option, void* data);
+ static void buyAlertCB(S32 option, void* data);
+ static void deedAlertCB(S32 option, void*);
+
+ static void callbackDivideLand(S32 option, void* data);
+ static void callbackJoinLand(S32 option, void* data);
+
+ //void finishClaim(BOOL user_to_user_sale, U32 join);
+
+private:
+ BOOL mSelected;
+ BOOL mSelectedMultipleOwners;
+ BOOL mWholeParcelSelected;
+ S32 mSelectedSelfCount;
+ S32 mSelectedOtherCount;
+ S32 mSelectedPublicCount;
+
+ LLParcel *mParcel; // selected parcel info
+ S32 mRequestResult; // result of last parcel request
+ LLVector3d mWestSouth;
+ LLVector3d mEastNorth;
+ F32 mSelectedDwell;
+
+ LLParcel *mAgentParcel; // info for parcel agent is in
+ S32 mAgentParcelSequenceID; // incrementing counter to suppress out of order updates
+
+ LLParcel* mHoverParcel;
+ S32 mHoverRequestResult;
+ LLVector3d mHoverWestSouth;
+ LLVector3d mHoverEastNorth;
+
+ LLDynamicArray<LLParcelObserver*> mObservers;
+
+ // Array of pieces of parcel edges to potentially draw
+ // Has (parcels_per_edge + 1) * (parcels_per_edge + 1) elements so
+ // we can represent edges of the grid.
+ // WEST_MASK = draw west edge
+ // SOUTH_MASK = draw south edge
+ S32 mParcelsPerEdge;
+ U8* mHighlightSegments;
+ U8* mAgentParcelOverlay;
+
+ // Raw data buffer for unpacking parcel overlay chunks
+ // Size = parcels_per_edge * parcels_per_edge / parcel_overlay_chunks
+ static U8* sPackedOverlay;
+
+ // Watch for pending collisions with a parcel you can't access.
+ // If it's coming, draw the parcel's boundaries.
+ LLParcel* mCollisionParcel;
+ U8* mCollisionSegments;
+ BOOL mRenderCollision;
+ BOOL mRenderSelection;
+ S32 mCollisionBanned;
+ LLFrameTimer mCollisionTimer;
+ LLUUID mBlockedImageID;
+ LLUUID mPassImageID;
+ LLPointer<LLViewerImage> mBlockedImage;
+ LLPointer<LLViewerImage> mPassImage;
+
+ // Media
+ S32 mMediaParcelId;
+ U64 mMediaRegionId;
+};
+
+extern LLViewerParcelMgr *gParcelMgr;
+
+void sanitize_corners(const LLVector3d &corner1, const LLVector3d &corner2,
+ LLVector3d &west_south_bottom, LLVector3d &east_north_top);
+
+#endif
diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
new file mode 100644
index 0000000000..2c04c073db
--- /dev/null
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -0,0 +1,850 @@
+/**
+ * @file llviewerparceloverlay.cpp
+ * @brief LLViewerParcelOverlay class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerparceloverlay.h"
+
+// indra includes
+#include "llparcel.h"
+#include "llgl.h"
+#include "v4color.h"
+#include "v2math.h"
+
+// newview includes
+#include "llviewerimage.h"
+#include "llviewercontrol.h"
+#include "llsurface.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llglheaders.h"
+
+const U8 OVERLAY_IMG_COMPONENTS = 4;
+
+LLViewerParcelOverlay::LLViewerParcelOverlay(LLViewerRegion* region, F32 region_width_meters)
+: mRegion( region ),
+ mParcelGridsPerEdge( S32( region_width_meters / PARCEL_GRID_STEP_METERS ) ),
+ mDirty( FALSE ),
+ mTimeSinceLastUpdate(),
+ mOverlayTextureIdx(-1),
+ mLineImageID( gViewerArt.getString("propertyline.tga") ),
+ mVertexCount(0),
+ mVertexArray(NULL),
+ mColorArray(NULL)
+// mTexCoordArray(NULL),
+{
+ // Create a texture to hold color information.
+ // 4 components
+ // Use mipmaps = FALSE, clamped, NEAREST filter, for sharp edges
+ mTexture = new LLImageGL(FALSE);
+ mImageRaw = new LLImageRaw(mParcelGridsPerEdge, mParcelGridsPerEdge, OVERLAY_IMG_COMPONENTS);
+ mTexture->createGLTexture(0, mImageRaw);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ mTexture->bind(0);
+ mTexture->setClamp(TRUE, TRUE);
+ mTexture->setMipFilterNearest(TRUE);
+
+ //
+ // Initialize the GL texture with empty data.
+ //
+ // Create the base texture.
+ U8 *raw = mImageRaw->getData();
+ const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge * OVERLAY_IMG_COMPONENTS;
+ for (S32 i = 0; i < COUNT; i++)
+ {
+ raw[i] = 0;
+ }
+ mTexture->setSubImage(mImageRaw, 0, 0, mParcelGridsPerEdge, mParcelGridsPerEdge);
+
+ // Create storage for ownership information from simulator
+ // and initialize it.
+ mOwnership = new U8[ mParcelGridsPerEdge * mParcelGridsPerEdge ];
+ for (S32 i = 0; i < mParcelGridsPerEdge * mParcelGridsPerEdge; i++)
+ {
+ mOwnership[i] = PARCEL_PUBLIC;
+ }
+
+ // Make sure the texture matches the ownership information.
+ updateOverlayTexture();
+}
+
+
+LLViewerParcelOverlay::~LLViewerParcelOverlay()
+{
+ delete[] mOwnership;
+ mOwnership = NULL;
+
+ delete[] mVertexArray;
+ mVertexArray = NULL;
+
+ delete[] mColorArray;
+ mColorArray = NULL;
+
+// JC No textures.
+// delete mTexCoordArray;
+// mTexCoordArray = NULL;
+
+ mImageRaw = NULL;
+}
+
+//---------------------------------------------------------------------------
+// ACCESSORS
+//---------------------------------------------------------------------------
+BOOL LLViewerParcelOverlay::isOwned(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return (PARCEL_PUBLIC != ownership(row, column));
+}
+
+BOOL LLViewerParcelOverlay::isOwnedSelf(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return (PARCEL_SELF == ownership(row, column));
+}
+
+BOOL LLViewerParcelOverlay::isOwnedGroup(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return (PARCEL_GROUP == ownership(row, column));
+}
+
+BOOL LLViewerParcelOverlay::isOwnedOther(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ U8 overlay = ownership(row, column);
+ return (PARCEL_OWNED == overlay || PARCEL_FOR_SALE == overlay);
+}
+
+BOOL LLViewerParcelOverlay::isSoundLocal(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return PARCEL_SOUND_LOCAL & mOwnership[row * mParcelGridsPerEdge + column];
+}
+
+U8 LLViewerParcelOverlay::ownership( const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return ownership(row, column);
+}
+
+F32 LLViewerParcelOverlay::getOwnedRatio() const
+{
+ S32 size = mParcelGridsPerEdge * mParcelGridsPerEdge;
+ S32 total = 0;
+
+ for (S32 i = 0; i < size; i++)
+ {
+ if ((mOwnership[i] & PARCEL_COLOR_MASK) != PARCEL_PUBLIC)
+ {
+ total++;
+ }
+ }
+
+ return (F32)total / (F32)size;
+
+}
+
+//---------------------------------------------------------------------------
+// MANIPULATORS
+//---------------------------------------------------------------------------
+
+// Color tables for owned land
+// Available = index 0
+// Other = index 1
+// Group = index 2
+// Self = index 3
+
+// Make sure the texture colors match the ownership data.
+// Note: Assumes that the ownership array and
+void LLViewerParcelOverlay::updateOverlayTexture()
+{
+ if (mOverlayTextureIdx < 0 && mDirty)
+ {
+ mOverlayTextureIdx = 0;
+ }
+ if (mOverlayTextureIdx < 0)
+ {
+ return;
+ }
+ // Can do this because gColors are actually stored as LLColor4U
+ const LLColor4U avail = gColors.getColor4U("PropertyColorAvail");
+ const LLColor4U owned = gColors.getColor4U("PropertyColorOther");
+ const LLColor4U group = gColors.getColor4U("PropertyColorGroup");
+ const LLColor4U self = gColors.getColor4U("PropertyColorSelf");
+ const LLColor4U for_sale = gColors.getColor4U("PropertyColorForSale");
+ const LLColor4U auction = gColors.getColor4U("PropertyColorAuction");
+
+ // Create the base texture.
+ U8 *raw = mImageRaw->getData();
+ const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge;
+ S32 max = mOverlayTextureIdx + mParcelGridsPerEdge;
+ if (max > COUNT) max = COUNT;
+ S32 pixel_index = mOverlayTextureIdx*OVERLAY_IMG_COMPONENTS;
+ S32 i;
+ for (i = mOverlayTextureIdx; i < max; i++)
+ {
+ U8 ownership = mOwnership[i];
+
+ U8 r,g,b,a;
+
+ // Color stored in low three bits
+ switch( ownership & 0x7 )
+ {
+ case PARCEL_PUBLIC:
+ r = avail.mV[VRED];
+ g = avail.mV[VGREEN];
+ b = avail.mV[VBLUE];
+ a = avail.mV[VALPHA];
+ break;
+ case PARCEL_OWNED:
+ r = owned.mV[VRED];
+ g = owned.mV[VGREEN];
+ b = owned.mV[VBLUE];
+ a = owned.mV[VALPHA];
+ break;
+ case PARCEL_GROUP:
+ r = group.mV[VRED];
+ g = group.mV[VGREEN];
+ b = group.mV[VBLUE];
+ a = group.mV[VALPHA];
+ break;
+ case PARCEL_SELF:
+ r = self.mV[VRED];
+ g = self.mV[VGREEN];
+ b = self.mV[VBLUE];
+ a = self.mV[VALPHA];
+ break;
+ case PARCEL_FOR_SALE:
+ r = for_sale.mV[VRED];
+ g = for_sale.mV[VGREEN];
+ b = for_sale.mV[VBLUE];
+ a = for_sale.mV[VALPHA];
+ break;
+ case PARCEL_AUCTION:
+ r = auction.mV[VRED];
+ g = auction.mV[VGREEN];
+ b = auction.mV[VBLUE];
+ a = auction.mV[VALPHA];
+ break;
+ default:
+ r = self.mV[VRED];
+ g = self.mV[VGREEN];
+ b = self.mV[VBLUE];
+ a = self.mV[VALPHA];
+ break;
+ }
+
+ raw[pixel_index + 0] = r;
+ raw[pixel_index + 1] = g;
+ raw[pixel_index + 2] = b;
+ raw[pixel_index + 3] = a;
+
+ pixel_index += OVERLAY_IMG_COMPONENTS;
+ }
+
+ // Copy data into GL texture from raw data
+ if (i >= COUNT)
+ {
+ mTexture->setSubImage(mImageRaw, 0, 0, mParcelGridsPerEdge, mParcelGridsPerEdge);
+ mOverlayTextureIdx = -1;
+ }
+ else
+ {
+ mOverlayTextureIdx = i;
+ }
+}
+
+
+void LLViewerParcelOverlay::uncompressLandOverlay(S32 chunk, U8 *packed_overlay)
+{
+ // Unpack the message data into the ownership array
+ S32 size = mParcelGridsPerEdge * mParcelGridsPerEdge;
+ S32 chunk_size = size / PARCEL_OVERLAY_CHUNKS;
+
+ memcpy(mOwnership + chunk*chunk_size, packed_overlay, chunk_size);
+
+ // Force property lines and overlay texture to update
+ setDirty();
+}
+
+
+void LLViewerParcelOverlay::updatePropertyLines()
+{
+ if (!gSavedSettings.getBOOL("ShowPropertyLines"))
+ return;
+
+ S32 row, col;
+
+ // Can do this because gColors are actually stored as LLColor4U
+ const LLColor4U self_coloru = gColors.getColor4U("PropertyColorSelf");
+ const LLColor4U other_coloru = gColors.getColor4U("PropertyColorOther");
+ const LLColor4U group_coloru = gColors.getColor4U("PropertyColorGroup");
+ const LLColor4U for_sale_coloru = gColors.getColor4U("PropertyColorForSale");
+ const LLColor4U auction_coloru = gColors.getColor4U("PropertyColorAuction");
+
+ // Build into dynamic arrays, then copy into static arrays.
+ LLDynamicArray<LLVector3, 256> new_vertex_array;
+ LLDynamicArray<LLColor4U, 256> new_color_array;
+ LLDynamicArray<LLVector2, 256> new_coord_array;
+
+ U8 overlay = 0;
+ BOOL add_edge = FALSE;
+ const F32 GRID_STEP = PARCEL_GRID_STEP_METERS;
+ const S32 GRIDS_PER_EDGE = mParcelGridsPerEdge;
+
+ for (row = 0; row < GRIDS_PER_EDGE; row++)
+ {
+ for (col = 0; col < GRIDS_PER_EDGE; col++)
+ {
+ overlay = mOwnership[row*GRIDS_PER_EDGE+col];
+
+ F32 left = col*GRID_STEP;
+ F32 right = left+GRID_STEP;
+
+ F32 bottom = row*GRID_STEP;
+ F32 top = bottom+GRID_STEP;
+
+ // West edge
+ if (overlay & PARCEL_WEST_LINE)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // East edge
+ if (col < GRIDS_PER_EDGE-1)
+ {
+ U8 east_overlay = mOwnership[row*GRIDS_PER_EDGE+col+1];
+ add_edge = east_overlay & PARCEL_WEST_LINE;
+ }
+ else
+ {
+ add_edge = TRUE;
+ }
+
+ if (add_edge)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // South edge
+ if (overlay & PARCEL_SOUTH_LINE)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ // North edge
+ if (row < GRIDS_PER_EDGE-1)
+ {
+ U8 north_overlay = mOwnership[(row+1)*GRIDS_PER_EDGE+col];
+ add_edge = north_overlay & PARCEL_SOUTH_LINE;
+ }
+ else
+ {
+ add_edge = TRUE;
+ }
+
+ if (add_edge)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // Now copy into static arrays for faster rendering.
+ // Attempt to recycle old arrays if possible to avoid memory
+ // shuffling.
+ S32 new_vertex_count = new_vertex_array.count();
+
+ // NOTE: If the new_vertex_count is 0 and wasn't 0 previously
+ // the arrays are still allocated as the arrays aren't set to NULL, etc.
+ // This won't cause any problems, but might waste a few cycles copying over
+ // old data. - jwolk
+ if ( !(mVertexArray && mColorArray && new_vertex_count == mVertexCount) && new_vertex_count > 0 )
+ {
+ // ...need new arrays
+ delete[] mVertexArray;
+ delete[] mColorArray;
+
+ mVertexCount = new_vertex_count;
+
+ mVertexArray = new F32[3 * mVertexCount];
+ mColorArray = new U8 [4 * mVertexCount];
+ }
+
+ // Copy the new data into the arrays
+ S32 i;
+ F32* vertex = mVertexArray;
+ for (i = 0; i < mVertexCount; i++)
+ {
+ const LLVector3& point = new_vertex_array.get(i);
+ *vertex = point.mV[VX];
+ vertex++;
+ *vertex = point.mV[VY];
+ vertex++;
+ *vertex = point.mV[VZ];
+ vertex++;
+ }
+
+ U8* colorp = mColorArray;
+ for (i = 0; i < mVertexCount; i++)
+ {
+ const LLColor4U& color = new_color_array.get(i);
+ *colorp = color.mV[VRED];
+ colorp++;
+ *colorp = color.mV[VGREEN];
+ colorp++;
+ *colorp = color.mV[VBLUE];
+ colorp++;
+ *colorp = color.mV[VALPHA];
+ colorp++;
+ }
+
+ // Everything's clean now
+ mDirty = FALSE;
+}
+
+
+void LLViewerParcelOverlay::addPropertyLine(
+ LLDynamicArray<LLVector3, 256>& vertex_array,
+ LLDynamicArray<LLColor4U, 256>& color_array,
+ LLDynamicArray<LLVector2, 256>& coord_array,
+ const F32 start_x, const F32 start_y,
+ const U32 edge,
+ const LLColor4U& color)
+{
+ LLColor4U underwater( color );
+ underwater.mV[VALPHA] /= 2;
+
+ LLSurface& land = mRegion->getLand();
+
+ F32 dx;
+ F32 dy;
+ F32 tick_dx;
+ F32 tick_dy;
+ //const F32 LINE_WIDTH = 0.125f;
+ const F32 LINE_WIDTH = 0.0625f;
+
+ switch(edge)
+ {
+ case WEST:
+ dx = 0.f;
+ dy = 1.f;
+ tick_dx = LINE_WIDTH;
+ tick_dy = 0.f;
+ break;
+
+ case EAST:
+ dx = 0.f;
+ dy = 1.f;
+ tick_dx = -LINE_WIDTH;
+ tick_dy = 0.f;
+ break;
+
+ case NORTH:
+ dx = 1.f;
+ dy = 0.f;
+ tick_dx = 0.f;
+ tick_dy = -LINE_WIDTH;
+ break;
+
+ case SOUTH:
+ dx = 1.f;
+ dy = 0.f;
+ tick_dx = 0.f;
+ tick_dy = LINE_WIDTH;
+ break;
+
+ default:
+ llerrs << "Invalid edge in addPropertyLine" << llendl;
+ return;
+ }
+
+ F32 outside_x = start_x;
+ F32 outside_y = start_y;
+ F32 outside_z = 0.f;
+ F32 inside_x = start_x + tick_dx;
+ F32 inside_y = start_y + tick_dy;
+ F32 inside_z = 0.f;
+
+ // First part, only one vertex
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx * LINE_WIDTH;
+ inside_y += dy * LINE_WIDTH;
+
+ outside_x += dx * LINE_WIDTH;
+ outside_y += dy * LINE_WIDTH;
+
+ // Then the "actual edge"
+ inside_z = land.resolveHeightRegion( inside_x, inside_y );
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (inside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(inside_x, inside_y, inside_z) );
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+
+ coord_array.put( LLVector2(outside_x - start_x, 1.f) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx * (dx - LINE_WIDTH);
+ inside_y += dy * (dy - LINE_WIDTH);
+
+ outside_x += dx * (dx - LINE_WIDTH);
+ outside_y += dy * (dy - LINE_WIDTH);
+
+ // Middle part, full width
+ S32 i;
+ const S32 GRID_STEP = S32( PARCEL_GRID_STEP_METERS );
+ for (i = 1; i < GRID_STEP; i++)
+ {
+ inside_z = land.resolveHeightRegion( inside_x, inside_y );
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (inside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(inside_x, inside_y, inside_z) );
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+
+ coord_array.put( LLVector2(outside_x - start_x, 1.f) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx;
+ inside_y += dy;
+
+ outside_x += dx;
+ outside_y += dy;
+ }
+
+ // Extra buffer for edge
+ inside_x -= dx * LINE_WIDTH;
+ inside_y -= dy * LINE_WIDTH;
+
+ outside_x -= dx * LINE_WIDTH;
+ outside_y -= dy * LINE_WIDTH;
+
+ inside_z = land.resolveHeightRegion( inside_x, inside_y );
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (inside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(inside_x, inside_y, inside_z) );
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+
+ coord_array.put( LLVector2(outside_x - start_x, 1.f) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx * LINE_WIDTH;
+ inside_y += dy * LINE_WIDTH;
+
+ outside_x += dx * LINE_WIDTH;
+ outside_y += dy * LINE_WIDTH;
+
+ // Last edge is not drawn to the edge
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+}
+
+
+void LLViewerParcelOverlay::setDirty()
+{
+ mDirty = TRUE;
+}
+
+void LLViewerParcelOverlay::idleUpdate(bool force_update)
+{
+ if (gGLManager.mIsDisabled)
+ {
+ return;
+ }
+ if (mOverlayTextureIdx >= 0 && (!(mDirty && force_update)))
+ {
+ // We are in the middle of updating the overlay texture
+ updateOverlayTexture();
+ return;
+ }
+ // Only if we're dirty and it's been a while since the last update.
+ if (mDirty)
+ {
+ if (force_update || mTimeSinceLastUpdate.getElapsedTimeF32() > 4.0f)
+ {
+ updateOverlayTexture();
+ updatePropertyLines();
+ mTimeSinceLastUpdate.reset();
+ }
+ }
+}
+
+S32 LLViewerParcelOverlay::renderPropertyLines ()
+{
+ if (!gSavedSettings.getBOOL("ShowPropertyLines"))
+ {
+ return 0;
+ }
+ if (!mVertexArray || !mColorArray)
+ {
+ return 0;
+ }
+
+ LLSurface& land = mRegion->getLand();
+
+ LLGLSUIDefault gls_ui; // called from pipeline
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest mDepthTest(GL_TRUE);
+
+ // JC - This doesn't work.
+ //gGLSUITextureDepth.set();
+ //LLViewerImage* image = gImageList.getImage( mLineImageID );
+ //image->bindTexture();
+
+ // Find camera height off the ground (not from zero)
+ F32 ground_height_at_camera = land.resolveHeightGlobal( gAgent.getCameraPositionGlobal() );
+ F32 camera_z = gCamera->getOrigin().mV[VZ];
+ F32 camera_height = camera_z - ground_height_at_camera;
+
+ camera_height = llclamp(camera_height, 0.f, 100.f);
+
+ // Pull lines toward camera by 1 cm per meter off the ground.
+ const LLVector3& CAMERA_AT = gCamera->getAtAxis();
+ F32 pull_toward_camera_scale = 0.01f * camera_height;
+ LLVector3 pull_toward_camera = CAMERA_AT;
+ pull_toward_camera *= -pull_toward_camera_scale;
+
+ // Always fudge a little vertically.
+ pull_toward_camera.mV[VZ] += 0.01f;
+
+ glMatrixMode( GL_MODELVIEW );
+ glPushMatrix();
+
+ // Move to appropriate region coords
+ LLVector3 origin = mRegion->getOriginAgent();
+ glTranslatef( origin.mV[VX], origin.mV[VY], origin.mV[VZ] );
+
+ glTranslatef(pull_toward_camera.mV[VX], pull_toward_camera.mV[VY],
+ pull_toward_camera.mV[VZ]);
+
+ // Include +1 because vertices are fenceposts.
+ // *2 because it's a quad strip
+ const S32 GRID_STEP = S32( PARCEL_GRID_STEP_METERS );
+ const S32 vertex_per_edge = 3 + 2 * (GRID_STEP-1) + 3;
+
+ /* JC - Don't do this. Unbinding AGP stalls the draw process,
+ dropping frame rate. Not unbinding AGP causes random crashes
+ on nVidia cards due to binding non-AGP arrays.
+
+ gPipeline.unbindAGP();
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, mVertexArray );
+ glColorPointer( 4, GL_UNSIGNED_BYTE, 0, mColorArray );
+
+ S32 i;
+ for (i = 0; i < mVertexCount; i += vertex_per_edge)
+ {
+ // Each edge is several vertices
+ glDrawArrays(GL_LINE_STRIP, i, vertex_per_edge);
+ }
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ */
+
+ // Stomp the camera into two dimensions
+ LLVector3 camera_region = mRegion->getPosRegionFromGlobal( gAgent.getCameraPositionGlobal() );
+
+ // Set up a cull plane 2 * PARCEL_GRID_STEP_METERS behind
+ // the camera. The cull plane normal is the camera's at axis.
+ LLVector3 cull_plane_point = gCamera->getAtAxis();
+ cull_plane_point *= -2.f * PARCEL_GRID_STEP_METERS;
+ cull_plane_point += camera_region;
+
+ LLVector3 vertex;
+
+ const S32 BYTES_PER_COLOR = 4;
+ const S32 FLOATS_PER_VERTEX = 3;
+ //const S32 FLOATS_PER_TEX_COORD = 2;
+ S32 i, j;
+ S32 drawn = 0;
+ F32* vertexp;
+ U8* colorp;
+
+ const F32 PROPERTY_LINE_CLIP_DIST = 256.f;
+
+ for (i = 0; i < mVertexCount; i += vertex_per_edge)
+ {
+ colorp = mColorArray + BYTES_PER_COLOR * i;
+ vertexp = mVertexArray + FLOATS_PER_VERTEX * i;
+
+ vertex.mV[VX] = *(vertexp);
+ vertex.mV[VY] = *(vertexp+1);
+ vertex.mV[VZ] = *(vertexp+2);
+
+ if (dist_vec_squared2D(vertex, camera_region) > PROPERTY_LINE_CLIP_DIST*PROPERTY_LINE_CLIP_DIST)
+ {
+ continue;
+ }
+
+ // Destroy vertex, transform to plane-local.
+ vertex -= cull_plane_point;
+
+ // negative dot product means it is in back of the plane
+ if ( vertex * CAMERA_AT < 0.f )
+ {
+ continue;
+ }
+
+ glBegin(GL_TRIANGLE_STRIP);
+
+ for (j = 0; j < vertex_per_edge; j++)
+ {
+ // JC - This doesn't work
+ //glTexCoord2fv(mTexCoordArray + FLOATS_PER_TEX_COORD*offset);
+ glColor4ubv(colorp);
+ glVertex3fv(vertexp);
+
+ colorp += BYTES_PER_COLOR;
+ vertexp += FLOATS_PER_VERTEX;
+ }
+
+ drawn += vertex_per_edge;
+
+ glEnd();
+ }
+
+ glPopMatrix();
+
+ return drawn;
+}
diff --git a/indra/newview/llviewerparceloverlay.h b/indra/newview/llviewerparceloverlay.h
new file mode 100644
index 0000000000..2e98cde297
--- /dev/null
+++ b/indra/newview/llviewerparceloverlay.h
@@ -0,0 +1,97 @@
+/**
+ * @file llviewerparceloverlay.h
+ * @brief LLViewerParcelOverlay class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARCELOVERLAY_H
+#define LL_LLVIEWERPARCELOVERLAY_H
+
+// The ownership data for land parcels.
+// One of these structures per region.
+
+#include "lldarray.h"
+#include "llframetimer.h"
+#include "lluuid.h"
+#include "llviewerimage.h"
+
+class LLViewerRegion;
+class LLVector3;
+class LLColor4U;
+class LLVector2;
+
+class LLViewerParcelOverlay
+{
+public:
+ LLViewerParcelOverlay(LLViewerRegion* region, F32 region_width_meters);
+ ~LLViewerParcelOverlay();
+
+ // ACCESS
+ LLImageGL* getTexture() const { return mTexture; }
+
+ BOOL isOwned(const LLVector3& pos) const;
+ BOOL isOwnedSelf(const LLVector3& pos) const;
+ BOOL isOwnedGroup(const LLVector3& pos) const;
+ BOOL isOwnedOther(const LLVector3& pos) const;
+ BOOL isSoundLocal(const LLVector3& pos) const;
+
+ BOOL isBuildCameraAllowed(const LLVector3& pos) const;
+ F32 getOwnedRatio() const;
+
+ // Returns the number of vertices drawn
+ S32 renderPropertyLines();
+
+ U8 ownership( const LLVector3& pos) const;
+
+ // MANIPULATE
+ void uncompressLandOverlay(S32 chunk, U8 *compressed_overlay);
+
+ // Indicate property lines and overlay texture need to be rebuilt.
+ void setDirty();
+
+ void idleUpdate(bool update_now = false);
+
+private:
+ // This is in parcel rows and columns, not grid rows and columns
+ // Stored in bottom three bits.
+ U8 ownership(S32 row, S32 col) const
+ { return 0x7 & mOwnership[row * mParcelGridsPerEdge + col]; }
+
+ void addPropertyLine(LLDynamicArray<LLVector3, 256>& vertex_array,
+ LLDynamicArray<LLColor4U, 256>& color_array,
+ LLDynamicArray<LLVector2, 256>& coord_array,
+ const F32 start_x, const F32 start_y,
+ const U32 edge,
+ const LLColor4U& color);
+
+ void updateOverlayTexture();
+ void updatePropertyLines();
+
+private:
+ // Back pointer to the region that owns this structure.
+ LLViewerRegion* mRegion;
+
+ S32 mParcelGridsPerEdge;
+
+ LLPointer<LLImageGL> mTexture;
+ LLPointer<LLImageRaw> mImageRaw;
+
+ // Size: mParcelGridsPerEdge * mParcelGridsPerEdge
+ // Each value is 0-3, PARCEL_AVAIL to PARCEL_SELF in the two low bits
+ // and other flags in the upper bits.
+ U8 *mOwnership;
+
+ // Update propery lines and overlay texture
+ BOOL mDirty;
+ LLFrameTimer mTimeSinceLastUpdate;
+ S32 mOverlayTextureIdx;
+
+ LLUUID mLineImageID;
+ S32 mVertexCount;
+ F32* mVertexArray;
+ U8* mColorArray;
+};
+
+#endif
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
new file mode 100644
index 0000000000..b67062e0a9
--- /dev/null
+++ b/indra/newview/llviewerpartsim.cpp
@@ -0,0 +1,626 @@
+/**
+ * @file llviewerpartsim.cpp
+ * @brief LLViewerPart class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerpartsim.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "llviewerobjectlist.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llvopartgroup.h"
+#include "llworld.h"
+#include "pipeline.h"
+
+const S32 MAX_PART_COUNT = 4096;
+
+const F32 PART_SIM_BOX_SIDE = 32.f;
+const F32 PART_SIM_BOX_OFFSET = 0.5f*PART_SIM_BOX_SIDE;
+const F32 PART_SIM_BOX_RAD = 0.5f*F_SQRT3*PART_SIM_BOX_SIDE;
+
+//static
+S32 LLViewerPartSim::sMaxParticleCount = 0;
+S32 LLViewerPartSim::sParticleCount = 0;
+
+
+U32 LLViewerPart::sNextPartID = 1;
+
+LLViewerPart::LLViewerPart()
+{
+ mPartSourcep = NULL;
+}
+
+LLViewerPart::~LLViewerPart()
+{
+ mPartSourcep = NULL;
+}
+
+LLViewerPart &LLViewerPart::operator=(const LLViewerPart &part)
+{
+ mPartID = part.mPartID;
+ mFlags = part.mFlags;
+ mMaxAge = part.mMaxAge;
+
+ mStartColor = part.mStartColor;
+ mEndColor = part.mEndColor;
+ mStartScale = part.mStartScale;
+ mEndScale = part.mEndScale;
+
+ mPosOffset = part.mPosOffset;
+ mParameter = part.mParameter;
+
+ mLastUpdateTime = part.mLastUpdateTime;
+ mVPCallback = part.mVPCallback;
+ mPartSourcep = part.mPartSourcep;
+
+ mImagep = part.mImagep;
+ mPosAgent = part.mPosAgent;
+ mVelocity = part.mVelocity;
+ mAccel = part.mAccel;
+ mColor = part.mColor;
+ mScale = part.mScale;
+
+
+ return *this;
+}
+
+void LLViewerPart::init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVPCallback cb)
+{
+ mPartID = LLViewerPart::sNextPartID;
+ LLViewerPart::sNextPartID++;
+ mFlags = 0x00f;
+ mLastUpdateTime = 0.f;
+ mMaxAge = 10.f;
+
+ mVPCallback = cb;
+ mPartSourcep = sourcep;
+
+ mImagep = imagep;
+}
+
+
+/////////////////////////////
+//
+// LLViewerPartGroup implementation
+//
+//
+
+
+LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 box_side)
+{
+ mVOPartGroupp = NULL;
+ mRegionp = gWorldPointer->getRegionFromPosAgent(center_agent);
+ if (!mRegionp)
+ {
+ //llwarns << "No region at position, using agent region!" << llendl;
+ mRegionp = gAgent.getRegion();
+ }
+ mCenterAgent = center_agent;
+ mBoxRadius = F_SQRT3*box_side*0.5f;
+
+ LLVector3 rad_vec(box_side*0.5f, box_side*0.5f, box_side*0.5f);
+ rad_vec += LLVector3(0.001f, 0.001f, 0.001f);
+ mMinObjPos = mCenterAgent - rad_vec;
+ mMaxObjPos = mCenterAgent + rad_vec;
+}
+
+LLViewerPartGroup::~LLViewerPartGroup()
+{
+ cleanup();
+ S32 count = mParticles.count();
+ S32 i;
+
+ for (i = 0; i < count; i++)
+ {
+ mParticles[i].mPartSourcep = NULL;
+ }
+ mParticles.reset();
+ LLViewerPartSim::decPartCount(count);
+}
+
+void LLViewerPartGroup::cleanup()
+{
+ if (mVOPartGroupp)
+ {
+ if (!mVOPartGroupp->isDead())
+ {
+ gObjectList.killObject(mVOPartGroupp);
+ }
+ mVOPartGroupp = NULL;
+ }
+}
+
+BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos)
+{
+ if ((pos.mV[VX] < mMinObjPos.mV[VX])
+ || (pos.mV[VY] < mMinObjPos.mV[VY])
+ || (pos.mV[VZ] < mMinObjPos.mV[VZ]))
+ {
+ return FALSE;
+ }
+
+ if ((pos.mV[VX] > mMaxObjPos.mV[VX])
+ || (pos.mV[VY] > mMaxObjPos.mV[VY])
+ || (pos.mV[VZ] > mMaxObjPos.mV[VZ]))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLViewerPartGroup::addPart(LLViewerPart &part)
+{
+ if (!posInGroup(part.mPosAgent) ||
+ (mVOPartGroupp.notNull() && (part.mImagep != mVOPartGroupp->getTEImage(0))))
+ {
+ return FALSE;
+ }
+
+ if (!mVOPartGroupp)
+ {
+ mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP, getRegion());
+ mVOPartGroupp->setViewerPartGroup(this);
+ mVOPartGroupp->setPositionAgent(getCenterAgent());
+ mVOPartGroupp->setScale(LLVector3(PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE));
+ mVOPartGroupp->setTEImage(0, part.mImagep);
+ gPipeline.addObject(mVOPartGroupp);
+ }
+
+ mParticles.put(part);
+ LLViewerPartSim::incPartCount(1);
+ return TRUE;
+}
+
+
+void LLViewerPartGroup::removePart(const S32 part_num)
+{
+ // Remove the entry for the particle we just deleted.
+ LLPointer<LLViewerPartSource> ps = mParticles[mParticles.count() - 1].mPartSourcep;
+
+ mParticles[mParticles.count() - 1].mPartSourcep = NULL;
+ mParticles.remove(part_num);
+ if (part_num < mParticles.count())
+ {
+ mParticles[part_num].mPartSourcep = ps;
+ }
+
+ LLViewerPartSim::decPartCount(1);
+}
+
+
+void LLViewerPartGroup::updateParticles(const F32 dt)
+{
+ S32 i, count;
+
+
+ LLVector3 gravity(0.f, 0.f, -9.8f);
+
+ LLViewerRegion *regionp = getRegion();
+ count = mParticles.count();
+ for (i = 0; i < count; i++)
+ {
+ LLVector3 a(0.f, 0.f, 0.f);
+ LLViewerPart &part = mParticles[i];
+
+ // Update current time
+ const F32 cur_time = part.mLastUpdateTime + dt;
+ const F32 frac = cur_time/part.mMaxAge;
+
+ // "Drift" the object based on the source object
+ if (part.mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
+ {
+ part.mPosAgent = part.mPartSourcep->mPosAgent;
+ part.mPosAgent += part.mPosOffset;
+ }
+
+ // Do a custom callback if we have one...
+ if (part.mVPCallback)
+ {
+ (*part.mVPCallback)(part, dt);
+ }
+
+ if (part.mFlags & LLPartData::LL_PART_WIND_MASK)
+ {
+ LLVector3 tempVel(part.mVelocity);
+ part.mVelocity *= 1.f - 0.1f*dt;
+ part.mVelocity += 0.1f*dt*regionp->mWind.getVelocity(regionp->getPosRegionFromAgent(part.mPosAgent));
+ }
+
+ // Now do interpolation towards a target
+ if (part.mFlags & LLPartData::LL_PART_TARGET_POS_MASK)
+ {
+ F32 remaining = part.mMaxAge - part.mLastUpdateTime;
+ F32 step = dt / remaining;
+
+ step = llclamp(step, 0.f, 0.1f);
+ step *= 5.f;
+ // we want a velocity that will result in reaching the target in the
+ // Interpolate towards the target.
+ LLVector3 delta_pos = part.mPartSourcep->mTargetPosAgent - part.mPosAgent;
+
+ delta_pos /= remaining;
+
+ part.mVelocity *= (1.f - step);
+ part.mVelocity += step*delta_pos;
+ //part.mPosAgent *= 1.f - to_target_frac;
+ //part.mPosAgent += to_target_frac*part.mPartSourcep->mTargetPosAgent;
+ }
+
+
+ if (part.mFlags & LLPartData::LL_PART_TARGET_LINEAR_MASK)
+ {
+ LLVector3 delta_pos = part.mPartSourcep->mTargetPosAgent - part.mPartSourcep->mPosAgent;
+ part.mPosAgent = part.mPartSourcep->mPosAgent;
+ part.mPosAgent += frac*delta_pos;
+ part.mVelocity = delta_pos;
+ }
+ else
+ {
+ // Do velocity interpolation
+ part.mPosAgent += dt*part.mVelocity;
+ part.mPosAgent += 0.5f*dt*dt*part.mAccel;
+ part.mVelocity += part.mAccel*dt;
+ }
+
+ // Do a bounce test
+ if (part.mFlags & LLPartData::LL_PART_BOUNCE_MASK)
+ {
+ // Need to do point vs. plane check...
+ // For now, just check relative to object height...
+ F32 dz = part.mPosAgent.mV[VZ] - part.mPartSourcep->mPosAgent.mV[VZ];
+ if (dz < 0)
+ {
+ part.mPosAgent.mV[VZ] += -2.f*dz;
+ part.mVelocity.mV[VZ] *= -0.75f;
+ }
+ }
+
+
+ // Reset the offset from the source position
+ if (part.mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
+ {
+ part.mPosOffset = part.mPosAgent;
+ part.mPosOffset -= part.mPartSourcep->mPosAgent;
+ }
+
+ // Do color interpolation
+ if (part.mFlags & LLPartData::LL_PART_INTERP_COLOR_MASK)
+ {
+ part.mColor.setVec(part.mStartColor);
+ part.mColor *= 1.f - frac;
+ part.mColor.mV[3] *= (1.f - frac)*part.mStartColor.mV[3];
+ part.mColor += frac*part.mEndColor;
+ part.mColor.mV[3] += frac*part.mEndColor.mV[3];
+ }
+
+ // Do scale interpolation
+ if (part.mFlags & LLPartData::LL_PART_INTERP_SCALE_MASK)
+ {
+ part.mScale.setVec(part.mStartScale);
+ part.mScale *= 1.f - frac;
+ part.mScale += frac*part.mEndScale;
+ }
+
+ // Set the last update time to now.
+ part.mLastUpdateTime = cur_time;
+
+
+ // Kill dead particles (either flagged dead, or too old)
+ if ((part.mLastUpdateTime > part.mMaxAge) || (LLViewerPart::LL_PART_DEAD_MASK == part.mFlags))
+ {
+ removePart(i);
+ i--;
+ count--;
+ }
+ else if (!posInGroup(part.mPosAgent))
+ {
+ // Transfer particles between groups
+ gWorldPointer->mPartSim.put(part);
+ removePart(i);
+ i--;
+ count--;
+ }
+ }
+
+ // Kill the viewer object if this particle group is empty
+ if (!mParticles.count())
+ {
+ gObjectList.killObject(mVOPartGroupp);
+ mVOPartGroupp = NULL;
+ }
+}
+
+
+void LLViewerPartGroup::shift(const LLVector3 &offset)
+{
+ mCenterAgent += offset;
+ mMinObjPos += offset;
+ mMaxObjPos += offset;
+
+ S32 count = mParticles.count();
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ mParticles[i].mPosAgent += offset;
+ }
+}
+
+
+//////////////////////////////////
+//
+// LLViewerPartSim implementation
+//
+//
+
+
+LLViewerPartSim::LLViewerPartSim()
+{
+ sMaxParticleCount = gSavedSettings.getS32("RenderMaxPartCount");
+}
+
+
+LLViewerPartSim::~LLViewerPartSim()
+{
+ S32 i;
+ S32 count;
+
+ // Kill all of the groups (and particles)
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ delete mViewerPartGroups[i];
+ }
+ mViewerPartGroups.reset();
+
+ // Kill all of the sources
+ count = mViewerPartSources.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartSources[i] = NULL;
+ }
+ mViewerPartSources.reset();
+}
+
+BOOL LLViewerPartSim::shouldAddPart()
+{
+ if (sParticleCount > 0.75f*sMaxParticleCount)
+ {
+
+ F32 frac = (F32)sParticleCount/(F32)sMaxParticleCount;
+ frac -= 0.75;
+ frac *= 3.f;
+ if (frand(1.f) < frac)
+ {
+ // Skip...
+ return FALSE;
+ }
+ }
+ if (sParticleCount >= MAX_PART_COUNT)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void LLViewerPartSim::addPart(LLViewerPart &part)
+{
+ if (sParticleCount < MAX_PART_COUNT)
+ {
+ put(part);
+ }
+}
+
+LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart &part)
+{
+ const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
+ if (part.mPosAgent.magVecSquared() > MAX_MAG)
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "LLViewerPartSim::put Part out of range!" << llendl;
+ llwarns << part.mPosAgent << llendl;
+#endif
+ return NULL;
+ }
+
+ S32 i;
+ S32 count;
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ if (mViewerPartGroups[i]->addPart(part))
+ {
+ // We found a spatial group that we fit into, add us and exit
+ return mViewerPartGroups[i];
+ }
+ }
+
+ // Hmm, we didn't fit in any of the existing spatial groups
+ // Create a new one...
+ LLViewerPartGroup *groupp = createViewerPartGroup(part.mPosAgent);
+ if (!groupp->addPart(part))
+ {
+ llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
+ llinfos << groupp->getCenterAgent() << llendl;
+ llinfos << part.mPosAgent << llendl;
+ return NULL;
+ }
+ return groupp;
+}
+
+LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent)
+{
+ F32 x_origin = ((S32)(pos_agent.mV[VX]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (x_origin > pos_agent.mV[VX])
+ {
+ x_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ F32 y_origin = ((S32)(pos_agent.mV[VY]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (y_origin > pos_agent.mV[VY])
+ {
+ y_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ F32 z_origin = ((S32)(pos_agent.mV[VZ]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (z_origin > pos_agent.mV[VZ])
+ {
+ z_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ LLVector3 group_center(x_origin + PART_SIM_BOX_OFFSET,
+ y_origin + PART_SIM_BOX_OFFSET,
+ z_origin + PART_SIM_BOX_OFFSET);
+
+ LLViewerPartGroup *groupp = new LLViewerPartGroup(group_center, PART_SIM_BOX_SIDE);
+ mViewerPartGroups.put(groupp);
+ return groupp;
+}
+
+
+void LLViewerPartSim::shift(const LLVector3 &offset)
+{
+ S32 i;
+ S32 count;
+
+ count = mViewerPartSources.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartSources[i]->mPosAgent += offset;
+ mViewerPartSources[i]->mTargetPosAgent += offset;
+ mViewerPartSources[i]->mLastUpdatePosAgent += offset;
+ }
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartGroups[i]->shift(offset);
+ }
+}
+
+
+void LLViewerPartSim::updateSimulation()
+{
+ LLMemType mt(LLMemType::MTYPE_PARTICLES);
+
+ static LLFrameTimer update_timer;
+
+ const F32 dt = update_timer.getElapsedTimeAndResetF32();
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
+ {
+ return;
+ }
+
+ // Start at a random particle system so the same
+ // particle system doesn't always get first pick at the
+ // particles. Theoretically we'd want to do this in distance
+ // order or something, but sorting particle sources will be a big
+ // pain.
+ S32 i;
+ S32 count = mViewerPartSources.count();
+ S32 start = (S32)frand((F32)count);
+ S32 dir = 1;
+ if (frand(1.0) > 0.5f)
+ {
+ dir = -1;
+ }
+
+ S32 num_updates = 0;
+ for (i = start; num_updates < count;)
+ {
+ if (i >= count)
+ {
+ i = 0;
+ }
+ if (i < 0)
+ {
+ i = count - 1;
+ }
+
+ if (!mViewerPartSources[i]->isDead())
+ {
+ mViewerPartSources[i]->update(dt);
+ }
+
+ if (mViewerPartSources[i]->isDead())
+ {
+ mViewerPartSources.remove(i);
+ count--;
+ }
+ else
+ {
+ i += dir;
+ }
+ num_updates++;
+ }
+
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartGroups[i]->updateParticles(dt);
+ if (!mViewerPartGroups[i]->getCount())
+ {
+ delete mViewerPartGroups[i];
+ mViewerPartGroups.remove(i);
+ i--;
+ count--;
+ }
+ }
+ //llinfos << "Particles: " << sParticleCount << llendl;
+}
+
+
+void LLViewerPartSim::addPartSource(LLViewerPartSource *sourcep)
+{
+ if (!sourcep)
+ {
+ llwarns << "Null part source!" << llendl;
+ return;
+ }
+ mViewerPartSources.put(sourcep);
+}
+
+
+void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
+{
+ S32 i, count;
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ if (mViewerPartGroups[i]->getRegion() == regionp)
+ {
+ delete mViewerPartGroups[i];
+ mViewerPartGroups.remove(i);
+ i--;
+ count--;
+ }
+ }
+}
+
+void LLViewerPartSim::cleanMutedParticles(const LLUUID& task_id)
+{
+ S32 i;
+ S32 count = mViewerPartSources.count();
+ for (i = 0; i < count; ++i)
+ {
+ if (mViewerPartSources[i]->getOwnerUUID() == task_id)
+ {
+ mViewerPartSources.remove(i);
+ i--;
+ count--;
+ }
+ }
+}
diff --git a/indra/newview/llviewerpartsim.h b/indra/newview/llviewerpartsim.h
new file mode 100644
index 0000000000..83c374a42e
--- /dev/null
+++ b/indra/newview/llviewerpartsim.h
@@ -0,0 +1,141 @@
+/**
+ * @file llviewerpartsim.h
+ * @brief LLViewerPart class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARTSIM_H
+#define LL_LLVIEWERPARTSIM_H
+
+#include "lldarrayptr.h"
+#include "llskiplist.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+
+#include "llpartdata.h"
+
+class LLViewerImage;
+class LLViewerPart;
+class LLViewerPartSource;
+class LLViewerRegion;
+class LLViewerImage;
+class LLVOPartGroup;
+
+typedef void (*LLVPCallback)(LLViewerPart &part, const F32 dt);
+
+///////////////////
+//
+// An individual particle
+//
+
+
+class LLViewerPart : public LLPartData
+{
+public:
+ LLViewerPart();
+ ~LLViewerPart();
+
+ LLViewerPart &operator=(const LLViewerPart &part);
+ void init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVPCallback cb);
+
+
+ U32 mPartID; // Particle ID used primarily for moving between groups
+ F32 mLastUpdateTime; // Last time the particle was updated
+
+
+ LLVPCallback mVPCallback; // Callback function for more complicated behaviors
+ LLPointer<LLViewerPartSource> mPartSourcep; // Particle source used for this object
+
+
+ // Current particle state (possibly used for rendering)
+ LLPointer<LLViewerImage> mImagep;
+ LLVector3 mPosAgent;
+ LLVector3 mVelocity;
+ LLVector3 mAccel;
+ LLColor4 mColor;
+ LLVector2 mScale;
+
+ static U32 sNextPartID;
+};
+
+
+
+class LLViewerPartGroup
+{
+public:
+ LLViewerPartGroup(const LLVector3 &center,
+ const F32 box_radius);
+ virtual ~LLViewerPartGroup();
+
+ void cleanup();
+
+ BOOL addPart(LLViewerPart &part);
+
+ void updateParticles(const F32 dt);
+
+ BOOL posInGroup(const LLVector3 &pos);
+
+ void shift(const LLVector3 &offset);
+
+ LLDynamicArray<LLViewerPart> mParticles;
+
+ const LLVector3 &getCenterAgent() const { return mCenterAgent; }
+ S32 getCount() const { return mParticles.count(); }
+ LLViewerRegion *getRegion() const { return mRegionp; }
+protected:
+ void removePart(const S32 part_num);
+
+protected:
+ LLVector3 mCenterAgent;
+ F32 mBoxRadius;
+ LLVector3 mMinObjPos;
+ LLVector3 mMaxObjPos;
+
+ LLPointer<LLVOPartGroup> mVOPartGroupp;
+ LLViewerRegion *mRegionp;
+};
+
+
+class LLViewerPartSim
+{
+public:
+ LLViewerPartSim();
+ virtual ~LLViewerPartSim();
+
+ void shift(const LLVector3 &offset);
+
+ void updateSimulation();
+
+ void addPartSource(LLViewerPartSource *sourcep);
+
+ void cleanupRegion(LLViewerRegion *regionp);
+
+ BOOL shouldAddPart(); // Just decides whether this particle should be added or not (for particle count capping)
+ void addPart(LLViewerPart &part);
+ void cleanMutedParticles(const LLUUID& task_id);
+
+ friend class LLViewerPartGroup;
+
+ BOOL aboveParticleLimit() const { return sParticleCount > sMaxParticleCount; }
+
+ static void setMaxPartCount(const S32 max_parts) { sMaxParticleCount = max_parts; }
+ static S32 getMaxPartCount() { return sMaxParticleCount; }
+ static void incPartCount(const S32 count) { sParticleCount += count; }
+ static void decPartCount(const S32 count) { sParticleCount -= count; }
+
+protected:
+ LLViewerPartGroup *createViewerPartGroup(const LLVector3 &pos_agent);
+ LLViewerPartGroup *put(LLViewerPart &part);
+
+protected:
+ LLDynamicArray<LLViewerPartGroup *> mViewerPartGroups;
+ LLDynamicArrayPtr<LLPointer<LLViewerPartSource> > mViewerPartSources;
+ LLFrameTimer mSimulationTimer;
+
+ static S32 sMaxParticleCount;
+ static S32 sParticleCount;
+};
+
+#endif // LL_LLVIEWERPARTSIM_H
diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp
new file mode 100644
index 0000000000..e73f77e463
--- /dev/null
+++ b/indra/newview/llviewerpartsource.cpp
@@ -0,0 +1,719 @@
+/**
+ * @file llviewerpartsource.cpp
+ * @brief LLViewerPartSource class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llviewerpartsource.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+
+LLViewerPartSource::LLViewerPartSource(const U32 type) :
+ mType(type),
+ mOwnerUUID(LLUUID::null)
+{
+ mLastUpdateTime = 0.f;
+ mLastPartTime = 0.f;
+ mIsDead = FALSE;
+}
+
+void LLViewerPartSource::setDead()
+{
+ mIsDead = TRUE;
+}
+
+
+void LLViewerPartSource::updatePart(LLViewerPart &part, const F32 dt)
+{
+}
+
+void LLViewerPartSource::update(const F32 dt)
+{
+ llerrs << "Creating default part source!" << llendl;
+}
+
+
+
+LLViewerPartSourceScript::LLViewerPartSourceScript(LLViewerObject *source_objp) :
+ LLViewerPartSource(LL_PART_SOURCE_SCRIPT)
+{
+ llassert(source_objp);
+ mSourceObjectp = source_objp;
+ mPosAgent = mSourceObjectp->getPositionAgent();
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ mImagep->bind();
+ mImagep->setClamp(TRUE, TRUE);
+}
+
+
+void LLViewerPartSourceScript::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+ mTargetObjectp = NULL;
+}
+
+
+
+void LLViewerPartSourceScript::update(const F32 dt)
+{
+ F32 old_update_time = mLastUpdateTime;
+ mLastUpdateTime += dt;
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+
+ // Update this for objects which have the follow flag set...
+ if (!mSourceObjectp.isNull())
+ {
+ if (mSourceObjectp->isDead())
+ {
+ mSourceObjectp = NULL;
+ }
+ else if (mSourceObjectp->mDrawable.notNull())
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ }
+
+ if (mTargetObjectp.isNull()
+ && mPartSysData.mTargetUUID.notNull())
+ {
+ //
+ // Hmm, missing object, let's see if we can find it...
+ //
+
+ LLViewerObject *target_objp = gObjectList.findObject(mPartSysData.mTargetUUID);
+ setTargetObject(target_objp);
+ }
+
+ if (!mTargetObjectp.isNull())
+ {
+ if (mTargetObjectp->isDead())
+ {
+ mTargetObjectp = NULL;
+ }
+ else if (mTargetObjectp->mDrawable.notNull())
+ {
+ mTargetPosAgent = mTargetObjectp->getRenderPosition();
+ }
+ }
+
+ if (!mTargetObjectp)
+ {
+ mTargetPosAgent = mPosAgent;
+ }
+
+ if (mPartSysData.mMaxAge && ((mPartSysData.mStartAge + mLastUpdateTime + dt_update) > mPartSysData.mMaxAge))
+ {
+ // Kill particle source because it has outlived its max age...
+ setDead();
+ return;
+ }
+
+ BOOL first_run = FALSE;
+ if (old_update_time <= 0.f)
+ {
+ first_run = TRUE;
+ }
+
+ F32 max_time = llmax(1.f, 10.f*mPartSysData.mBurstRate);
+ dt_update = llmin(max_time, dt_update);
+ while ((dt_update > mPartSysData.mBurstRate) || first_run)
+ {
+ first_run = FALSE;
+ LLViewerPart part;
+
+ part.init(this, mImagep, NULL);
+ part.mFlags = mPartSysData.mPartData.mFlags;
+ part.mMaxAge = mPartSysData.mPartData.mMaxAge;
+ part.mStartColor = mPartSysData.mPartData.mStartColor;
+ part.mEndColor = mPartSysData.mPartData.mEndColor;
+ part.mColor = part.mStartColor;
+
+ part.mStartScale = mPartSysData.mPartData.mStartScale;
+ part.mEndScale = mPartSysData.mPartData.mEndScale;
+ part.mScale = part.mStartScale;
+
+ part.mAccel = mPartSysData.mPartAccel;
+
+ // Update the rotation of the particle source by the angular velocity
+ // First check to see if there is still an angular velocity.
+ F32 angular_velocity_mag = mPartSysData.mAngularVelocity.magVec();
+ if (angular_velocity_mag != 0.0f)
+ {
+ F32 av_angle = dt * angular_velocity_mag;
+ LLQuaternion dquat(av_angle, mPartSysData.mAngularVelocity);
+ mRotation *= dquat;
+ }
+ else
+ {
+ // No angular velocity. Reset our rotation.
+ mRotation.setQuat(0, 0, 0);
+ }
+
+ if (gWorldPointer->mPartSim.aboveParticleLimit())
+ {
+ // Don't bother doing any more updates if we're above the particle limit,
+ // just give up.
+ mLastPartTime = mLastUpdateTime;
+ break;
+ }
+
+ S32 i;
+ for (i = 0; i < mPartSysData.mBurstPartCount; i++)
+ {
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ continue;
+ }
+
+ if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_DROP)
+ {
+ part.mPosAgent = mPosAgent;
+ part.mVelocity.setVec(0.f, 0.f, 0.f);
+ }
+ else if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE)
+ {
+ part.mPosAgent = mPosAgent;
+ LLVector3 part_dir_vector;
+
+ F32 mvs;
+ do
+ {
+ part_dir_vector.mV[VX] = frand(2.f) - 1.f;
+ part_dir_vector.mV[VY] = frand(2.f) - 1.f;
+ part_dir_vector.mV[VZ] = frand(2.f) - 1.f;
+ mvs = part_dir_vector.magVecSquared();
+ }
+ while ((mvs > 1.f) || (mvs < 0.01f));
+
+ part_dir_vector.normVec();
+ part.mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
+ part.mVelocity = part_dir_vector;
+ F32 speed = mPartSysData.mBurstSpeedMin + frand(mPartSysData.mBurstSpeedMax - mPartSysData.mBurstSpeedMin);
+ part.mVelocity *= speed;
+ }
+ else if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE
+ || mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE)
+ {
+ part.mPosAgent = mPosAgent;
+
+ // original implemenetation for part_dir_vector was just:
+ LLVector3 part_dir_vector(0.0, 0.0, 1.0);
+
+ // params from the script...
+ // outer = outer cone angle
+ // inner = inner cone angle
+ // between outer and inner there will be particles
+ F32 innerAngle = mPartSysData.mInnerAngle;
+ F32 outerAngle = mPartSysData.mOuterAngle;
+
+ // generate a random angle within the given space...
+ F32 angle = innerAngle + frand(outerAngle - innerAngle);
+
+ // split which side it will go on randomly...
+ if (frand(1.0) < 0.5)
+ {
+ angle = -angle;
+ }
+
+ // Both patterns rotate around the x-axis first:
+ part_dir_vector.rotVec(angle, 1.0, 0.0, 0.0);
+
+ // If this is a cone pattern, rotate again to create the cone.
+ if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE)
+ {
+ part_dir_vector.rotVec(frand(4*F_PI), 0.0, 0.0, 1.0);
+ }
+
+ // Only apply this rotation if using the deprecated angles.
+ if (! (mPartSysData.mFlags & LLPartSysData::LL_PART_USE_NEW_ANGLE))
+ {
+ // Deprecated...
+ part_dir_vector.rotVec(outerAngle, 1.0, 0.0, 0.0);
+ }
+
+ if (mSourceObjectp)
+ {
+ part_dir_vector = part_dir_vector * mSourceObjectp->getRenderRotation();
+ }
+ part_dir_vector = part_dir_vector * mRotation;
+
+ part.mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
+
+ part.mVelocity = part_dir_vector;
+
+ F32 speed = mPartSysData.mBurstSpeedMin + frand(mPartSysData.mBurstSpeedMax - mPartSysData.mBurstSpeedMin);
+ part.mVelocity *= speed;
+ }
+ else
+ {
+ part.mPosAgent = mPosAgent;
+ part.mVelocity.setVec(0.f, 0.f, 0.f);
+ //llwarns << "Unknown source pattern " << (S32)mPartSysData.mPattern << llendl;
+ }
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+
+ mLastPartTime = mLastUpdateTime;
+ dt_update -= mPartSysData.mBurstRate;
+ }
+}
+
+// static
+LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, const S32 block_num)
+{
+ if (!pssp)
+ {
+ if (LLPartSysData::isNullPS(block_num))
+ {
+ return NULL;
+ }
+ LLViewerPartSourceScript *new_pssp = new LLViewerPartSourceScript(source_objp);
+ if (!new_pssp->mPartSysData.unpackBlock(block_num))
+ {
+ return NULL;
+ }
+ if (new_pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(new_pssp->mPartSysData.mTargetUUID);
+ new_pssp->setTargetObject(target_objp);
+ }
+ return new_pssp;
+ }
+ else
+ {
+ if (LLPartSysData::isNullPS(block_num))
+ {
+ return NULL;
+ }
+
+ if (!pssp->mPartSysData.unpackBlock(block_num))
+ {
+ return NULL;
+ }
+ if (pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(pssp->mPartSysData.mTargetUUID);
+ pssp->setTargetObject(target_objp);
+ }
+ return pssp;
+ }
+}
+
+
+LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, LLDataPacker &dp)
+{
+ if (!pssp)
+ {
+ LLViewerPartSourceScript *new_pssp = new LLViewerPartSourceScript(source_objp);
+ if (!new_pssp->mPartSysData.unpack(dp))
+ {
+ return NULL;
+ }
+ if (new_pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(new_pssp->mPartSysData.mTargetUUID);
+ new_pssp->setTargetObject(target_objp);
+ }
+ return new_pssp;
+ }
+ else
+ {
+ if (!pssp->mPartSysData.unpack(dp))
+ {
+ return NULL;
+ }
+ if (pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(pssp->mPartSysData.mTargetUUID);
+ pssp->setTargetObject(target_objp);
+ }
+ return pssp;
+ }
+}
+
+void LLViewerPartSourceScript::setImage(LLViewerImage *imagep)
+{
+ mImagep = imagep;
+}
+
+void LLViewerPartSourceScript::setTargetObject(LLViewerObject *objp)
+{
+ mTargetObjectp = objp;
+}
+
+
+
+
+
+LLViewerPartSourceSpiral::LLViewerPartSourceSpiral(const LLVector3 &pos) :
+ LLViewerPartSource(LL_PART_SOURCE_CHAT)
+{
+ mPosAgent = pos;
+}
+
+void LLViewerPartSourceSpiral::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+}
+
+
+void LLViewerPartSourceSpiral::updatePart(LLViewerPart &part, const F32 dt)
+{
+ F32 frac = part.mLastUpdateTime/part.mMaxAge;
+
+ LLVector3 center_pos;
+ LLViewerPartSource *ps = (LLViewerPartSource*)part.mPartSourcep;
+ LLViewerPartSourceSpiral *pss = (LLViewerPartSourceSpiral *)ps;
+ if (!pss->mSourceObjectp.isNull() && !pss->mSourceObjectp->mDrawable.isNull())
+ {
+ part.mPosAgent = pss->mSourceObjectp->getRenderPosition();
+ }
+ else
+ {
+ part.mPosAgent = pss->mPosAgent;
+ }
+ F32 x = sin(F_TWO_PI*frac + part.mParameter);
+ F32 y = cos(F_TWO_PI*frac + part.mParameter);
+
+ part.mPosAgent.mV[VX] += x;
+ part.mPosAgent.mV[VY] += y;
+ part.mPosAgent.mV[VZ] += -0.5f + frac;
+}
+
+
+void LLViewerPartSourceSpiral::update(const F32 dt)
+{
+ if (!mImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ }
+
+ const F32 RATE = 0.025f;
+
+ mLastUpdateTime += dt;
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+ F32 max_time = llmax(1.f, 10.f*RATE);
+ dt_update = llmin(max_time, dt_update);
+
+ if (dt_update > RATE)
+ {
+ mLastPartTime = mLastUpdateTime;
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ return;
+ }
+
+ if (!mSourceObjectp.isNull() && !mSourceObjectp->mDrawable.isNull())
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ LLViewerPart part;
+ part.init(this, mImagep, updatePart);
+ part.mStartColor = mColor;
+ part.mEndColor = mColor;
+ part.mEndColor.mV[3] = 0.f;
+ part.mPosAgent = mPosAgent;
+ part.mMaxAge = 1.f;
+ part.mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
+ part.mLastUpdateTime = 0.f;
+ part.mScale.mV[0] = 0.25f;
+ part.mScale.mV[1] = 0.25f;
+ part.mParameter = frand(F_TWO_PI);
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+}
+
+void LLViewerPartSourceSpiral::setSourceObject(LLViewerObject *objp)
+{
+ mSourceObjectp = objp;
+}
+
+void LLViewerPartSourceSpiral::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
+
+
+
+
+LLViewerPartSourceBeam::LLViewerPartSourceBeam() :
+ LLViewerPartSource(LL_PART_SOURCE_BEAM)
+{
+}
+
+LLViewerPartSourceBeam::~LLViewerPartSourceBeam()
+{
+}
+
+void LLViewerPartSourceBeam::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+ mTargetObjectp = NULL;
+}
+
+void LLViewerPartSourceBeam::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
+
+void LLViewerPartSourceBeam::updatePart(LLViewerPart &part, const F32 dt)
+{
+ F32 frac = part.mLastUpdateTime/part.mMaxAge;
+
+ LLViewerPartSource *ps = (LLViewerPartSource*)part.mPartSourcep;
+ LLViewerPartSourceBeam *psb = (LLViewerPartSourceBeam *)ps;
+ if (psb->mSourceObjectp.isNull())
+ {
+ part.mFlags = LLPartData::LL_PART_DEAD_MASK;
+ return;
+ }
+
+ LLVector3 source_pos_agent;
+ LLVector3 target_pos_agent;
+ if (!psb->mSourceObjectp.isNull() && !psb->mSourceObjectp->mDrawable.isNull())
+ {
+ if (psb->mSourceObjectp->isAvatar())
+ {
+ LLViewerObject *objp = psb->mSourceObjectp;
+ LLVOAvatar *avp = (LLVOAvatar *)objp;
+ source_pos_agent = avp->mWristLeftp->getWorldPosition();
+ }
+ else
+ {
+ source_pos_agent = psb->mSourceObjectp->getRenderPosition();
+ }
+ }
+ if (!psb->mTargetObjectp.isNull() && !psb->mTargetObjectp->mDrawable.isNull())
+ {
+ target_pos_agent = psb->mTargetObjectp->getRenderPosition();
+ }
+
+ part.mPosAgent = (1.f - frac) * source_pos_agent;
+ if (psb->mTargetObjectp.isNull())
+ {
+ part.mPosAgent += frac * (gAgent.getPosAgentFromGlobal(psb->mLKGTargetPosGlobal));
+ }
+ else
+ {
+ part.mPosAgent += frac * target_pos_agent;
+ }
+}
+
+
+void LLViewerPartSourceBeam::update(const F32 dt)
+{
+
+ const F32 RATE = 0.025f;
+
+ mLastUpdateTime += dt;
+
+ if (!mSourceObjectp.isNull() && !mSourceObjectp->mDrawable.isNull())
+ {
+ if (mSourceObjectp->isAvatar())
+ {
+ LLViewerObject *objp = mSourceObjectp;
+ LLVOAvatar *avp = (LLVOAvatar *)objp;
+ mPosAgent = avp->mWristLeftp->getWorldPosition();
+ }
+ else
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ }
+
+ if (!mTargetObjectp.isNull() && !mTargetObjectp->mDrawable.isNull())
+ {
+ mTargetPosAgent = mTargetObjectp->getRenderPosition();
+ }
+ else if (!mLKGTargetPosGlobal.isExactlyZero())
+ {
+ mTargetPosAgent = gAgent.getPosAgentFromGlobal(mLKGTargetPosGlobal);
+ }
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+ F32 max_time = llmax(1.f, 10.f*RATE);
+ dt_update = llmin(max_time, dt_update);
+
+ if (dt_update > RATE)
+ {
+ mLastPartTime = mLastUpdateTime;
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ return;
+ }
+
+ if (!mImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ }
+
+ LLViewerPart part;
+ part.init(this, mImagep, NULL);
+
+ part.mFlags = LLPartData::LL_PART_INTERP_COLOR_MASK |
+ LLPartData::LL_PART_INTERP_SCALE_MASK |
+ LLPartData::LL_PART_TARGET_POS_MASK |
+ LLPartData::LL_PART_FOLLOW_VELOCITY_MASK;
+ part.mMaxAge = 0.5f;
+ part.mStartColor = mColor;
+ part.mEndColor = part.mStartColor;
+ part.mEndColor.mV[3] = 0.4f;
+ part.mColor = part.mStartColor;
+
+ part.mStartScale = LLVector2(0.1f, 0.1f);
+ part.mEndScale = LLVector2(0.1f, 0.1f);
+ part.mScale = part.mStartScale;
+
+ part.mPosAgent = mPosAgent;
+ part.mVelocity = mTargetPosAgent - mPosAgent;
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+}
+
+void LLViewerPartSourceBeam::setSourceObject(LLViewerObject* objp)
+{
+ mSourceObjectp = objp;
+}
+
+void LLViewerPartSourceBeam::setTargetObject(LLViewerObject* objp)
+{
+ mTargetObjectp = objp;
+}
+
+
+
+
+LLViewerPartSourceChat::LLViewerPartSourceChat(const LLVector3 &pos) :
+ LLViewerPartSource(LL_PART_SOURCE_SPIRAL)
+{
+ mPosAgent = pos;
+}
+
+void LLViewerPartSourceChat::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+}
+
+
+void LLViewerPartSourceChat::updatePart(LLViewerPart &part, const F32 dt)
+{
+ F32 frac = part.mLastUpdateTime/part.mMaxAge;
+
+ LLVector3 center_pos;
+ LLViewerPartSource *ps = (LLViewerPartSource*)part.mPartSourcep;
+ LLViewerPartSourceChat *pss = (LLViewerPartSourceChat *)ps;
+ if (!pss->mSourceObjectp.isNull() && !pss->mSourceObjectp->mDrawable.isNull())
+ {
+ part.mPosAgent = pss->mSourceObjectp->getRenderPosition();
+ }
+ else
+ {
+ part.mPosAgent = pss->mPosAgent;
+ }
+ F32 x = sin(F_TWO_PI*frac + part.mParameter);
+ F32 y = cos(F_TWO_PI*frac + part.mParameter);
+
+ part.mPosAgent.mV[VX] += x;
+ part.mPosAgent.mV[VY] += y;
+ part.mPosAgent.mV[VZ] += -0.5f + frac;
+}
+
+
+void LLViewerPartSourceChat::update(const F32 dt)
+{
+ if (!mImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ }
+
+
+ const F32 RATE = 0.025f;
+
+ mLastUpdateTime += dt;
+
+ if (mLastUpdateTime > 2.f)
+ {
+ // Kill particle source because it has outlived its max age...
+ setDead();
+ return;
+ }
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+
+ // Clamp us to generating at most one second's worth of particles on a frame.
+ F32 max_time = llmax(1.f, 10.f*RATE);
+ dt_update = llmin(max_time, dt_update);
+
+ if (dt_update > RATE)
+ {
+ mLastPartTime = mLastUpdateTime;
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ return;
+ }
+
+ if (!mSourceObjectp.isNull() && !mSourceObjectp->mDrawable.isNull())
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ LLViewerPart part;
+ part.init(this, mImagep, updatePart);
+ part.mStartColor = mColor;
+ part.mEndColor = mColor;
+ part.mEndColor.mV[3] = 0.f;
+ part.mPosAgent = mPosAgent;
+ part.mMaxAge = 1.f;
+ part.mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
+ part.mLastUpdateTime = 0.f;
+ part.mScale.mV[0] = 0.25f;
+ part.mScale.mV[1] = 0.25f;
+ part.mParameter = frand(F_TWO_PI);
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+}
+
+void LLViewerPartSourceChat::setSourceObject(LLViewerObject *objp)
+{
+ mSourceObjectp = objp;
+}
+
+void LLViewerPartSourceChat::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
diff --git a/indra/newview/llviewerpartsource.h b/indra/newview/llviewerpartsource.h
new file mode 100644
index 0000000000..05fa007ead
--- /dev/null
+++ b/indra/newview/llviewerpartsource.h
@@ -0,0 +1,188 @@
+/**
+ * @file llviewerpartsource.h
+ * @brief LLViewerPartSource class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARTSOURCE_H
+#define LL_LLVIEWERPARTSOURCE_H
+
+#include "llmemory.h"
+#include "llpartdata.h"
+#include "llquaternion.h"
+#include "v3math.h"
+
+////////////////////
+//
+// A particle source - subclassed to generate particles with different behaviors
+//
+//
+
+class LLViewerImage;
+class LLViewerObject;
+class LLViewerPart;
+
+class LLViewerPartSource : public LLRefCount
+{
+public:
+ enum
+ {
+ LL_PART_SOURCE_NULL,
+ LL_PART_SOURCE_SCRIPT,
+ LL_PART_SOURCE_SPIRAL,
+ LL_PART_SOURCE_BEAM,
+ LL_PART_SOURCE_CHAT
+ };
+
+ LLViewerPartSource(const U32 type);
+
+ virtual void update(const F32 dt); // Return FALSE if this source is dead...
+
+ virtual void setDead();
+ BOOL isDead() const { return mIsDead; }
+
+ U32 getType() const { return mType; }
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ void setOwnerUUID(const LLUUID& owner_id) { mOwnerUUID = owner_id; }
+ LLUUID getOwnerUUID() const { return mOwnerUUID; }
+
+ LLVector3 mPosAgent; // Location of the particle source
+ LLVector3 mTargetPosAgent; // Location of the target position
+ LLVector3 mLastUpdatePosAgent;
+protected:
+ U32 mType;
+ BOOL mIsDead;
+ F32 mLastUpdateTime;
+ F32 mLastPartTime;
+ LLUUID mOwnerUUID;
+
+ // Particle information
+ U32 mPartFlags; // Flags for the particle
+};
+
+
+
+///////////////////////////////
+//
+// LLViewerPartSourceScript
+//
+// Particle source that handles the "generic" script-drive particle source
+// attached to objects
+//
+
+
+class LLViewerPartSourceScript : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceScript(LLViewerObject *source_objp);
+ /*virtual*/ void update(const F32 dt);
+
+ /*virtual*/ void setDead();
+
+ BOOL updateFromMesg();
+
+ // Returns a new particle source to attach to an object...
+ static LLViewerPartSourceScript *unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, const S32 block_num);
+ static LLViewerPartSourceScript *unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, LLDataPacker &dp);
+
+ LLViewerImage *getImage() const { return mImagep; }
+ void setImage(LLViewerImage *imagep);
+ LLPartSysData mPartSysData;
+
+ void setTargetObject(LLViewerObject *objp);
+
+protected:
+ LLQuaternion mRotation; // Current rotation for particle source
+ LLPointer<LLViewerImage> mImagep; // Cached image pointer of the mPartSysData UUID
+ LLPointer<LLViewerObject> mSourceObjectp; // Source object that this particle system is attached to
+ LLPointer<LLViewerObject> mTargetObjectp; // Target object for the particle source
+};
+
+
+////////////////////////////
+//
+// Particle source for spiral effect (customize avatar, mostly)
+//
+
+class LLViewerPartSourceSpiral : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceSpiral(const LLVector3 &pos);
+
+ /*virtual*/ void setDead();
+
+ /*virtual*/ void update(const F32 dt);
+
+ void setSourceObject(LLViewerObject *objp);
+ void setColor(const LLColor4 &color);
+
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ LLColor4 mColor;
+protected:
+ LLPointer<LLViewerImage> mImagep;
+ LLPointer<LLViewerObject> mSourceObjectp;
+ LLVector3d mLKGSourcePosGlobal;
+};
+
+
+////////////////////////////
+//
+// Particle source for tractor(editing) beam
+//
+
+class LLViewerPartSourceBeam : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceBeam();
+ ~LLViewerPartSourceBeam();
+
+ /*virtual*/ void setDead();
+
+ /*virtual*/ void update(const F32 dt);
+
+ void setSourceObject(LLViewerObject *objp);
+ void setTargetObject(LLViewerObject *objp);
+ void setSourcePosGlobal(const LLVector3d &pos_global);
+ void setTargetPosGlobal(const LLVector3d &pos_global);
+ void setColor(const LLColor4 &color);
+
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ LLPointer<LLViewerImage> mImagep;
+ LLPointer<LLViewerObject> mSourceObjectp;
+ LLPointer<LLViewerObject> mTargetObjectp;
+ LLVector3d mLKGTargetPosGlobal;
+ LLColor4 mColor;
+protected:
+};
+
+
+
+//////////////////////////
+//
+// Particle source for chat effect
+//
+
+class LLViewerPartSourceChat : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceChat(const LLVector3 &pos);
+
+ /*virtual*/ void setDead();
+
+ /*virtual*/ void update(const F32 dt);
+
+ void setSourceObject(LLViewerObject *objp);
+ void setColor(const LLColor4 &color);
+
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ LLColor4 mColor;
+protected:
+ LLPointer<LLViewerImage> mImagep;
+ LLPointer<LLViewerObject> mSourceObjectp;
+ LLVector3d mLKGSourcePosGlobal;
+};
+
+
+#endif // LL_LLVIEWERPARTSOURCE_H
diff --git a/indra/newview/llviewerprecompiledheaders.cpp b/indra/newview/llviewerprecompiledheaders.cpp
new file mode 100644
index 0000000000..3de6af18dd
--- /dev/null
+++ b/indra/newview/llviewerprecompiledheaders.cpp
@@ -0,0 +1,16 @@
+/**
+ * @file llviewerprecompiledheaders.cpp
+ * @brief precompiled headers for newview project
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// source file that includes just the standard includes
+// newview.pch will be the pre-compiled header
+// llviewerprecompiledheaders.obj will contain the pre-compiled type information
+
+#include "llviewerprecompiledheaders.h"
+
+// TODO: reference any additional headers you need in llviewerprecompiledheaders.h
+// and not in this file
diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h
new file mode 100644
index 0000000000..5c50fef58e
--- /dev/null
+++ b/indra/newview/llviewerprecompiledheaders.h
@@ -0,0 +1,218 @@
+/**
+ * @file llviewerprecompiledheaders.h
+ * @brief precompiled headers for newview project
+ * @author James Cook
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLVIEWERPRECOMPILEDHEADERS_H
+#define LL_LLVIEWERPRECOMPILEDHEADERS_H
+
+// This file MUST be the first one included by each .cpp file
+// in viewer.
+// It is used to precompile headers for improved build speed.
+
+// Reference headers your program requires here:
+#include "linden_common.h"
+
+// The rest of the common system headers go here:
+
+// Work around stupid Microsoft STL warning
+#ifdef LL_WINDOWS
+#pragma warning (disable : 4702) // warning C4702: unreachable code
+#endif
+
+#include <algorithm>
+#include <deque>
+#include <functional>
+#include <map>
+#include <set>
+
+#ifdef LL_WINDOWS
+#pragma warning (3 : 4702) // we like level 3, not 4
+#endif
+
+// Library headers from llcommon project:
+#include "bitpack.h"
+#include "doublelinkedlist.h"
+#include "imageids.h"
+#include "indra_constants.h"
+//#include "linden_common.h"
+//#include "llpreprocessor.h"
+#include "linked_lists.h"
+#include "llapp.h"
+#include "llapr.h"
+#include "llassoclist.h"
+#include "llcriticaldamp.h"
+#include "lldarray.h"
+#include "lldarrayptr.h"
+#include "lldefs.h"
+#include "lldepthstack.h"
+#include "lldlinked.h"
+#include "lldqueueptr.h"
+#include "llendianswizzle.h"
+#include "llerror.h"
+#include "llerrorbuffer.h"
+#include "llerrorstream.h"
+#include "llfasttimer.h"
+#include "llfixedbuffer.h"
+#include "llframetimer.h"
+#include "llhash.h"
+#include "lllinkedqueue.h"
+#include "lllocalidhashmap.h"
+#include "llmap.h"
+#include "llmemory.h"
+#include "llnametable.h"
+#include "llpagemem.h"
+#include "llpriqueuemap.h"
+#include "llprocessor.h"
+#include "llptrskiplist.h"
+#include "llptrskipmap.h"
+//#include "llsecondlifeurls.h"
+#include "llskiplist.h"
+#include "llskipmap.h"
+#include "llstack.h"
+#include "llstat.h"
+#include "llstl.h"
+#include "llstrider.h"
+#include "llstring.h"
+#include "llstringtable.h"
+#include "llsys.h"
+#include "llthread.h"
+#include "lltimer.h"
+#include "lluuidhashmap.h"
+//#include "llversion.h"
+//#include "processor.h"
+#include "stdenums.h"
+#include "stdtypes.h"
+//#include "string_table.h"
+//#include "timer.h"
+#include "timing.h"
+#include "u64.h"
+
+// Library includes from llimage
+#include "kdc_flow_control.h"
+#include "kde_flow_control.h"
+#include "kdu_image.h"
+#include "kdu_image_local.h"
+#include "llblockdata.h"
+#include "llblockdecoder.h"
+#include "llblockencoder.h"
+#include "llimage.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagejpeg.h"
+#include "llimagetga.h"
+#include "llkdumem.h"
+#include "llmapimagetype.h"
+
+// Library includes from llmath project
+//#include "camera.h"
+//#include "coordframe.h"
+#include "llmath.h"
+#include "llbboxlocal.h"
+#include "llcamera.h"
+#include "llcoord.h"
+#include "llcoordframe.h"
+#include "llcrc.h"
+#include "llinterp.h"
+#include "llmd5.h"
+#include "llperlin.h"
+#include "llplane.h"
+#include "llquantize.h"
+#include "llrand.h"
+#include "llrect.h"
+#include "lluuid.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "raytrace.h"
+#include "v2math.h"
+#include "v3color.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v4math.h"
+////#include "vmath.h"
+#include "xform.h"
+
+// Library includes from llmessage project
+//#include "llassetstorage.h"
+#include "llcachename.h"
+#include "llcallbacklisth.h"
+#include "llcircuit.h"
+#include "llcrypto.h"
+#include "lldatapacker.h"
+#include "lldbstrings.h"
+#include "lldispatcher.h"
+#include "lleventflags.h"
+#include "llhost.h"
+#include "llinstantmessage.h"
+#include "llinvite.h"
+//#include "llloginflags.h"
+#include "lllogtextmessage.h"
+#include "llmail.h"
+#include "llmessagethrottle.h"
+#include "llnamevalue.h"
+#include "llpacketack.h"
+#include "llpacketbuffer.h"
+#include "llpacketring.h"
+#include "llpartdata.h"
+//#include "llqueryflags.h"
+//#include "llregionflags.h"
+#include "llregionhandle.h"
+#include "lltaskname.h"
+#include "llteleportflags.h"
+#include "llthrottle.h"
+#include "lltransfermanager.h"
+#include "lltransfersourceasset.h"
+#include "lltransfersourcefile.h"
+#include "lltransfertargetfile.h"
+#include "lltransfertargetvfile.h"
+#include "lluseroperation.h"
+#include "llvehicleparams.h"
+#include "llxfer.h"
+#include "llxfer_file.h"
+#include "llxfer_mem.h"
+#include "llxfer_vfile.h"
+#include "llxfermanager.h"
+#include "machine.h"
+#include "mean_collision_data.h"
+#include "message.h"
+#include "message_prehash.h"
+#include "net.h"
+//#include "network.h"
+#include "partsyspacket.h"
+#include "patch_code.h"
+#include "patch_dct.h"
+#include "sound_ids.h"
+
+// Library includes from llprimitive
+#include "imageids.h"
+#include "legacy_object_types.h"
+#include "llmaterialtable.h"
+//#include "llprimitive.h"
+#include "lltextureanim.h"
+//#include "lltextureentry.h"
+#include "lltreeparams.h"
+//#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "material_codes.h"
+
+// Library includes from llxml
+#include "llxmlnode.h"
+
+// Library includes from llvfs
+#include "llassettype.h"
+#include "lldir.h"
+//#include "lldir_linux.h"
+//#include "lldir_mac.h"
+//#include "lldir_win32.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+#endif
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
new file mode 100644
index 0000000000..bcbc81ce5c
--- /dev/null
+++ b/indra/newview/llviewerregion.cpp
@@ -0,0 +1,1301 @@
+/**
+ * @file llviewerregion.cpp
+ * @brief Implementation of the LLViewerRegion class.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerregion.h"
+
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llhttpclient.h"
+#include "llregionflags.h"
+#include "llregionhandle.h"
+#include "llsdmessagesystem.h"
+#include "llsurface.h"
+#include "message.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "lldir.h"
+#include "lleventpoll.h"
+#include "llfloatergodtools.h"
+#include "llfloaterregioninfo.h"
+#include "llhttpnode.h"
+#include "llnetmap.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llvlmanager.h"
+#include "llvlcomposition.h"
+#include "llvocache.h"
+#include "llvoclouds.h"
+#include "llworld.h"
+
+extern BOOL gNoRender;
+
+const F32 WATER_TEXTURE_SCALE = 8.f; // Number of times to repeat the water texture across a region
+const S16 MAX_MAP_DIST = 10;
+
+
+
+
+LLViewerRegion::LLViewerRegion(const U64 &handle,
+ const LLHost &host,
+ const U32 grids_per_region_edge,
+ const U32 grids_per_patch_edge,
+ const F32 region_width_meters)
+: mCenterGlobal(),
+ mHandle(handle),
+ mHost( host ),
+ mTimeDilation(1.0f),
+ mName(""),
+ mZoning(""),
+ mOwnerID(),
+ mIsEstateManager(FALSE),
+ mCompositionp(NULL),
+ mRegionFlags( REGION_FLAGS_DEFAULT ),
+ mSimAccess( SIM_ACCESS_MIN ),
+ mBillableFactor(1.0),
+ mMaxTasks(MAX_TASKS_PER_REGION),
+ mCacheLoaded(FALSE),
+ mCacheMap(),
+ mCacheEntriesCount(0),
+ mCacheID(),
+ mEventPoll(NULL)
+{
+ mWidth = region_width_meters;
+
+ mOriginGlobal = from_region_handle(handle);
+
+ mLandp = new LLSurface('l', NULL);
+ if (!gNoRender)
+ {
+ // Create the composition layer for the surface
+ mCompositionp = new LLVLComposition(mLandp, grids_per_region_edge, region_width_meters/grids_per_region_edge);
+ mCompositionp->setSurface(mLandp);
+
+ // Create the surfaces
+ mLandp->setRegion(this);
+ mLandp->create(grids_per_region_edge,
+ grids_per_patch_edge,
+ mOriginGlobal,
+ mWidth);
+ }
+
+ if (!gNoRender)
+ {
+ mParcelOverlay = new LLViewerParcelOverlay(this, region_width_meters);
+ }
+ else
+ {
+ mParcelOverlay = NULL;
+ }
+
+ setOriginGlobal(from_region_handle(handle));
+ calculateCenterGlobal();
+
+ // Create the object lists
+ initStats();
+
+ mCacheStart.append(mCacheEnd);
+
+}
+
+
+void LLViewerRegion::initStats()
+{
+ mLastNetUpdate.reset();
+ mPacketsIn = 0;
+ mBitsIn = 0;
+ mLastBitsIn = 0;
+ mLastPacketsIn = 0;
+ mPacketsOut = 0;
+ mLastPacketsOut = 0;
+ mPacketsLost = 0;
+ mLastPacketsLost = 0;
+ mPingDelay = 0;
+ mAlive = FALSE; // can become false if circuit disconnects
+}
+
+
+
+LLViewerRegion::~LLViewerRegion()
+{
+ gVLManager.cleanupData(this);
+ // Can't do this on destruction, because the neighbor pointers might be invalid.
+ // This should be reference counted...
+ disconnectAllNeighbors();
+ mCloudLayer.destroy();
+ gWorldPointer->mPartSim.cleanupRegion(this);
+
+ gObjectList.killObjects(this);
+
+ delete mCompositionp;
+ delete mParcelOverlay;
+ delete mLandp;
+ delete mEventPoll;
+
+ saveCache();
+}
+
+
+void LLViewerRegion::loadCache()
+{
+ if (mCacheLoaded)
+ {
+ return;
+ }
+
+ // Presume success. If it fails, we don't want to try again.
+ mCacheLoaded = TRUE;
+
+ LLVOCacheEntry *entry;
+
+ char filename[256];
+ sprintf(filename, "%s%sobjects_%d_%d.slc",
+ gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ U32(mHandle>>32)/REGION_WIDTH_UNITS,
+ U32(mHandle)/REGION_WIDTH_UNITS );
+
+ FILE *fp = LLFile::fopen(filename, "rb");
+ if (!fp)
+ {
+ // might not have a file, which is normal
+ return;
+ }
+
+ U32 zero;
+ fread(&zero, 1, sizeof(U32), fp);
+ if (zero)
+ {
+ // a non-zero value here means bad things!
+ // skip reading the cached values
+ llinfos << "Cache file invalid" << llendl;
+ fclose(fp);
+ return;
+ }
+
+ U32 version;
+ fread(&version, 1, sizeof(U32), fp);
+ if (version != INDRA_OBJECT_CACHE_VERSION)
+ {
+ // a version mismatch here means we've changed the binary format!
+ // skip reading the cached values
+ llinfos << "Cache version changed, discarding" << llendl;
+ fclose(fp);
+ return;
+ }
+
+ LLUUID cache_id;
+ fread(&cache_id.mData, UUID_BYTES, sizeof(U8), fp);
+ if (mCacheID != cache_id)
+ {
+ llinfos << "Cache ID doesn't match for this region, discarding"
+ << llendl;
+ fclose(fp);
+ return;
+ }
+
+ S32 num_entries;
+ fread(&num_entries, 1, sizeof(S32), fp);
+ S32 i;
+ for (i = 0; i < num_entries; i++)
+ {
+ entry = new LLVOCacheEntry(fp);
+ if (!entry->getLocalID())
+ {
+ llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
+ delete entry;
+ entry = NULL;
+ break;
+ }
+ mCacheEnd.insert(*entry);
+ mCacheMap[entry->getLocalID()] = entry;
+ mCacheEntriesCount++;
+ }
+
+ fclose(fp);
+}
+
+
+void LLViewerRegion::saveCache()
+{
+ if (!mCacheLoaded)
+ {
+ return;
+ }
+
+ S32 num_entries = mCacheEntriesCount;
+ if (0 == num_entries)
+ {
+ return;
+ }
+
+ char filename[256];
+ sprintf(filename, "%s%sobjects_%d_%d.slc",
+ gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ U32(mHandle>>32)/REGION_WIDTH_UNITS,
+ U32(mHandle)/REGION_WIDTH_UNITS );
+
+ FILE *fp = LLFile::fopen(filename, "wb");
+ if (!fp)
+ {
+ llwarns << "Unable to write cache file " << filename << llendl;
+ return;
+ }
+
+ // write out zero to indicate a version cache file
+ U32 zero = 0;
+ fwrite(&zero, 1, sizeof(U32), fp);
+
+ // write out version number
+ U32 version = INDRA_OBJECT_CACHE_VERSION;
+ fwrite(&version, 1, sizeof(U32), fp);
+
+ // write the cache id for this sim
+ fwrite(&mCacheID.mData, UUID_BYTES, sizeof(U8), fp);
+
+ fwrite(&num_entries, 1, sizeof(S32), fp);
+
+ LLVOCacheEntry *entry;
+
+ for (entry = mCacheStart.getNext(); entry && (entry != &mCacheEnd); entry = entry->getNext())
+ {
+ entry->writeToFile(fp);
+ }
+
+ mCacheMap.removeAllData();
+ mCacheEnd.unlink();
+ mCacheEnd.init();
+ mCacheStart.deleteAll();
+ mCacheStart.init();
+
+ fclose(fp);
+}
+
+void LLViewerRegion::sendMessage()
+{
+ gMessageSystem->sendMessage(mHost);
+}
+
+void LLViewerRegion::sendReliableMessage()
+{
+ gMessageSystem->sendReliable(mHost);
+}
+
+
+void LLViewerRegion::setAllowDamage(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_DAMAGE;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_DAMAGE;
+ }
+}
+
+
+void LLViewerRegion::setAllowLandmark(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_LANDMARK;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_LANDMARK;
+ }
+}
+
+void LLViewerRegion::setAllowSetHome(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_SET_HOME;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_SET_HOME;
+ }
+}
+
+void LLViewerRegion::setResetHomeOnTeleport(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+}
+
+void LLViewerRegion::setSunFixed(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_SUN_FIXED;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_SUN_FIXED;
+ }
+}
+
+void LLViewerRegion::setBlockFly(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_BLOCK_FLY;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_BLOCK_FLY;
+ }
+}
+
+void LLViewerRegion::setAllowDirectTeleport(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_DIRECT_TELEPORT;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_DIRECT_TELEPORT;
+ }
+}
+
+void LLViewerRegion::setWaterHeight(F32 water_level)
+{
+ mLandp->setWaterHeight(water_level);
+}
+
+F32 LLViewerRegion::getWaterHeight() const
+{
+ return mLandp->getWaterHeight();
+}
+
+void LLViewerRegion::setRegionFlags(U32 flags)
+{
+ mRegionFlags = flags;
+}
+
+
+void LLViewerRegion::setOriginGlobal(const LLVector3d &origin_global)
+{
+ mOriginGlobal = origin_global;
+ mLandp->setOriginGlobal(origin_global);
+ mWind.setOriginGlobal(origin_global);
+ mCloudLayer.setOriginGlobal(origin_global);
+ calculateCenterGlobal();
+}
+
+
+void LLViewerRegion::setTimeDilation(F32 time_dilation)
+{
+ mTimeDilation = time_dilation;
+}
+
+
+LLVector3 LLViewerRegion::getOriginAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mOriginGlobal);
+}
+
+
+LLVector3 LLViewerRegion::getCenterAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mCenterGlobal);
+}
+
+
+void LLViewerRegion::setRegionNameAndZone(const char* name_and_zone)
+{
+ LLString name_zone(name_and_zone);
+ std::string::size_type pipe_pos = name_zone.find('|');
+ S32 length = name_zone.size();
+ if (pipe_pos != std::string::npos)
+ {
+ mName = name_zone.substr(0, pipe_pos);
+ mZoning = name_zone.substr(pipe_pos+1, length-(pipe_pos+1));
+ }
+ else
+ {
+ mName = name_zone;
+ mZoning = "";
+ }
+
+ LLString::stripNonprintable(mName);
+ LLString::stripNonprintable(mZoning);
+}
+
+BOOL LLViewerRegion::canManageEstate() const
+{
+ return gAgent.isGodlike()
+ || isEstateManager()
+ || gAgent.getID() == getOwner();
+}
+
+const char* LLViewerRegion::getSimAccessString() const
+{
+ return accessToString(mSimAccess);
+}
+
+
+// static
+std::string LLViewerRegion::regionFlagsToString(U32 flags)
+{
+ std::string result;
+
+ if (flags & REGION_FLAGS_SANDBOX)
+ {
+ result += "Sandbox";
+ }
+
+ if (flags & REGION_FLAGS_ALLOW_DAMAGE)
+ {
+ result += " Not Safe";
+ }
+
+ return result;
+}
+
+char* SIM_ACCESS_STR[] = { "Free Trial",
+ "PG",
+ "Mature",
+ "Offline",
+ "Unknown" };
+
+// static
+const char* LLViewerRegion::accessToString(U8 access)
+{
+ switch(access)
+ {
+ case SIM_ACCESS_TRIAL:
+ return SIM_ACCESS_STR[0];
+
+ case SIM_ACCESS_PG:
+ return SIM_ACCESS_STR[1];
+
+ case SIM_ACCESS_MATURE:
+ return SIM_ACCESS_STR[2];
+
+ case SIM_ACCESS_DOWN:
+ return SIM_ACCESS_STR[3];
+
+ case SIM_ACCESS_MIN:
+ default:
+ return SIM_ACCESS_STR[4];
+ }
+}
+
+// static
+U8 LLViewerRegion::stringToAccess(const char* access_str)
+{
+ U8 access = SIM_ACCESS_MIN;
+ if (0 == strcmp(access_str, SIM_ACCESS_STR[0]))
+ {
+ access = SIM_ACCESS_TRIAL;
+ }
+ else if (0 == strcmp(access_str, SIM_ACCESS_STR[1]))
+ {
+ access = SIM_ACCESS_PG;
+ }
+ else if (0 == strcmp(access_str, SIM_ACCESS_STR[2]))
+ {
+ access = SIM_ACCESS_MATURE;
+ }
+ return access;
+}
+
+// static
+const char* LLViewerRegion::accessToShortString(U8 access)
+{
+ switch(access)
+ {
+ case SIM_ACCESS_PG:
+ return "PG";
+
+ case SIM_ACCESS_TRIAL:
+ return "TR";
+
+ case SIM_ACCESS_MATURE:
+ return "M";
+
+ case SIM_ACCESS_MIN:
+ default:
+ return "U";
+ }
+}
+
+// static
+void LLViewerRegion::processRegionInfo(LLMessageSystem* msg, void**)
+{
+ // send it to 'observers'
+ LLFloaterGodTools::processRegionInfo(msg);
+ LLFloaterRegionInfo::processRegionInfo(msg);
+}
+
+
+
+S32 LLViewerRegion::renderPropertyLines()
+{
+ if (mParcelOverlay)
+ {
+ return mParcelOverlay->renderPropertyLines();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// This gets called when the height field changes.
+void LLViewerRegion::dirtyHeights()
+{
+ // Property lines need to be reconstructed when the land changes.
+ if (mParcelOverlay)
+ {
+ mParcelOverlay->setDirty();
+ }
+}
+
+BOOL LLViewerRegion::idleUpdate(LLTimer &timer, const F32 max_time)
+{
+ BOOL done = mLandp->idleUpdate();
+
+ if (mParcelOverlay)
+ {
+ mParcelOverlay->idleUpdate();
+ }
+
+ return done;
+}
+
+
+// As above, but forcibly do the update.
+void LLViewerRegion::forceUpdate()
+{
+ mLandp->idleUpdate();
+
+ if (mParcelOverlay)
+ {
+ mParcelOverlay->idleUpdate(true);
+ }
+}
+
+void LLViewerRegion::connectNeighbor(LLViewerRegion *neighborp, U32 direction)
+{
+ mLandp->connectNeighbor(neighborp->mLandp, direction);
+ mCloudLayer.connectNeighbor(&(neighborp->mCloudLayer), direction);
+}
+
+
+void LLViewerRegion::disconnectAllNeighbors()
+{
+ mLandp->disconnectAllNeighbors();
+ mCloudLayer.disconnectAllNeighbors();
+}
+
+
+F32 LLViewerRegion::getCompositionXY(const S32 x, const S32 y) const
+{
+ if (x >= 256)
+ {
+ if (y >= 256)
+ {
+ LLVector3d center = getCenterGlobal() + LLVector3d(256.f, 256.f, 0.f);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(center);
+ if (regionp)
+ {
+ // OK, we need to do some hackery here - different simulators no longer use
+ // the same composition values, necessarily.
+ // If we're attempting to blend, then we want to make the fractional part of
+ // this region match the fractional of the adjacent. For now, just minimize
+ // the delta.
+ F32 our_comp = getComposition()->getValueScaled(255, 255);
+ F32 adj_comp = regionp->getComposition()->getValueScaled(x - 256.f, y - 256.f);
+ while (llabs(our_comp - adj_comp) >= 1.f)
+ {
+ if (our_comp > adj_comp)
+ {
+ adj_comp += 1.f;
+ }
+ else
+ {
+ adj_comp -= 1.f;
+ }
+ }
+ return adj_comp;
+ }
+ }
+ else
+ {
+ LLVector3d center = getCenterGlobal() + LLVector3d(256.f, 0, 0.f);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(center);
+ if (regionp)
+ {
+ // OK, we need to do some hackery here - different simulators no longer use
+ // the same composition values, necessarily.
+ // If we're attempting to blend, then we want to make the fractional part of
+ // this region match the fractional of the adjacent. For now, just minimize
+ // the delta.
+ F32 our_comp = getComposition()->getValueScaled(255.f, (F32)y);
+ F32 adj_comp = regionp->getComposition()->getValueScaled(x - 256.f, (F32)y);
+ while (llabs(our_comp - adj_comp) >= 1.f)
+ {
+ if (our_comp > adj_comp)
+ {
+ adj_comp += 1.f;
+ }
+ else
+ {
+ adj_comp -= 1.f;
+ }
+ }
+ return adj_comp;
+ }
+ }
+ }
+ else if (y >= 256)
+ {
+ LLVector3d center = getCenterGlobal() + LLVector3d(0.f, 256.f, 0.f);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(center);
+ if (regionp)
+ {
+ // OK, we need to do some hackery here - different simulators no longer use
+ // the same composition values, necessarily.
+ // If we're attempting to blend, then we want to make the fractional part of
+ // this region match the fractional of the adjacent. For now, just minimize
+ // the delta.
+ F32 our_comp = getComposition()->getValueScaled((F32)x, 255.f);
+ F32 adj_comp = regionp->getComposition()->getValueScaled((F32)x, y - 256.f);
+ while (llabs(our_comp - adj_comp) >= 1.f)
+ {
+ if (our_comp > adj_comp)
+ {
+ adj_comp += 1.f;
+ }
+ else
+ {
+ adj_comp -= 1.f;
+ }
+ }
+ return adj_comp;
+ }
+ }
+
+ return getComposition()->getValueScaled((F32)x, (F32)y);
+}
+
+
+// ---------------- Friends ----------------
+
+std::ostream& operator<<(std::ostream &s, const LLViewerRegion &region)
+{
+ s << "{ ";
+ s << region.mHost;
+ s << " mOriginGlobal = " << region.getOriginGlobal()<< "\n";
+ s << "}";
+ return s;
+}
+
+
+// ---------------- Protected Member Functions ----------------
+
+void LLViewerRegion::calculateCenterGlobal()
+{
+ mCenterGlobal = mOriginGlobal;
+ mCenterGlobal.mdV[VX] += 0.5 * mWidth;
+ mCenterGlobal.mdV[VY] += 0.5 * mWidth;
+ if (mLandp)
+ {
+ mCenterGlobal.mdV[VZ] = 0.5*mLandp->getMinZ() + mLandp->getMaxZ();
+ }
+ else
+ {
+ mCenterGlobal.mdV[VZ] = F64( getWaterHeight() );
+ }
+}
+
+
+
+void LLViewerRegion::updateNetStats()
+{
+ F32 dt = mLastNetUpdate.getElapsedTimeAndResetF32();
+
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mHost);
+ if (!cdp)
+ {
+ mAlive = FALSE;
+ return;
+ }
+
+ mAlive = TRUE;
+ mDeltaTime = dt;
+
+ mLastPacketsIn = mPacketsIn;
+ mLastBitsIn = mBitsIn;
+ mLastPacketsOut = mPacketsOut;
+ mLastPacketsLost = mPacketsLost;
+
+ mPacketsIn = cdp->getPacketsIn();
+ mBitsIn = 8 * cdp->getBytesIn();
+ mPacketsOut = cdp->getPacketsOut();
+ mPacketsLost = cdp->getPacketsLost();
+ mPingDelay = cdp->getPingDelay();
+
+ mBitStat.addValue(mBitsIn - mLastBitsIn);
+ mPacketsStat.addValue(mPacketsIn - mLastPacketsIn);
+ mPacketsLostStat.addValue(mPacketsLost);
+}
+
+
+U32 LLViewerRegion::getPacketsLost() const
+{
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mHost);
+ if (!cdp)
+ {
+ llinfos << "LLViewerRegion::getPacketsLost couldn't find circuit for " << mHost << llendl;
+ return 0;
+ }
+ else
+ {
+ return cdp->getPacketsLost();
+ }
+}
+
+BOOL LLViewerRegion::pointInRegionGlobal(const LLVector3d &point_global) const
+{
+ LLVector3 pos_region = getPosRegionFromGlobal(point_global);
+
+ if (pos_region.mV[VX] < 0)
+ {
+ return FALSE;
+ }
+ if (pos_region.mV[VX] >= mWidth)
+ {
+ return FALSE;
+ }
+ if (pos_region.mV[VY] < 0)
+ {
+ return FALSE;
+ }
+ if (pos_region.mV[VY] >= mWidth)
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+LLVector3 LLViewerRegion::getPosRegionFromGlobal(const LLVector3d &point_global) const
+{
+ LLVector3 pos_region;
+ pos_region.setVec(point_global - mOriginGlobal);
+ return pos_region;
+}
+
+LLVector3d LLViewerRegion::getPosGlobalFromRegion(const LLVector3 &pos_region) const
+{
+ LLVector3d pos_region_d;
+ pos_region_d.setVec(pos_region);
+ return pos_region_d + mOriginGlobal;
+}
+
+LLVector3 LLViewerRegion::getPosAgentFromRegion(const LLVector3 &pos_region) const
+{
+ LLVector3d pos_global = getPosGlobalFromRegion(pos_region);
+
+ return gAgent.getPosAgentFromGlobal(pos_global);
+}
+
+LLVector3 LLViewerRegion::getPosRegionFromAgent(const LLVector3 &pos_agent) const
+{
+ return getPosRegionFromGlobal(gAgent.getPosGlobalFromAgent(pos_agent));
+}
+
+F32 LLViewerRegion::getLandHeightRegion(const LLVector3& region_pos)
+{
+ return mLandp->resolveHeightRegion( region_pos );
+}
+
+BOOL LLViewerRegion::isOwnedSelf(const LLVector3& pos)
+{
+ return mParcelOverlay->isOwnedSelf(pos);
+}
+
+// Owned by a group you belong to? (officer or member)
+BOOL LLViewerRegion::isOwnedGroup(const LLVector3& pos)
+{
+ return mParcelOverlay->isOwnedGroup(pos);
+}
+
+void LLViewerRegion::updateCoarseLocations(LLMessageSystem* msg)
+{
+ //llinfos << "CoarseLocationUpdate" << llendl;
+ mMapAvatars.reset();
+
+ U8 x_pos = 0;
+ U8 y_pos = 0;
+ U8 z_pos = 0;
+
+ U32 pos = 0x0;
+
+ S16 agent_index;
+ S16 target_index;
+ msg->getS16Fast(_PREHASH_Index, _PREHASH_You, agent_index);
+ msg->getS16Fast(_PREHASH_Index, _PREHASH_Prey, target_index);
+
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_Location);
+ for(S32 i = 0; i < count; i++)
+ {
+ msg->getU8Fast(_PREHASH_Location, _PREHASH_X, x_pos, i);
+ msg->getU8Fast(_PREHASH_Location, _PREHASH_Y, y_pos, i);
+ msg->getU8Fast(_PREHASH_Location, _PREHASH_Z, z_pos, i);
+
+ //llinfos << " object X: " << (S32)x_pos << " Y: " << (S32)y_pos
+ // << " Z: " << (S32)(z_pos * 4)
+ // << llendl;
+
+ // treat the target specially for the map, and don't add you
+ // or the target
+ if(i == target_index)
+ {
+ LLVector3d global_pos(mOriginGlobal);
+ global_pos.mdV[VX] += (F64)(x_pos);
+ global_pos.mdV[VY] += (F64)(y_pos);
+ global_pos.mdV[VZ] += (F64)(z_pos) * 4.0;
+ LLAvatarTracker::instance().setTrackedCoarseLocation(global_pos);
+ }
+ else if( i != agent_index)
+ {
+ pos = 0x0;
+ pos |= x_pos;
+ pos <<= 8;
+ pos |= y_pos;
+ pos <<= 8;
+ pos |= z_pos;
+ mMapAvatars.put(pos);
+ }
+ }
+}
+
+LLString LLViewerRegion::getInfoString()
+{
+ char tmp_buf[256];
+ LLString info;
+
+ info = "Region: ";
+ getHost().getString(tmp_buf, 256);
+ info += tmp_buf;
+ info += ":";
+ info += getName();
+ info += "\n";
+
+ U32 x, y;
+ from_region_handle(getHandle(), &x, &y);
+ sprintf(tmp_buf, "%d:%d", x, y);
+ info += "Handle:";
+ info += tmp_buf;
+ info += "\n";
+ return info;
+}
+
+
+void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp)
+{
+ U32 local_id = objectp->getLocalID();
+ U32 crc = objectp->getCRC();
+
+ LLVOCacheEntry *entry = mCacheMap.getIfThere(local_id);
+
+ if (entry)
+ {
+ // we've seen this object before
+ if (entry->getCRC() == crc)
+ {
+ // Record a hit
+ entry->recordDupe();
+ }
+ else
+ {
+ // Update the cache entry
+ mCacheMap.removeData(local_id);
+ delete entry;
+ entry = new LLVOCacheEntry(local_id, crc, dp);
+ mCacheEnd.insert(*entry);
+ mCacheMap[local_id] = entry;
+ }
+ }
+ else
+ {
+ // we haven't seen this object before
+
+ // Create new entry and add to map
+ if (mCacheEntriesCount > MAX_OBJECT_CACHE_ENTRIES)
+ {
+ entry = mCacheStart.getNext();
+ mCacheMap.removeData(entry->getLocalID());
+ delete entry;
+ mCacheEntriesCount--;
+ }
+ entry = new LLVOCacheEntry(local_id, crc, dp);
+
+ mCacheEnd.insert(*entry);
+ mCacheMap[local_id] = entry;
+ mCacheEntriesCount++;
+ }
+ return ;
+}
+
+// Get data packer for this object, if we have cached data
+// AND the CRC matches. JC
+LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc)
+{
+ llassert(mCacheLoaded);
+
+ LLVOCacheEntry *entry = mCacheMap.getIfThere(local_id);
+
+ if (entry)
+ {
+ // we've seen this object before
+ if (entry->getCRC() == crc)
+ {
+ // Record a hit
+ entry->recordHit();
+ return entry->getDP(crc);
+ }
+ else
+ {
+ // llinfos << "CRC miss for " << local_id << llendl;
+ mCacheMissCRC.put(local_id);
+ }
+ }
+ else
+ {
+ // llinfos << "Cache miss for " << local_id << llendl;
+ mCacheMissFull.put(local_id);
+ }
+ return NULL;
+}
+
+void LLViewerRegion::addCacheMissFull(const U32 local_id)
+{
+ mCacheMissFull.put(local_id);
+}
+
+void LLViewerRegion::requestCacheMisses()
+{
+ S32 full_count = mCacheMissFull.count();
+ S32 crc_count = mCacheMissCRC.count();
+ if (full_count == 0 && crc_count == 0) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_new_message = TRUE;
+ S32 blocks = 0;
+ S32 i;
+
+ const U8 CACHE_MISS_TYPE_FULL = 0;
+ const U8 CACHE_MISS_TYPE_CRC = 1;
+
+ // Send full cache miss updates. For these, we KNOW we don't
+ // have a viewer object.
+ for (i = 0; i < full_count; i++)
+ {
+ if (start_new_message)
+ {
+ msg->newMessageFast(_PREHASH_RequestMultipleObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ start_new_message = FALSE;
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU8Fast(_PREHASH_CacheMissType, CACHE_MISS_TYPE_FULL);
+ msg->addU32Fast(_PREHASH_ID, mCacheMissFull[i]);
+ blocks++;
+
+ if (blocks >= 255)
+ {
+ sendReliableMessage();
+ start_new_message = TRUE;
+ blocks = 0;
+ }
+ }
+
+ // Send CRC miss updates. For these, we _might_ have a viewer object,
+ // but probably not.
+ for (i = 0; i < crc_count; i++)
+ {
+ if (start_new_message)
+ {
+ msg->newMessageFast(_PREHASH_RequestMultipleObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ start_new_message = FALSE;
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU8Fast(_PREHASH_CacheMissType, CACHE_MISS_TYPE_CRC);
+ msg->addU32Fast(_PREHASH_ID, mCacheMissCRC[i]);
+ blocks++;
+
+ if (blocks >= 255)
+ {
+ sendReliableMessage();
+ start_new_message = TRUE;
+ blocks = 0;
+ }
+ }
+
+ // finish any pending message
+ if (!start_new_message)
+ {
+ sendReliableMessage();
+ }
+ mCacheMissFull.reset();
+ mCacheMissCRC.reset();
+
+ // llinfos << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << llendl;
+}
+
+void LLViewerRegion::dumpCache()
+{
+ const S32 BINS = 4;
+ S32 hit_bin[BINS];
+ S32 change_bin[BINS];
+
+ S32 i;
+ for (i = 0; i < BINS; ++i)
+ {
+ hit_bin[i] = 0;
+ change_bin[i] = 0;
+ }
+
+ LLVOCacheEntry *entry;
+
+ for (entry = mCacheStart.getNext(); entry && (entry != &mCacheEnd); entry = entry->getNext())
+ {
+ S32 hits = entry->getHitCount();
+ S32 changes = entry->getCRCChangeCount();
+
+ hits = llclamp(hits, 0, BINS-1);
+ changes = llclamp(changes, 0, BINS-1);
+
+ hit_bin[hits]++;
+ change_bin[changes]++;
+ }
+
+ llinfos << "Count " << mCacheEntriesCount << llendl;
+ for (i = 0; i < BINS; i++)
+ {
+ llinfos << "Hits " << i << " " << hit_bin[i] << llendl;
+ }
+ for (i = 0; i < BINS; i++)
+ {
+ llinfos << "Changes " << i << " " << change_bin[i] << llendl;
+ }
+}
+
+void LLViewerRegion::unpackRegionHandshake()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ const S32 SIM_NAME_BUF = 256;
+ U32 region_flags;
+ U8 sim_access;
+ char sim_name[SIM_NAME_BUF];
+ LLUUID sim_owner;
+ BOOL is_estate_manager;
+ F32 water_height;
+ F32 billable_factor;
+ LLUUID cache_id;
+
+ msg->getU32 ("RegionInfo", "RegionFlags", region_flags);
+ msg->getU8 ("RegionInfo", "SimAccess", sim_access);
+ msg->getString ("RegionInfo", "SimName", SIM_NAME_BUF, sim_name);
+ msg->getUUID ("RegionInfo", "SimOwner", sim_owner);
+ msg->getBOOL ("RegionInfo", "IsEstateManager", is_estate_manager);
+ msg->getF32 ("RegionInfo", "WaterHeight", water_height);
+ msg->getF32 ("RegionInfo", "BillableFactor", billable_factor);
+ msg->getUUID ("RegionInfo", "CacheID", cache_id );
+
+ setRegionFlags(region_flags);
+ setSimAccess(sim_access);
+ setRegionNameAndZone(sim_name);
+ setOwner(sim_owner);
+ setIsEstateManager(is_estate_manager);
+ setWaterHeight(water_height);
+ setBillableFactor(billable_factor);
+ setCacheID(cache_id);
+
+ LLVLComposition *compp = getComposition();
+ if (compp)
+ {
+ LLUUID tmp_id;
+
+ msg->getUUID("RegionInfo", "TerrainDetail0", tmp_id);
+ compp->setDetailTextureID(0, tmp_id);
+ msg->getUUID("RegionInfo", "TerrainDetail1", tmp_id);
+ compp->setDetailTextureID(1, tmp_id);
+ msg->getUUID("RegionInfo", "TerrainDetail2", tmp_id);
+ compp->setDetailTextureID(2, tmp_id);
+ msg->getUUID("RegionInfo", "TerrainDetail3", tmp_id);
+ compp->setDetailTextureID(3, tmp_id);
+
+ F32 tmp_f32;
+ msg->getF32("RegionInfo", "TerrainStartHeight00", tmp_f32);
+ compp->setStartHeight(0, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainStartHeight01", tmp_f32);
+ compp->setStartHeight(1, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainStartHeight10", tmp_f32);
+ compp->setStartHeight(2, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainStartHeight11", tmp_f32);
+ compp->setStartHeight(3, tmp_f32);
+
+ msg->getF32("RegionInfo", "TerrainHeightRange00", tmp_f32);
+ compp->setHeightRange(0, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainHeightRange01", tmp_f32);
+ compp->setHeightRange(1, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainHeightRange10", tmp_f32);
+ compp->setHeightRange(2, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainHeightRange11", tmp_f32);
+ compp->setHeightRange(3, tmp_f32);
+
+ // If this is an UPDATE (params already ready, we need to regenerate
+ // all of our terrain stuff, by
+ if (compp->getParamsReady())
+ {
+ getLand().dirtyAllPatches();
+ }
+ else
+ {
+ compp->setParamsReady();
+ }
+ }
+
+
+ // Now that we have the name, we can load the cache file
+ // off disk.
+ loadCache();
+
+ // After loading cache, signal that simulator can start
+ // sending data.
+ // TODO: Send all upstream viewer->sim handshake info here.
+ LLHost host = msg->getSender();
+ msg->newMessage("RegionHandshakeReply");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("RegionInfo");
+ msg->addU32("Flags", 0x0 );
+ msg->sendReliable(host);
+}
+
+
+
+class BaseCapabilitiesComplete : public LLHTTPClient::Responder
+{
+public:
+ BaseCapabilitiesComplete(LLViewerRegion* region)
+ : mRegion(region)
+ { }
+
+ void error(U32 statusNum, const std::string& reason)
+ {
+ llinfos << "BaseCapabilitiesComplete::error "
+ << statusNum << ": " << reason << llendl;
+ }
+
+ void result(const LLSD& content)
+ {
+ LLSD::map_const_iterator iter;
+ for(iter = content.beginMap(); iter != content.endMap(); ++iter)
+ {
+ mRegion->setCapability(iter->first, iter->second);
+ llinfos << "BaseCapabilitiesComplete::result got capability for "
+ << iter->first << llendl;
+ }
+ }
+
+ static boost::intrusive_ptr<BaseCapabilitiesComplete> build(
+ LLViewerRegion* region)
+ {
+ return boost::intrusive_ptr<BaseCapabilitiesComplete>(
+ new BaseCapabilitiesComplete(region));
+ }
+
+private:
+ LLViewerRegion* mRegion;
+};
+
+
+void LLViewerRegion::setSeedCapability(const std::string& url)
+{
+ delete mEventPoll;
+ mEventPoll = NULL;
+
+ mCapabilities.clear();
+ setCapability("Seed", url);
+
+ LLSD capabilityNames = LLSD::emptyArray();
+ capabilityNames.append("MapLayer");
+ capabilityNames.append("MapLayerGod");
+ capabilityNames.append("NewAgentInventory");
+ capabilityNames.append("EventQueueGet");
+ LLHTTPClient::post(url, capabilityNames, BaseCapabilitiesComplete::build(this));
+}
+
+static
+LLEventPoll* createViewerEventPoll(const std::string& url)
+{
+ static LLHTTPNode eventRoot;
+ static bool eventRootServicesAdded = false;
+ if (!eventRootServicesAdded)
+ {
+ LLSDMessageSystem::useServices();
+ LLHTTPRegistrar::buildAllServices(eventRoot);
+ eventRootServicesAdded = true;
+ }
+
+ return new LLEventPoll(url, eventRoot);
+}
+
+
+void LLViewerRegion::setCapability(const std::string& name, const std::string& url)
+{
+ mCapabilities[name] = url;
+
+ if (name == "EventQueueGet")
+ {
+ delete mEventPoll;
+ mEventPoll = NULL;
+ mEventPoll = createViewerEventPoll(url);
+ }
+}
+
+std::string LLViewerRegion::getCapability(const std::string& name) const
+{
+ CapabilityMap::const_iterator iter = mCapabilities.find(name);
+ if(iter == mCapabilities.end())
+ {
+ return "";
+ }
+ return iter->second;
+}
+
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
new file mode 100644
index 0000000000..984655144b
--- /dev/null
+++ b/indra/newview/llviewerregion.h
@@ -0,0 +1,348 @@
+/**
+ * @file llviewerregion.h
+ * @brief Description of the LLViewerRegion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERREGION_H
+#define LL_LLVIEWERREGION_H
+
+// A ViewerRegion is a class that contains a bunch of objects and surfaces
+// that are in to a particular region.
+#include <string>
+
+#include "lldarray.h"
+#include "llwind.h"
+#include "llcloud.h"
+#include "llstat.h"
+#include "v3dmath.h"
+#include "llhost.h"
+#include "llstring.h"
+#include "llregionflags.h"
+#include "llptrskipmap.h"
+#include "lluuid.h"
+#include "lldatapacker.h"
+#include "llvocache.h"
+
+// Surface id's
+#define LAND 1
+#define WATER 2
+const U32 MAX_OBJECT_CACHE_ENTRIES = 10000;
+
+
+class LLEventPoll;
+class LLVLComposition;
+class LLViewerObject;
+class LLMessageSystem;
+class LLNetMap;
+class LLViewerParcelOverlay;
+class LLSurface;
+class LLVOCache;
+class LLVOCacheEntry;
+
+class LLViewerRegion
+{
+public:
+ LLViewerRegion(const U64 &handle,
+ const LLHost &host,
+ const U32 surface_grid_width,
+ const U32 patch_grid_width,
+ const F32 region_width_meters);
+ ~LLViewerRegion();
+
+ // Call this after you have the region name and handle.
+ void loadCache();
+
+ void saveCache();
+
+ void sendMessage(); // Send the current message to this region's simulator
+ void sendReliableMessage(); // Send the current message to this region's simulator
+
+ void setOriginGlobal(const LLVector3d &origin);
+ void setAgentOffset(const LLVector3d &offset);
+
+ void setAllowDamage(BOOL b);
+ void setAllowLandmark(BOOL b);
+ void setAllowSetHome(BOOL b);
+ void setResetHomeOnTeleport(BOOL b);
+ void setSunFixed(BOOL b);
+ void setBlockFly(BOOL b);
+ void setAllowDirectTeleport(BOOL b);
+
+ inline BOOL getAllowDamage() const;
+ inline BOOL getAllowLandmark() const;
+ inline BOOL getAllowSetHome() const;
+ inline BOOL getResetHomeOnTeleport() const;
+ inline BOOL getSunFixed() const;
+ inline BOOL getBlockFly() const;
+ inline BOOL getAllowDirectTeleport() const;
+ inline BOOL isPrelude() const;
+ inline BOOL getAllowTerraform() const;
+ inline BOOL getRestrictPushObject() const;
+
+ void setWaterHeight(F32 water_level);
+ F32 getWaterHeight() const;
+
+ void setBillableFactor(F32 billable_factor) { mBillableFactor = billable_factor; }
+ F32 getBillableFactor() const { return mBillableFactor; }
+
+ // Maximum number of primitives allowed, regardless of object
+ // bonus factor.
+ U32 getMaxTasks() const { return mMaxTasks; }
+ void setMaxTasks(U32 max_tasks) { mMaxTasks = max_tasks; }
+
+ // Draw lines in the dirt showing ownership. Return number of
+ // vertices drawn.
+ S32 renderPropertyLines();
+
+ // Call this whenever you change the height data in the region.
+ // (Automatically called by LLSurfacePatch's update routine)
+ void dirtyHeights();
+
+ LLViewerParcelOverlay *getParcelOverlay() const
+ { return mParcelOverlay; }
+
+ void setRegionFlags(U32 flags);
+ U32 getRegionFlags() const { return mRegionFlags; }
+
+ void setTimeDilation(F32 time_dilation);
+ F32 getTimeDilation() const { return mTimeDilation; }
+
+ // Origin height is at zero.
+ const LLVector3d &getOriginGlobal() const { return mOriginGlobal; }
+ LLVector3 getOriginAgent() const;
+
+ // Center is at the height of the water table.
+ const LLVector3d &getCenterGlobal() const { return mCenterGlobal; }
+ LLVector3 getCenterAgent() const;
+
+ void setRegionNameAndZone(const char* name_and_zone);
+ const LLString& getName() const { return mName; }
+ const LLString& getZoning() const { return mZoning; }
+
+ void setOwner(const LLUUID& owner_id) { mOwnerID = owner_id; }
+ const LLUUID& getOwner() const { return mOwnerID; }
+
+ // Is the current agent on the estate manager list for this region?
+ void setIsEstateManager(BOOL b) { mIsEstateManager = b; }
+ BOOL isEstateManager() const { return mIsEstateManager; }
+ BOOL canManageEstate() const;
+
+ void setSimAccess(U8 sim_access) { mSimAccess = sim_access; }
+ U8 getSimAccess() const { return mSimAccess; }
+ const char* getSimAccessString() const;
+
+ // Returns "Sandbox", "Expensive", etc.
+ static std::string regionFlagsToString(U32 flags);
+
+ // Returns "Mature", "PG", etc.
+ static const char* accessToString(U8 access);
+
+ static U8 stringToAccess(const char* access_str);
+
+ // Returns "M", "PG", etc.
+ static const char* accessToShortString(U8 access);
+
+ // helper function which just makes sure all interested parties
+ // can process the message.
+ static void processRegionInfo(LLMessageSystem* msg, void**);
+
+ void setCacheID(const LLUUID& id) { mCacheID = id; }
+
+ F32 getWidth() const { return mWidth; }
+
+ BOOL idleUpdate(LLTimer &timer, const F32 max_time);
+
+ // Like idleUpdate, but forces everything to complete regardless of
+ // how long it takes.
+ void forceUpdate();
+
+ void connectNeighbor(LLViewerRegion *neighborp, U32 direction);
+
+ void updateNetStats();
+
+ U32 getPacketsLost() const;
+
+ // Get/set named capability URLs for this region.
+ void setSeedCapability(const std::string& url);
+ void setCapability(const std::string& name, const std::string& url);
+ std::string getCapability(const std::string& name) const;
+
+ const LLHost &getHost() const { return mHost; }
+ const U64 &getHandle() const { return mHandle; }
+
+ LLSurface &getLand() const { return *mLandp; }
+
+ BOOL pointInRegionGlobal(const LLVector3d &point_global) const;
+ LLVector3 getPosRegionFromGlobal(const LLVector3d &point_global) const;
+ LLVector3 getPosRegionFromAgent(const LLVector3 &agent_pos) const;
+ LLVector3 getPosAgentFromRegion(const LLVector3 &region_pos) const;
+ LLVector3d getPosGlobalFromRegion(const LLVector3 &offset) const;
+
+ LLVLComposition *getComposition() const { return mCompositionp; }
+ F32 getCompositionXY(const S32 x, const S32 y) const;
+
+ BOOL isOwnedSelf(const LLVector3& pos);
+
+ // Owned by a group you belong to? (officer OR member)
+ BOOL isOwnedGroup(const LLVector3& pos);
+
+ // deal with map object updates in the world.
+ void updateCoarseLocations(LLMessageSystem* msg);
+
+ F32 getLandHeightRegion(const LLVector3& region_pos);
+
+ LLString getInfoString();
+
+ // handle a full update message
+ void cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp);
+ LLDataPacker *getDP(U32 local_id, U32 crc);
+ void requestCacheMisses();
+ void addCacheMissFull(const U32 local_id);
+
+ void dumpCache();
+
+ void unpackRegionHandshake();
+
+ void calculateCenterGlobal();
+
+ friend std::ostream& operator<<(std::ostream &s, const LLViewerRegion &region);
+
+protected:
+ void disconnectAllNeighbors();
+ void initStats();
+
+public:
+ LLWind mWind;
+ LLCloudLayer mCloudLayer;
+ LLViewerParcelOverlay *mParcelOverlay;
+
+ BOOL mAlive; // can become false if circuit disconnects
+
+ LLStat mBitStat;
+ LLStat mPacketsStat;
+ LLStat mPacketsLostStat;
+
+ LLDynamicArray<U32> mMapAvatars;
+
+protected:
+ // The surfaces and other layers
+ LLSurface* mLandp;
+
+ // Region geometry data
+ LLVector3d mOriginGlobal; // Location of southwest corner of region (meters)
+ LLVector3d mCenterGlobal; // Location of center in world space (meters)
+ F32 mWidth; // Width of region on a side (meters)
+
+ U64 mHandle;
+ LLHost mHost;
+
+ F32 mTimeDilation; // time dilation of physics simulation on simulator
+
+ // simulator name
+ LLString mName;
+ LLString mZoning;
+
+ // region/estate owner - usually null.
+ LLUUID mOwnerID;
+
+ // Is this agent on the estate managers list for this region?
+ BOOL mIsEstateManager;
+
+ // Network statistics for the region's circuit...
+ LLTimer mLastNetUpdate;
+ U32 mPacketsIn;
+ U32 mBitsIn;
+ U32 mLastBitsIn;
+ U32 mLastPacketsIn;
+ U32 mPacketsOut;
+ U32 mLastPacketsOut;
+ S32 mPacketsLost;
+ S32 mLastPacketsLost;
+ U32 mPingDelay;
+ F32 mDeltaTime; // Time since last measurement of lastPackets, Bits, etc
+
+ LLVLComposition *mCompositionp; // Composition layer for the surface
+
+ U32 mRegionFlags; // includes damage flags
+ U8 mSimAccess;
+ F32 mBillableFactor;
+ U32 mMaxTasks; // max prim count
+
+ // Maps local ids to cache entries.
+ // Regions can have order 10,000 objects, so assume
+ // a structure of size 2^14 = 16,000
+ BOOL mCacheLoaded;
+ LLPtrSkipMap<U32, LLVOCacheEntry *, 14> mCacheMap;
+ LLVOCacheEntry mCacheStart;
+ LLVOCacheEntry mCacheEnd;
+ U32 mCacheEntriesCount;
+ LLDynamicArray<U32> mCacheMissFull;
+ LLDynamicArray<U32> mCacheMissCRC;
+ // time?
+ // LRU info?
+
+ // Cache ID is unique per-region, across renames, moving locations,
+ // etc.
+ LLUUID mCacheID;
+
+ typedef std::map<std::string, std::string> CapabilityMap;
+ CapabilityMap mCapabilities;
+
+ LLEventPoll* mEventPoll;
+};
+
+inline BOOL LLViewerRegion::getAllowDamage() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_DAMAGE) !=0);
+}
+
+inline BOOL LLViewerRegion::getAllowLandmark() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_LANDMARK) !=0);
+}
+
+inline BOOL LLViewerRegion::getAllowSetHome() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_SET_HOME) != 0);
+}
+
+inline BOOL LLViewerRegion::getResetHomeOnTeleport() const
+{
+ return ((mRegionFlags & REGION_FLAGS_RESET_HOME_ON_TELEPORT) !=0);
+}
+
+inline BOOL LLViewerRegion::getSunFixed() const
+{
+ return ((mRegionFlags & REGION_FLAGS_SUN_FIXED) !=0);
+}
+
+inline BOOL LLViewerRegion::getBlockFly() const
+{
+ return ((mRegionFlags & REGION_FLAGS_BLOCK_FLY) !=0);
+}
+
+inline BOOL LLViewerRegion::getAllowDirectTeleport() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_DIRECT_TELEPORT) !=0);
+}
+
+inline BOOL LLViewerRegion::isPrelude() const
+{
+ return is_prelude( mRegionFlags );
+}
+
+inline BOOL LLViewerRegion::getAllowTerraform() const
+{
+ return ((mRegionFlags & REGION_FLAGS_BLOCK_TERRAFORM) == 0);
+}
+
+inline BOOL LLViewerRegion::getRestrictPushObject() const
+{
+ return ((mRegionFlags & REGION_FLAGS_RESTRICT_PUSHOBJECT) != 0);
+}
+
+#endif
+
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
new file mode 100644
index 0000000000..bb490959c7
--- /dev/null
+++ b/indra/newview/llviewerstats.cpp
@@ -0,0 +1,301 @@
+/**
+ * @file llviewerstats.cpp
+ * @brief LLViewerStats class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerstats.h"
+#include "llviewerthrottle.h"
+
+#include "message.h"
+#include "lltimer.h"
+
+LLViewerStats *gViewerStats = NULL;
+
+extern U32 gFrameCount;
+extern LLTimer gRenderStartTime;
+
+class StatAttributes
+{
+public:
+ StatAttributes(const char *name,
+ const BOOL enabled,
+ const BOOL is_timer)
+ : mName(name),
+ mEnabled(enabled),
+ mIsTimer(is_timer)
+ {
+ }
+
+ const char *mName;
+ const BOOL mEnabled;
+ const BOOL mIsTimer;
+};
+
+const StatAttributes STAT_INFO[LLViewerStats::ST_COUNT] =
+{
+ // ST_MOUSELOOK_SECONDS
+ StatAttributes("Seconds in Mouselook", FALSE, TRUE),
+ // ST_AVATAR_EDIT_SECONDS
+ StatAttributes("Seconds in Edit Appearence", FALSE, TRUE),
+ // ST_TOOLBOX_SECONDS
+ StatAttributes("Seconds using Toolbox", FALSE, TRUE),
+ // ST_CHAT_COUNT
+ StatAttributes("Chat messages sent", FALSE, FALSE),
+ // ST_IM_COUNT
+ StatAttributes("IMs sent", FALSE, FALSE),
+ // ST_FULLSCREEN_BOOL
+ StatAttributes("Fullscreen mode", TRUE, FALSE),
+ // ST_RELEASE_COUNT
+ StatAttributes("Object release count", FALSE, FALSE),
+ // ST_CREATE_COUNT
+ StatAttributes("Object create count", FALSE, FALSE),
+ // ST_REZ_COUNT
+ StatAttributes("Object rez count", FALSE, FALSE),
+ // ST_FPS_10_SECONDS
+ StatAttributes("Seconds below 10 FPS", FALSE, TRUE),
+ // ST_FPS_5_SECONDS
+ StatAttributes("Seconds below 5 FPS", FALSE, TRUE),
+ // ST_FPS_2_SECONDS
+ StatAttributes("Seconds below 2 FPS", FALSE, TRUE),
+ // ST_FLY_COUNT
+ StatAttributes("Fly count", FALSE, FALSE),
+ // ST_TELEPORT_COUNT
+ StatAttributes("Teleport count", FALSE, FALSE),
+ // ST_OBJECT_DELETE_COUNT
+ StatAttributes("Objects deleted", FALSE, FALSE),
+ // ST_SNAPSHOT_COUNT
+ StatAttributes("Snapshots taken", FALSE, FALSE),
+ // ST_UPLOAD_SOUND_COUNT
+ StatAttributes("Sounds uploaded", FALSE, FALSE),
+ // ST_UPLOAD_TEXTURE_COUNT
+ StatAttributes("Textures uploaded", FALSE, FALSE),
+ // ST_EDIT_TEXTURE_COUNT
+ StatAttributes("Changes to textures on objects", FALSE, FALSE),
+ // ST_KILLED_COUNT
+ StatAttributes("Number of times killed", FALSE, FALSE),
+ // ST_FRAMETIME_JITTER
+ StatAttributes("Average delta between sucessive frame times", FALSE, FALSE),
+ // ST_FRAMETIME_SLEW
+ StatAttributes("Average delta between frame time and mean", FALSE, FALSE),
+ // ST_INVENTORY_TOO_LONG
+ StatAttributes("Inventory took too long to load", FALSE, FALSE),
+ // ST_WEARABLES_TOO_LONG
+ StatAttributes("Wearables took too long to load", FALSE, FALSE),
+ // ST_LOGIN_SECONDS
+ StatAttributes("Time between LoginRequest and LoginReply", FALSE, FALSE),
+ // ST_LOGIN_TIMEOUT_COUNT
+ StatAttributes("Number of login attempts that timed out", FALSE, FALSE),
+ // ST_HAS_BAD_TIMER
+ StatAttributes("Known bad timer if != 0.0", FALSE, FALSE),
+ // ST_DOWNLOAD_FAILED
+ StatAttributes("Number of times LLAssetStorage::getAssetData() has failed", FALSE, FALSE),
+ // ST_LSL_SAVE_COUNT
+ StatAttributes("Number of times user has saved a script", FALSE, FALSE),
+ // ST_UPLOAD_ANIM_COUNT
+ StatAttributes("Animations uploaded", FALSE, FALSE),
+ // ST_FPS_8_SECONDS
+ StatAttributes("Seconds below 8 FPS", TRUE, TRUE),
+ // ST_SIM_FPS_20_SECONDS
+ StatAttributes("Seconds with sim FPS below 20", TRUE, TRUE),
+ // ST_PHYS_FPS_20_SECONDS
+ StatAttributes("Seconds with physics FPS below 20", TRUE, TRUE),
+ // ST_LOSS_05_SECONDS
+ StatAttributes("Seconds with packet loss > 5%", TRUE, TRUE),
+ // ST_FPS_DROP_50_RATIO
+ StatAttributes("Ratio of frames 2x longer than previous", TRUE, FALSE),
+ // ST_MEDIA_OBJECT_LIST_LENGTH
+ StatAttributes("Number of objects that want to display web pages", TRUE, FALSE),
+ // ST_DELTA_BANDWIDTH
+ StatAttributes("Increase/Decrease in bandwidth based on packet loss", TRUE, FALSE),
+ // ST_MAX_BANDWIDTH
+ StatAttributes("Max bandwidth setting", TRUE, FALSE),
+ // ST_LIGHTING_DETAIL
+ StatAttributes("Lighting Detail", TRUE, FALSE),
+ // ST_VISIBLE_AVATARS
+ StatAttributes("Visible Avatars", TRUE, FALSE),
+ // ST_SHADER_OJECTS
+ StatAttributes("Object Shaders", TRUE, FALSE),
+ // ST_SHADER_ENVIRONMENT
+ StatAttributes("Environment Shaders", TRUE, FALSE),
+ // ST_VISIBLE_DRAW_DIST
+ StatAttributes("Draw Distance", TRUE, FALSE),
+ // ST_VISIBLE_CHAT_BUBBLES
+ StatAttributes("Chat Bubbles Enabled", TRUE, FALSE),
+ // ST_SHADER_AVATAR
+ StatAttributes("Avatar Shaders", TRUE, FALSE),
+ // ST_FRAME_SECS
+ StatAttributes("FRAME_SECS", TRUE, FALSE),
+ // ST_UPDATE_SECS
+ StatAttributes("UPDATE_SECS", TRUE, FALSE),
+ // ST_NETWORK_SECS
+ StatAttributes("NETWORK_SECS", TRUE, FALSE),
+ // ST_IMAGE_SECS
+ StatAttributes("IMAGE_SECS", TRUE, FALSE),
+ // ST_REBUILD_SECS
+ StatAttributes("REBUILD_SECS", TRUE, FALSE),
+ // ST_RENDER_SECS
+ StatAttributes("RENDER_SECS", TRUE, FALSE),
+ // ST_CROSSING_AVG
+ StatAttributes("CROSSING_AVG", TRUE, FALSE),
+ // ST_CROSSING_MAX
+ StatAttributes("CROSSING_MAX", TRUE, FALSE),
+ // ST_LIBXUL_WIDGET_USED
+ StatAttributes("LibXUL Widget used", TRUE, FALSE),
+ // ST_WINDOW_WIDTH
+ StatAttributes("Window width", TRUE, FALSE),
+ // ST_WINDOW_HEIGHT
+ StatAttributes("Window height", TRUE, FALSE),
+ // ST_TEX_BAKES
+ StatAttributes("Texture Bakes", TRUE, FALSE),
+ // ST_TEX_REBAKES
+ StatAttributes("Texture Rebakes", TRUE, FALSE)
+};
+
+LLViewerStats::LLViewerStats()
+ : mPacketsLostPercentStat(64),
+ mLastTimeDiff(0.0)
+{
+ for (S32 i = 0; i < ST_COUNT; i++)
+ {
+ mStats[i] = 0.0;
+ }
+
+ if (LLTimer::knownBadTimer())
+ {
+ mStats[ST_HAS_BAD_TIMER] = 1.0;
+ }
+}
+
+LLViewerStats::~LLViewerStats()
+{
+}
+
+void LLViewerStats::resetStats()
+{
+ gViewerStats->mKBitStat.reset();
+ gViewerStats->mLayersKBitStat.reset();
+ gViewerStats->mObjectKBitStat.reset();
+ gViewerStats->mTextureKBitStat.reset();
+ gViewerStats->mVFSPendingOperations.reset();
+ gViewerStats->mAssetKBitStat.reset();
+ gViewerStats->mPacketsInStat.reset();
+ gViewerStats->mPacketsLostStat.reset();
+ gViewerStats->mPacketsOutStat.reset();
+ gViewerStats->mFPSStat.reset();
+ gViewerStats->mTexturePacketsStat.reset();
+}
+
+
+F64 LLViewerStats::getStat(EStatType type) const
+{
+ return mStats[type];
+}
+
+F64 LLViewerStats::setStat(EStatType type, F64 value)
+{
+ mStats[type] = value;
+ return mStats[type];
+}
+
+F64 LLViewerStats::incStat(EStatType type, F64 value)
+{
+ mStats[type] += value;
+ return mStats[type];
+}
+
+void LLViewerStats::updateFrameStats(const F64 time_diff)
+{
+ if (mPacketsLostPercentStat.getCurrent() > 5.0)
+ {
+ incStat(LLViewerStats::ST_LOSS_05_SECONDS, time_diff);
+ }
+
+ if (mSimFPS.getCurrent() < 20.f && mSimFPS.getCurrent() > 0.f)
+ {
+ incStat(LLViewerStats::ST_SIM_FPS_20_SECONDS, time_diff);
+ }
+
+ if (mSimPhysicsFPS.getCurrent() < 20.f && mSimPhysicsFPS.getCurrent() > 0.f)
+ {
+ incStat(LLViewerStats::ST_PHYS_FPS_20_SECONDS, time_diff);
+ }
+
+ if (time_diff >= 0.5)
+ {
+ incStat(LLViewerStats::ST_FPS_2_SECONDS, time_diff);
+ }
+ if (time_diff >= 0.2)
+ {
+ incStat(LLViewerStats::ST_FPS_5_SECONDS, time_diff);
+ }
+ if (time_diff >= 0.125)
+ {
+ incStat(LLViewerStats::ST_FPS_8_SECONDS, time_diff);
+ }
+ if (time_diff >= 0.1)
+ {
+ incStat(LLViewerStats::ST_FPS_10_SECONDS, time_diff);
+ }
+
+ if (gFrameCount && mLastTimeDiff > 0.0)
+ {
+ // new "stutter" meter
+ setStat(LLViewerStats::ST_FPS_DROP_50_RATIO,
+ (getStat(LLViewerStats::ST_FPS_DROP_50_RATIO) * (F64)(gFrameCount - 1) +
+ (time_diff >= 2.0 * mLastTimeDiff ? 1.0 : 0.0)) / gFrameCount);
+
+
+ // old stats that were never really used
+ setStat(LLViewerStats::ST_FRAMETIME_JITTER,
+ (getStat(LLViewerStats::ST_FRAMETIME_JITTER) * (gFrameCount - 1) +
+ fabs(mLastTimeDiff - time_diff) / mLastTimeDiff) / gFrameCount);
+
+ F32 average_frametime = gRenderStartTime.getElapsedTimeF32() / (F32)gFrameCount;
+ setStat(LLViewerStats::ST_FRAMETIME_SLEW,
+ (getStat(LLViewerStats::ST_FRAMETIME_SLEW) * (gFrameCount - 1) +
+ fabs(average_frametime - time_diff) / average_frametime) / gFrameCount);
+
+ F32 max_bandwidth = gViewerThrottle.getMaxBandwidth();
+ F32 delta_bandwidth = gViewerThrottle.getCurrentBandwidth() - max_bandwidth;
+ setStat(LLViewerStats::ST_DELTA_BANDWIDTH, delta_bandwidth / 1024.f);
+
+ setStat(LLViewerStats::ST_MAX_BANDWIDTH, max_bandwidth / 1024.f);
+
+ }
+
+ mLastTimeDiff = time_diff;
+
+}
+
+void LLViewerStats::addToMessage() const
+{
+ for (S32 i = 0; i < ST_COUNT; i++)
+ {
+ if (STAT_INFO[i].mEnabled)
+ {
+ // TODO: send timer value so dataserver can normalize
+ gMessageSystem->nextBlockFast(_PREHASH_MiscStats);
+ gMessageSystem->addU32Fast(_PREHASH_Type, (U32)i);
+ gMessageSystem->addF64Fast(_PREHASH_Value, mStats[i]);
+ llinfos << "STAT: " << STAT_INFO[i].mName << ": " << mStats[i] << llendl;
+ }
+ }
+}
+
+// static
+const char *LLViewerStats::statTypeToText(EStatType type)
+{
+ if (type >= 0 && type < ST_COUNT)
+ {
+ return STAT_INFO[type].mName;
+ }
+ else
+ {
+ return "Unknown statistic";
+ }
+}
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
new file mode 100644
index 0000000000..b58012d01c
--- /dev/null
+++ b/indra/newview/llviewerstats.h
@@ -0,0 +1,167 @@
+/**
+ * @file llviewerstats.h
+ * @brief LLViewerStats class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERSTATS_H
+#define LL_LLVIEWERSTATS_H
+
+#include "llstat.h"
+
+class LLViewerStats
+{
+public:
+ LLStat mKBitStat;
+ LLStat mLayersKBitStat;
+ LLStat mObjectKBitStat;
+ LLStat mAssetKBitStat;
+ LLStat mTextureKBitStat;
+ LLStat mVFSPendingOperations;
+ LLStat mObjectsDrawnStat;
+ LLStat mObjectsCulledStat;
+ LLStat mObjectsTestedStat;
+ LLStat mObjectsComparedStat;
+ LLStat mObjectsOccludedStat;
+ LLStat mFPSStat;
+ LLStat mPacketsInStat;
+ LLStat mPacketsLostStat;
+ LLStat mPacketsOutStat;
+ LLStat mPacketsLostPercentStat;
+ LLStat mTexturePacketsStat;
+ LLStat mActualInKBitStat; // From the packet ring (when faking a bad connection)
+ LLStat mActualOutKBitStat; // From the packet ring (when faking a bad connection)
+
+ // Simulator stats
+ LLStat mSimTimeDilation;
+
+ LLStat mSimFPS;
+ LLStat mSimPhysicsFPS;
+ LLStat mSimAgentUPS;
+ LLStat mSimLSLIPS;
+
+ LLStat mSimFrameMsec;
+ LLStat mSimNetMsec;
+ LLStat mSimSimOtherMsec;
+ LLStat mSimSimPhysicsMsec;
+ LLStat mSimAgentMsec;
+ LLStat mSimImagesMsec;
+ LLStat mSimScriptMsec;
+
+ LLStat mSimMainAgents;
+ LLStat mSimChildAgents;
+ LLStat mSimObjects;
+ LLStat mSimActiveObjects;
+ LLStat mSimActiveScripts;
+
+ LLStat mSimInPPS;
+ LLStat mSimOutPPS;
+ LLStat mSimPendingDownloads;
+ LLStat mSimPendingUploads;
+ LLStat mSimPendingLocalUploads;
+ LLStat mSimTotalUnackedBytes;
+
+ /*
+ LLStat mSimCPUUsageStat;
+ LLStat mSimMemTotalStat;
+ LLStat mSimMemRSSStat;
+ */
+
+
+ LLStat mSimPingStat;
+ LLStat mUserserverPingStat;
+
+ void resetStats();
+public:
+ // If you change this, please also add a corresponding text label
+ // in statTypeToText in llviewerstats.cpp
+ enum EStatType
+ {
+ ST_MOUSELOOK_SECONDS = 0,
+ ST_AVATAR_EDIT_SECONDS = 1,
+ ST_TOOLBOX_SECONDS = 2,
+ ST_CHAT_COUNT = 3,
+ ST_IM_COUNT = 4,
+ ST_FULLSCREEN_BOOL = 5,
+ ST_RELEASE_COUNT= 6,
+ ST_CREATE_COUNT = 7,
+ ST_REZ_COUNT = 8,
+ ST_FPS_10_SECONDS = 9,
+ ST_FPS_5_SECONDS = 10,
+ ST_FPS_2_SECONDS = 11,
+ ST_FLY_COUNT = 12,
+ ST_TELEPORT_COUNT = 13,
+ ST_OBJECT_DELETE_COUNT = 14,
+ ST_SNAPSHOT_COUNT = 15,
+ ST_UPLOAD_SOUND_COUNT = 16,
+ ST_UPLOAD_TEXTURE_COUNT = 17,
+ ST_EDIT_TEXTURE_COUNT = 18,
+ ST_KILLED_COUNT = 19,
+ ST_FRAMETIME_JITTER = 20,
+ ST_FRAMETIME_SLEW = 21,
+ ST_INVENTORY_TOO_LONG = 22,
+ ST_WEARABLES_TOO_LONG = 23,
+ ST_LOGIN_SECONDS = 24,
+ ST_LOGIN_TIMEOUT_COUNT = 25,
+ ST_HAS_BAD_TIMER = 26,
+ ST_DOWNLOAD_FAILED = 27,
+ ST_LSL_SAVE_COUNT = 28,
+ ST_UPLOAD_ANIM_COUNT = 29,
+ ST_FPS_8_SECONDS = 30,
+ ST_SIM_FPS_20_SECONDS = 31,
+ ST_PHYS_FPS_20_SECONDS = 32,
+ ST_LOSS_05_SECONDS = 33,
+ ST_FPS_DROP_50_RATIO = 34,
+ ST_MEDIA_OBJECT_LIST_LENGTH = 35,
+ ST_DELTA_BANDWIDTH = 36,
+ ST_MAX_BANDWIDTH = 37,
+ ST_LIGHTING_DETAIL = 38,
+ ST_VISIBLE_AVATARS = 39,
+ ST_SHADER_OBJECTS = 40,
+ ST_SHADER_ENVIRONMENT = 41,
+ ST_DRAW_DIST = 42,
+ ST_CHAT_BUBBLES = 43,
+ ST_SHADER_AVATAR = 44,
+ ST_FRAME_SECS = 45,
+ ST_UPDATE_SECS = 46,
+ ST_NETWORK_SECS = 47,
+ ST_IMAGE_SECS = 48,
+ ST_REBUILD_SECS = 49,
+ ST_RENDER_SECS = 50,
+ ST_CROSSING_AVG = 51,
+ ST_CROSSING_MAX = 52,
+ ST_LIBXUL_WIDGET_USED = 53,
+ ST_WINDOW_WIDTH = 54,
+ ST_WINDOW_HEIGHT = 55,
+ ST_TEX_BAKES = 56,
+ ST_TEX_REBAKES = 57,
+
+ ST_COUNT = 58
+ };
+
+
+ LLViewerStats();
+ ~LLViewerStats();
+
+ // all return latest value of given stat
+ F64 getStat(EStatType type) const;
+ F64 setStat(EStatType type, F64 value); // set the stat to value
+ F64 incStat(EStatType type, F64 value = 1.f); // add value to the stat
+
+ void updateFrameStats(const F64 time_diff);
+
+ void addToMessage() const;
+
+ static const char *statTypeToText(EStatType type);
+
+private:
+ F64 mStats[ST_COUNT];
+
+ F64 mLastTimeDiff; // used for time stat updates
+};
+
+extern LLViewerStats *gViewerStats;
+
+#endif // LL_LLVIEWERSTATS_H
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
new file mode 100644
index 0000000000..ddbf577f0a
--- /dev/null
+++ b/indra/newview/llviewertexteditor.cpp
@@ -0,0 +1,1475 @@
+/**
+ * @file llviewertexteditor.cpp
+ * @brief Text editor widget to let users enter a a multi-line ASCII document.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfocusmgr.h"
+#include "audioengine.h"
+#include "llagent.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+
+#include "llviewertexteditor.h"
+
+#include "llfloaterchat.h"
+#include "llfloaterworldmap.h"
+#include "llnotify.h"
+#include "llpreview.h"
+#include "llpreviewtexture.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewlandmark.h"
+#include "llscrollbar.h"
+#include "lltooldraganddrop.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llviewerinventory.h"
+#include "llnotecard.h"
+#include "llmemorystream.h"
+
+extern BOOL gPacificDaylightTime;
+
+////////////////////////////////////////////////////////////
+// LLEmbeddedItems
+//
+// Embedded items are stored as:
+// * A global map of llwchar to LLInventoryItem
+// ** This is unique for each item embedded in any notecard
+// to support copy/paste across notecards
+// * A per-notecard set of embeded llwchars for easy removal
+// from the global list
+// * A per-notecard vector of embedded lwchars for mapping from
+// old style 0x80 + item format notechards
+
+class LLEmbeddedItems
+{
+public:
+ LLEmbeddedItems(const LLViewerTextEditor* editor);
+ ~LLEmbeddedItems();
+ void clear();
+
+ void bindEmbeddedChars(const LLFontGL* font);
+ void unbindEmbeddedChars(const LLFontGL* font);
+
+ BOOL insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new);
+ BOOL removeEmbeddedItem( llwchar ext_char );
+
+ BOOL hasEmbeddedItem(llwchar ext_char); // returns TRUE if /this/ editor has an entry for this item
+
+ void getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items );
+ void addItems(const std::vector<LLPointer<LLInventoryItem> >& items);
+
+ llwchar getEmbeddedCharFromIndex(S32 index);
+
+ void removeUnusedChars();
+ void copyUsedCharsToIndexed();
+ S32 getIndexFromEmbeddedChar(llwchar wch);
+
+ void markSaved();
+
+ static LLInventoryItem* getEmbeddedItem(llwchar ext_char); // returns item from static list
+ static BOOL getEmbeddedItemSaved(llwchar ext_char); // returns whether item from static list is saved
+
+ struct embedded_info_t
+ {
+ LLPointer<LLInventoryItem> mItem;
+ BOOL mSaved;
+ };
+private:
+ typedef std::map<llwchar, embedded_info_t > item_map_t;
+ static item_map_t sEntries;
+ static std::stack<llwchar> sFreeEntries;
+
+ std::set<llwchar> mEmbeddedUsedChars; // list of used llwchars
+ std::vector<llwchar> mEmbeddedIndexedChars; // index -> wchar for 0x80 + index format
+ const LLViewerTextEditor* mEditor;
+};
+
+//statics
+LLEmbeddedItems::item_map_t LLEmbeddedItems::sEntries;
+std::stack<llwchar> LLEmbeddedItems::sFreeEntries;
+
+LLEmbeddedItems::LLEmbeddedItems(const LLViewerTextEditor* editor)
+ : mEditor(editor)
+{
+}
+
+LLEmbeddedItems::~LLEmbeddedItems()
+{
+ clear();
+}
+
+void LLEmbeddedItems::clear()
+{
+ // Remove entries for this editor from static list
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
+ iter != mEmbeddedUsedChars.end();)
+ {
+ std::set<llwchar>::iterator nextiter = iter++;
+ removeEmbeddedItem(*nextiter);
+ }
+ mEmbeddedUsedChars.clear();
+}
+
+// Inserts a new unique entry
+BOOL LLEmbeddedItems::insertEmbeddedItem( LLInventoryItem* item, llwchar* ext_char, bool is_new)
+{
+ // Now insert a new one
+ llwchar wc_emb;
+ if (!sFreeEntries.empty())
+ {
+ wc_emb = sFreeEntries.top();
+ sFreeEntries.pop();
+ }
+ else if (sEntries.empty())
+ {
+ wc_emb = FIRST_EMBEDDED_CHAR;
+ }
+ else
+ {
+ item_map_t::iterator last = sEntries.end();
+ --last;
+ wc_emb = last->first;
+ if (wc_emb >= LAST_EMBEDDED_CHAR)
+ {
+ return FALSE;
+ }
+ ++wc_emb;
+ }
+
+ sEntries[wc_emb].mItem = item;
+ sEntries[wc_emb].mSaved = is_new ? FALSE : TRUE;
+ *ext_char = wc_emb;
+ mEmbeddedUsedChars.insert(wc_emb);
+ return TRUE;
+}
+
+// Removes an entry (all entries are unique)
+BOOL LLEmbeddedItems::removeEmbeddedItem( llwchar ext_char )
+{
+ mEmbeddedUsedChars.erase(ext_char);
+ item_map_t::iterator iter = sEntries.find(ext_char);
+ if (iter != sEntries.end())
+ {
+ sEntries.erase(ext_char);
+ sFreeEntries.push(ext_char);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// static
+LLInventoryItem* LLEmbeddedItems::getEmbeddedItem(llwchar ext_char)
+{
+ if( ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR )
+ {
+ item_map_t::iterator iter = sEntries.find(ext_char);
+ if (iter != sEntries.end())
+ {
+ return iter->second.mItem;
+ }
+ }
+ return NULL;
+}
+
+// static
+BOOL LLEmbeddedItems::getEmbeddedItemSaved(llwchar ext_char)
+{
+ if( ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR )
+ {
+ item_map_t::iterator iter = sEntries.find(ext_char);
+ if (iter != sEntries.end())
+ {
+ return iter->second.mSaved;
+ }
+ }
+ return FALSE;
+}
+
+llwchar LLEmbeddedItems::getEmbeddedCharFromIndex(S32 index)
+{
+ if (index >= (S32)mEmbeddedIndexedChars.size())
+ {
+ llwarns << "No item for embedded char " << index << " using LL_UNKNOWN_CHAR" << llendl;
+ return LL_UNKNOWN_CHAR;
+ }
+ return mEmbeddedIndexedChars[index];
+}
+
+void LLEmbeddedItems::removeUnusedChars()
+{
+ std::set<llwchar> used = mEmbeddedUsedChars;
+ const LLWString& wtext = mEditor->getWText();
+ for (S32 i=0; i<(S32)wtext.size(); i++)
+ {
+ llwchar wc = wtext[i];
+ if( wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR )
+ {
+ used.erase(wc);
+ }
+ }
+ // Remove chars not actually used
+ for (std::set<llwchar>::iterator iter = used.begin();
+ iter != used.end(); ++iter)
+ {
+ removeEmbeddedItem(*iter);
+ }
+}
+
+void LLEmbeddedItems::copyUsedCharsToIndexed()
+{
+ // Prune unused items
+ removeUnusedChars();
+
+ // Copy all used llwchars to mEmbeddedIndexedChars
+ mEmbeddedIndexedChars.clear();
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
+ iter != mEmbeddedUsedChars.end(); ++iter)
+ {
+ mEmbeddedIndexedChars.push_back(*iter);
+ }
+}
+
+S32 LLEmbeddedItems::getIndexFromEmbeddedChar(llwchar wch)
+{
+ S32 idx = 0;
+ for (std::vector<llwchar>::iterator iter = mEmbeddedIndexedChars.begin();
+ iter != mEmbeddedIndexedChars.end(); ++iter)
+ {
+ if (wch == *iter)
+ break;
+ ++idx;
+ }
+ if (idx < (S32)mEmbeddedIndexedChars.size())
+ {
+ return idx;
+ }
+ else
+ {
+ llwarns << "Embedded char " << wch << " not found, using 0" << llendl;
+ return 0;
+ }
+}
+
+BOOL LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char)
+{
+ std::set<llwchar>::iterator iter = mEmbeddedUsedChars.find(ext_char);
+ if (iter != mEmbeddedUsedChars.end())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLEmbeddedItems::bindEmbeddedChars( const LLFontGL* font )
+{
+ if( sEntries.empty() )
+ {
+ return;
+ }
+
+ for (std::set<llwchar>::iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1)
+ {
+ llwchar wch = *iter1;
+ item_map_t::iterator iter2 = sEntries.find(wch);
+ if (iter2 == sEntries.end())
+ {
+ continue;
+ }
+ LLInventoryItem* item = iter2->second.mItem;
+ if (!item)
+ {
+ continue;
+ }
+ const char* img_name;
+ switch( item->getType() )
+ {
+ case LLAssetType::AT_TEXTURE:
+ if(item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)
+ {
+ img_name = "inv_item_snapshot.tga";
+ }
+ else
+ {
+ img_name = "inv_item_texture.tga";
+ }
+
+ break;
+ case LLAssetType::AT_SOUND: img_name = "inv_item_sound.tga"; break;
+ case LLAssetType::AT_LANDMARK:
+ if (item->getFlags() & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ img_name = "inv_item_landmark_visited.tga";
+ }
+ else
+ {
+ img_name = "inv_item_landmark.tga";
+ }
+ break;
+ case LLAssetType::AT_CLOTHING: img_name = "inv_item_clothing.tga"; break;
+ case LLAssetType::AT_OBJECT: img_name = "inv_item_object.tga"; break;
+ case LLAssetType::AT_NOTECARD: img_name = "inv_item_notecard.tga"; break;
+ case LLAssetType::AT_LSL_TEXT: img_name = "inv_item_script.tga"; break;
+ case LLAssetType::AT_BODYPART: img_name = "inv_item_bodypart.tga"; break;
+ case LLAssetType::AT_ANIMATION: img_name = "inv_item_animation.tga";break;
+ case LLAssetType::AT_GESTURE: img_name = "inv_item_gesture.tga"; break;
+ default: llassert(0); continue;
+ }
+
+ LLViewerImage* image = gImageList.getImage(LLUUID(gViewerArt.getString(img_name)), MIPMAP_FALSE, TRUE);
+
+ ((LLFontGL*)font)->addEmbeddedChar( wch, image, item->getName() );
+ }
+}
+
+void LLEmbeddedItems::unbindEmbeddedChars( const LLFontGL* font )
+{
+ if( sEntries.empty() )
+ {
+ return;
+ }
+
+ for (std::set<llwchar>::iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1)
+ {
+ ((LLFontGL*)font)->removeEmbeddedChar(*iter1);
+ }
+}
+
+void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items)
+{
+ for (std::vector<LLPointer<LLInventoryItem> >::const_iterator iter = items.begin();
+ iter != items.end(); ++iter)
+ {
+ LLInventoryItem* item = *iter;
+ if (item)
+ {
+ llwchar wc;
+ if (!insertEmbeddedItem( item, &wc, false ))
+ {
+ break;
+ }
+ mEmbeddedIndexedChars.push_back(wc);
+ }
+ }
+}
+
+void LLEmbeddedItems::getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items )
+{
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
+ {
+ llwchar wc = *iter;
+ LLPointer<LLInventoryItem> item = getEmbeddedItem(wc);
+ if (item)
+ {
+ items.push_back(item);
+ }
+ }
+}
+
+void LLEmbeddedItems::markSaved()
+{
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
+ {
+ llwchar wc = *iter;
+ sEntries[wc].mSaved = TRUE;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextCmdInsertEmbeddedItem : public LLTextCmd
+{
+public:
+ LLTextCmdInsertEmbeddedItem::LLTextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item )
+ : LLTextCmd(pos, FALSE),
+ mExtCharValue(0)
+ {
+ mItem = item;
+ }
+
+ virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ {
+ LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor;
+ // Take this opportunity to remove any unused embedded items from this editor
+ viewer_editor->mEmbeddedItemList->removeUnusedChars();
+ if(viewer_editor->mEmbeddedItemList->insertEmbeddedItem( mItem, &mExtCharValue, true ) )
+ {
+ LLWString ws;
+ ws.assign(1, mExtCharValue);
+ *delta = insert(editor, mPos, ws );
+ return (*delta != 0);
+ }
+ return FALSE;
+ }
+
+ virtual S32 undo( LLTextEditor* editor )
+ {
+ remove(editor, mPos, 1);
+ return mPos;
+ }
+
+ virtual S32 redo( LLTextEditor* editor )
+ {
+ LLWString ws;
+ ws += mExtCharValue;
+ insert(editor, mPos, ws );
+ return mPos + 1;
+ }
+ virtual BOOL hasExtCharValue( llwchar value )
+ {
+ return (value == mExtCharValue);
+ }
+
+private:
+ LLPointer<LLInventoryItem> mItem;
+ llwchar mExtCharValue;
+};
+
+struct LLNotecardCopyInfo
+{
+ LLNotecardCopyInfo(LLViewerTextEditor *ed, LLInventoryItem *item)
+ : mTextEd(ed)
+ {
+ mItem = item;
+ }
+
+ LLViewerTextEditor* mTextEd;
+ // need to make this be a copy (not a * here) because it isn't stable.
+ // I wish we had passed LLPointers all the way down, but we didn't
+ LLPointer<LLInventoryItem> mItem;
+};
+
+//----------------------------------------------------------------------------
+
+//
+// Member functions
+//
+
+LLViewerTextEditor::LLViewerTextEditor(const LLString& name,
+ const LLRect& rect,
+ S32 max_length,
+ const LLString& default_text,
+ const LLFontGL* font,
+ BOOL allow_embedded_items)
+ : LLTextEditor(name, rect, max_length, default_text, font, allow_embedded_items),
+ mDragItemSaved(FALSE)
+{
+ mEmbeddedItemList = new LLEmbeddedItems(this);
+}
+
+LLViewerTextEditor::~LLViewerTextEditor()
+{
+ delete mEmbeddedItemList;
+}
+
+///////////////////////////////////////////////////////////////////
+// virtual
+void LLViewerTextEditor::makePristine()
+{
+ mEmbeddedItemList->markSaved();
+ LLTextEditor::makePristine();
+}
+
+///////////////////////////////////////////////////////////////////
+
+BOOL LLViewerTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+ if (pointInView(x, y) && getVisible())
+ {
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *viewp = *child_iter;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
+ {
+ return TRUE;
+ }
+ }
+
+ if( mSegments.empty() )
+ {
+ return TRUE;
+ }
+
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment )
+ {
+ BOOL has_tool_tip = FALSE;
+ if( cur_segment->getStyle().getIsEmbeddedItem() )
+ {
+ LLWString wtip;
+ has_tool_tip = getEmbeddedItemToolTipAtPos(cur_segment->getStart(), wtip);
+ msg = wstring_to_utf8str(wtip);
+ }
+ else
+ {
+ has_tool_tip = cur_segment->getToolTip( msg );
+ }
+ if( has_tool_tip )
+ {
+ // Just use a slop area around the cursor
+ // Convert rect local to screen coordinates
+ S32 SLOP = 8;
+ localPointToScreen(
+ x - SLOP, y - SLOP,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
+ sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Let scrollbar have first dibs
+ handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+
+ // enable I Agree checkbox if the user scrolled through entire text
+ BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
+ if (mOnScrollEndCallback && was_scrolled_to_bottom)
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if (!(mask & MASK_SHIFT))
+ {
+ deselect();
+ }
+
+ BOOL start_select = TRUE;
+ if( mAllowEmbeddedItems )
+ {
+ setCursorAtLocalPos( x, y, FALSE );
+ llwchar wc = 0;
+ if (mCursorPos < getLength())
+ {
+ wc = mWText[mCursorPos];
+ }
+ LLInventoryItem* item_at_pos = LLEmbeddedItems::getEmbeddedItem(wc);
+ if (item_at_pos)
+ {
+ mDragItem = item_at_pos;
+ mDragItemSaved = LLEmbeddedItems::getEmbeddedItemSaved(wc);
+ gFocusMgr.setMouseCapture( this, NULL );
+ mMouseDownX = x;
+ mMouseDownY = y;
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ gToolDragAndDrop->setDragStart( screen_x, screen_y );
+
+ start_select = FALSE;
+ }
+ else
+ {
+ mDragItem = NULL;
+ }
+ }
+
+ if( start_select )
+ {
+ // If we're not scrolling (handled by child), then we're selecting
+ if (mask & MASK_SHIFT)
+ {
+ S32 old_cursor_pos = mCursorPos;
+ setCursorAtLocalPos( x, y, TRUE );
+
+ if (hasSelection())
+ {
+ /* Mac-like behavior - extend selection towards the cursor
+ if (mCursorPos < mSelectionStart
+ && mCursorPos < mSelectionEnd)
+ {
+ // ...left of selection
+ mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = mCursorPos;
+ }
+ else if (mCursorPos > mSelectionStart
+ && mCursorPos > mSelectionEnd)
+ {
+ // ...right of selection
+ mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionEnd = mCursorPos;
+ }
+ */
+ // Windows behavior
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionStart = old_cursor_pos;
+ mSelectionEnd = mCursorPos;
+ }
+ // assume we're starting a drag select
+ mIsSelecting = TRUE;
+ }
+ else
+ {
+ setCursorAtLocalPos( x, y, TRUE );
+ startSelection();
+ }
+ gFocusMgr.setMouseCapture( this, &LLTextEditor::onMouseCaptureLost );
+ //FIXME:
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ handled = TRUE;
+ }
+
+ if (mTakesFocus)
+ {
+ setFocus( TRUE );
+ handled = TRUE;
+ }
+
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ return handled;
+}
+
+
+BOOL LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if (!mDragItem)
+ {
+ // leave hover segment active during drag and drop
+ mHoverSegment = NULL;
+ }
+ if( getVisible() )
+ {
+ if(gFocusMgr.getMouseCapture() == this )
+ {
+ if( mIsSelecting )
+ {
+ if (x != mLastSelectionX || y != mLastSelectionY)
+ {
+ mLastSelectionX = x;
+ mLastSelectionY = y;
+ //FIXME:
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ if( y > mTextRect.mTop )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ }
+ else
+ if( y < mTextRect.mBottom )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
+ }
+
+ setCursorAtLocalPos( x, y, TRUE );
+ mSelectionEnd = mCursorPos;
+
+ updateScrollFromCursor();
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ }
+ else if( mDragItem )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) )
+ {
+ gToolDragAndDrop->beginDrag(
+ LLAssetType::lookupDragAndDropType( mDragItem->getType() ),
+ mDragItem->getUUID(),
+ LLToolDragAndDrop::SOURCE_NOTECARD,
+ mSourceID, mObjectID);
+
+ return gToolDragAndDrop->handleHover( x, y, mask );
+ }
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ handled = TRUE;
+ }
+
+ if( !handled )
+ {
+ // Pass to children
+ handled = LLView::childrenHandleHover(x, y, mask) != NULL;
+ }
+
+ if( handled )
+ {
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+ }
+
+ // Opaque
+ if( !handled && mTakesNonScrollClicks)
+ {
+ // Check to see if we're over an HTML-style link
+ if( !mSegments.empty() )
+ {
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment )
+ {
+ if(cur_segment->getStyle().isLink())
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ handled = TRUE;
+ }
+ else
+ if(cur_segment->getStyle().getIsEmbeddedItem())
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ //getWindow()->setCursor(UI_CURSOR_ARROW);
+ handled = TRUE;
+ }
+ mHoverSegment = cur_segment;
+ }
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ if (!mScrollbar->getVisible() || x < mRect.getWidth() - SCROLLBAR_SIZE)
+ {
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ handled = TRUE;
+ }
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // let scrollbar have first dibs
+ handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
+
+ // enable I Agree checkbox if the user scrolled through entire text
+ BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
+ if (mOnScrollEndCallback && was_scrolled_to_bottom)
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if( mIsSelecting )
+ {
+ // Finish selection
+ if( y > mTextRect.mTop )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ }
+ else
+ if( y < mTextRect.mBottom )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
+ }
+
+ setCursorAtLocalPos( x, y, TRUE );
+ endSelection();
+
+ updateScrollFromCursor();
+ }
+
+ if( !hasSelection() )
+ {
+ handleMouseUpOverSegment( x, y, mask );
+ }
+
+ handled = TRUE;
+ }
+
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ if (mDragItem)
+ {
+ // mouse down was on an item
+ S32 dx = x - mMouseDownX;
+ S32 dy = y - mMouseDownY;
+ if (-2 < dx && dx < 2 && -2 < dy && dy < 2)
+ {
+ openEmbeddedItem(mDragItem, mDragItemSaved);
+ }
+ }
+ mDragItem = NULL;
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+
+BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // let scrollbar have first dibs
+ handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if( mAllowEmbeddedItems )
+ {
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment && cur_segment->getStyle().getIsEmbeddedItem() )
+ {
+ if( openEmbeddedItemAtPos( cur_segment->getStart() ) )
+ {
+ deselect();
+ setFocus( FALSE );
+ return TRUE;
+ }
+ }
+ }
+
+ if (mTakesFocus)
+ {
+ setFocus( TRUE );
+ }
+
+ setCursorAtLocalPos( x, y, FALSE );
+ deselect();
+
+ const LLWString &text = getWText();
+
+ if( isPartOfWord( text[mCursorPos] ) )
+ {
+ // Select word the cursor is over
+ while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1]))
+ {
+ mCursorPos--;
+ }
+ startSelection();
+
+ while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) )
+ {
+ mCursorPos++;
+ }
+
+ mSelectionEnd = mCursorPos;
+ }
+ else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) )
+ {
+ // Select the character the cursor is over
+ startSelection();
+ mCursorPos++;
+ mSelectionEnd = mCursorPos;
+ }
+
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection here.
+ mIsSelecting = FALSE;
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ handled = TRUE;
+ }
+ return handled;
+}
+
+
+// Allow calling cards to be dropped onto text fields. Append the name and
+// a carriage return.
+// virtual
+BOOL LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ if (mTakesNonScrollClicks)
+ {
+ if (getEnabled() && !mReadOnly)
+ {
+ switch( cargo_type )
+ {
+ case DAD_CALLINGCARD:
+ if(mAcceptCallingCardNames)
+ {
+ if (drop)
+ {
+ LLInventoryItem *item = (LLInventoryItem *)cargo_data;
+ LLString name = item->getName();
+ appendText(name, true, true);
+ }
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ break;
+
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_CLOTHING:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLInventoryItem *item = (LLInventoryItem *)cargo_data;
+ if( mAllowEmbeddedItems )
+ {
+ U32 mask_next = item->getPermissions().getMaskNextOwner();
+ if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ if( drop )
+ {
+ deselect();
+ S32 old_cursor = mCursorPos;
+ setCursorAtLocalPos( x, y, TRUE );
+ S32 insert_pos = mCursorPos;
+ setCursorPos(old_cursor);
+ BOOL inserted = insertEmbeddedItem( insert_pos, item );
+ if( inserted && (old_cursor > mCursorPos) )
+ {
+ setCursorPos(mCursorPos + 1);
+ }
+
+ updateLineStartList();
+ }
+ *accept = ACCEPT_YES_COPY_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ tooltip_msg.assign("Only items with unrestricted\n"
+ "'next owner' permissions \n"
+ "can be attached to notecards.");
+ }
+ }
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+ }
+ else
+ {
+ // Not enabled
+ *accept = ACCEPT_NO;
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLViewerTextEditor " << getName() << llendl;
+ }
+
+ return handled;
+}
+
+void LLViewerTextEditor::setASCIIEmbeddedText(const LLString& instr)
+{
+ LLWString wtext;
+ const U8* buffer = (U8*)(instr.c_str());
+ while (*buffer)
+ {
+ llwchar wch;
+ U8 c = *buffer++;
+ if (c >= 0x80)
+ {
+ S32 index = (S32)(c - 0x80);
+ wch = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
+ }
+ else
+ {
+ wch = (llwchar)c;
+ }
+ wtext.push_back(wch);
+ }
+ setWText(wtext);
+}
+
+void LLViewerTextEditor::setEmbeddedText(const LLString& instr)
+{
+ LLWString wtext = utf8str_to_wstring(instr);
+ for (S32 i=0; i<(S32)wtext.size(); i++)
+ {
+ llwchar wch = wtext[i];
+ if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
+ {
+ S32 index = wch - FIRST_EMBEDDED_CHAR;
+ wtext[i] = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
+ }
+ }
+ setWText(wtext);
+}
+
+LLString LLViewerTextEditor::getEmbeddedText()
+{
+#if 1
+ // New version (Version 2)
+ mEmbeddedItemList->copyUsedCharsToIndexed();
+ LLWString outtextw;
+ for (S32 i=0; i<(S32)mWText.size(); i++)
+ {
+ llwchar wch = mWText[i];
+ if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
+ {
+ S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
+ wch = FIRST_EMBEDDED_CHAR + index;
+ }
+ outtextw.push_back(wch);
+ }
+ LLString outtext = wstring_to_utf8str(outtextw);
+ return outtext;
+#else
+ // Old version (Version 1)
+ mEmbeddedItemList->copyUsedCharsToIndexed();
+ LLString outtext;
+ for (S32 i=0; i<(S32)mWText.size(); i++)
+ {
+ llwchar wch = mWText[i];
+ if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
+ {
+ S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
+ wch = 0x80 | index % 128;
+ }
+ else if (wch >= 0x80)
+ {
+ wch = LL_UNKNOWN_CHAR;
+ }
+ outtext.push_back((U8)wch);
+ }
+ return outtext;
+#endif
+}
+
+LLString LLViewerTextEditor::appendTime(bool prepend_newline)
+{
+ U32 utc_time;
+ utc_time = time_corrected();
+
+ // There's only one internal tm buffer.
+ struct tm* timep;
+
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(utc_time, gPacificDaylightTime);
+
+ LLString text = llformat("[%d:%02d] ", timep->tm_hour, timep->tm_min);
+ appendColoredText(text, false, prepend_newline, LLColor4::grey);
+
+ return text;
+}
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char)
+{
+ if (mEmbeddedItemList->hasEmbeddedItem(ext_char))
+ {
+ return ext_char; // already exists in my list
+ }
+ LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(ext_char);
+ if (item)
+ {
+ // Add item to my list and return new llwchar associated with it
+ llwchar new_wc;
+ if (mEmbeddedItemList->insertEmbeddedItem( item, &new_wc, true ))
+ {
+ return new_wc;
+ }
+ }
+ return LL_UNKNOWN_CHAR; // item not found or list full
+}
+
+void LLViewerTextEditor::bindEmbeddedChars(const LLFontGL* font)
+{
+ mEmbeddedItemList->bindEmbeddedChars( font );
+}
+
+void LLViewerTextEditor::unbindEmbeddedChars(const LLFontGL* font)
+{
+ mEmbeddedItemList->unbindEmbeddedChars( font );
+}
+
+BOOL LLViewerTextEditor::getEmbeddedItemToolTipAtPos(S32 pos, LLWString &msg)
+{
+ if (pos < getLength())
+ {
+ LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(mWText[pos]);
+ if( item )
+ {
+ msg = utf8str_to_wstring(item->getName());
+ msg += '\n';
+ msg += utf8str_to_wstring(item->getDescription());
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos)
+{
+ if( pos < getLength())
+ {
+ LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem( mWText[pos] );
+ if( item )
+ {
+ BOOL saved = LLEmbeddedItems::getEmbeddedItemSaved( mWText[pos] );
+ return openEmbeddedItem(item, saved);
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL LLViewerTextEditor::openEmbeddedItem(LLInventoryItem* item, BOOL saved)
+{
+ switch( item->getType() )
+ {
+ case LLAssetType::AT_TEXTURE:
+ openEmbeddedTexture( item );
+ return TRUE;
+
+ case LLAssetType::AT_SOUND:
+ openEmbeddedSound( item );
+ return TRUE;
+
+ case LLAssetType::AT_NOTECARD:
+ openEmbeddedNotecard( item, saved );
+ return TRUE;
+
+ case LLAssetType::AT_LANDMARK:
+ showLandmarkDialog( item );
+ return TRUE;
+
+ case LLAssetType::AT_LSL_TEXT:
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_OBJECT:
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_ANIMATION:
+ case LLAssetType::AT_GESTURE:
+ showCopyToInvDialog( item );
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+void LLViewerTextEditor::openEmbeddedTexture( LLInventoryItem* item )
+{
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( item->getUUID() ) )
+ {
+ // There isn't one, so make a new preview
+ if(item)
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewTextureRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewTexture* preview = new LLPreviewTexture("preview texture",
+ rect,
+ item->getName(),
+ item->getAssetUUID(),
+ TRUE);
+ preview->setAuxItem( item );
+ preview->setNotecardInfo(mNotecardInventoryID, mObjectID);
+ }
+ }
+}
+
+void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item )
+{
+ // Play sound locally
+ LLVector3d lpos_global = gAgent.getPositionGlobal();
+ const F32 SOUND_GAIN = 1.0f;
+ if(gAudiop)
+ {
+ gAudiop->triggerSound(
+ item->getAssetUUID(), gAgentID, SOUND_GAIN, lpos_global);
+ }
+ showCopyToInvDialog( item );
+}
+
+/*
+void LLViewerTextEditor::openEmbeddedLandmark( LLInventoryItem* item )
+{
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( item->getUUID() ) )
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewLandmarkRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewLandmark* preview = new LLPreviewLandmark(
+ "preview landmark",
+ rect,
+ item->getName(),
+ item->getUUID());
+ preview->setAuxItem( item );
+ preview->addCopyToInvButton();
+ preview->open();
+ }
+}*/
+
+void LLViewerTextEditor::openEmbeddedNotecard( LLInventoryItem* item, BOOL saved )
+{
+ if (saved)
+ {
+ // Copy to inventory
+ copyInventory(item);
+ }
+ else
+ {
+ LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
+ gViewerWindow->alertXml("ConfirmNotecardSave",
+ LLViewerTextEditor::onNotecardDialog, (void*)info);
+ }
+}
+
+// static
+void LLViewerTextEditor::onNotecardDialog( S32 option, void* userdata )
+{
+ LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
+ if( option == 0 )
+ {
+ // itemptr is deleted by LLPreview::save
+ LLPointer<LLInventoryItem>* itemptr = new LLPointer<LLInventoryItem>(info->mItem);
+ LLPreview::save( info->mTextEd->mNotecardInventoryID, itemptr);
+ }
+}
+
+
+void LLViewerTextEditor::showLandmarkDialog( LLInventoryItem* item )
+{
+ LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
+ gViewerWindow->alertXml("ConfirmLandmarkCopy",
+ LLViewerTextEditor::onLandmarkDialog, (void*)info);
+}
+
+// static
+void LLViewerTextEditor::onLandmarkDialog( S32 option, void* userdata )
+{
+ LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
+ if( option == 0 )
+ {
+ // Copy to inventory
+ info->mTextEd->copyInventory(info->mItem);
+ /*
+ * XXXPAM
+ *
+ * Yes, this is broken. We don't show the map yet.
+ *
+ LLInventoryItem* orig_item = (LLInventoryItem*)userdata;
+
+ // Copy to inventory
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem;
+ cloneInventoryItemToViewer(orig_item, new_item);
+ U32 flags = new_item->getFlags();
+ flags &= ~LLInventoryItem::II_FLAGS_LANDMARK_VISITED;
+ new_item->setFlags(flags);
+ new_item->updateServer(TRUE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(new_item->getUUID(), TAKE_FOCUS_NO);
+ }
+
+ if( (0 == option) && gFloaterWorldMap )
+ {
+ // Note: there's a minor race condition here.
+ // If the user immediately tries to teleport to the landmark, the dataserver may
+ // not yet know that the user has the landmark in his inventory and so may
+ // disallow the teleport. However, the user will need to be pretty fast to make
+ // this happen, and, if it does, they haven't lost anything. Once the dataserver
+ // knows about the new item, the user will be able to teleport to it successfully.
+ gFloaterWorldMap->trackLandmark(new_item->getUUID());
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }*/
+ }
+ delete info;
+}
+
+
+void LLViewerTextEditor::showCopyToInvDialog( LLInventoryItem* item )
+{
+ LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
+ gViewerWindow->alertXml( "ConfirmItemCopy",
+ LLViewerTextEditor::onCopyToInvDialog, (void*)info);
+}
+
+// static
+void LLViewerTextEditor::onCopyToInvDialog( S32 option, void* userdata )
+{
+ LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
+ if( 0 == option )
+ {
+ info->mTextEd->copyInventory(info->mItem);
+ }
+ delete info;
+}
+
+
+
+// Returns change in number of characters in mWText
+S32 LLViewerTextEditor::insertEmbeddedItem( S32 pos, LLInventoryItem* item )
+{
+ return execute( new LLTextCmdInsertEmbeddedItem( pos, item ) );
+}
+
+bool LLViewerTextEditor::importStream(std::istream& str)
+{
+ LLNotecard nc(MAX_NOTECARD_SIZE);
+ bool success = nc.importStream(str);
+ if (success)
+ {
+ const std::vector<LLPointer<LLInventoryItem> >& items = nc.getItems();
+ mEmbeddedItemList->addItems(items);
+ // Actually set the text
+ if (mAllowEmbeddedItems)
+ {
+ if (nc.getVersion() == 1)
+ setASCIIEmbeddedText( nc.getText() );
+ else
+ setEmbeddedText( nc.getText() );
+ }
+ else
+ {
+ setText( nc.getText() );
+ }
+ }
+ return success;
+}
+
+void LLViewerTextEditor::copyInventory(LLInventoryItem* item)
+{
+ copy_inventory_from_notecard(mObjectID,
+ mNotecardInventoryID,
+ item);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+BOOL LLViewerTextEditor::importBuffer( const LLString& buffer )
+{
+ LLMemoryStream str((U8*)buffer.c_str(), buffer.length());
+ return importStream(str);
+}
+
+BOOL LLViewerTextEditor::exportBuffer( LLString& buffer )
+{
+ LLNotecard nc(MAX_NOTECARD_SIZE);
+
+ std::vector<LLPointer<LLInventoryItem> > embedded_items;
+ mEmbeddedItemList->getEmbeddedItemList(embedded_items);
+
+ nc.setItems(embedded_items);
+ nc.setText(getEmbeddedText());
+
+ std::stringstream out_stream;
+ nc.exportStream(out_stream);
+
+ buffer = out_stream.str();
+
+ return TRUE;
+}
+
+LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("text_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ U32 max_text_length = 255;
+ node->getAttributeU32("max_length", max_text_length);
+
+ BOOL allow_embedded_items = FALSE;
+ node->getAttributeBOOL("embedded_items", allow_embedded_items);
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ LLString text = node->getValue();
+
+ if (text.size() > max_text_length)
+ {
+ // Erase everything from max_text_length on.
+ text.erase(max_text_length);
+ }
+
+ LLViewerTextEditor* text_editor = new LLViewerTextEditor(name,
+ rect,
+ max_text_length,
+ text,
+ font,
+ allow_embedded_items);
+
+ BOOL ignore_tabs = text_editor->mTabToNextField;
+ node->getAttributeBOOL("ignore_tab", ignore_tabs);
+
+ text_editor->setTabToNextField(ignore_tabs);
+
+
+ text_editor->setTextEditorParameters(node);
+
+ BOOL hide_scrollbar = FALSE;
+ node->getAttributeBOOL("hide_scrollbar",hide_scrollbar);
+ text_editor->setHideScrollbarForShortDocs(hide_scrollbar);
+
+ text_editor->initFromXML(node, parent);
+
+ return text_editor;
+}
diff --git a/indra/newview/llviewertexteditor.h b/indra/newview/llviewertexteditor.h
new file mode 100644
index 0000000000..b537d07ffa
--- /dev/null
+++ b/indra/newview/llviewertexteditor.h
@@ -0,0 +1,102 @@
+/**
+ * @file llviewertexteditor.h
+ * @brief Text editor widget to let users enter a a multi-line document//
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VIEWERTEXTEDITOR_H
+#define LL_VIEWERTEXTEDITOR_H
+
+#include "lltexteditor.h"
+
+class LLInventoryItem;
+
+
+//
+// Classes
+//
+class LLViewerTextEditor : public LLTextEditor
+{
+ friend class LLEmbeddedItems;
+ friend class LLTextCmdInsertEmbeddedItem;
+
+public:
+ LLViewerTextEditor(const LLString& name,
+ const LLRect& rect,
+ S32 max_length,
+ const LLString& default_text = "",
+ const LLFontGL* glfont = NULL,
+ BOOL allow_embedded_items = FALSE);
+
+ virtual ~LLViewerTextEditor();
+
+ virtual void makePristine();
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ // mousehandler overrides
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask );
+
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type,
+ void *cargo_data, EAcceptance *accept, LLString& tooltip_msg);
+
+ const LLInventoryItem* getDragItem() { return mDragItem; }
+ virtual BOOL importBuffer(const LLString& buffer);
+ virtual bool importStream(std::istream& str);
+ virtual BOOL exportBuffer(LLString& buffer);
+ void setNotecardInfo(const LLUUID& notecard_item_id, const LLUUID& object_id)
+ {
+ mNotecardInventoryID = notecard_item_id;
+ mObjectID = object_id;
+ }
+
+ void setASCIIEmbeddedText(const LLString& instr);
+ void setEmbeddedText(const LLString& instr);
+ LLString getEmbeddedText();
+
+ LLString appendTime(bool prepend_newline);
+ // Appends Second Life time, small font, grey
+ // If this starts a line, you need to prepend a newline.
+
+ void copyInventory(LLInventoryItem* item);
+
+protected:
+ // Embedded object operations
+ virtual llwchar pasteEmbeddedItem(llwchar ext_char);
+ virtual void bindEmbeddedChars(const LLFontGL* font);
+ virtual void unbindEmbeddedChars(const LLFontGL* font);
+
+ BOOL getEmbeddedItemToolTipAtPos(S32 pos, LLWString &wmsg);
+ BOOL openEmbeddedItemAtPos( S32 pos );
+ BOOL openEmbeddedItem(LLInventoryItem* item, BOOL saved);
+
+ S32 insertEmbeddedItem(S32 pos, LLInventoryItem* item);
+
+ void openEmbeddedTexture( LLInventoryItem* item );
+ void openEmbeddedSound( LLInventoryItem* item );
+ //void openEmbeddedLandmark( LLInventoryItem* item );
+ void openEmbeddedNotecard( LLInventoryItem* item, BOOL saved );
+ void showCopyToInvDialog( LLInventoryItem* item );
+ void showLandmarkDialog( LLInventoryItem* item );
+
+ static void onCopyToInvDialog( S32 option, void* userdata );
+ static void onNotecardDialog( S32 option, void* userdata );
+ static void onLandmarkDialog( S32 option, void* userdata );
+
+protected:
+ LLPointer<LLInventoryItem> mDragItem;
+ BOOL mDragItemSaved;
+ LLEmbeddedItems* mEmbeddedItemList;
+
+ LLUUID mObjectID;
+ LLUUID mNotecardInventoryID;
+};
+
+#endif // LL_VIEWERTEXTEDITOR_H
diff --git a/indra/newview/llviewertextureanim.cpp b/indra/newview/llviewertextureanim.cpp
new file mode 100644
index 0000000000..b40320ad2d
--- /dev/null
+++ b/indra/newview/llviewertextureanim.cpp
@@ -0,0 +1,191 @@
+/**
+ * @file llviewertextureanim.cpp
+ * @brief LLViewerTextureAnim class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewertextureanim.h"
+
+#include "llmath.h"
+#include "llerror.h"
+
+LLViewerTextureAnim::LLViewerTextureAnim() : LLTextureAnim()
+{
+ mLastFrame = -1.f; // Force an update initially
+ mLastTime = 0.f;
+}
+
+LLViewerTextureAnim::~LLViewerTextureAnim()
+{
+}
+
+void LLViewerTextureAnim::reset()
+{
+ LLTextureAnim::reset();
+ mTimer.reset();
+}
+
+
+S32 LLViewerTextureAnim::animateTextures(F32 &off_s, F32 &off_t,
+ F32 &scale_s, F32 &scale_t,
+ F32 &rot)
+{
+ S32 result = 0;
+ if (!(mMode & ON))
+ {
+ mLastTime = 0.f;
+ mLastFrame = -1.f;
+ return result;
+ }
+
+
+ F32 num_frames = 1.0;
+ F32 full_length = 1.0;
+
+ if (mLength)
+ {
+ num_frames = mLength;
+ }
+ else
+ {
+ num_frames = llmax(1.f, (F32)(mSizeX * mSizeY));
+ }
+
+ if (mMode & PING_PONG)
+ {
+ if (mMode & SMOOTH)
+ {
+ full_length = 2.f*num_frames;
+ }
+ else if (mMode & LOOP)
+ {
+ full_length = 2.f*num_frames - 2.f;
+ full_length = llmax(1.f, full_length);
+ }
+ else
+ {
+ full_length = 2.f*num_frames - 1.f;
+ full_length = llmax(1.f, full_length);
+ }
+ }
+ else
+ {
+ full_length = num_frames;
+ }
+
+
+ F32 frame_counter;
+ if (mMode & SMOOTH)
+ {
+ frame_counter = mTimer.getElapsedTimeAndResetF32() * mRate + (F32)mLastTime;
+ }
+ else
+ {
+ frame_counter = mTimer.getElapsedTimeF32() * mRate;
+ }
+ mLastTime = frame_counter;
+
+ if (mMode & LOOP)
+ {
+ frame_counter = fmod(frame_counter, full_length);
+ }
+ else
+ {
+ frame_counter = llmin(full_length - 1.f, frame_counter);
+ }
+
+ if (!(mMode & SMOOTH))
+ {
+ frame_counter = (F32)llfloor(frame_counter + 0.01f);
+ }
+
+ if (mMode & PING_PONG)
+ {
+ if (frame_counter >= num_frames)
+ {
+ if (mMode & SMOOTH)
+ {
+ frame_counter = num_frames - (frame_counter - num_frames);
+ }
+ else
+ {
+ frame_counter = (num_frames - 1.99f) - (frame_counter - num_frames);
+ }
+ }
+ }
+
+ if (mMode & REVERSE)
+ {
+ if (mMode & SMOOTH)
+ {
+ frame_counter = num_frames - frame_counter;
+ }
+ else
+ {
+ frame_counter = (num_frames - 0.99f) - frame_counter;
+ }
+ }
+
+ frame_counter += mStart;
+
+ if (!(mMode & SMOOTH))
+ {
+ frame_counter = (F32)llround(frame_counter);
+ }
+
+ //
+ // Now that we've calculated the frame time, do an update.
+ // Will we correctly update stuff if the texture anim has
+ // changed, but not the frame counter?
+ //
+ if (mLastFrame != frame_counter)
+ {
+ mLastFrame = frame_counter;
+ if (mMode & ROTATE)
+ {
+ result |= ROTATE;
+ rot = frame_counter;
+ }
+ else if (mMode & SCALE)
+ {
+ result |= SCALE;
+ scale_s = frame_counter;
+ scale_t = frame_counter;
+ }
+ else
+ {
+ result |= TRANSLATE;
+ F32 x_frame;
+ S32 y_frame;
+ F32 x_pos;
+ F32 y_pos;
+
+ if ( (mSizeX)
+ &&(mSizeY))
+ {
+ result |= SCALE;
+ scale_s = 1.f/mSizeX;
+ scale_t = 1.f/mSizeY;
+ x_frame = fmod(frame_counter, mSizeX);
+ y_frame = (S32)(frame_counter / mSizeX);
+ x_pos = x_frame * scale_s;
+ y_pos = y_frame * scale_t;
+ off_s = (-0.5f + 0.5f*scale_s)+ x_pos;
+ off_t = (0.5f - 0.5f*scale_t) - y_pos;
+ }
+ else
+ {
+ scale_s = 1.f;
+ scale_t = 1.f;
+ x_pos = frame_counter * scale_s;
+ off_s = (-0.5f + 0.5f*scale_s)+ x_pos;
+ off_t = 0.f;
+ }
+ }
+ }
+ return result;
+}
diff --git a/indra/newview/llviewertextureanim.h b/indra/newview/llviewertextureanim.h
new file mode 100644
index 0000000000..c7e75c565c
--- /dev/null
+++ b/indra/newview/llviewertextureanim.h
@@ -0,0 +1,33 @@
+/**
+ * @file llviewertextureanim.h
+ * @brief LLViewerTextureAnim class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERTEXTUREANIM_H
+#define LL_LLVIEWERTEXTUREANIM_H
+
+#include "lltextureanim.h"
+#include "llframetimer.h"
+
+class LLViewerTextureAnim : public LLTextureAnim
+{
+public:
+ LLViewerTextureAnim();
+ virtual ~LLViewerTextureAnim();
+
+ /*virtual*/ void reset();
+
+ S32 animateTextures(F32 &off_s, F32 &off_t, F32 &scale_s, F32 &scale_t, F32 &rotate);
+ enum
+ {
+ TRANSLATE = 0x01 // Result code JUST for animateTextures
+ };
+protected:
+ LLFrameTimer mTimer;
+ F64 mLastTime;
+ F32 mLastFrame;
+};
+#endif
diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp
new file mode 100644
index 0000000000..4ffdabbbc9
--- /dev/null
+++ b/indra/newview/llviewerthrottle.cpp
@@ -0,0 +1,313 @@
+/**
+ * @file llviewerthrottle.cpp
+ * @brief LLViewerThrottle class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llviewerthrottle.h"
+
+#include "llviewercontrol.h"
+#include "message.h"
+#include "llagent.h"
+#include "llframetimer.h"
+#include "llviewerstats.h"
+#include "lldatapacker.h"
+
+// consts
+
+// The viewer is allowed to set the under-the-hood bandwidth to 50%
+// greater than the prefs UI shows, under the assumption that the
+// viewer won't receive all the different message types at once.
+// I didn't design this, don't know who did. JC
+const F32 MAX_FRACTIONAL = 1.5f;
+const F32 MIN_FRACTIONAL = 0.2f;
+
+const F32 MIN_BANDWIDTH = 50.f;
+const F32 MAX_BANDWIDTH = 1500.f;
+const F32 STEP_FRACTIONAL = 0.1f;
+const F32 TIGHTEN_THROTTLE_THRESHOLD = 3.0f; // packet loss % per s
+const F32 EASE_THROTTLE_THRESHOLD = 0.5f; // packet loss % per s
+const F32 DYNAMIC_UPDATE_DURATION = 5.0f; // seconds
+
+LLViewerThrottle gViewerThrottle;
+
+// static
+const char *LLViewerThrottle::sNames[TC_EOF] = {
+ "Resend",
+ "Land",
+ "Wind",
+ "Cloud",
+ "Task",
+ "Texture",
+ "Asset"
+ };
+
+
+// Bandwidth settings for different bit rates, they're interpolated/extrapolated.
+// Resend Land Wind Cloud Task Texture Asset
+const F32 BW_PRESET_50[TC_EOF] = { 5, 10, 3, 3, 10, 10, 9 };
+const F32 BW_PRESET_300[TC_EOF] = { 30, 40, 9, 9, 86, 86, 40 };
+const F32 BW_PRESET_500[TC_EOF] = { 50, 70, 14, 14, 136, 136, 80 };
+const F32 BW_PRESET_1000[TC_EOF] = { 100, 100, 20, 20, 310, 310, 140 };
+
+LLViewerThrottleGroup::LLViewerThrottleGroup()
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mThrottles[i] = 0.f;
+ }
+ mThrottleTotal = 0.f;
+}
+
+
+LLViewerThrottleGroup::LLViewerThrottleGroup(const F32 settings[])
+{
+ mThrottleTotal = 0.f;
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mThrottles[i] = settings[i];
+ mThrottleTotal += settings[i];
+ }
+}
+
+
+LLViewerThrottleGroup LLViewerThrottleGroup::operator*(const F32 frac) const
+{
+ LLViewerThrottleGroup res;
+ res.mThrottleTotal = 0.f;
+
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ res.mThrottles[i] = mThrottles[i] * frac;
+ res.mThrottleTotal += res.mThrottles[i];
+ }
+
+ return res;
+}
+
+
+LLViewerThrottleGroup LLViewerThrottleGroup::operator+(const LLViewerThrottleGroup &b) const
+{
+ LLViewerThrottleGroup res;
+ res.mThrottleTotal = 0.f;
+
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ res.mThrottles[i] = mThrottles[i] + b.mThrottles[i];
+ res.mThrottleTotal += res.mThrottles[i];
+ }
+
+ return res;
+}
+
+
+LLViewerThrottleGroup LLViewerThrottleGroup::operator-(const LLViewerThrottleGroup &b) const
+{
+ LLViewerThrottleGroup res;
+ res.mThrottleTotal = 0.f;
+
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ res.mThrottles[i] = mThrottles[i] - b.mThrottles[i];
+ res.mThrottleTotal += res.mThrottles[i];
+ }
+
+ return res;
+}
+
+
+void LLViewerThrottleGroup::sendToSim() const
+{
+ llinfos << "Sending throttle settings, total BW " << mThrottleTotal << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_AgentThrottle);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode);
+
+ msg->nextBlockFast(_PREHASH_Throttle);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+
+ // Pack up the throttle data
+ U8 tmp[64];
+ LLDataPackerBinaryBuffer dp(tmp, MAX_THROTTLE_SIZE);
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ //sim wants BPS, not KBPS
+ dp.packF32(mThrottles[i] * 1024.0f, "Throttle");
+ }
+ S32 len = dp.getCurrentSize();
+ msg->addBinaryDataFast(_PREHASH_Throttles, tmp, len);
+
+ gAgent.sendReliableMessage();
+}
+
+
+void LLViewerThrottleGroup::dump()
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ llinfos << LLViewerThrottle::sNames[i] << ": " << mThrottles[i] << llendl;
+ }
+ llinfos << "Total: " << mThrottleTotal << llendl;
+}
+
+class LLBPSListener : public LLSimpleListener
+{
+public:
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gViewerThrottle.setMaxBandwidth((F32) event->getValue().asReal()*1024);
+ return true;
+ }
+};
+
+LLViewerThrottle::LLViewerThrottle() :
+ mMaxBandwidth(0.f),
+ mCurrentBandwidth(0.f),
+ mThrottleFrac(1.f)
+{
+ // Need to be pushed on in bandwidth order
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_50));
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_300));
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_500));
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_1000));
+}
+
+
+void LLViewerThrottle::setMaxBandwidth(F32 kbits_per_second, BOOL from_event)
+{
+ if (!from_event)
+ {
+ gSavedSettings.setF32("ThrottleBandwidthKBPS", kbits_per_second);
+ }
+ gViewerThrottle.load();
+
+ if (gAgent.getRegion())
+ {
+ gViewerThrottle.sendToSim();
+ }
+}
+
+void LLViewerThrottle::load()
+{
+ mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS")*1024;
+ resetDynamicThrottle();
+ mCurrent.dump();
+}
+
+
+void LLViewerThrottle::save() const
+{
+ gSavedSettings.setF32("ThrottleBandwidthKBPS", mMaxBandwidth/1024);
+}
+
+
+void LLViewerThrottle::sendToSim() const
+{
+ mCurrent.sendToSim();
+}
+
+
+LLViewerThrottleGroup LLViewerThrottle::getThrottleGroup(const F32 bandwidth_kbps)
+{
+ //Clamp the bandwidth users can set.
+ F32 set_bandwidth = llclamp(bandwidth_kbps, MIN_BANDWIDTH, MAX_BANDWIDTH);
+
+ S32 count = mPresets.size();
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (mPresets[i].getTotal() > set_bandwidth)
+ {
+ break;
+ }
+ }
+
+ if (i == 0)
+ {
+ // We return the minimum if it's less than the minimum
+ return mPresets[0];
+ }
+ else if (i == count)
+ {
+ // Higher than the highest preset, we extrapolate out based on the
+ // last two presets. This allows us to keep certain throttle channels from
+ // growing in bandwidth
+ F32 delta_bw = set_bandwidth - mPresets[count-1].getTotal();
+ LLViewerThrottleGroup delta_throttle = mPresets[count - 1] - mPresets[count - 2];
+ F32 delta_total = delta_throttle.getTotal();
+ F32 delta_frac = delta_bw / delta_total;
+ delta_throttle = delta_throttle * delta_frac;
+ return mPresets[count-1] + delta_throttle;
+ }
+ else
+ {
+ // In between two presets, just interpolate
+ F32 delta_bw = set_bandwidth - mPresets[i - 1].getTotal();
+ LLViewerThrottleGroup delta_throttle = mPresets[i] - mPresets[i - 1];
+ F32 delta_total = delta_throttle.getTotal();
+ F32 delta_frac = delta_bw / delta_total;
+ delta_throttle = delta_throttle * delta_frac;
+ return mPresets[i - 1] + delta_throttle;
+ }
+}
+
+
+// static
+void LLViewerThrottle::resetDynamicThrottle()
+{
+ mThrottleFrac = MAX_FRACTIONAL;
+
+ mCurrentBandwidth = mMaxBandwidth*MAX_FRACTIONAL;
+ mCurrent = getThrottleGroup(mCurrentBandwidth / 1024.0f);
+}
+
+void LLViewerThrottle::updateDynamicThrottle()
+{
+ if (mUpdateTimer.getElapsedTimeF32() < DYNAMIC_UPDATE_DURATION)
+ {
+ return;
+ }
+ mUpdateTimer.reset();
+
+ if (gViewerStats->mPacketsLostPercentStat.getMean() > TIGHTEN_THROTTLE_THRESHOLD)
+ {
+ if (mThrottleFrac <= MIN_FRACTIONAL || mCurrentBandwidth / 1024.0f <= MIN_BANDWIDTH)
+ {
+ return;
+ }
+ mThrottleFrac -= STEP_FRACTIONAL;
+ mThrottleFrac = llmax(MIN_FRACTIONAL, mThrottleFrac);
+ mCurrentBandwidth = mMaxBandwidth * mThrottleFrac;
+ mCurrent = getThrottleGroup(mCurrentBandwidth / 1024.0f);
+ mCurrent.sendToSim();
+ llinfos << "Tightening network throttle to " << mCurrentBandwidth << llendl;
+ }
+ else if (gViewerStats->mPacketsLostPercentStat.getMean() <= EASE_THROTTLE_THRESHOLD)
+ {
+ if (mThrottleFrac >= MAX_FRACTIONAL || mCurrentBandwidth / 1024.0f >= MAX_BANDWIDTH)
+ {
+ return;
+ }
+ mThrottleFrac += STEP_FRACTIONAL;
+ mThrottleFrac = llmin(MAX_FRACTIONAL, mThrottleFrac);
+ mCurrentBandwidth = mMaxBandwidth * mThrottleFrac;
+ mCurrent = getThrottleGroup(mCurrentBandwidth/1024.0f);
+ mCurrent.sendToSim();
+ llinfos << "Easing network throttle to " << mCurrentBandwidth << llendl;
+ }
+}
diff --git a/indra/newview/llviewerthrottle.h b/indra/newview/llviewerthrottle.h
new file mode 100644
index 0000000000..ec4323ba81
--- /dev/null
+++ b/indra/newview/llviewerthrottle.h
@@ -0,0 +1,72 @@
+/**
+ * @file llviewerthrottle.h
+ * @brief LLViewerThrottle class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERTHROTTLE_H
+#define LL_LLVIEWERTHROTTLE_H
+
+#include <vector>
+
+#include "llstring.h"
+#include "llframetimer.h"
+#include "llthrottle.h"
+
+class LLViewerThrottleGroup
+{
+ LLViewerThrottleGroup();
+ LLViewerThrottleGroup(const F32 settings[TC_EOF]);
+
+ LLViewerThrottleGroup operator*(const F32 frac) const;
+ LLViewerThrottleGroup operator+(const LLViewerThrottleGroup &b) const;
+ LLViewerThrottleGroup operator-(const LLViewerThrottleGroup &b) const;
+
+ F32 getTotal() { return mThrottleTotal; }
+ void sendToSim() const;
+
+ void dump();
+
+ friend class LLViewerThrottle;
+protected:
+ F32 mThrottles[TC_EOF];
+ F32 mThrottleTotal;
+};
+
+class LLViewerThrottle
+{
+public:
+ LLViewerThrottle();
+
+ void setMaxBandwidth(F32 kbits_per_second, BOOL from_event = FALSE);
+
+ void load();
+ void save() const;
+ void sendToSim() const;
+
+ F32 getMaxBandwidth()const { return mMaxBandwidth; }
+ F32 getCurrentBandwidth() const { return mCurrentBandwidth; }
+
+ void updateDynamicThrottle();
+ void resetDynamicThrottle();
+
+ LLViewerThrottleGroup getThrottleGroup(const F32 bandwidth_kbps);
+
+ static const char *sNames[TC_EOF];
+protected:
+ F32 mMaxBandwidth;
+ F32 mCurrentBandwidth;
+
+ LLViewerThrottleGroup mCurrent;
+
+ std::vector<LLViewerThrottleGroup> mPresets;
+
+ LLFrameTimer mUpdateTimer;
+ F32 mThrottleFrac;
+};
+
+extern LLViewerThrottle gViewerThrottle;
+
+#endif // LL_LLVIEWERTHROTTLE_H
diff --git a/indra/newview/llviewervisualparam.cpp b/indra/newview/llviewervisualparam.cpp
new file mode 100644
index 0000000000..8c28d58f40
--- /dev/null
+++ b/indra/newview/llviewervisualparam.cpp
@@ -0,0 +1,132 @@
+/**
+ * @file llviewervisualparam.cpp
+ * @brief Implementation of LLViewerVisualParam class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewervisualparam.h"
+#include "llxmltree.h"
+#include "llui.h"
+#include "llwearable.h"
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParamInfo()
+//-----------------------------------------------------------------------------
+LLViewerVisualParamInfo::LLViewerVisualParamInfo()
+ :
+ mWearableType( WT_INVALID ),
+ mCamDist( 0.5f ),
+ mCamAngle( 0.f ),
+ mCamElevation( 0.f ),
+ mEditGroupDisplayOrder( 0 ),
+ mShowSimple(FALSE),
+ mSimpleMin(0.f),
+ mSimpleMax(100.f)
+{
+}
+
+LLViewerVisualParamInfo::~LLViewerVisualParamInfo()
+{
+}
+
+//-----------------------------------------------------------------------------
+// parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLViewerVisualParamInfo::parseXml(LLXmlTreeNode *node)
+{
+ llassert( node->hasName( "param" ) );
+
+ if (!LLVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ // VIEWER SPECIFIC PARAMS
+
+ LLString wearable;
+ static LLStdStringHandle wearable_string = LLXmlTree::addAttributeString("wearable");
+ if( node->getFastAttributeString( wearable_string, wearable) )
+ {
+ mWearableType = LLWearable::typeNameToType( wearable );
+ }
+
+ static LLStdStringHandle edit_group_string = LLXmlTree::addAttributeString("edit_group");
+ if (!node->getFastAttributeString( edit_group_string, mEditGroup))
+ {
+ mEditGroup = "";
+ }
+
+ // Optional camera offsets from the current joint center. Used for generating "hints" (thumbnails).
+ static LLStdStringHandle camera_distance_string = LLXmlTree::addAttributeString("camera_distance");
+ node->getFastAttributeF32( camera_distance_string, mCamDist );
+ static LLStdStringHandle camera_angle_string = LLXmlTree::addAttributeString("camera_angle");
+ node->getFastAttributeF32( camera_angle_string, mCamAngle ); // in degrees
+ static LLStdStringHandle camera_elevation_string = LLXmlTree::addAttributeString("camera_elevation");
+ node->getFastAttributeF32( camera_elevation_string, mCamElevation );
+ static LLStdStringHandle camera_target_string = LLXmlTree::addAttributeString("camera_target");
+ node->getFastAttributeString( camera_target_string, mCamTargetName );
+
+ mCamAngle += 180;
+
+ static S32 params_loaded = 0;
+
+ // By default, parameters are displayed in the order in which they appear in the xml file.
+ // "edit_group_order" overriddes.
+ static LLStdStringHandle edit_group_order_string = LLXmlTree::addAttributeString("edit_group_order");
+ if( !node->getFastAttributeF32( edit_group_order_string, mEditGroupDisplayOrder ) )
+ {
+ mEditGroupDisplayOrder = (F32)params_loaded;
+ }
+
+ params_loaded++;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParam()
+//-----------------------------------------------------------------------------
+LLViewerVisualParam::LLViewerVisualParam()
+{
+}
+
+/*
+//=============================================================================
+// These virtual functions should always be overridden,
+// but are included here for use as templates
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+
+BOOL LLViewerVisualParam::setInfo(LLViewerVisualParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLViewerVisualParam::parseData(LLXmlTreeNode *node)
+{
+ LLViewerVisualParamInfo* info = new LLViewerVisualParamInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ return FALSE;
+
+ return TRUE;
+}
+*/
diff --git a/indra/newview/llviewervisualparam.h b/indra/newview/llviewervisualparam.h
new file mode 100644
index 0000000000..053d2b6e0c
--- /dev/null
+++ b/indra/newview/llviewervisualparam.h
@@ -0,0 +1,83 @@
+/**
+ * @file llviewervisualparam.h
+ * @brief viewer side visual params (with data file parsing)
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLViewerVisualParam_H
+#define LL_LLViewerVisualParam_H
+
+#include "v3math.h"
+#include "llstring.h"
+#include "llvisualparam.h"
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParamInfo
+//-----------------------------------------------------------------------------
+class LLViewerVisualParamInfo : public LLVisualParamInfo
+{
+ friend class LLViewerVisualParam;
+public:
+ LLViewerVisualParamInfo();
+ /*virtual*/ ~LLViewerVisualParamInfo();
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ S32 mWearableType;
+ LLString mEditGroup;
+ F32 mCamDist;
+ F32 mCamAngle; // degrees
+ F32 mCamElevation;
+ LLString mCamTargetName;
+ F32 mEditGroupDisplayOrder;
+ BOOL mShowSimple; // show edit controls when in "simple ui" mode?
+ F32 mSimpleMin; // when in simple UI, apply this minimum, range 0.f to 100.f
+ F32 mSimpleMax; // when in simple UI, apply this maximum, range 0.f to 100.f
+};
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParam
+// VIRTUAL CLASS
+// a viewer side interface class for a generalized parametric modification of the avatar mesh
+//-----------------------------------------------------------------------------
+class LLViewerVisualParam : public LLVisualParam
+{
+public:
+ LLViewerVisualParam();
+ /*virtual*/ ~LLViewerVisualParam(){};
+
+ // Special: These functions are overridden by child classes
+ LLViewerVisualParamInfo *getInfo() const { return (LLViewerVisualParamInfo*)mInfo; };
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLViewerVisualParamInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+
+ // New Virtual functions
+ virtual F32 getTotalDistortion() = 0;
+ virtual const LLVector3& getAvgDistortion() = 0;
+ virtual F32 getMaxDistortion() = 0;
+ virtual LLVector3 getVertexDistortion(S32 index, LLPolyMesh *mesh) = 0;
+ virtual const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **mesh) = 0;
+ virtual const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **mesh) = 0;
+
+ // interface methods
+ F32 getDisplayOrder() { return getInfo()->mEditGroupDisplayOrder; }
+ S32 getWearableType() const { return getInfo()->mWearableType; }
+ const LLString& getEditGroup() const { return getInfo()->mEditGroup; }
+
+ F32 getCameraDistance() const { return getInfo()->mCamDist; }
+ F32 getCameraAngle() const { return getInfo()->mCamAngle; } // degrees
+ F32 getCameraElevation() const { return getInfo()->mCamElevation; }
+ const std::string& getCameraTargetName() const { return getInfo()->mCamTargetName; }
+
+ BOOL getShowSimple() const { return getInfo()->mShowSimple; }
+ F32 getSimpleMin() const { return getInfo()->mSimpleMin; }
+ F32 getSimpleMax() const { return getInfo()->mSimpleMax; }
+};
+
+#endif // LL_LLViewerVisualParam_H
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
new file mode 100644
index 0000000000..3836b2d1d7
--- /dev/null
+++ b/indra/newview/llviewerwindow.cpp
@@ -0,0 +1,4816 @@
+/**
+ * @file llviewerwindow.cpp
+ * @brief Implementation of the LLViewerWindow class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// system library includes
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+
+#include "llviewerwindow.h"
+#include "llviewquery.h"
+#include "llxmltree.h"
+#include "llviewercamera.h"
+//#include "imdebug.h"
+
+#ifdef SABINRIG
+#include "cbw.h"
+#endif //SABINRIG
+
+//
+// TODO: Many of these includes are unnecessary. Remove them.
+//
+
+// linden library includes
+#include "audioengine.h" // mute on minimize
+#include "indra_constants.h"
+#include "linked_lists.h"
+#include "llassetstorage.h"
+#include "llfontgl.h"
+#include "llmediaengine.h" // mute on minimize
+#include "llrect.h"
+#include "llsky.h"
+#include "llstring.h"
+#include "llui.h"
+#include "lluuid.h"
+#include "llview.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "object_flags.h"
+#include "text_out.h"
+#include "lltimer.h"
+#include "timing.h"
+#include "llviewermenu.h"
+
+// newview includes
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llbox.h"
+#include "llcameraview.h"
+#include "llchatbar.h"
+#include "llconsole.h"
+#include "llviewercontrol.h"
+#include "llcylinder.h"
+#include "lldebugview.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolbump.h"
+#include "lldrawpoolwater.h"
+#include "llmaniptranslate.h"
+#include "llface.h"
+#include "llfeaturemanager.h"
+#include "llfilepicker.h"
+#include "llfloater.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor
+#include "llfloaterland.h"
+#include "llfloatermap.h"
+#include "llfloatermute.h"
+#include "llfloaternamedesc.h"
+#include "llfloatersnapshot.h"
+#include "llfloatertools.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llframestatview.h"
+#include "llgesturemgr.h"
+#include "llglheaders.h"
+#include "llhippo.h"
+#include "llhoverview.h"
+#include "llhudmanager.h"
+#include "llhudview.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llmodaldialog.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llnotify.h"
+#include "lloverlaybar.h"
+#include "llpreviewtexture.h"
+#include "llprogressview.h"
+#include "llresmgr.h"
+#include "llrootview.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "llstartup.h"
+#include "llstatusbar.h"
+#include "llstatview.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llimview.h"
+#include "lltexlayer.h"
+#include "lltextbox.h"
+#include "lltextureview.h"
+#include "lltool.h"
+#include "lltoolbar.h"
+#include "lltoolcomp.h"
+#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolmorph.h"
+#include "lltoolpie.h"
+#include "lltoolplacer.h"
+#include "lltoolselect.h"
+#include "lltoolselectland.h"
+#include "lltoolview.h"
+#include "llvieweruictrlfactory.h"
+#include "lluploaddialog.h"
+#include "llviewercamera.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewerkeyboard.h"
+#include "llviewermenu.h"
+#include "llviewermessage.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvoavatar.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "llworldmapview.h"
+#include "moviemaker.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+#if LL_WINDOWS
+#include "llwindebug.h"
+#include <tchar.h> // For Unicode conversion methods
+#endif
+
+//
+// Globals
+//
+
+LLBottomPanel* gBottomPanel = NULL;
+
+extern BOOL gDebugClicks;
+extern BOOL gDisplaySwapBuffers;
+extern S32 gJamesInt;
+
+LLViewerWindow *gViewerWindow = NULL;
+LLVelocityBar *gVelocityBar = NULL;
+
+LLVector3d gLastHitPosGlobal;
+LLVector3d gLastHitObjectOffset;
+LLUUID gLastHitObjectID;
+S32 gLastHitObjectFace = -1;
+BOOL gLastHitLand = FALSE;
+F32 gLastHitUCoord;
+F32 gLastHitVCoord;
+
+
+LLVector3d gLastHitNonFloraPosGlobal;
+LLVector3d gLastHitNonFloraObjectOffset;
+LLUUID gLastHitNonFloraObjectID;
+S32 gLastHitNonFloraObjectFace = -1;
+BOOL gLastHitParcelWall = FALSE;
+
+S32 gLastHitUIElement = 0;
+LLHUDIcon* gLastHitHUDIcon = NULL;
+
+BOOL gDebugSelect = FALSE;
+U8 gLastPickAlpha = 255;
+BOOL gUseGLPick = FALSE;
+
+// On the next pick pass (whenever that happens)
+// should we try to pick individual faces?
+// Cleared to FALSE every time a pick happens.
+BOOL gPickFaces = FALSE;
+
+LLFrameTimer gMouseIdleTimer;
+LLFrameTimer gAwayTimer;
+LLFrameTimer gAwayTriggerTimer;
+LLFrameTimer gAlphaFadeTimer;
+
+BOOL gShowOverlayTitle = FALSE;
+BOOL gPickTransparent = TRUE;
+
+BOOL gDebugFastUIRender = FALSE;
+
+BOOL gbCapturing = FALSE;
+MovieMaker gMovieMaker;
+
+S32 CHAT_BAR_HEIGHT = 28;
+S32 OVERLAY_BAR_HEIGHT = 20;
+
+const U8 NO_FACE = 255;
+BOOL gQuietSnapshot = FALSE;
+
+const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before coming back
+const F32 MAX_FAST_FRAME_TIME = 0.5f;
+const F32 FAST_FRAME_INCREMENT = 0.1f;
+
+const S32 PICK_HALF_WIDTH = 5;
+const S32 PICK_DIAMETER = 2 * PICK_HALF_WIDTH+1;
+
+const F32 MIN_DISPLAY_SCALE = 0.85f;
+
+#ifdef SABINRIG
+/// ALL RIG STUFF
+bool rigControl = false;
+bool voltDisplay = true;
+bool nominalX = false;
+bool nominalY = false;
+static F32 nomerX = 0.0f;
+static F32 nomerY = 0.0f;
+const BOARD_NUM = 0; // rig stuff!
+const ADRANGE = BIP10VOLTS; // rig stuff!
+static unsigned short DataVal; // rig stuff!
+static F32 oldValueX = 0;
+static F32 newValueX = 50;
+static F32 oldValueY = 0;
+static F32 newValueY = 50;
+static S32 mouseX = 50;
+static S32 mouseY = 50;
+static float VoltageX = 50; // rig stuff!
+static float VoltageY = 50; // rig stuff!
+static float nVoltX = 0;
+static float nVoltY = 0;
+static F32 temp1 = 50.f;
+static F32 temp2 = 20.f;
+LLCoordGL new_gl;
+#endif //SABINRIG
+
+char LLViewerWindow::sSnapshotBaseName[LL_MAX_PATH];
+char LLViewerWindow::sSnapshotDir[LL_MAX_PATH];
+
+char LLViewerWindow::sMovieBaseName[LL_MAX_PATH];
+
+extern void toggle_debug_menus(void*);
+
+#ifdef SABINRIG
+// static
+void LLViewerWindow::printFeedback()
+{
+ if(rigControl == true)
+ {
+ cbAIn (BOARD_NUM, 0, ADRANGE, &DataVal);
+ cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageX); //Convert raw to voltage for X-axis
+ cbAIn (BOARD_NUM, 1, ADRANGE, &DataVal);
+ cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageY); //Convert raw to voltage for Y-axis
+ if(voltDisplay == true)
+ {
+ llinfos << "Current Voltages - X:" << VoltageX << " Y:" << VoltageY << llendl; //Display voltage
+ }
+
+ if(nVoltX == 0)
+ {
+ nVoltX = VoltageX;
+ nVoltY = VoltageY; //First time setup of nominal values.
+ }
+
+ newValueX = VoltageX;
+ newValueY = VoltageY; //Take in current voltage and set to a separate value for adjustment.
+
+ mouseX = mCurrentMousePoint.mX;
+ mouseY = mCurrentMousePoint.mY; //Take in current cursor position and set to separate value for adjustment.
+
+ if( abs(newValueX - nVoltX) > nomerX )
+ {
+ if( (newValueX - oldValueX) < 0)
+ {
+ mouseX += (S32)( ((newValueX - oldValueX)*.5)) * -temp;
+ }
+ else
+ {
+ mouseX += (S32)( ((newValueX - oldValueX)*.5) * temp1);
+ }
+ }
+ else
+ {
+ mouseX = getWindowWidth() / 2;
+ }
+ if( abs(newValueY - nVoltY) > nomerY )
+ {
+ if( (newValueY - oldValueY) < 0)
+ {
+ mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * -temp2);
+ }
+ else
+ {
+ mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * temp2);
+ }
+ }
+ else
+ {
+ mouseY = getWindowHeight() / 2;
+ }
+ //mouseX += (S32)( (newValueX - nVoltX) * temp1 + 0.5 );
+ // (newValueX - oldValueX) = difference between current position and nominal position
+ // * temp1 = the amplification of the number that sets sensitivity
+ // + 0.5 = fixes rounding errors
+
+
+ //mouseY += (S32)( (newValueY - nVoltY) * temp2 + 0.5 ); //Algorithm to adjust voltage for mouse adjustment.
+
+ oldValueX = newValueX;
+ oldValueY = newValueY;
+
+ new_gl.mX = mouseX;
+ new_gl.mY = mouseY; //Setup final coordinate to move mouse to.
+
+ setCursorPosition(new_gl); //Set final cursor position
+ }
+}
+#endif //SABINRIG
+
+
+//
+// LLViewerWindow
+//
+
+BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse down at " << x << "," << y << llendl;
+ }
+
+ mLeftMouseDown = TRUE;
+
+ // Make sure we get a coresponding mouseup event, even if the mouse leaves the window
+ mWindow->captureMouse();
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mousedown
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mousedown
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+ }
+
+ if (gToolMgr)
+ {
+ // Don't let the user move the mouse out of the window until mouse up.
+ if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(TRUE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleMouseDown(local_x, local_y, mask);
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleMouseDown(local_x, local_y, mask)) return TRUE;
+ }
+
+ // Give the UI views a chance to process the click
+ if( mRootView->handleMouseDown(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down not handled by view" << llendl;
+ }
+
+ if (gDisconnected)
+ {
+ return FALSE;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleMouseDown( x, y, mask ) )
+ {
+ // This is necessary to force clicks in the world to cause edit
+ // boxes that might have keyboard focus to relinquish it, and hence
+ // cause a commit to update their value. JC
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse double-click at " << x << "," << y << llendl;
+ }
+
+ mLeftMouseDown = TRUE;
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleDoubleClick(local_x, local_y, mask);
+ }
+
+ // Check for hit on UI.
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleDoubleClick(local_x, local_y, mask)) return TRUE;
+ }
+
+ if (mRootView->handleDoubleClick(x, y, mask))
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down not handled by view" << llendl;
+ }
+
+ // Why is this here? JC 9/3/2002
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleDoubleClick( x, y, mask ) )
+ {
+ return TRUE;
+ }
+ }
+
+ // if we got this far and nothing handled a double click, pass a normal mouse down
+ return handleMouseDown(window, pos, mask);
+}
+
+BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse up" << llendl;
+ }
+
+ mLeftMouseDown = FALSE;
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mouseup
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mouseup
+ if (gHoverView) gHoverView->cancelHover();
+
+ BOOL handled = FALSE;
+
+ mWindow->releaseMouse();
+
+ LLTool *tool = NULL;
+ if (gToolMgr)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if( tool->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(FALSE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Up handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleMouseUp(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleMouseUp(local_x, local_y, mask);
+ }
+
+ if( !handled )
+ {
+ handled = mRootView->handleMouseUp(x, y, mask);
+ }
+
+ if (LLView::sDebugMouseHandling)
+ {
+ if (handled)
+ {
+ llinfos << "Left Mouse Up" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ else
+ {
+ llinfos << "Left Mouse Up not handled by view" << llendl;
+ }
+ }
+
+ if( !handled )
+ {
+ if (tool)
+ {
+ handled = tool->handleMouseUp(x, y, mask);
+ }
+ }
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+
+BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow right mouse down at " << x << "," << y << llendl;
+ }
+
+ mRightMouseDown = TRUE;
+
+ // Make sure we get a coresponding mouseup event, even if the mouse leaves the window
+ mWindow->captureMouse();
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mousedown
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+ }
+
+ if (gToolMgr)
+ {
+ // Don't let the user move the mouse out of the window until mouse up.
+ if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(TRUE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return mouse_captor->handleRightMouseDown(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleRightMouseDown(local_x, local_y, mask)) return TRUE;
+ }
+
+ if( mRootView->handleRightMouseDown(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down not handled by view" << llendl;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleRightMouseDown( x, y, mask ) )
+ {
+ // This is necessary to force clicks in the world to cause edit
+ // boxes that might have keyboard focus to relinquish it, and hence
+ // cause a commit to update their value. JC
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ return TRUE;
+ }
+ }
+
+ //FIXME: this should be rolled into the composite tool logic, not hardcoded at the top level
+ if (gToolPie && (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode()) )
+ {
+ // If the current tool didn't process the click, we should show
+ // the pie menu. This can be done by passing the event to the pie
+ // menu tool.
+ gToolPie->handleRightMouseDown(x, y, mask);
+ // show_context_menu( x, y, mask );
+ }
+
+ return TRUE;
+}
+
+BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ // Don't care about caps lock for mouse events.
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow right mouse up" << llendl;
+ }
+
+ mRightMouseDown = FALSE;
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mouseup
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mouseup
+ if (gHoverView) gHoverView->cancelHover();
+
+ BOOL handled = FALSE;
+
+ mWindow->releaseMouse();
+
+ LLTool *tool = NULL;
+ if (gToolMgr)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if( tool->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(FALSE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Up handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return mouse_captor->handleRightMouseUp(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleRightMouseUp(local_x, local_y, mask);
+ }
+
+ if( !handled )
+ {
+ handled = mRootView->handleRightMouseUp(x, y, mask);
+ }
+
+ if (LLView::sDebugMouseHandling)
+ {
+ if (handled)
+ {
+ llinfos << "Right Mouse Up" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ else
+ {
+ llinfos << "Right Mouse Up not handled by view" << llendl;
+ }
+ }
+
+ if( !handled )
+ {
+ if (tool)
+ {
+ handled = tool->handleRightMouseUp(x, y, mask);
+ }
+ }
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+
+void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ mMouseInWindow = TRUE;
+
+ // Save mouse point for access during idle() and display()
+
+ LLCoordGL prev_saved_mouse_point = mCurrentMousePoint;
+ LLCoordGL mouse_point(x, y);
+ saveLastMouse(mouse_point);
+ BOOL mouse_actually_moved = (prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY);
+
+ gMouseIdleTimer.reset();
+
+ mWindow->showCursorFromMouseMove();
+
+ if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
+ {
+ gAgent.clearAFK();
+ }
+
+ if(mToolTip && mouse_actually_moved)
+ {
+ mToolTipBlocked = FALSE; // Blocking starts on keyboard events and (only) ends here.
+ if( mToolTip->getVisible() && !mToolTipStickyRect.pointInRect( x, y ) )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+ }
+
+ // Activate the hover picker on mouse move.
+ if (gHoverView)
+ {
+ gHoverView->setTyping(FALSE);
+ }
+}
+
+void LLViewerWindow::handleMouseLeave(LLWindow *window)
+{
+ // Note: we won't get this if we have captured the mouse.
+ llassert( gFocusMgr.getMouseCapture() == NULL );
+ mMouseInWindow = FALSE;
+ if (mToolTip)
+ {
+ mToolTip->setVisible( FALSE );
+ }
+}
+
+BOOL LLViewerWindow::handleCloseRequest(LLWindow *window)
+{
+ // User has indicated they want to close, but we may need to ask
+ // about modified documents.
+ app_request_quit();
+ // Don't quit immediately
+ return FALSE;
+}
+
+void LLViewerWindow::handleQuit(LLWindow *window)
+{
+ app_force_quit(NULL);
+}
+
+void LLViewerWindow::handleResize(LLWindow *window, S32 width, S32 height)
+{
+ reshape(width, height);
+}
+
+// The top-level window has gained focus (e.g. via ALT-TAB)
+void LLViewerWindow::handleFocus(LLWindow *window)
+{
+ gFocusMgr.setAppHasFocus(TRUE);
+ LLModalDialog::onAppFocusGained();
+ if (gToolMgr)
+ {
+ gToolMgr->onAppFocusGained();
+ }
+
+ gShowTextEditCursor = TRUE;
+
+ // See if we're coming in with modifier keys held down
+ if (gKeyboard)
+ {
+ gKeyboard->resetMaskKeys();
+ }
+}
+
+// The top-level window has lost focus (e.g. via ALT-TAB)
+void LLViewerWindow::handleFocusLost(LLWindow *window)
+{
+ gFocusMgr.setAppHasFocus(FALSE);
+ //LLModalDialog::onAppFocusLost();
+ if( gToolMgr )
+ {
+ gToolMgr->onAppFocusLost();
+ }
+ gFocusMgr.setMouseCapture( NULL, NULL );
+
+ // restore mouse cursor
+ gViewerWindow->showCursor();
+ gViewerWindow->getWindow()->setMouseClipping(FALSE);
+
+ // JC - Leave keyboard focus, so if you're popping in and out editing
+ // a script, you don't have to click in the editor again and again.
+ // gFocusMgr.setKeyboardFocus( NULL, NULL );
+ gShowTextEditCursor = FALSE;
+
+ // If losing focus while keys are down, reset them.
+ if (gKeyboard)
+ {
+ gKeyboard->resetKeys();
+ }
+}
+
+
+BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
+{
+ if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
+ {
+ gAgent.clearAFK();
+ }
+
+ // HACK: We want to interpret KEY_RETURN later when it arrives as a Unicode char,
+ // not as a keydown. Otherwise when client frame rate is really low, hitting
+ // return sends your chat text before it's all entered/processed.
+ if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ return FALSE;
+ }
+
+ return gViewerKeyboard.handleKey(key, mask, repeated);
+}
+
+BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask)
+{
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
+{
+ return gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
+}
+
+
+
+
+BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
+{
+ if (activated)
+ {
+ mActive = TRUE;
+ send_agent_resume();
+ gAgent.clearAFK();
+ if (mWindow->getFullscreen() && !mIgnoreActivate)
+ {
+ if (!gQuit)
+ {
+ if (gStartupState >= STATE_STARTED)
+ {
+ // if we're in world, show a progress bar to hide reloading of textures
+ llinfos << "Restoring GL during activate" << llendl;
+ restoreGL("Restoring...");
+ }
+ else
+ {
+ // otherwise restore immediately
+ restoreGL();
+ }
+ }
+ else
+ {
+ llwarns << "Activating while quitting" << llendl;
+ }
+ }
+
+ // Unmute audio
+ if (!gSavedSettings.getBOOL("MuteAudio"))
+ {
+ if (gAudiop) gAudiop->setMuted(FALSE);
+ F32 volume = gSavedSettings.getF32("MediaAudioVolume");
+ if(LLMediaEngine::getInstance())
+ {
+ LLMediaEngine::getInstance()->setVolume(volume);
+ LLMediaEngine::updateClass(volume);
+ }
+ }
+ }
+ else
+ {
+ mActive = FALSE;
+ gAgent.setAFK();
+ send_agent_pause();
+ if (mWindow->getFullscreen() && !mIgnoreActivate)
+ {
+ llinfos << "Stopping GL during deactivation" << llendl;
+ stopGL();
+ }
+ // Mute audio
+ if (gSavedSettings.getBOOL("MuteWhenMinimized"))
+ {
+ llinfos << "Muting audio on minimize" << llendl;
+ if (gAudiop) gAudiop->setMuted(TRUE);
+ F32 volume = 0.f;
+ LLMediaEngine::getInstance()->setVolume(volume);
+ LLMediaEngine::updateClass(volume);
+ }
+ }
+ return TRUE;
+}
+
+
+void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item)
+{
+}
+
+
+BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height)
+{
+#if LL_WINDOWS
+ if (gNoRender)
+ {
+ HWND window_handle = llwindow_get_hwnd(window);
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ RECT wnd_rect;
+ wnd_rect.left = 0;
+ wnd_rect.top = 0;
+ wnd_rect.bottom = 200;
+ wnd_rect.right = 500;
+
+ hdc = BeginPaint(window_handle, &ps);
+ //SetBKColor(hdc, RGB(255, 255, 255));
+ FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255)));
+
+ LLString name_str;
+ gAgent.getName(name_str);
+
+ S32 len;
+ char temp_str[255];
+ sprintf(temp_str, "%s FPS %3.1f Phy FPS %2.1f Time Dil %1.3f",
+ name_str.c_str(),
+ gViewerStats->mFPSStat.getMeanPerSec(),
+ gViewerStats->mSimPhysicsFPS.getPrev(0),
+ gViewerStats->mSimTimeDilation.getPrev(0));
+ len = strlen(temp_str);
+ TextOutA(hdc, 0, 0, temp_str, len);
+
+
+ LLVector3d pos_global = gAgent.getPositionGlobal();
+ sprintf(temp_str, "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]);
+ len = strlen(temp_str);
+ TextOutA(hdc, 0, 25, temp_str, len);
+
+ TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61);
+ EndPaint(window_handle, &ps);
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScrollWheel(LLWindow *window, S32 clicks)
+{
+ gViewerWindow->handleScrollWheel( clicks );
+}
+
+void LLViewerWindow::handleWindowBlock(LLWindow *window)
+{
+ send_agent_pause();
+}
+
+void LLViewerWindow::handleWindowUnblock(LLWindow *window)
+{
+ send_agent_resume();
+}
+
+void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data)
+{
+ switch (data_type)
+ {
+ case 0:
+ // received URL
+ if (LLURLSimString::unpack_data(data))
+ {
+ if(gFloaterWorldMap)
+ {
+ gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName,
+ LLURLSimString::sInstance.mX,
+ LLURLSimString::sInstance.mY,
+ LLURLSimString::sInstance.mZ);
+
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }
+
+ // bring window to foreground, as it has just been "launched" from a URL
+ mWindow->bringToFront();
+ }
+ break;
+ }
+}
+
+
+//
+// Classes
+//
+LLViewerWindow::LLViewerWindow(
+ char* title, char* name,
+ S32 x, S32 y,
+ S32 width, S32 height,
+ BOOL fullscreen, BOOL ignore_pixel_depth)
+ :
+ mActive(TRUE),
+ mWantFullscreen(fullscreen),
+ mShowFullscreenProgress(FALSE),
+ mWindowRect(0, height, width, 0),
+ mVirtualWindowRect(0, height, width, 0),
+ mLeftMouseDown(FALSE),
+ mRightMouseDown(FALSE),
+ mToolTip(NULL),
+ mToolTipBlocked(FALSE),
+ mMouseInWindow( FALSE ),
+ mLastMask( MASK_NONE ),
+ mToolStored( NULL ),
+ mSuppressToolbox( FALSE ),
+ mHideCursorPermanent( FALSE ),
+ mPickPending(FALSE),
+ mIgnoreActivate( FALSE ),
+ mRenderFullFrame(FALSE)
+{
+ // Default to application directory.
+ strcpy(LLViewerWindow::sSnapshotBaseName, "Snapshot");
+ strcpy(LLViewerWindow::sMovieBaseName, "SLmovie");
+ LLViewerWindow::sSnapshotDir[0] = '\0';
+
+ mFastFrameTimer.stop();
+
+ // create window
+ mWindow = LLWindowManager::createWindow(
+ title, name, x, y, width, height, 0,
+ fullscreen,
+ gNoRender,
+ gSavedSettings.getBOOL("DisableVerticalSync"),
+ !gNoRender,
+ ignore_pixel_depth);
+#if LL_WINDOWS
+ if (!LLWinDebug::setupExceptionHandler())
+ {
+ llwarns << " Someone took over my exception handler (post createWindow)!" << llendl;
+ }
+#endif
+
+ if (NULL == mWindow)
+ {
+ LLSplashScreen::update("Shutting down...");
+#if LL_LINUX
+ llwarns << "Unable to create window, be sure screen is set at 32-bit color and your graphics driver is configured correctly. See README-linux.txt for further information."
+ << llendl;
+#else
+ llwarns << "Unable to create window, be sure screen is set at 32-bit color in Control Panels->Display->Settings"
+ << llendl;
+#endif
+ app_force_exit(1);
+ }
+
+ // Get the real window rect the window was created with (since there are various OS-dependent reasons why
+ // the size of a window or fullscreen context may have been adjusted slightly...)
+ F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
+
+ mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
+ mDisplayScale *= ui_scale_factor;
+ LLUI::setScaleFactor(mDisplayScale);
+
+ {
+ LLCoordWindow size;
+ mWindow->getSize(&size);
+ mWindowRect.set(0, size.mY, size.mX, 0);
+ mVirtualWindowRect.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0);
+ }
+
+ LLFontManager::initClass();
+
+ //
+ // We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off
+ // stuff like AGP if we think that it'll crash the viewer.
+ //
+ gFeatureManagerp->initGraphicsFeatureMasks();
+ if (gFeatureManagerp->isSafe()
+ || (gSavedSettings.getS32("LastFeatureVersion") != gFeatureManagerp->getVersion()))
+ {
+ gFeatureManagerp->applyRecommendedFeatures();
+ }
+
+ S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting");
+ // -1 indicates use default (max)
+ if (idx == -1)
+ {
+ idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting
+ gSavedSettings.setS32("GraphicsCardMemorySetting", idx);
+ }
+
+ if (!gNoRender)
+ {
+ //
+ // Initialize GL stuff
+ //
+
+ gPipeline.init();
+ stop_glerror();
+ initGLDefaults();
+ LLViewerImage::initClass();
+ }
+
+ //
+ // Done initing GL stuff.
+ //
+
+ // set callbacks
+ mWindow->setCallbacks(this);
+
+ // Init the image list. Must happen after GL is initialized and before the images that
+ // LLViewerWindow needs are requested.
+ gImageList.init();
+ gBumpImageList.init();
+
+ // Create container for all sub-views
+ mRootView = new LLRootView("root", mVirtualWindowRect, FALSE);
+ mRootView->setRenderInFastFrame(FALSE);
+
+ if (!gNoRender)
+ {
+ // Init default fonts
+ initFonts();
+ }
+
+ // Init Resource Manager
+ gResMgr = new LLResMgr();
+
+ // Make avatar head look forward at start
+ mCurrentMousePoint.mX = getWindowWidth() / 2;
+ mCurrentMousePoint.mY = getWindowHeight() / 2;
+
+ mPickBuffer = new U8[PICK_DIAMETER * PICK_DIAMETER * 4];
+
+ gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle");
+ mOverlayTitle = gSavedSettings.getString("OverlayTitle");
+ // Can't have spaces in settings.ini strings, so use underscores instead and convert them.
+ LLString::replaceChar(mOverlayTitle, '_', ' ');
+
+ gAwayTimer.stop();
+
+ LLAlertDialog::setDisplayCallback(alertCallback); // call this before calling any modal dialogs
+
+ // sync the keyboard's setting with the saved setting
+ gSavedSettings.getControl("NumpadControl")->firePropertyChanged();
+}
+
+void LLViewerWindow::initGLDefaults()
+{
+ //LLGLState::reset();
+ //gGLSDefault.set();
+ //LLGLState::verify(TRUE);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
+
+ F32 ambient[4] = {0.f,0.f,0.f,0.f };
+ F32 diffuse[4] = {1.f,1.f,1.f,1.f };
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse);
+
+ glPixelStorei(GL_PACK_ALIGNMENT,1);
+ glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+
+ // lights for objects
+ glShadeModel( GL_SMOOTH );
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+
+ glCullFace(GL_BACK);
+
+ // RN: Need this for translation and stretch manip.
+ gCone.prerender();
+ gBox.prerender();
+ gSphere.prerender();
+ gCylinder.prerender();
+
+ LLVOAvatar::initVertexPrograms();
+}
+
+void LLViewerWindow::initBase()
+{
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ LLRect full_window(0, height, width, 0);
+
+ adjustRectanglesForFirstUse(full_window);
+
+ ////////////////////
+ //
+ // Set the gamma
+ //
+
+ F32 gamma = gSavedSettings.getF32("RenderGamma");
+ if (gamma != 0.0f)
+ {
+ gViewerWindow->getWindow()->setGamma(gamma);
+ }
+
+ // Create global views
+
+ // Create the floater view at the start so that other views can add children to it.
+ // (But wait to add it as a child of the root view so that it will be in front of the
+ // other views.)
+
+ // Constrain floaters to inside the menu and status bar regions.
+ LLRect floater_view_rect = full_window;
+ // make space for menu bar if we have one
+ floater_view_rect.mTop -= MENU_BAR_HEIGHT;
+ floater_view_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 2;
+
+ // Check for non-first startup
+ S32 floater_view_bottom = gSavedSettings.getS32("FloaterViewBottom");
+ if (floater_view_bottom >= 0)
+ {
+ floater_view_rect.mBottom = floater_view_bottom;
+ }
+ gFloaterView = new LLFloaterView("Floater View", floater_view_rect );
+ gFloaterView->setVisible(TRUE);
+
+ gSnapshotFloaterView = new LLSnapshotFloaterView("Snapshot Floater View", full_window);
+ gSnapshotFloaterView->setVisible(TRUE);
+
+ // Console
+ llassert( !gConsole );
+ LLRect console_rect = full_window;
+ console_rect.mTop -= 24;
+ console_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 12;
+ console_rect.mLeft += 24; //gSavedSettings.getS32("StatusBarButtonWidth") + gSavedSettings.getS32("StatusBarPad");
+
+ if (gSavedSettings.getBOOL("ChatFullWidth"))
+ {
+ console_rect.mRight -= 10;
+ }
+ else
+ {
+ // Make console rect somewhat narrow so having inventory open is
+ // less of a problem.
+ console_rect.mRight = console_rect.mLeft + 2 * width / 3;
+ }
+
+ gConsole = new LLConsole(
+ "console",
+ gSavedSettings.getS32("ConsoleBufferSize"),
+ console_rect,
+ gSavedSettings.getS32("ChatFontSize"),
+ gSavedSettings.getF32("ChatPersistTime") );
+ gConsole->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
+ mRootView->addChild(gConsole);
+
+ // Debug view over the console
+ gDebugView = new LLDebugView("gDebugView", full_window);
+ gDebugView->setFollowsAll();
+ gDebugView->setVisible(TRUE);
+ mRootView->addChild(gDebugView);
+
+ // HUD elements just below floaters
+ LLRect hud_rect = full_window;
+ hud_rect.mTop -= 24;
+ hud_rect.mBottom += STATUS_BAR_HEIGHT;
+ gHUDView = new LLHUDView("hud_view", hud_rect);
+ gHUDView->setFollowsAll();
+ mRootView->addChild(gHUDView);
+
+ // Add floater view at the end so it will be on top, and give it tab priority over others
+ mRootView->addChild(gFloaterView, -1);
+ mRootView->addChild(gSnapshotFloaterView);
+
+ // notify above floaters!
+ LLRect notify_rect = full_window;
+ //notify_rect.mTop -= 24;
+ notify_rect.mBottom += STATUS_BAR_HEIGHT;
+ gNotifyBoxView = new LLNotifyBoxView("notify", notify_rect, FALSE, FOLLOWS_ALL);
+ mRootView->addChild(gNotifyBoxView, -2);
+
+ // Tooltips go above floaters
+ mToolTip = new LLTextBox( "tool tip", LLRect(0, 1, 1, 0 ) );
+ mToolTip->setHPad( 4 );
+ mToolTip->setVPad( 2 );
+ mToolTip->setColor( gColors.getColor( "ToolTipTextColor" ) );
+ mToolTip->setBorderColor( gColors.getColor( "ToolTipBorderColor" ) );
+ mToolTip->setBorderVisible( FALSE );
+ mToolTip->setBackgroundColor( gColors.getColor( "ToolTipBgColor" ) );
+ mToolTip->setBackgroundVisible( TRUE );
+ mToolTip->setDropshadowVisible( FALSE );
+ mToolTip->setBorderDropshadowVisible( TRUE );
+ mToolTip->setVisible( FALSE );
+
+ // Add the progress bar view (startup view), which overrides everything
+ mProgressView = new LLProgressView("ProgressView", full_window);
+ mRootView->addChild(mProgressView);
+ setShowProgress(FALSE);
+ setProgressCancelButtonVisible(FALSE, "");
+}
+
+
+void adjust_rect_top_left(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setLeftTopAndSize(0, window.getHeight(), r.getWidth(), r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+void adjust_rect_top_right(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setLeftTopAndSize(window.getWidth() - r.getWidth(),
+ window.getHeight(),
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+void adjust_rect_bottom_center(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setOriginAndSize(
+ window.getWidth()/2 - r.getWidth()/2,
+ 0,
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+// Many rectangles can't be placed until we know the screen size.
+// These rectangles have their bottom-left corner as 0,0
+void LLViewerWindow::adjustRectanglesForFirstUse(const LLRect& window)
+{
+ LLRect r;
+
+ adjust_rect_bottom_center("FloaterMoveRect", window);
+
+ adjust_rect_bottom_center("FloaterCameraRect", window);
+
+ adjust_rect_top_left("FloaterCustomizeAppearanceRect", window);
+
+ adjust_rect_top_left("FloaterLandRect5", window);
+
+ adjust_rect_top_left("FloaterFindRect2", window);
+
+ adjust_rect_top_left("FloaterGestureRect", window);
+
+ adjust_rect_top_right("FloaterMapRect", window);
+
+ adjust_rect_top_left("FloaterBuildOptionsRect", window);
+
+ // bottom-right
+ r = gSavedSettings.getRect("FloaterInventoryRect");
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setOriginAndSize(
+ window.getWidth() - r.getWidth(),
+ 0,
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect("FloaterInventoryRect", r);
+ }
+}
+
+
+void LLViewerWindow::initWorldUI()
+{
+ pre_init_menus();
+
+ S32 height = mRootView->getRect().getHeight();
+ S32 width = mRootView->getRect().getWidth();
+ LLRect full_window(0, height, width, 0);
+
+ LLRect bar_rect(-1, STATUS_BAR_HEIGHT, width+1, -1);
+ gToolBar = new LLToolBar("toolbar", bar_rect);
+
+ LLRect chat_bar_rect(-1,CHAT_BAR_HEIGHT, width+1, -1);
+ chat_bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ gChatBar = new LLChatBar("chat", chat_bar_rect);
+
+ bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ bar_rect.translate(0, CHAT_BAR_HEIGHT-1);
+ gOverlayBar = new LLOverlayBar("overlay", bar_rect);
+
+ // panel containing chatbar, toolbar, and overlay, over floaters
+ LLRect bottom_rect(-1, 2*STATUS_BAR_HEIGHT + CHAT_BAR_HEIGHT, width+1, -1);
+ gBottomPanel = new LLBottomPanel("bottom panel", bottom_rect);
+
+ // the order here is important
+ gBottomPanel->addChild(gChatBar);
+ gBottomPanel->addChild(gToolBar);
+ gBottomPanel->addChild(gOverlayBar);
+ mRootView->addChild(gBottomPanel);
+
+ // View for hover information
+ gHoverView = new LLHoverView("gHoverView", full_window);
+ gHoverView->setVisible(TRUE);
+ mRootView->addChild(gHoverView);
+
+ //
+ // Map
+ //
+ // TODO: Move instance management into class
+ gFloaterMap = new LLFloaterMap("Map");
+ gFloaterMap->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT);
+ gFloaterMap->setVisible( gSavedSettings.getBOOL("ShowMiniMap") );
+
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(gFloaterMap, FALSE);
+
+ if (gSavedSettings.getBOOL("ShowCameraControls"))
+ {
+ LLFloaterCamera::show(NULL);
+ }
+
+ if (gSavedSettings.getBOOL("ShowMovementControls"))
+ {
+ LLFloaterMove::show(NULL);
+ }
+
+ // Must have one global chat floater so it can actually store
+ // the history. JC
+ gFloaterChat = new LLFloaterChat();
+ gFloaterChat->setVisible( FALSE );
+
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) gFloaterChat->loadHistory();
+
+ gIMView = new LLIMView("gIMView", LLRect() );
+ gIMView->setFollowsAll();
+
+ LLRect morph_view_rect = full_window;
+ morph_view_rect.stretch( -STATUS_BAR_HEIGHT );
+ morph_view_rect.mTop = full_window.mTop - 32;
+ gMorphView = new LLMorphView("gMorphView", morph_view_rect );
+ mRootView->addChild(gMorphView);
+ gMorphView->setVisible(FALSE);
+
+ gFloaterMute = new LLFloaterMute();
+ gFloaterMute->setVisible(FALSE);
+
+ LLWorldMapView::initClass();
+
+ LLRect world_map_rect = gSavedSettings.getRect("FloaterWorldMapRect");
+ // if 0,0,0,0 then use fullscreen
+ if (world_map_rect.mTop == 0
+ && world_map_rect.mLeft == 0
+ && world_map_rect.mRight == 0
+ && world_map_rect.mBottom == 0)
+ {
+ world_map_rect.set(0, height-TOOL_BAR_HEIGHT, width, STATUS_BAR_HEIGHT);
+ world_map_rect.stretch(-4);
+ gSavedSettings.setRect("FloaterWorldMapRect", world_map_rect);
+ }
+ gFloaterWorldMap = new LLFloaterWorldMap();
+ gFloaterWorldMap->setVisible(FALSE);
+
+ //
+ // Tools for building
+ //
+
+ // Toolbox floater
+ init_menus();
+
+ gFloaterTools = new LLFloaterTools();
+ gFloaterTools->setVisible(FALSE);
+
+ // Status bar
+ //FIXME change this back
+ S32 menu_bar_height = gMenuBarView->getRect().getHeight();
+ LLRect root_rect = gViewerWindow->getRootView()->getRect();
+ LLRect status_rect(0, root_rect.getHeight(), root_rect.getWidth(), root_rect.getHeight() - menu_bar_height);
+ gStatusBar = new LLStatusBar("status", status_rect);
+ gStatusBar->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+
+ gStatusBar->reshape(root_rect.getWidth(), gStatusBar->getRect().getHeight(), TRUE);
+ gStatusBar->translate(0, root_rect.getHeight() - gStatusBar->getRect().getHeight());
+
+ gViewerWindow->getRootView()->addChild(gStatusBar);
+}
+
+
+LLViewerWindow::~LLViewerWindow()
+{
+ gSavedSettings.setS32("FloaterViewBottom", gFloaterView->getRect().mBottom);
+
+ // Cleanup global views
+ if (gMorphView)
+ {
+ gMorphView->setVisible(FALSE);
+ }
+
+ // Delete all child views.
+ delete mRootView;
+ mRootView = NULL;
+
+ // Automatically deleted as children of mRootView. Fix the globals.
+ gFloaterTools = NULL;
+ gStatusBar = NULL;
+ gFloaterCamera = NULL;
+ gIMView = NULL;
+ gHoverView = NULL;
+
+ gFloaterView = NULL;
+ gMorphView = NULL;
+
+ gFloaterChat = NULL;
+ gFloaterMute = NULL;
+
+ gFloaterMap = NULL;
+ gHUDView = NULL;
+
+ gNotifyBoxView = NULL;
+
+ delete mToolTip;
+ mToolTip = NULL;
+
+ delete gResMgr;
+ gResMgr = NULL;
+
+ //--------------------------------------------------------
+ // Shutdown GL cleanly. Order is very important here.
+ //--------------------------------------------------------
+ LLFontGL::destroyDefaultFonts();
+ LLFontManager::cleanupClass();
+ stop_glerror();
+
+ gSky.cleanup();
+ stop_glerror();
+
+ gImageList.shutdown();
+ stop_glerror();
+
+ gBumpImageList.shutdown();
+ stop_glerror();
+
+ LLWorldMapView::cleanupTextures();
+
+ LLViewerImage::cleanupClass();
+
+ delete[] mPickBuffer;
+ mPickBuffer = NULL;
+
+ llinfos << "Stopping GL during shutdown" << llendl;
+ if (!gNoRender)
+ {
+ stopGL(FALSE);
+ stop_glerror();
+ }
+
+ if (gSelectMgr)
+ {
+ llinfos << "Cleaning up select manager" << llendl;
+ gSelectMgr->cleanup();
+ }
+
+ llinfos << "Cleaning up pipeline" << llendl;
+ gPipeline.cleanup();
+ stop_glerror();
+ llinfos << "Destroying Window" << llendl;
+ destroyWindow();
+}
+
+
+void LLViewerWindow::setCursor( ECursorType c )
+{
+ mWindow->setCursor( c );
+}
+
+void LLViewerWindow::showCursor()
+{
+ mWindow->showCursor();
+}
+
+void LLViewerWindow::hideCursor()
+{
+ // Hide tooltips
+ if(mToolTip ) mToolTip->setVisible( FALSE );
+
+ // Also hide hover info
+ if (gHoverView) gHoverView->cancelHover();
+
+ // And hide the cursor
+ mWindow->hideCursor();
+}
+
+void LLViewerWindow::sendShapeToSim()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ if(!msg) return;
+ msg->newMessageFast(_PREHASH_AgentHeightWidth);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
+ msg->nextBlockFast(_PREHASH_HeightWidthBlock);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+ U16 height16 = (U16) mWindowRect.getHeight();
+ U16 width16 = (U16) mWindowRect.getWidth();
+ msg->addU16Fast(_PREHASH_Height, height16);
+ msg->addU16Fast(_PREHASH_Width, width16);
+ gAgent.sendReliableMessage();
+}
+
+// Must be called after window is created to set up agent
+// camera variables and UI variables.
+void LLViewerWindow::reshape(S32 width, S32 height)
+{
+ // Destroying the window at quit time generates spurious
+ // reshape messages. We don't care about these, and we
+ // don't want to send messages because the message system
+ // may have been destructed.
+ if (!gQuit)
+ {
+ if (gNoRender)
+ {
+ return;
+ }
+
+ glViewport(0, 0, width, height );
+
+ if (height > 0 && gCamera)
+ {
+ gCamera->setViewHeightInPixels( height );
+ if (mWindow->getFullscreen())
+ {
+ // force to 4:3 aspect for odd resolutions
+ gCamera->setAspect( getDisplayAspectRatio() );
+ }
+ else
+ {
+ gCamera->setAspect( width / (F32) height);
+ }
+ }
+
+ // update our window rectangle
+ mWindowRect.mRight = mWindowRect.mLeft + width;
+ mWindowRect.mTop = mWindowRect.mBottom + height;
+ calcDisplayScale();
+
+ BOOL display_scale_changed = mDisplayScale != LLUI::sGLScaleFactor;
+ LLUI::setScaleFactor(mDisplayScale);
+
+ // update our window rectangle
+ mVirtualWindowRect.mRight = mVirtualWindowRect.mLeft + llround((F32)width / mDisplayScale.mV[VX]);
+ mVirtualWindowRect.mTop = mVirtualWindowRect.mBottom + llround((F32)height / mDisplayScale.mV[VY]);
+
+ setupViewport();
+
+ // Inform lower views of the change
+ // round up when converting coordinates to make sure there are no gaps at edge of window
+ LLView::sForceReshape = display_scale_changed;
+ mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY]));
+ LLView::sForceReshape = FALSE;
+
+ // clear font width caches
+ if (display_scale_changed)
+ {
+ LLHUDText::reshape();
+ }
+
+ sendShapeToSim();
+
+
+ // store the mode the user wants (even if not there yet)
+ gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
+
+ // store new settings for the mode we are in, regardless
+ if (mWindow->getFullscreen())
+ {
+ gSavedSettings.setS32("FullScreenWidth", width);
+ gSavedSettings.setS32("FullScreenHeight", height);
+ }
+ else
+ {
+ // Only save size if not maximized
+ BOOL maximized = mWindow->getMaximized();
+ gSavedSettings.setBOOL("WindowMaximized", maximized);
+
+ LLCoordScreen window_size;
+ if (!maximized
+ && mWindow->getSize(&window_size))
+ {
+ gSavedSettings.setS32("WindowWidth", window_size.mX);
+ gSavedSettings.setS32("WindowHeight", window_size.mY);
+ }
+ }
+
+ gViewerStats->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width);
+ gViewerStats->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height);
+ }
+}
+
+
+void LLViewerWindow::draw()
+{
+#if LL_DEBUG
+ LLView::sIsDrawing = TRUE;
+#endif
+ stop_glerror();
+
+ LLUI::setLineWidth(1.f);
+ //popup alerts from the UI
+ LLAlertInfo alert;
+ while (LLPanel::nextAlert(alert))
+ {
+ alertXml(alert.mLabel, alert.mArgs);
+ }
+
+ LLUI::setLineWidth(1.f);
+ // Reset any left-over transforms
+ glMatrixMode(GL_MODELVIEW);
+
+ glLoadIdentity();
+
+ //S32 screen_x, screen_y;
+
+ // HACK for timecode debugging
+ if (gSavedSettings.getBOOL("DisplayTimecode"))
+ {
+ // draw timecode block
+ char text[256];
+
+ glLoadIdentity();
+
+ microsecondsToTimecodeString(gFrameTime,text);
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ font->renderUTF8(text, 0,
+ llround((getWindowWidth()/2)-100.f),
+ llround((getWindowHeight()-60.f)),
+ LLColor4( 1.f, 1.f, 1.f, 1.f ),
+ LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ // Draw all nested UI views.
+ // No translation needed, this view is glued to 0,0
+
+ glPushMatrix();
+ {
+ // scale view by UI global scale factor and aspect ratio correction factor
+ glScalef(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f);
+
+ LLVector2 old_scale_factor = LLUI::sGLScaleFactor;
+ if (gCamera)
+ {
+ // apply camera zoom transform (for high res screenshots)
+ F32 zoom_factor = gCamera->getZoomFactor();
+ S16 sub_region = gCamera->getZoomSubRegion();
+ if (zoom_factor > 1.f)
+ {
+ //decompose subregion number to x and y values
+ int pos_y = sub_region / llceil(zoom_factor);
+ int pos_x = sub_region - (pos_y*llceil(zoom_factor));
+ // offset for this tile
+ glTranslatef((F32)gViewerWindow->getWindowWidth() * -(F32)pos_x,
+ (F32)gViewerWindow->getWindowHeight() * -(F32)pos_y,
+ 0.f);
+ glScalef(zoom_factor, zoom_factor, 1.f);
+ LLUI::sGLScaleFactor *= zoom_factor;
+ }
+ }
+
+ {
+ LLGLSTexture gls_texture;
+ show_text_gl();
+ }
+
+ if (gToolMgr)
+ {
+ // Draw tool specific overlay on world
+ gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) )->draw();
+ }
+
+ if( gAgent.cameraMouselook() )
+ {
+ drawMouselookInstructions();
+ stop_glerror();
+ }
+
+ // Draw all nested UI views.
+ // No translation needed, this view is glued to 0,0
+ mRootView->draw();
+
+ // Draw optional on-top-of-everyone view
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view && top_view->getVisible())
+ {
+ S32 screen_x, screen_y;
+ top_view->localPointToScreen(0, 0, &screen_x, &screen_y);
+
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ LLUI::translate( (F32) screen_x, (F32) screen_y, 0.f);
+ top_view->draw();
+ LLUI::popMatrix();
+ }
+
+ // Draw tooltips
+ // Adjust their rectangle so they don't go off the top or bottom
+ // of the screen.
+ if( mToolTip && mToolTip->getVisible() )
+ {
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ S32 tip_height = mToolTip->getRect().getHeight();
+
+ S32 screen_x, screen_y;
+ mToolTip->localPointToScreen(0, -24 - tip_height,
+ &screen_x, &screen_y);
+
+ // If tooltip would draw off the bottom of the screen,
+ // show it from the cursor tip position.
+ if (screen_y < tip_height)
+ {
+ mToolTip->localPointToScreen(0, 0, &screen_x, &screen_y);
+ }
+ LLUI::translate( (F32) screen_x, (F32) screen_y, 0);
+ mToolTip->draw();
+ }
+ LLUI::popMatrix();
+ }
+
+ if( gShowOverlayTitle && !mOverlayTitle.empty() )
+ {
+ // Used for special titles such as "Second Life - Special E3 2003 Beta"
+ const S32 DIST_FROM_TOP = 20;
+ LLGLSTexture gls_texture;
+ LLFontGL::sSansSerifBig->renderUTF8(
+ mOverlayTitle, 0,
+ llround( gViewerWindow->getWindowWidth() * 0.5f),
+ gViewerWindow->getWindowHeight() - DIST_FROM_TOP,
+ LLColor4(1, 1, 1, 0.4f),
+ LLFontGL::HCENTER, LLFontGL::TOP);
+ }
+
+ LLUI::sGLScaleFactor = old_scale_factor;
+ }
+ glPopMatrix();
+
+
+#if LL_DEBUG
+ LLView::sIsDrawing = FALSE;
+#endif
+}
+
+// Takes a single keydown event, usually when UI is visible
+BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
+{
+ if (gFocusMgr.getKeyboardFocus() && !(mask & (MASK_CONTROL | MASK_ALT)))
+ {
+ // We have keyboard focus, and it's not an accelerator
+
+ if (key < 0x80)
+ {
+ // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first.
+ return gFocusMgr.childHasKeyboardFocus(mRootView);
+ }
+ }
+
+ // HACK look for UI editing keys
+ if (LLView::sEditingUI)
+ {
+ if (LLFloaterEditUI::handleKey(key, mask))
+ {
+ return TRUE;
+ }
+ }
+
+ // Hide tooltips on keypress
+ if(mToolTip )
+ {
+ mToolTipBlocked = TRUE; // block until next time mouse is moved
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on keypress
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+
+ gHoverView->setTyping(TRUE);
+ }
+
+ // Explicit hack for debug menu.
+ if ((MASK_ALT & mask) &&
+ (MASK_CONTROL & mask) &&
+ ('D' == key || 'd' == key))
+ {
+ toggle_debug_menus(NULL);
+ }
+
+ // Example "bug" for bug reporter web page
+ if ((MASK_SHIFT & mask)
+ && (MASK_ALT & mask)
+ && (MASK_CONTROL & mask)
+ && ('H' == key || 'h' == key))
+ {
+ trigger_hippo_bug(NULL);
+ }
+
+ // handle escape key
+ if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ if (gMenuHolder && gMenuHolder->hideMenus())
+ {
+ return TRUE;
+ }
+ //FIXME: get this to play well with mouselook and hidden cursor modes, etc.
+ //if (gFocusMgr.getMouseCapture())
+ //{
+ // gFocusMgr.setMouseCapture(NULL, NULL);
+ // return TRUE;
+ //}
+ }
+
+ // let menus handle navigation keys
+ if (gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE))
+ {
+ return TRUE;
+ }
+
+ // Traverses up the hierarchy
+ LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ if( keyboard_focus )
+ {
+ // arrow keys move avatar while chatting hack
+ if (gChatBar && gChatBar->inputEditorHasFocus())
+ {
+ if (gChatBar->getCurrentChat().empty() || gSavedSettings.getBOOL("ArrowKeysMoveAvatar"))
+ {
+ switch(key)
+ {
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PAGE_UP:
+ case KEY_PAGE_DOWN:
+ case KEY_HOME:
+ // when chatbar is empty or ArrowKeysMoveAvatar set, pass arrow keys on to avatar...
+ return FALSE;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (keyboard_focus->handleKey(key, mask, FALSE))
+ {
+ return TRUE;
+ }
+ }
+
+ if (gToolMgr)
+ {
+ if( gToolMgr->getCurrentTool(mask)->handleKey(key, mask) )
+ {
+ return TRUE;
+ }
+ }
+
+ // Try for a new-format gesture
+ if (gGestureManager.triggerGesture(key, mask))
+ {
+ return TRUE;
+ }
+
+ // See if this is a gesture trigger. If so, eat the key and
+ // don't pass it down to the menus.
+ if (gGestureList.trigger(key, mask))
+ {
+ return TRUE;
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ //FIXME: get rid of this?
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ if( top_view->handleKey( key, mask, TRUE ) )
+ {
+ return TRUE;
+ }
+ }
+
+ // give floaters first chance to handle TAB key
+ // so frontmost floater gets focus
+ if (key == KEY_TAB)
+ {
+ // if nothing has focus, go to first or last UI element as appropriate
+ if (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL)
+ {
+ if (gMenuHolder) gMenuHolder->hideMenus();
+
+ // if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode
+ gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0);
+
+ // do CTRL-TAB and CTRL-SHIFT-TAB logic
+ if (mask & MASK_SHIFT)
+ {
+ mRootView->focusPrevRoot();
+ }
+ else
+ {
+ mRootView->focusNextRoot();
+ }
+ return TRUE;
+ }
+ }
+
+ // give menus a chance to handle keys
+ if (gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask))
+ {
+ return TRUE;
+ }
+
+ // don't pass keys on to world when something in ui has focus
+ return gFocusMgr.childHasKeyboardFocus(mRootView);
+}
+
+
+BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
+{
+ // HACK: We delay processing of return keys until they arrive as a Unicode char,
+ // so that if you're typing chat text at low frame rate, we don't send the chat
+ // until all keystrokes have been entered. JC
+ // HACK: Numeric keypad <enter> on Mac is Unicode 3
+ // HACK: Control-M on Windows is Unicode 13
+ if ((uni_char == 13 && mask != MASK_CONTROL)
+ || (uni_char == 3 && mask == MASK_NONE))
+ {
+ return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN));
+ }
+
+ // Traverses up the hierarchy
+ LLView* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ if( keyboard_focus )
+ {
+ if (keyboard_focus->handleUnicodeChar(uni_char, FALSE))
+ {
+ return TRUE;
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view && top_view->handleUnicodeChar( uni_char, FALSE ) )
+ {
+ return TRUE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScrollWheel(S32 clicks)
+{
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
+ mouse_captor->handleScrollWheel(local_x, local_y, clicks);
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return;
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x;
+ S32 local_y;
+ top_view->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
+ if (top_view->handleScrollWheel(local_x, local_y, clicks)) return;
+ }
+
+ if (mRootView->handleScrollWheel(mCurrentMousePoint.mX, mCurrentMousePoint.mY, clicks) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel not handled by view" << llendl;
+ }
+
+ if (gWorldPointer)
+ {
+ // Zoom the camera in and out behavior
+ gAgent.handleScrollWheel(clicks);
+ }
+
+ return;
+}
+
+void LLViewerWindow::moveCursorToCenter()
+{
+#if 1 // old version
+
+#if 0 // Dave's changes - this reportedly is making the drift worse on some systems?
+ S32 x = llround((F32) mVirtualWindowRect.getWidth() / 2);
+ S32 y = llround((F32) mVirtualWindowRect.getHeight() / 2);
+#else
+ S32 x = mVirtualWindowRect.getWidth() / 2;
+ S32 y = mVirtualWindowRect.getHeight() / 2;
+#endif
+
+ //on a forced move, all deltas get zeroed out to prevent jumping
+ mCurrentMousePoint.set(x,y);
+ mLastMousePoint.set(x,y);
+ mCurrentMouseDelta.set(0,0);
+
+ LLUI::setCursorPositionScreen(x, y);
+
+#else // Richard's version - fails on intel macs
+
+ S32 x = llround((F32) mWindowRect.getWidth() / 2);
+ S32 y = llround((F32) mWindowRect.getHeight() / 2);
+
+ LLCoordWindow window_point;
+ mWindow->convertCoords(LLCoordGL(x, y), &window_point);
+ mWindow->setCursorPosition(window_point);
+
+ // read back cursor position
+ mWindow->getCursorPosition(&window_point);
+ LLCoordGL new_mouse_pos;
+ mWindow->convertCoords(window_point, &new_mouse_pos);
+ new_mouse_pos.mX = llround((F32)new_mouse_pos.mX / mDisplayScale.mV[VX]);
+ new_mouse_pos.mY = llround((F32)new_mouse_pos.mY / mDisplayScale.mV[VY]);
+
+ //on a forced move, all deltas get zeroed out to prevent jumping
+ mCurrentMousePoint = new_mouse_pos;
+ mLastMousePoint = new_mouse_pos;
+ mCurrentMouseDelta.set(0,0);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Hover handlers
+//
+
+// Update UI based on stored mouse position from mouse-move
+// event processing.
+BOOL LLViewerWindow::handlePerFrameHover()
+{
+ static std::string last_handle_msg;
+
+ //RN: fix for asynchronous notification of mouse leaving window not working
+ LLCoordWindow mouse_pos;
+ mWindow->getCursorPosition(&mouse_pos);
+ if (mouse_pos.mX < 0 ||
+ mouse_pos.mY < 0 ||
+ mouse_pos.mX > mWindowRect.getWidth() ||
+ mouse_pos.mY > mWindowRect.getHeight())
+ {
+ mMouseInWindow = FALSE;
+ }
+ else
+ {
+ mMouseInWindow = TRUE;
+ }
+
+ S32 dx = mCurrentMousePoint.mX - mLastMousePoint.mX;
+ S32 dy = mCurrentMousePoint.mY - mLastMousePoint.mY;
+ mCurrentMouseDelta.set(dx,dy);
+ LLVector2 mouse_vel((F32)dx, (F32)dy);
+ mMouseVelocityStat.addValue(mouse_vel.magVec());
+
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ S32 x = mCurrentMousePoint.mX;
+ S32 y = mCurrentMousePoint.mY;
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ // clean up current focus
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus)
+ {
+ if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain())
+ {
+ gFocusMgr.releaseFocusIfNeeded(cur_focus);
+
+ LLView* parent = cur_focus->getParent();
+ LLView* focus_root = cur_focus->findRootMostFocusRoot();
+ while(parent)
+ {
+ if (parent->isCtrl() &&
+ (((LLUICtrl*)parent)->hasTabStop() || parent == focus_root) &&
+ !((LLUICtrl*)parent)->getIsChrome() &&
+ parent->isInVisibleChain() &&
+ parent->isInEnabledChain())
+ {
+ if (!parent->focusFirstItem())
+ {
+ ((LLUICtrl*)parent)->setFocus(TRUE);
+ }
+ break;
+ }
+ parent = parent->getParent();
+ }
+ }
+ else if (cur_focus->isFocusRoot())
+ {
+ // focus roots keep trying to delegate focus to their first valid descendant
+ // this assumes that focus roots are not valid focus holders on their own
+ cur_focus->focusFirstItem();
+ }
+ }
+
+ // Show joints while in edit mode and hold down alt key.
+ if (gHUDManager)
+ {
+ if (gSavedSettings.getBOOL("AltShowsPhysical")
+ || (gFloaterTools && gFloaterTools->getVisible()))
+ {
+ gHUDManager->toggleShowPhysical( mask & MASK_ALT );
+ }
+ }
+
+ BOOL handled = FALSE;
+
+ BOOL handled_by_top_view = FALSE;
+ LLView* top_view = gFocusMgr.getTopView();
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ // Pass hover events to object capturing mouse events.
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = mouse_captor->handleHover(local_x, local_y, mask);
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl;
+ }
+ }
+ else
+ {
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleHover(local_x, local_y, mask);
+ handled_by_top_view = TRUE;
+ }
+
+ if ( !handled )
+ {
+ // x and y are from last time mouse was in window
+ // mMouseInWindow tracks *actual* mouse location
+ if (mMouseInWindow && mRootView->handleHover(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg)
+ {
+ last_handle_msg = LLView::sMouseHandlerMessage;
+ llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl;
+ }
+ LLView::sMouseHandlerMessage = "";
+ handled = TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ if (last_handle_msg != "")
+ {
+ last_handle_msg = "";
+ llinfos << "Hover not handled by view" << llendl;
+ }
+ }
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl;
+ }
+ }
+
+ //FIXME: sometimes tools handle the mouse as a captor, so this
+ // logic is a little confusing
+ LLTool *tool = NULL;
+ if (gToolMgr && gHoverView)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if(!handled && tool)
+ {
+ handled = tool->handleHover(x, y, mask);
+
+ if (!mWindow->isCursorHidden())
+ {
+ gHoverView->updateHover(tool);
+ }
+ }
+ else
+ {
+ // Cancel hovering if any UI element handled the event.
+ gHoverView->cancelHover();
+ }
+
+ // Suppress the toolbox view if our source tool was the pie tool,
+ // and we've overridden to something else.
+ mSuppressToolbox =
+ (gToolMgr->getCurrentTool(MASK_NONE) == gToolPie) &&
+ (gToolMgr->getCurrentTool(mask) != gToolPie);
+
+ }
+
+ //llinfos << (mToolTipBlocked ? "BLOCKED" : "NOT BLOCKED") << llendl;
+ // Show a new tool tip (or update one that is alrady shown)
+ BOOL tool_tip_handled = FALSE;
+ LLString tool_tip_msg;
+ F32 tooltip_delay = gSavedSettings.getF32( "ToolTipDelay" );
+ //HACK: hack for tool-based tooltips which need to pop up more quickly
+ //Also for show xui names as tooltips debug mode
+ if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames)
+ {
+ tooltip_delay = gSavedSettings.getF32( "DragAndDropToolTipDelay" );
+ }
+ if( handled &&
+ !mToolTipBlocked &&
+ (gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay) &&
+ !mWindow->isCursorHidden() )
+ {
+ LLRect screen_sticky_rect;
+
+ if (mouse_captor)
+ {
+ S32 local_x, local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ tool_tip_handled = mouse_captor->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect );
+ }
+ else if (handled_by_top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ tool_tip_handled = top_view->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect );
+ }
+ else
+ {
+ tool_tip_handled = mRootView->handleToolTip(x, y, tool_tip_msg, &screen_sticky_rect );
+ }
+
+ if( tool_tip_handled && !tool_tip_msg.empty() )
+ {
+ mToolTipStickyRect = screen_sticky_rect;
+ mToolTip->setWrappedText( tool_tip_msg, 200 );
+ mToolTip->reshapeToFitText();
+ mToolTip->setOrigin( x, y );
+ LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0);
+ mToolTip->translateIntoRect( virtual_window_rect, FALSE );
+ mToolTip->setVisible( TRUE );
+ }
+ }
+
+ if (tool != gToolNull && tool != gToolDragAndDrop && !gSavedSettings.getBOOL("FreezeTime"))
+ {
+ LLMouseHandler *captor = gFocusMgr.getMouseCapture();
+ // With the null tool or drag and drop tool, don't muck
+ // with visibility.
+
+ if (gFloaterTools->isMinimized() ||
+ (tool != gToolPie // not default tool
+ && tool != gToolGun // not coming out of mouselook
+ && !mSuppressToolbox // not override in third person
+ && gCurrentToolset != gFaceEditToolset // not special mode
+ && gCurrentToolset != gMouselookToolset
+ && (!captor || captor->isView())) // not dragging
+ )
+ {
+ // Force floater tools to be visible (unless minimized)
+ if (!gFloaterTools->isMinimized() && !gFloaterTools->getVisible())
+ {
+ gFloaterTools->open();
+ }
+ // Update the location of the blue box tool popup
+ LLCoordGL select_center_screen;
+ gFloaterTools->updatePopup( select_center_screen, mask );
+ }
+ else
+ {
+ gFloaterTools->setVisible(FALSE);
+ }
+ }
+ if (gToolBar)
+ {
+ gToolBar->refresh();
+ }
+
+ if (gChatBar)
+ {
+ gChatBar->refresh();
+ }
+
+ if (gOverlayBar)
+ {
+ gOverlayBar->refresh();
+ }
+
+ // Update rectangles for the various toolbars
+ if (gToolBar && gChatBar && gOverlayBar && gNotifyBoxView && gConsole)
+ {
+ LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1);
+ if (gToolBar->getVisible())
+ {
+ gToolBar->setRect(bar_rect);
+ bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ }
+
+ if (gChatBar->getVisible())
+ {
+ // fix up the height
+ LLRect chat_bar_rect = bar_rect;
+ chat_bar_rect.mTop = chat_bar_rect.mBottom + CHAT_BAR_HEIGHT + 1;
+ gChatBar->setRect(chat_bar_rect);
+ bar_rect.translate(0, CHAT_BAR_HEIGHT-1);
+ }
+
+ LLRect notify_box_rect = gNotifyBoxView->getRect();
+ notify_box_rect.mBottom = bar_rect.mBottom;
+ gNotifyBoxView->reshape(notify_box_rect.getWidth(), notify_box_rect.getHeight());
+ gNotifyBoxView->setRect(notify_box_rect);
+
+ // make sure floaters snap to visible rect by adjusting floater view rect
+ LLRect floater_rect = gFloaterView->getRect();
+ if (floater_rect.mBottom != bar_rect.mBottom+1)
+ {
+ floater_rect.mBottom = bar_rect.mBottom+1;
+ // Don't bounce the floaters up and down.
+ gFloaterView->reshape(floater_rect.getWidth(), floater_rect.getHeight(),
+ TRUE, ADJUST_VERTICAL_NO);
+ gFloaterView->setRect(floater_rect);
+ }
+
+ if (gOverlayBar->getVisible())
+ {
+ LLRect overlay_rect = bar_rect;
+ overlay_rect.mTop = overlay_rect.mBottom + OVERLAY_BAR_HEIGHT;
+
+ // Fitt's Law: Push buttons flush with bottom of screen if
+ // nothing else visible.
+ if (!gToolBar->getVisible()
+ && !gChatBar->getVisible())
+ {
+ // FIXME: this is highly depenent on the XML describing the position of the buttons
+ overlay_rect.translate(0, 0);
+ }
+
+ gOverlayBar->setRect(overlay_rect);
+ gOverlayBar->updateRect();
+ bar_rect.translate(0, gOverlayBar->getRect().getHeight());
+
+ gFloaterView->setSnapOffsetBottom(OVERLAY_BAR_HEIGHT);
+ }
+ else
+ {
+ gFloaterView->setSnapOffsetBottom(0);
+ }
+
+ // fix rectangle of bottom panel focus indicator
+ if(gBottomPanel && gBottomPanel->getFocusIndicator())
+ {
+ LLRect focus_rect = gBottomPanel->getFocusIndicator()->getRect();
+ focus_rect.mTop = (gToolBar->getVisible() ? STATUS_BAR_HEIGHT : 0) +
+ (gChatBar->getVisible() ? CHAT_BAR_HEIGHT : 0) - 2;
+ gBottomPanel->getFocusIndicator()->setRect(focus_rect);
+ }
+
+ // Always update console
+ LLRect console_rect = gConsole->getRect();
+ console_rect.mBottom = bar_rect.mBottom + 8;
+ gConsole->reshape(console_rect.getWidth(), console_rect.getHeight());
+ gConsole->setRect(console_rect);
+ }
+
+ mLastMousePoint = mCurrentMousePoint;
+
+ // last ditch force of edit menu to selection manager
+ if (gEditMenuHandler == NULL && gSelectMgr && gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+
+ if (gFloaterView->getCycleMode())
+ {
+ // sync all floaters with their focus state
+ gFloaterView->highlightFocusedFloater();
+ gSnapshotFloaterView->highlightFocusedFloater();
+ if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0)
+ {
+ // control key no longer held down, finish cycle mode
+ gFloaterView->setCycleMode(FALSE);
+
+ gFloaterView->syncFloaterTabOrder();
+ }
+ else
+ {
+ // user holding down CTRL, don't update tab order of floaters
+ }
+ }
+ else
+ {
+ // update focused floater
+ gFloaterView->highlightFocusedFloater();
+ gSnapshotFloaterView->highlightFocusedFloater();
+ // make sure floater visible order is in sync with tab order
+ gFloaterView->syncFloaterTabOrder();
+ }
+
+ if (gSavedSettings.getBOOL("ChatBarStealsFocus") && gChatBar && gFocusMgr.getKeyboardFocus() == NULL && gChatBar->getVisible())
+ {
+ gChatBar->startChat(NULL);
+ }
+
+ // sync land selection with edit and about land dialogs
+ if (gParcelMgr && !LLFloaterLand::floaterVisible() && (!gFloaterTools || !gFloaterTools->getVisible()))
+ {
+ gParcelMgr->deselectLand();
+ }
+
+ return handled;
+}
+
+
+void LLViewerWindow::saveLastMouse(const LLCoordGL &point)
+{
+ // Store last mouse location.
+ // If mouse leaves window, pretend last point was on edge of window
+ if (point.mX < 0)
+ {
+ mCurrentMousePoint.mX = 0;
+ }
+ else if (point.mX > getWindowWidth())
+ {
+ mCurrentMousePoint.mX = getWindowWidth();
+ }
+ else
+ {
+ mCurrentMousePoint.mX = point.mX;
+ }
+
+ if (point.mY < 0)
+ {
+ mCurrentMousePoint.mY = 0;
+ }
+ else if (point.mY > getWindowHeight() )
+ {
+ mCurrentMousePoint.mY = getWindowHeight();
+ }
+ else
+ {
+ mCurrentMousePoint.mY = point.mY;
+ }
+}
+
+
+// Draws the selection outlines for the currently selected objects
+// Must be called after displayObjects is called, which sets the mGLName parameter
+// NOTE: This function gets called 3 times:
+// render_ui_3d: FALSE, FALSE, TRUE
+// renderObjectsForSelect: TRUE, pick_parcel_wall, FALSE
+// render_hud_elements: FALSE, FALSE, FALSE
+void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud )
+{
+ LLViewerObject* object;
+
+ if (!for_hud && !for_gl_pick)
+ {
+ // Call this once and only once
+ gSelectMgr->updateSilhouettes();
+ }
+
+ // Draw fence around land selections
+ if (for_gl_pick)
+ {
+ if (pick_parcel_walls)
+ {
+ gParcelMgr->renderParcelCollision();
+ }
+ }
+ else if (( for_hud && gSelectMgr->getSelectType() == SELECT_TYPE_HUD) ||
+ (!for_hud && gSelectMgr->getSelectType() != SELECT_TYPE_HUD))
+ {
+ gSelectMgr->renderSilhouettes(for_hud);
+
+ stop_glerror();
+
+ // setup HUD render
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount())
+ {
+ LLBBox hud_bbox = gAgent.getAvatarObject()->getHUDBBox();
+
+ // set up transform to encompass bounding box of HUD
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
+ glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, depth);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+ glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f);
+ }
+
+ // Render light for editing
+ if (LLSelectMgr::sRenderLightRadius)
+ {
+ LLGLEnable gls_blend(GL_BLEND);
+ LLGLEnable gls_cull(GL_CULL_FACE);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+ for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ LLDrawable* drawable = object->mDrawable;
+ if (drawable && drawable->isLight())
+ {
+ LLVOVolume* vovolume = drawable->getVOVolume();
+ glPushMatrix();
+
+ LLVector3 center = drawable->getPositionAgent();
+ glTranslatef(center[0], center[1], center[2]);
+ F32 scale = vovolume->getLightRadius();
+ glScalef(scale, scale, scale);
+
+ LLColor4 color(vovolume->getLightColor(), .5f);
+ glColor4fv(color.mV);
+
+ F32 pixel_area = 100000.f;
+ // Render Outside
+ gSphere.render(pixel_area);
+
+ // Render Inside
+ glCullFace(GL_FRONT);
+ gSphere.render(pixel_area);
+ glCullFace(GL_BACK);
+
+ glPopMatrix();
+ }
+ }
+ glPopMatrix();
+ }
+
+ // NOTE: The average position for the axis arrows of the selected objects should
+ // not be recalculated at this time. If they are, then group rotations will break.
+
+ // Draw arrows at average center of all selected objects
+ LLTool* tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (tool)
+ {
+ if(tool->isAlwaysRendered())
+ {
+ tool->render();
+ }
+ else
+ {
+ if( !gSelectMgr->isEmpty() )
+ {
+ BOOL moveable_object_selected = FALSE;
+ BOOL all_selected_objects_move = TRUE;
+ BOOL all_selected_objects_modify = TRUE;
+ BOOL selecting_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ BOOL this_object_movable = FALSE;
+ if (object->permMove() && (object->permModify() || selecting_linked_set))
+ {
+ moveable_object_selected = TRUE;
+ this_object_movable = TRUE;
+ }
+ all_selected_objects_move = all_selected_objects_move && this_object_movable;
+ all_selected_objects_modify = all_selected_objects_modify && object->permModify();
+ }
+
+ BOOL draw_handles = TRUE;
+
+ if (tool == gToolTranslate && (!moveable_object_selected || !all_selected_objects_move))
+ {
+ draw_handles = FALSE;
+ }
+
+ if (tool == gToolRotate && (!moveable_object_selected || !all_selected_objects_move))
+ {
+ draw_handles = FALSE;
+ }
+
+ if ( !all_selected_objects_modify && tool == gToolStretch )
+ {
+ draw_handles = FALSE;
+ }
+
+ if( draw_handles )
+ {
+ tool->render();
+ }
+ }
+ }
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount())
+ {
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ stop_glerror();
+ }
+ }
+ }
+}
+
+// Return a point near the clicked object representative of the place the object was clicked.
+LLVector3d LLViewerWindow::clickPointInWorldGlobal(S32 x, S32 y_from_bot, LLViewerObject* clicked_object) const
+{
+ // create a normalized vector pointing from the camera center into the
+ // world at the location of the mouse click
+ LLVector3 mouse_direction_global = mouseDirectionGlobal( x, y_from_bot );
+
+ LLVector3d relative_object = clicked_object->getPositionGlobal() - gAgent.getCameraPositionGlobal();
+
+ // make mouse vector as long as object vector, so it touchs a point near
+ // where the user clicked on the object
+ mouse_direction_global *= (F32) relative_object.magVec();
+
+ LLVector3d new_pos;
+ new_pos.setVec(mouse_direction_global);
+ // transform mouse vector back to world coords
+ new_pos += gAgent.getCameraPositionGlobal();
+
+ return new_pos;
+}
+
+
+BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const
+{
+ BOOL intersect = FALSE;
+
+// U8 shape = objectp->mPrimitiveCode & LL_PCODE_BASE_MASK;
+ if (!intersect)
+ {
+ point_global = clickPointInWorldGlobal(x, y, objectp);
+ llinfos << "approx intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
+ }
+ else
+ {
+ llinfos << "good intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
+ }
+
+ return intersect;
+}
+
+void LLViewerWindow::hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent, BOOL pick_parcel_walls)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
+ S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
+
+ F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32();
+
+ BOOL in_build_mode = gFloaterTools && gFloaterTools->getVisible();
+ if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha)
+ {
+ // build mode allows interaction with all transparent objects
+ // "Show Debug Alpha" means no object actually transparent
+ pick_transparent = TRUE;
+ }
+ gPickTransparent = pick_transparent;
+
+ if (gPickTransparent)
+ {
+ gPickAlphaTargetThreshold = 0.f;
+ gPickAlphaThreshold = llmax(gPickAlphaTargetThreshold, gPickAlphaThreshold - (delta_time * 0.7f));
+ }
+ else
+ {
+ gPickAlphaTargetThreshold = 1.f;
+ gPickAlphaThreshold = llmin(gPickAlphaTargetThreshold, gPickAlphaThreshold + (delta_time * 0.7f));
+ }
+
+ gUseGLPick = FALSE;
+ mPickCallback = callback;
+
+ // Default to not hitting anything
+ gLastHitPosGlobal.zeroVec();
+ gLastHitObjectOffset.zeroVec();
+ gLastHitObjectID.setNull();
+ gLastHitObjectFace = -1;
+
+ gLastHitNonFloraPosGlobal.zeroVec();
+ gLastHitNonFloraObjectOffset.zeroVec();
+ gLastHitNonFloraObjectID.setNull();
+ gLastHitNonFloraObjectFace = -1;
+
+ gLastHitParcelWall = FALSE;
+
+ LLCamera pick_camera;
+ pick_camera.setOrigin(gCamera->getOrigin());
+ pick_camera.setOriginAndLookAt(gCamera->getOrigin(),
+ gCamera->getUpAxis(),
+ gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
+ pick_camera.setView(0.5f*DEG_TO_RAD);
+ pick_camera.setNear(gCamera->getNear());
+ pick_camera.setFar(gCamera->getFar());
+ pick_camera.setAspect(1.f);
+
+ // save our drawing state
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // build perspective transform and picking viewport
+ // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
+ // Don't limit the select distance for this pick.
+ // make viewport big enough to handle antialiased frame buffers
+ gCamera->setPerspective(FOR_SELECTION, scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4, FALSE);
+ pick_camera.calcAgentFrustumPlanes(gCamera->mAgentFrustum);
+ // make viewport big enough to handle antialiased frame buffers
+ glViewport(scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4);
+ stop_glerror();
+
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ // Draw the objects so the user can select them.
+ // The starting ID is 1, since land is zero.
+ gObjectList.renderObjectsForSelect(pick_camera, pick_parcel_walls);
+
+ stop_glerror();
+
+ // restore drawing state
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ setupViewport();
+
+ mPickPoint.set(x, y_from_bot);
+ mPickOffset.set(0, 0);
+ mPickMask = mask;
+ mPickPending = TRUE;
+
+ // delay further event processing until we receive results of pick
+ mWindow->delayInputProcessing();
+}
+
+void LLViewerWindow::hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask))
+{
+ // Performs the GL UI pick.
+ // Stores its results in global, gLastHitUIElement
+ if (gNoRender)
+ {
+ return;
+ }
+
+ hitUIElementAsync(x, y, gKeyboard->currentMask(TRUE), NULL);
+ performPick();
+ if (callback)
+ {
+ callback(x, y, gKeyboard->currentMask(TRUE));
+ }
+}
+
+//RN: this currently doesn't do anything
+void LLViewerWindow::hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask))
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+// F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32();
+
+ gUseGLPick = FALSE;
+ mPickCallback = callback;
+
+ // Default to not hitting anything
+ gLastHitUIElement = 0;
+
+ LLCamera pick_camera;
+ pick_camera.setOrigin(gCamera->getOrigin());
+ pick_camera.setOriginAndLookAt(gCamera->getOrigin(),
+ gCamera->getUpAxis(),
+ gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
+ pick_camera.setView(0.5f*DEG_TO_RAD);
+ pick_camera.setNear(gCamera->getNear());
+ pick_camera.setFar(gCamera->getFar());
+ pick_camera.setAspect(1.f);
+
+ // save our drawing state
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // build orthogonal transform and picking viewport
+ // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
+ // Don't limit the select distance for this pick.
+ gViewerWindow->setup2DRender();
+ const LLVector2& display_scale = gViewerWindow->getDisplayScale();
+ glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // make viewport big enough to handle antialiased frame buffers
+ glViewport(x - (PICK_HALF_WIDTH + 2), y_from_bot - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4);
+ stop_glerror();
+
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ // Draw the objects so the user can select them.
+ // The starting ID is 1, since land is zero.
+ //gViewerWindow->drawForSelect();
+
+ stop_glerror();
+
+ // restore drawing state
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ setupViewport();
+
+ mPickPoint.set(x, y_from_bot);
+ mPickOffset.set(0, 0);
+ mPickMask = mask;
+ mPickPending = TRUE;
+}
+
+void LLViewerWindow::performPick()
+{
+ if (gNoRender || !mPickPending)
+ {
+ return;
+ }
+
+ finishFastFrame();
+
+ mPickPending = FALSE;
+ U32 te_offset = NO_FACE;
+
+ // find pick region that is fully onscreen
+ LLCoordGL scaled_pick_point = mPickPoint;
+ scaled_pick_point.mX = llclamp(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayWidth() - PICK_HALF_WIDTH);
+ scaled_pick_point.mY = llclamp(llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayHeight() - PICK_HALF_WIDTH);
+
+ glReadPixels(scaled_pick_point.mX - PICK_HALF_WIDTH, scaled_pick_point.mY - PICK_HALF_WIDTH, PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+
+ S32 pixel_index = PICK_HALF_WIDTH * PICK_DIAMETER + PICK_HALF_WIDTH;
+ S32 name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+
+ if (name >= (S32)GL_NAME_UI_RESERVED && name < (S32)GL_NAME_INDEX_OFFSET)
+ {
+ // hit a UI element
+ gLastHitUIElement = name;
+ if (mPickCallback)
+ {
+ mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
+ }
+ }
+
+ //imdebug("rgba rbga=bbba b=8 w=%d h=%d %p", PICK_DIAMETER, PICK_DIAMETER, mPickBuffer);
+
+ S32 x_offset = mPickPoint.mX - llround((F32)scaled_pick_point.mX / mDisplayScale.mV[VX]);
+ S32 y_offset = mPickPoint.mY - llround((F32)scaled_pick_point.mY / mDisplayScale.mV[VY]);
+
+
+ // we hit nothing, scan surrounding pixels for something useful
+ if (!name)
+ {
+ S32 closest_distance = 10000;
+ //S32 closest_pick_name = 0;
+ for (S32 col = 0; col < PICK_DIAMETER; col++)
+ {
+ for (S32 row = 0; row < PICK_DIAMETER; row++)
+ {
+ S32 distance_squared = (llabs(col - x_offset - PICK_HALF_WIDTH) * llabs(col - x_offset - PICK_HALF_WIDTH)) + (llabs(row - y_offset - PICK_HALF_WIDTH) * llabs(row - y_offset - PICK_HALF_WIDTH));
+ pixel_index = row * PICK_DIAMETER + col;
+ S32 test_name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+ if (test_name && distance_squared < closest_distance)
+ {
+ closest_distance = distance_squared;
+ name = test_name;
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+ mPickOffset.mX = col - PICK_HALF_WIDTH;
+ mPickOffset.mY = row - PICK_HALF_WIDTH;
+ }
+ }
+ }
+ }
+
+ if (name)
+ {
+ mPickPoint.mX += llround((F32)mPickOffset.mX * mDisplayScale.mV[VX]);
+ mPickPoint.mY += llround((F32)mPickOffset.mY * mDisplayScale.mV[VY]);
+ }
+
+ if (gPickFaces)
+ {
+ te_offset = ((U32)name >> 20);
+ name &= 0x000fffff;
+ // don't clear gPickFaces, as we still need to check for UV coordinates
+ }
+
+ LLViewerObject *objectp = NULL;
+
+ // Frontmost non-foreground object that isn't trees or grass
+ LLViewerObject* nonflora_objectp = NULL;
+ S32 nonflora_name = -1;
+ S32 nonflora_te_offset = NO_FACE;
+
+ if (name == (S32)GL_NAME_PARCEL_WALL)
+ {
+ gLastHitParcelWall = TRUE;
+ }
+
+ gLastHitHUDIcon = NULL;
+
+ objectp = gObjectList.getSelectedObject(name);
+ if (objectp)
+ {
+ if (objectp->mbCanSelect)
+ {
+ te_offset = (te_offset == 16) ? NO_FACE : te_offset;
+
+ // If the hit object isn't a plant, store it as the frontmost non-flora object.
+ LLPCode pcode = objectp->getPCode();
+ if( (LL_PCODE_LEGACY_GRASS != pcode) &&
+ (LL_PCODE_LEGACY_TREE != pcode) &&
+ (LL_PCODE_TREE_NEW != pcode))
+ {
+ nonflora_objectp = objectp;
+ nonflora_name = name;
+ nonflora_te_offset = te_offset;
+ }
+ }
+ else
+ {
+ //llinfos << "Hit object you can't select" << llendl;
+ }
+ }
+ else
+ {
+ // was this name referring to a hud icon?
+ gLastHitHUDIcon = LLHUDIcon::handlePick(name);
+ }
+
+ analyzeHit(
+ mPickPoint.mX, mPickPoint.mY, objectp, te_offset,
+ &gLastHitObjectID, &gLastHitObjectFace, &gLastHitPosGlobal, &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord );
+
+ if (objectp && !gLastHitObjectID.isNull())
+ {
+ gLastHitObjectOffset = gAgent.calcFocusOffset(objectp, mPickPoint.mX, mPickPoint.mY);
+ }
+
+ if( objectp == nonflora_objectp )
+ {
+ gLastHitNonFloraObjectID = gLastHitObjectID;
+ gLastHitNonFloraObjectFace = gLastHitObjectFace;
+ gLastHitNonFloraPosGlobal = gLastHitPosGlobal;
+ gLastHitNonFloraObjectOffset= gLastHitObjectOffset;
+ }
+ else
+ {
+ analyzeHit( mPickPoint.mX, mPickPoint.mY, nonflora_objectp, nonflora_te_offset,
+ &gLastHitNonFloraObjectID, &gLastHitNonFloraObjectFace, &gLastHitNonFloraPosGlobal,
+ &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord);
+
+ if( nonflora_objectp )
+ {
+ gLastHitNonFloraObjectOffset = gAgent.calcFocusOffset(nonflora_objectp, mPickPoint.mX, mPickPoint.mY);
+ }
+ }
+
+ if (mPickCallback)
+ {
+ mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
+ }
+
+ gPickFaces = FALSE;
+}
+
+// Performs the GL object/land pick.
+// Stores its results in globals, gHit*
+void LLViewerWindow::hitObjectOrLandGlobalImmediate(S32 x, S32 y_from_bot, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ hitObjectOrLandGlobalAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent);
+ performPick();
+ if (callback)
+ {
+ callback(x, y_from_bot, gKeyboard->currentMask(TRUE));
+ }
+}
+
+LLViewerObject* LLViewerWindow::getObjectUnderCursor(const F32 depth)
+{
+ S32 x = getCurrentMouseX();
+ S32 y = getCurrentMouseY();
+
+ LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
+ LLVector3 camera_pos_global = gCamera->getOrigin();
+ LLVector3 pick_end = camera_pos_global + mouse_direction_global * depth;
+ LLVector3 collision_point;
+ return gPipeline.pickObject(camera_pos_global, pick_end, collision_point);
+}
+
+void LLViewerWindow::analyzeHit(
+ S32 x, // input
+ S32 y_from_bot, // input
+ LLViewerObject* objectp, // input
+ U32 te_offset, // input
+ LLUUID* hit_object_id_p,// output
+ S32* hit_face_p, // output
+ LLVector3d* hit_pos_p, // output
+ BOOL* hit_land, // output
+ F32* hit_u_coord, // output
+ F32* hit_v_coord) // output
+{
+ // Clean up inputs
+ S32 face = -1;
+
+ if (te_offset != NO_FACE )
+ {
+ face = te_offset;
+ }
+
+ *hit_land = FALSE;
+
+ if (objectp)
+ {
+ if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH )
+ {
+ // Hit land
+ *hit_land = TRUE;
+
+ // put global position into land_pos
+ LLVector3d land_pos;
+ if (mousePointOnLandGlobal(x, y_from_bot, &land_pos))
+ {
+ *hit_object_id_p = LLUUID::null;
+ *hit_face_p = -1;
+
+ // Fudge the land focus a little bit above ground.
+ *hit_pos_p = land_pos + LLVector3d(0.f, 0.f, 0.1f);
+ //llinfos << "DEBUG Hit Land " << *hit_pos_p << llendl;
+ return;
+ }
+ else
+ {
+ //llinfos << "Hit land but couldn't find position" << llendl;
+ // Fall through to "Didn't hit anything"
+ }
+ }
+ else
+ {
+ *hit_object_id_p = objectp->mID;
+ *hit_face_p = face;
+
+ // Hit an object
+ if (objectp->isAvatar())
+ {
+ *hit_pos_p = gAgent.getPosGlobalFromAgent(((LLVOAvatar*)objectp)->mPelvisp->getWorldPosition());
+ }
+ else if (objectp->mDrawable.notNull())
+ {
+ *hit_pos_p = gAgent.getPosGlobalFromAgent(objectp->getRenderPosition());
+ }
+ else
+ {
+ // regular object
+ *hit_pos_p = objectp->getPositionGlobal();
+ }
+
+ if (gPickFaces && face > -1 &&
+ objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME &&
+ face < objectp->mDrawable->getNumFaces())
+ {
+ // render red-blue gradient to get 1/256 precision
+ // then render green grid to get final 1/4096 precision
+ S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
+ S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
+ const S32 UV_PICK_WIDTH = 41;
+ const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2;
+ U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4];
+ S32 pick_face = ((LLVOVolume*)objectp)->getAllTEsSame() ? 0 : face;
+ LLFace* facep = objectp->mDrawable->getFace(objectp->getFaceIndexOffset() + pick_face);
+ gCamera->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE);
+ glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
+ gPipeline.renderFaceForUVSelect(facep);
+
+ glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer);
+ U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)];
+ *hit_u_coord = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f;
+ *hit_v_coord = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f;
+ }
+ else
+ {
+ *hit_u_coord = 0.f;
+ *hit_v_coord = 0.f;
+ }
+
+ //llinfos << "DEBUG Hit Object " << *hit_pos_p << llendl;
+ return;
+ }
+ }
+
+ // Didn't hit anything.
+ *hit_object_id_p = LLUUID::null;
+ *hit_face_p = -1;
+ *hit_pos_p = LLVector3d::zero;
+ *hit_u_coord = 0.f;
+ *hit_v_coord = 0.f;
+ //llinfos << "DEBUG Hit Nothing " << llendl;
+}
+
+
+void LLViewerWindow::requestFastFrame(LLView *view)
+{
+ if (!mPickPending &&
+ mWindow->getSwapMethod() != LLWindow::SWAP_METHOD_UNDEFINED &&
+ gStartupState >= STATE_STARTED &&
+ gSavedSettings.getBOOL("RenderFastUI") &&
+ !gbCapturing)
+ {
+ if (!mFastFrameTimer.getStarted())
+ {
+ // we're double buffered, so when first requesting a fast ui update
+ // we need to render the scene again so that the front and back buffers
+ // are synced
+ mRenderFullFrame = TRUE;
+ }
+ // calculation new expiration time and reset timer
+ F32 expiration;
+ if (mFastFrameTimer.hasExpired())
+ {
+ expiration = FAST_FRAME_INCREMENT;
+ }
+ else
+ {
+ expiration = llmin(MAX_FAST_FRAME_TIME, mFastFrameTimer.getTimeToExpireF32() + FAST_FRAME_INCREMENT);
+ }
+
+ mFastFrameTimer.start();
+ mFastFrameTimer.setTimerExpirySec(expiration);
+
+ LLView::sFastFrameView = view->getRootMostFastFrameView();
+ if (!LLView::sFastFrameView)
+ {
+ LLView::sFastFrameView = view;
+ }
+ }
+}
+
+
+// Returns unit vector relative to camera
+// indicating direction of point on screen x,y
+LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const
+{
+ // find vertical field of view
+ F32 fov = gCamera->getView();
+
+ // find screen resolution
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ // calculate pixel distance to screen
+ F32 distance = (height / 2.f) / (tan(fov / 2.f));
+
+ // calculate click point relative to middle of screen
+ F32 click_x = x - width / 2.f;
+ F32 click_y = y - height / 2.f;
+
+ // compute mouse vector
+ LLVector3 mouse_vector = distance * gCamera->getAtAxis()
+ - click_x * gCamera->getLeftAxis()
+ + click_y * gCamera->getUpAxis();
+
+ mouse_vector.normVec();
+
+ return mouse_vector;
+}
+
+
+// Returns unit vector relative to camera in camera space
+// indicating direction of point on screen x,y
+LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const
+{
+ // find vertical field of view
+ F32 fov_height = gCamera->getView();
+ F32 fov_width = fov_height * gCamera->getAspect();
+
+ // find screen resolution
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ // calculate click point relative to middle of screen
+ F32 click_x = (((F32)x / (F32)width) - 0.5f) * fov_width * -1.f;
+ F32 click_y = (((F32)y / (F32)height) - 0.5f) * fov_height;
+
+ // compute mouse vector
+ LLVector3 mouse_vector = LLVector3(0.f, 0.f, -1.f);
+ LLQuaternion mouse_rotate;
+ mouse_rotate.setQuat(click_y, click_x, 0.f);
+
+ mouse_vector = mouse_vector * mouse_rotate;
+ // project to z = -1 plane;
+ mouse_vector = mouse_vector * (-1.f / mouse_vector.mV[VZ]);
+
+ return mouse_vector;
+}
+
+
+
+BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y,
+ const LLVector3d &plane_point_global,
+ const LLVector3 &plane_normal_global)
+{
+ LLVector3d mouse_direction_global_d;
+
+ mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y));
+ LLVector3d plane_normal_global_d;
+ plane_normal_global_d.setVec(plane_normal_global);
+ F64 mouse_look_at_scale = (plane_normal_global_d * (plane_point_global - gAgent.getCameraPositionGlobal()))
+ / (plane_normal_global_d * mouse_direction_global_d);
+ point = gAgent.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d;
+
+ return mouse_look_at_scale > 0.0;
+}
+
+
+// Returns global position
+BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_position_global)
+{
+ LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
+ F32 mouse_dir_scale;
+ BOOL hit_land = FALSE;
+ LLViewerRegion *regionp;
+ F32 land_z;
+ const F32 FIRST_PASS_STEP = 1.0f; // meters
+ const F32 SECOND_PASS_STEP = 0.1f; // meters
+ LLVector3d camera_pos_global;
+
+ camera_pos_global = gAgent.getCameraPositionGlobal();
+ LLVector3d probe_point_global;
+ LLVector3 probe_point_region;
+
+ // walk forwards to find the point
+ for (mouse_dir_scale = FIRST_PASS_STEP; mouse_dir_scale < gAgent.mDrawDistance; mouse_dir_scale += FIRST_PASS_STEP)
+ {
+ LLVector3d mouse_direction_global_d;
+ mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
+ probe_point_global = camera_pos_global + mouse_direction_global_d;
+
+ regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global);
+
+ if (!regionp)
+ {
+ // ...we're outside the world somehow
+ continue;
+ }
+
+ S32 i = (S32) (probe_point_region.mV[VX]/regionp->getLand().getMetersPerGrid());
+ S32 j = (S32) (probe_point_region.mV[VY]/regionp->getLand().getMetersPerGrid());
+ S32 grids_per_edge = (S32) regionp->getLand().mGridsPerEdge;
+ if ((i >= grids_per_edge) || (j >= grids_per_edge))
+ {
+ //llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
+ continue;
+ }
+
+ land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
+
+ //llinfos << "mousePointOnLand initial z " << land_z << llendl;
+
+ if (probe_point_region.mV[VZ] < land_z)
+ {
+ // ...just went under land
+
+ // cout << "under land at " << probe_point << " scale " << mouse_vec_scale << endl;
+
+ hit_land = TRUE;
+ break;
+ }
+ }
+
+
+ if (hit_land)
+ {
+ // Don't go more than one step beyond where we stopped above.
+ // This can't just be "mouse_vec_scale" because floating point error
+ // will stop the loop before the last increment.... X - 1.0 + 0.1 + 0.1 + ... + 0.1 != X
+ F32 stop_mouse_dir_scale = mouse_dir_scale + FIRST_PASS_STEP;
+
+ // take a step backwards, then walk forwards again to refine position
+ for ( mouse_dir_scale -= FIRST_PASS_STEP; mouse_dir_scale <= stop_mouse_dir_scale; mouse_dir_scale += SECOND_PASS_STEP)
+ {
+ LLVector3d mouse_direction_global_d;
+ mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
+ probe_point_global = camera_pos_global + mouse_direction_global_d;
+
+ regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global);
+
+ if (!regionp)
+ {
+ // ...we're outside the world somehow
+ continue;
+ }
+
+ /*
+ i = (S32) (local_probe_point.mV[VX]/regionp->getLand().getMetersPerGrid());
+ j = (S32) (local_probe_point.mV[VY]/regionp->getLand().getMetersPerGrid());
+ if ((i >= regionp->getLand().mGridsPerEdge) || (j >= regionp->getLand().mGridsPerEdge))
+ {
+ // llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
+ continue;
+ }
+ land_z = regionp->getLand().mSurfaceZ[ i + j * (regionp->getLand().mGridsPerEdge) ];
+ */
+
+ land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
+
+ //llinfos << "mousePointOnLand refine z " << land_z << llendl;
+
+ if (probe_point_region.mV[VZ] < land_z)
+ {
+ // ...just went under land again
+
+ *land_position_global = probe_point_global;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+// Saves an image to the harddrive as "SnapshotX" where X >= 1.
+BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw)
+{
+ if (! raw)
+ {
+ return FALSE;
+ }
+
+ // Get a directory if this is the first time.
+ if (strlen(sSnapshotDir) == 0)
+ {
+ LLString proposed_name( sSnapshotBaseName );
+ proposed_name.append( ".bmp" );
+
+ // pick a directory in which to save
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_BMP, proposed_name.c_str()))
+ {
+ // Clicked cancel
+ return FALSE;
+ }
+
+ // Copy the directory + file name
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ // Smash the file extension
+ S32 length = strlen(directory);
+ S32 index = length;
+
+ // Back up over ".bmp"
+ index -= 4;
+ if (index >= 0 && directory[index] == '.')
+ {
+ directory[index] = '\0';
+ }
+ else
+ {
+ index = length;
+ }
+
+ // Find trailing backslash
+ while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0])
+ {
+ index--;
+ }
+
+ // If we found one, truncate the string there
+ if (index >= 0)
+ {
+ if (index + 1 <= length)
+ {
+ strcpy(LLViewerWindow::sSnapshotBaseName, directory + index + 1);
+ }
+
+ index++;
+ directory[index] = '\0';
+ strcpy(LLViewerWindow::sSnapshotDir, directory);
+ }
+ }
+
+ // Look for an unused file name
+ LLString filepath;
+ S32 i = 1;
+ S32 err = 0;
+
+ do
+ {
+ char extension[100];
+ sprintf( extension, "_%.3d.bmp", i );
+ filepath = sSnapshotDir;
+ filepath += sSnapshotBaseName;
+ filepath += extension;
+
+ struct stat stat_info;
+ err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info );
+ i++;
+ }
+ while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
+
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+ LLImageBase::setSizeOverride(TRUE);
+ BOOL success = bmp_image->encode(raw);
+ if( success )
+ {
+ success = bmp_image->save(filepath);
+ }
+ else
+ {
+ llwarns << "Unable to encode bmp snapshot" << llendl;
+ }
+ LLImageBase::setSizeOverride(FALSE);
+
+ return success;
+}
+
+void LLViewerWindow::saveMovieNumbered(void*)
+{
+ if (!gbCapturing)
+ {
+ // Get a directory if this is the first time.
+ if (strlen(sSnapshotDir) == 0)
+ {
+ LLString proposed_name( sMovieBaseName );
+#if LL_DARWIN
+ proposed_name.append( ".mov" );
+#else
+ proposed_name.append( ".avi" );
+#endif
+
+ // pick a directory in which to save
+ LLFilePicker &picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_AVI, proposed_name.c_str()))
+ {
+ // Clicked cancel
+ return;
+ }
+
+ // Copy the directory + file name
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ // Smash the file extension
+ S32 length = strlen(directory);
+ S32 index = length;
+
+ // Back up over ".bmp"
+ index -= 4;
+ if (index >= 0 && directory[index] == '.')
+ {
+ directory[index] = '\0';
+ }
+ else
+ {
+ index = length;
+ }
+
+ // Find trailing backslash
+ while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0])
+ {
+ index--;
+ }
+
+ // If we found one, truncate the string there
+ if (index >= 0)
+ {
+ if (index + 1 <= length)
+ {
+ strcpy(LLViewerWindow::sMovieBaseName, directory + index + 1);
+ }
+
+ index++;
+ directory[index] = '\0';
+ strcpy(LLViewerWindow::sSnapshotDir, directory);
+ }
+ }
+
+ // Look for an unused file name
+ LLString filepath;
+ S32 i = 1;
+ S32 err = 0;
+
+ do
+ {
+ char extension[100];
+#if LL_DARWIN
+ sprintf( extension, "_%.3d.mov", i );
+#else
+ sprintf( extension, "_%.3d.avi", i );
+#endif
+ filepath.assign( sSnapshotDir );
+ filepath.append( sMovieBaseName );
+ filepath.append( extension );
+
+ struct stat stat_info;
+ err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info );
+ i++;
+ }
+ while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
+ S32 x = gViewerWindow->getWindowWidth();
+ S32 y = gViewerWindow->getWindowHeight();
+
+ gbCapturing = TRUE;
+ gMovieMaker.StartCapture((char *)filepath.c_str(), x, y);
+ }
+ else
+ {
+ gMovieMaker.EndCapture();
+ gbCapturing = FALSE;
+ }
+}
+
+static S32 BORDERHEIGHT = 0;
+static S32 BORDERWIDTH = 0;
+
+void LLViewerWindow::movieSize(S32 new_width, S32 new_height)
+{
+ LLCoordScreen size;
+ gViewerWindow->mWindow->getSize(&size);
+ if ( (size.mX != new_width + BORDERWIDTH)
+ ||(size.mY != new_height + BORDERHEIGHT))
+ {
+ S32 x = gViewerWindow->getWindowWidth();
+ S32 y = gViewerWindow->getWindowHeight();
+ BORDERWIDTH = size.mX - x;
+ BORDERHEIGHT = size.mY- y;
+ LLCoordScreen new_size(new_width + BORDERWIDTH,
+ new_height + BORDERHEIGHT);
+ BOOL disable_sync = gSavedSettings.getBOOL("DisableVerticalSync");
+ if (gViewerWindow->mWindow->getFullscreen())
+ {
+ gViewerWindow->changeDisplaySettings(FALSE,
+ new_size,
+ disable_sync,
+ TRUE);
+ }
+ else
+ {
+ gViewerWindow->mWindow->setSize(new_size);
+ }
+ }
+}
+
+BOOL LLViewerWindow::saveSnapshot( const LLString& filepath, S32 image_width, S32 image_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
+{
+ llinfos << "Saving snapshot to: " << filepath << llendl;
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, show_ui, do_rebuild);
+
+ if (success)
+ {
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+ success = bmp_image->encode(raw);
+ if( success )
+ {
+ success = bmp_image->save(filepath);
+ }
+ else
+ {
+ llwarns << "Unable to encode bmp snapshot" << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "Unable to capture raw snapshot" << llendl;
+ }
+
+ return success;
+}
+
+
+void LLViewerWindow::playSnapshotAnimAndSound()
+{
+ gAgent.sendAnimationRequest(ANIM_AGENT_SNAPSHOT, ANIM_REQUEST_START);
+ send_sound_trigger(LLUUID(gSavedSettings.getString("UISndSnapshot")), 1.0f);
+}
+
+
+// Saves the image from the screen to the specified filename and path.
+BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height,
+ BOOL keep_window_aspect, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
+{
+ F32 image_aspect_ratio = ((F32)image_width) / ((F32)image_height);
+ F32 window_aspect_ratio = ((F32)getWindowWidth()) / ((F32)getWindowHeight());
+
+ if ((!gWorldPointer) ||
+ (!raw))
+ {
+ return FALSE;
+ }
+
+ // IW 3/5/04 We don'a wan' nunna yer fest frumes har!
+ finishFastFrame();
+
+ // PRE SNAPSHOT
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ setCursor(UI_CURSOR_WAIT);
+
+ // Hide all the UI widgets first and draw a frame
+ BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI);
+
+ if ( prev_draw_ui != show_ui)
+ {
+ LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
+ }
+
+ BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD);
+ if (hide_hud)
+ {
+ LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+ }
+
+ // Copy screen to a buffer
+ // crop sides or top and bottom, if taking a snapshot of different aspect ratio
+ // from window
+ S32 snapshot_width = mWindowRect.getWidth();
+ S32 snapshot_height = mWindowRect.getHeight();
+ if (!keep_window_aspect)
+ {
+ if (image_aspect_ratio > window_aspect_ratio)
+ {
+ snapshot_height = llround((F32)snapshot_width / image_aspect_ratio);
+ }
+ else if (image_aspect_ratio < window_aspect_ratio)
+ {
+ snapshot_width = llround((F32)snapshot_height * image_aspect_ratio);
+ }
+ }
+
+ F32 scale_factor = llmax(1.f, (F32)image_width / snapshot_width, (F32)image_height / snapshot_height);
+ raw->resize(llfloor(snapshot_width*scale_factor), llfloor(snapshot_height *scale_factor), type == SNAPSHOT_TYPE_DEPTH ? 4 : 3);
+
+ BOOL high_res = scale_factor > 1.f;
+ if (high_res)
+ {
+ send_agent_pause();
+ //rescale fonts
+ initFonts(scale_factor);
+ LLHUDText::reshape();
+ }
+
+ // SNAPSHOT
+ S32 window_width = mWindowRect.getWidth();
+ S32 window_height = mWindowRect.getHeight();
+ S32 buffer_x_offset = llfloor(((window_width - snapshot_width) * scale_factor) / 2.f);
+ S32 buffer_y_offset = llfloor(((window_height - snapshot_height) * scale_factor) / 2.f);
+
+ S32 output_buffer_offset_y = 0;
+
+ F32 depth_conversion_factor_1 = (gCamera->getFar() + gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear());
+ F32 depth_conversion_factor_2 = (gCamera->getFar() - gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear());
+
+ for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y)
+ {
+ S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);;
+ // handle fractional columns
+ U32 read_height = llmax(0, (window_height - subimage_y_offset) -
+ llmax(0, (window_height * (subimage_y + 1)) - (buffer_y_offset + raw->getHeight())));
+
+ S32 output_buffer_offset_x = 0;
+ for (int subimage_x = 0; subimage_x < scale_factor; ++subimage_x)
+ {
+ gDisplaySwapBuffers = FALSE;
+ if (type == SNAPSHOT_TYPE_OBJECT_ID)
+ {
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ gCamera->setZoomParameters(scale_factor, subimage_x+(subimage_y*llceil(scale_factor)));
+ setup3DRender();
+ setupViewport();
+ BOOL first_time_through = (subimage_x + subimage_y == 0);
+ gPickTransparent = FALSE;
+ gPickAlphaThreshold = 0.1f;
+ gObjectList.renderObjectsForSelect(*gCamera, FALSE, !first_time_through);
+ }
+ else
+ {
+ display(do_rebuild, scale_factor, subimage_x+(subimage_y*llceil(scale_factor)));
+ }
+ glFlush();
+ S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width);
+ // handle fractional rows
+ U32 read_width = llmax(0, (window_width - subimage_x_offset) -
+ llmax(0, (window_width * (subimage_x + 1)) - (buffer_x_offset + raw->getWidth())));
+ for(U32 out_y = 0; out_y < read_height ; out_y++)
+ {
+ if (type == SNAPSHOT_TYPE_OBJECT_ID || type == SNAPSHOT_TYPE_COLOR)
+ {
+ glReadPixels(
+ subimage_x_offset, out_y + subimage_y_offset,
+ read_width, 1,
+ GL_RGB, GL_UNSIGNED_BYTE,
+ raw->getData() + // current output pixel is beginning of buffer...
+ (
+ (out_y * (raw->getWidth())) // ...plus iterated y...
+ + (window_width * subimage_x) // ...plus subimage start in x...
+ + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y...
+ - output_buffer_offset_x // ...minus buffer padding x...
+ - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y...
+ ) * 3 // times 3 bytes per pixel
+ );
+ }
+ else // SNAPSHOT_TYPE_DEPTH
+ {
+ S32 output_buffer_offset = (
+ (out_y * (raw->getWidth())) // ...plus iterated y...
+ + (window_width * subimage_x) // ...plus subimage start in x...
+ + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y...
+ - output_buffer_offset_x // ...minus buffer padding x...
+ - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y...
+ ) * 4; // times 4 bytes per pixel
+
+ glReadPixels(
+ subimage_x_offset, out_y + subimage_y_offset,
+ read_width, 1,
+ GL_DEPTH_COMPONENT, GL_FLOAT,
+ raw->getData() + output_buffer_offset// current output pixel is beginning of buffer...
+ );
+
+ for (S32 i = output_buffer_offset; i < output_buffer_offset + (S32)read_width * 4; i += 4)
+ {
+ F32 depth_float = *(F32*)(raw->getData() + i);
+
+ F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float * depth_conversion_factor_2));
+ U8 depth_byte = F32_to_U8(linear_depth_float, gCamera->getNear(), gCamera->getFar());
+ *(raw->getData() + i + 0) = depth_byte;
+ *(raw->getData() + i + 1) = depth_byte;
+ *(raw->getData() + i + 2) = depth_byte;
+ *(raw->getData() + i + 3) = 255;
+ }
+ }
+ }
+ output_buffer_offset_x += subimage_x_offset;
+ stop_glerror();
+ }
+ output_buffer_offset_y += subimage_y_offset;
+ }
+
+ // POST SNAPSHOT
+ if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
+ {
+ LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
+ }
+
+ if (hide_hud)
+ {
+ LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+ }
+
+ if (high_res)
+ {
+ initFonts(1.f);
+ LLHUDText::reshape();
+ }
+
+ gDisplaySwapBuffers = TRUE;
+
+ // Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding)
+ // Note: this formula depends on the number of components being 3. Not obvious, but it's correct.
+ image_width += (image_width * (type == SNAPSHOT_TYPE_DEPTH ? 4 : 3)) % 4;
+
+ // Resize image
+ raw->scale( image_width, image_height );
+
+ setCursor(UI_CURSOR_ARROW);
+
+ if (do_rebuild)
+ {
+ // If we had to do a rebuild, that means that the lists of drawables to be rendered
+ // was empty before we started.
+ // Need to reset these, otherwise we call state sort on it again when render gets called the next time
+ // and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of
+ // objects on them.
+ gPipeline.resetDrawOrders();
+ }
+
+ if (high_res)
+ {
+ send_agent_resume();
+ }
+
+ return TRUE;
+}
+
+void LLViewerWindow::destroyWindow()
+{
+ if (mWindow)
+ {
+ LLWindowManager::destroyWindow(mWindow);
+ }
+ mWindow = NULL;
+}
+
+
+void LLViewerWindow::drawMouselookInstructions()
+{
+ // Draw instructions for mouselook ("Press ESC to leave Mouselook" in a box at the top of the screen.)
+ const char* instructions = "Press ESC to leave Mouselook.";
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ const S32 INSTRUCTIONS_PAD = 5;
+ LLRect instructions_rect;
+ instructions_rect.setLeftTopAndSize(
+ INSTRUCTIONS_PAD,
+ gViewerWindow->getWindowHeight() - INSTRUCTIONS_PAD,
+ font->getWidth( instructions ) + 2 * INSTRUCTIONS_PAD,
+ llround(font->getLineHeight() + 2 * INSTRUCTIONS_PAD));
+
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4f( 0.9f, 0.9f, 0.9f, 1.0f );
+ gl_rect_2d( instructions_rect );
+ }
+
+ font->renderUTF8(
+ instructions, 0,
+ instructions_rect.mLeft + INSTRUCTIONS_PAD,
+ instructions_rect.mTop - INSTRUCTIONS_PAD,
+ LLColor4( 0.0f, 0.0f, 0.0f, 1.f ),
+ LLFontGL::LEFT, LLFontGL::TOP);
+}
+
+
+// These functions are here only because LLViewerWindow used to do the work that gFocusMgr does now.
+// They let other objects continue to work without change.
+
+void LLViewerWindow::setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus))
+{
+ gFocusMgr.setKeyboardFocus( new_focus, on_focus_lost );
+}
+
+LLUICtrl* LLViewerWindow::getKeyboardFocus()
+{
+ return gFocusMgr.getKeyboardFocus();
+}
+
+BOOL LLViewerWindow::hasKeyboardFocus(const LLUICtrl* possible_focus) const
+{
+ return possible_focus == gFocusMgr.getKeyboardFocus();
+}
+
+BOOL LLViewerWindow::childHasKeyboardFocus(const LLView* parent) const
+{
+ return gFocusMgr.childHasKeyboardFocus( parent );
+}
+
+void LLViewerWindow::setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor))
+{
+ gFocusMgr.setMouseCapture( new_captor, on_capture_lost );
+}
+
+LLMouseHandler* LLViewerWindow::getMouseCaptor() const
+{
+ return gFocusMgr.getMouseCapture();
+}
+
+BOOL LLViewerWindow::hasMouseCapture(const LLMouseHandler* possible_captor) const
+{
+ return possible_captor == gFocusMgr.getMouseCapture();
+}
+
+S32 LLViewerWindow::getWindowHeight() const
+{
+ return mVirtualWindowRect.getHeight();
+}
+
+S32 LLViewerWindow::getWindowWidth() const
+{
+ return mVirtualWindowRect.getWidth();
+}
+
+S32 LLViewerWindow::getWindowDisplayHeight() const
+{
+ return mWindowRect.getHeight();
+}
+
+S32 LLViewerWindow::getWindowDisplayWidth() const
+{
+ return mWindowRect.getWidth();
+}
+
+LLView* LLViewerWindow::getTopView() const
+{
+ return gFocusMgr.getTopView();
+}
+
+BOOL LLViewerWindow::hasTopView(LLView* view) const
+{
+ return view == gFocusMgr.getTopView();
+}
+
+void LLViewerWindow::setTopView(LLView* new_top,void (*on_top_lost)(LLView* old_top))
+{
+ gFocusMgr.setTopView( new_top, on_top_lost );
+}
+
+void LLViewerWindow::setupViewport(S32 x_offset, S32 y_offset)
+{
+ glViewport(x_offset, y_offset, mWindowRect.getWidth(), mWindowRect.getHeight());
+}
+
+void LLViewerWindow::setup3DRender()
+{
+ gCamera->setPerspective(NOT_FOR_SELECTION, 0, 0, mWindowRect.getWidth(), mWindowRect.getHeight(), FALSE, gCamera->getNear(), MAX_FAR_PLANE);
+}
+
+void LLViewerWindow::setup2DRender()
+{
+ gl_state_for_2d(mWindowRect.getWidth(), mWindowRect.getHeight());
+}
+
+// Could cache the pointer from the last hitObjectOrLand here.
+LLViewerObject *LLViewerWindow::lastObjectHit()
+{
+ return gObjectList.findObject( gLastHitObjectID );
+}
+
+const LLVector3d& LLViewerWindow::lastObjectHitOffset()
+{
+ return gLastHitObjectOffset;
+}
+
+// Could cache the pointer from the last hitObjectOrLand here.
+LLViewerObject *LLViewerWindow::lastNonFloraObjectHit()
+{
+ return gObjectList.findObject( gLastHitNonFloraObjectID );
+}
+
+const LLVector3d& LLViewerWindow::lastNonFloraObjectHitOffset()
+{
+ return gLastHitNonFloraObjectOffset;
+}
+
+
+void LLViewerWindow::setShowProgress(const BOOL show)
+{
+ if (mProgressView)
+ {
+ mProgressView->setVisible(show);
+ }
+}
+
+BOOL LLViewerWindow::getShowProgress() const
+{
+ return (mProgressView && mProgressView->getVisible());
+}
+
+
+void LLViewerWindow::moveProgressViewToFront()
+{
+ if( mProgressView && mRootView )
+ {
+ mRootView->removeChild( mProgressView );
+ mRootView->addChild( mProgressView );
+ }
+}
+
+void LLViewerWindow::setProgressString(const LLString& string)
+{
+ if (mProgressView)
+ {
+ mProgressView->setText(string);
+ }
+}
+
+void LLViewerWindow::setProgressMessage(const LLString& msg)
+{
+ if(mProgressView)
+ {
+ mProgressView->setMessage(msg);
+ }
+}
+
+void LLViewerWindow::setProgressPercent(const F32 percent)
+{
+ if (mProgressView)
+ {
+ mProgressView->setPercent(percent);
+ }
+}
+
+void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const LLString& label )
+{
+ if (mProgressView)
+ {
+ mProgressView->setCancelButtonVisible( b, label );
+ }
+}
+
+
+LLProgressView *LLViewerWindow::getProgressView() const
+{
+ return mProgressView;
+}
+
+void LLViewerWindow::dumpState()
+{
+ llinfos << "LLViewerWindow Active " << S32(mActive) << llendl;
+ llinfos << "mWindow visible " << S32(mWindow->getVisible())
+ << " minimized " << S32(mWindow->getMinimized())
+ << llendl;
+}
+
+void LLViewerWindow::stopGL(BOOL save_state)
+{
+ if (!gGLManager.mIsDisabled)
+ {
+ llinfos << "Shutting down GL..." << llendl;
+ gSky.destroyGL();
+ stop_glerror();
+
+ gImageList.destroyGL(save_state);
+ stop_glerror();
+
+ gBumpImageList.destroyGL();
+ stop_glerror();
+
+ LLFontGL::destroyGL();
+ stop_glerror();
+
+ LLVOAvatar::destroyGL();
+ stop_glerror();
+
+ LLDynamicTexture::destroyGL();
+ stop_glerror();
+
+ if(gParcelMgr) gParcelMgr->destroyGL();
+
+ gPipeline.destroyGL();
+
+ gCone.cleanupGL();
+ gBox.cleanupGL();
+ gSphere.cleanupGL();
+ gCylinder.cleanupGL();
+
+ gGLManager.mIsDisabled = TRUE;
+ stop_glerror();
+
+ llinfos << "Remaining allocated texture memory: " << LLImageGL::sGlobalTextureMemory << " bytes" << llendl;
+ }
+}
+
+void LLViewerWindow::restoreGL(const LLString& progress_message)
+{
+ if (gGLManager.mIsDisabled)
+ {
+ llinfos << "Restoring GL..." << llendl;
+ gGLManager.mIsDisabled = FALSE;
+
+ // for future support of non-square pixels, and fonts that are properly stretched
+ //LLFontGL::destroyDefaultFonts();
+ initFonts();
+ initGLDefaults();
+ LLGLState::restoreGL();
+ gSky.restoreGL();
+ gPipeline.restoreGL();
+ LLDrawPoolWater::restoreGL();
+ LLManipTranslate::restoreGL();
+ gImageList.restoreGL();
+ gBumpImageList.restoreGL();
+ gPipeline.setUseAGP(gSavedSettings.getBOOL("RenderUseAGP"));
+ LLDynamicTexture::restoreGL();
+ LLVOAvatar::restoreGL();
+ if (gParcelMgr) gParcelMgr->restoreGL();
+
+ if (gFloaterCustomize && gFloaterCustomize->getVisible())
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+
+ if (!progress_message.empty())
+ {
+ gRestoreGLTimer.reset();
+ gRestoreGL = TRUE;
+ setShowProgress(TRUE);
+ setProgressString(progress_message);
+ }
+ llinfos << "...Restoring GL done" << llendl;
+#if LL_WINDOWS
+ if (SetUnhandledExceptionFilter(LLWinDebug::handleException) != LLWinDebug::handleException)
+ {
+ llwarns << " Someone took over my exception handler (post restoreGL)!" << llendl;
+ }
+#endif
+
+ }
+}
+
+void LLViewerWindow::initFonts(F32 zoom_factor)
+{
+ LLFontGL::destroyGL();
+ LLFontGL::initDefaultFonts( gSavedSettings.getF32("FontScreenDPI"),
+ mDisplayScale.mV[VX] * zoom_factor,
+ mDisplayScale.mV[VY] * zoom_factor,
+ gSavedSettings.getString("FontMonospace"),
+ gSavedSettings.getF32("FontSizeMonospace"),
+ gSavedSettings.getString("FontSansSerif"),
+ gSavedSettings.getString("FontSansSerifFallback"),
+ gSavedSettings.getF32("FontSansSerifFallbackScale"),
+ gSavedSettings.getF32("FontSizeSmall"),
+ gSavedSettings.getF32("FontSizeMedium"),
+ gSavedSettings.getF32("FontSizeLarge"),
+ gSavedSettings.getF32("FontSizeHuge"),
+ gSavedSettings.getString("FontSansSerifBold"),
+ gSavedSettings.getF32("FontSizeMedium"),
+ gDirUtilp->getAppRODataDir()
+ );
+}
+void LLViewerWindow::toggleFullscreen(BOOL show_progress)
+{
+ if (mWindow)
+ {
+ mWantFullscreen = mWindow->getFullscreen() ? FALSE : TRUE;
+ mShowFullscreenProgress = show_progress;
+ }
+}
+
+void LLViewerWindow::getTargetWindow(BOOL& fullscreen, S32& width, S32& height) const
+{
+ fullscreen = mWantFullscreen;
+
+ if (gViewerWindow->mWindow
+ && gViewerWindow->mWindow->getFullscreen() == mWantFullscreen)
+ {
+ width = gViewerWindow->getWindowDisplayWidth();
+ height = gViewerWindow->getWindowDisplayHeight();
+ }
+ else if (mWantFullscreen)
+ {
+ width = gSavedSettings.getS32("FullScreenWidth");
+ height = gSavedSettings.getS32("FullScreenHeight");
+ }
+ else
+ {
+ width = gSavedSettings.getS32("WindowWidth");
+ height = gSavedSettings.getS32("WindowHeight");
+ }
+}
+
+
+BOOL LLViewerWindow::checkSettings()
+{
+ BOOL is_fullscreen = gViewerWindow->mWindow->getFullscreen();
+ if (is_fullscreen && !mWantFullscreen)
+ {
+ gViewerWindow->changeDisplaySettings(FALSE,
+ LLCoordScreen(gSavedSettings.getS32("WindowWidth"),
+ gSavedSettings.getS32("WindowHeight")),
+ TRUE,
+ mShowFullscreenProgress);
+ return TRUE;
+ }
+ else if (!is_fullscreen && mWantFullscreen)
+ {
+ if (!LLStartUp::canGoFullscreen())
+ {
+ return FALSE;
+ }
+
+ gViewerWindow->changeDisplaySettings(TRUE,
+ LLCoordScreen(gSavedSettings.getS32("FullScreenWidth"),
+ gSavedSettings.getS32("FullScreenHeight")),
+ gSavedSettings.getBOOL("DisableVerticalSync"),
+ mShowFullscreenProgress);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLViewerWindow::restartDisplay(BOOL show_progress_bar)
+{
+ llinfos << "Restaring GL" << llendl;
+ stopGL();
+ if (show_progress_bar)
+ {
+ restoreGL("Changing Resolution...");
+ }
+ else
+ {
+ restoreGL();
+ }
+}
+
+BOOL LLViewerWindow::changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar)
+{
+ BOOL was_maximized = gSavedSettings.getBOOL("WindowMaximized");
+ mWantFullscreen = fullscreen;
+ mShowFullscreenProgress = show_progress_bar;
+ gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
+
+ BOOL old_fullscreen = mWindow->getFullscreen();
+ if (!old_fullscreen && fullscreen && !LLStartUp::canGoFullscreen())
+ {
+ // we can't do this now, so do it later
+
+ gSavedSettings.setS32("FullScreenWidth", size.mX);
+ gSavedSettings.setS32("FullScreenHeight", size.mY);
+ //gSavedSettings.setBOOL("DisableVerticalSync", disable_vsync);
+
+ return TRUE; // a lie..., because we'll get to it later
+ }
+
+ // going from windowed to windowed
+ if (!old_fullscreen && !fullscreen)
+ {
+ // if not maximized, use the request size
+ if (!mWindow->getMaximized())
+ {
+ mWindow->setSize(size);
+ }
+ return TRUE;
+ }
+
+ // Close floaters that don't handle settings change
+ LLFloaterSnapshot::hide(0);
+
+ BOOL result_first_try = FALSE;
+ BOOL result_second_try = FALSE;
+
+ LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ send_agent_pause();
+ llinfos << "Stopping GL during changeDisplaySettings" << llendl;
+ stopGL();
+ mIgnoreActivate = TRUE;
+ LLCoordScreen old_size;
+ LLCoordScreen old_pos;
+ mWindow->getSize(&old_size);
+ BOOL got_position = mWindow->getPosition(&old_pos);
+
+ if (!old_fullscreen && fullscreen && got_position)
+ {
+ // switching from windowed to fullscreen, so save window position
+ gSavedSettings.setS32("WindowX", old_pos.mX);
+ gSavedSettings.setS32("WindowY", old_pos.mY);
+ }
+
+ result_first_try = mWindow->switchContext(fullscreen, size, disable_vsync);
+ if (!result_first_try)
+ {
+ // try to switch back
+ result_second_try = mWindow->switchContext(old_fullscreen, old_size, disable_vsync);
+
+ if (!result_second_try)
+ {
+ // we are stuck...try once again with a minimal resolution?
+ send_agent_resume();
+ mIgnoreActivate = FALSE;
+ return FALSE;
+ }
+ }
+ send_agent_resume();
+
+ llinfos << "Restoring GL during resolution change" << llendl;
+ if (show_progress_bar)
+ {
+ restoreGL("Changing Resolution...");
+ }
+ else
+ {
+ restoreGL();
+ }
+
+ if (!result_first_try)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[RESX]"] = llformat("%d",size.mX);
+ args["[RESY]"] = llformat("%d",size.mY);
+ alertXml("ResolutionSwitchFail", args);
+ size = old_size; // for reshape below
+ }
+
+ BOOL success = result_first_try || result_second_try;
+ if (success)
+ {
+#if LL_WINDOWS
+ // Only trigger a reshape after switching to fullscreen; otherwise rely on the windows callback
+ // (otherwise size is wrong; this is the entire window size, reshape wants the visible window size)
+ if (fullscreen)
+#endif
+ {
+ reshape(size.mX, size.mY);
+ }
+ }
+
+ if (!mWindow->getFullscreen() && success)
+ {
+ // maximize window if was maximized, else reposition
+ if (was_maximized)
+ {
+ mWindow->maximize();
+ }
+ else
+ {
+ S32 windowX = gSavedSettings.getS32("WindowX");
+ S32 windowY = gSavedSettings.getS32("WindowY");
+
+ mWindow->setPosition(LLCoordScreen ( windowX, windowY ) );
+ }
+ }
+
+ mIgnoreActivate = FALSE;
+ gFocusMgr.setKeyboardFocus(keyboard_focus, NULL);
+ mWantFullscreen = mWindow->getFullscreen();
+ mShowFullscreenProgress = FALSE;
+
+ return success;
+}
+
+
+F32 LLViewerWindow::getDisplayAspectRatio() const
+{
+ if (mWindow->getFullscreen())
+ {
+ if (gSavedSettings.getBOOL("FullScreenAutoDetectAspectRatio"))
+ {
+ return mWindow->getNativeAspectRatio();
+ }
+ else
+ {
+ return gSavedSettings.getF32("FullScreenAspectRatio");
+ }
+ }
+ else
+ {
+ return mWindow->getNativeAspectRatio();
+ }
+}
+
+
+void LLViewerWindow::drawPickBuffer() const
+{
+ if (mPickBuffer)
+ {
+ LLGLDisable no_blend(GL_BLEND);
+ LLGLDisable no_alpha_test(GL_ALPHA_TEST);
+ LLGLSNoTexture no_texture;
+ glPixelZoom(10.f, 10.f);
+ glRasterPos2f(((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f),
+ ((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
+ glDrawPixels(PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+ glPixelZoom(1.f, 1.f);
+ glColor4fv(LLColor4::white.mV);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+ FALSE);
+ gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f + 10.f));
+ gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f + 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
+ glTranslatef(10.f, 10.f, 0.f);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]),
+ FALSE);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX)* 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY + 1) * 10.f),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX + 1) * 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY) * 10.f),
+ FALSE);
+ glPopMatrix();
+ }
+}
+
+void LLViewerWindow::calcDisplayScale()
+{
+ F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
+ LLVector2 display_scale;
+ display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
+ F32 height_normalization = gSavedSettings.getBOOL("UIAutoScale") ? ((F32)mWindowRect.getHeight() / display_scale.mV[VY]) / 768.f : 1.f;
+ if(mWindow->getFullscreen())
+ {
+ display_scale *= (ui_scale_factor * height_normalization);
+ }
+ else
+ {
+ display_scale *= ui_scale_factor;
+ }
+
+ // limit minimum display scale
+ if (display_scale.mV[VX] < MIN_DISPLAY_SCALE || display_scale.mV[VY] < MIN_DISPLAY_SCALE)
+ {
+ display_scale *= MIN_DISPLAY_SCALE / llmin(display_scale.mV[VX], display_scale.mV[VY]);
+ }
+
+ if (mWindow->getFullscreen())
+ {
+ display_scale.mV[0] = llround(display_scale.mV[0], 2.0f/(F32) mWindowRect.getWidth());
+ display_scale.mV[1] = llround(display_scale.mV[1], 2.0f/(F32) mWindowRect.getHeight());
+ }
+
+ if (display_scale != mDisplayScale)
+ {
+ llinfos << "Setting display scale to " << display_scale << llendl;
+
+ mDisplayScale = display_scale;
+ // Init default fonts
+ initFonts();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// static
+bool LLViewerWindow::alertCallback(S32 modal)
+{
+ if (gNoRender)
+ {
+ return false;
+ }
+ else
+ {
+// if (modal) // we really always want to take you out of mouselook
+ {
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+ }
+ return true;
+ }
+}
+
+LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename,
+ LLAlertDialog::alert_callback_t callback, void* user_data)
+{
+ LLString::format_map_t args;
+ return alertXml( xml_filename, args, callback, user_data );
+}
+
+LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data)
+{
+ if (gNoRender)
+ {
+ llinfos << "Alert: " << xml_filename << llendl;
+ if (callback)
+ {
+ callback(-1, user_data);
+ }
+ return NULL;
+ }
+
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // Note: object adds, removes, and destroys itself.
+ return LLAlertDialog::showXml( xml_filename, args, callback, user_data );
+}
+
+LLAlertDialog* LLViewerWindow::alertXmlEditText(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data,
+ LLAlertDialog::alert_text_callback_t text_callback, void *text_data,
+ const LLString::format_map_t& edit_args, BOOL draw_asterixes)
+{
+ if (gNoRender)
+ {
+ llinfos << "Alert: " << xml_filename << llendl;
+ if (callback)
+ {
+ callback(-1, user_data);
+ }
+ return NULL;
+ }
+
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // Note: object adds, removes, and destroys itself.
+ LLAlertDialog* alert = LLAlertDialog::createXml( xml_filename, args, callback, user_data );
+ if (alert)
+ {
+ if (text_callback)
+ {
+ alert->setEditTextCallback(text_callback, text_data);
+ }
+ alert->setEditTextArgs(edit_args);
+ alert->setDrawAsterixes(draw_asterixes);
+ alert->show();
+ }
+ return alert;
+}
+
+LLBottomPanel::LLBottomPanel(const LLString &name, const LLRect &rect) :
+ LLPanel(name, rect, FALSE),
+ mIndicator(NULL)
+{
+ // bottom panel is focus root, so Tab moves through the toolbar and button bar, and overlay
+ setFocusRoot(TRUE);
+ // don't capture mouse clicks that don't hit a child
+ setMouseOpaque(FALSE);
+ setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
+ setIsChrome(TRUE);
+}
+
+void LLBottomPanel::setFocusIndicator(LLView * indicator)
+{
+ mIndicator = indicator;
+}
+
+void LLBottomPanel::draw()
+{
+ if(mIndicator)
+ {
+ BOOL hasFocus = gFocusMgr.childHasKeyboardFocus(this);
+ mIndicator->setVisible(hasFocus);
+ mIndicator->setEnabled(hasFocus);
+ }
+ LLPanel::draw();
+}
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
new file mode 100644
index 0000000000..9c8e78a29d
--- /dev/null
+++ b/indra/newview/llviewerwindow.h
@@ -0,0 +1,391 @@
+/**
+ * @file llviewerwindow.h
+ * @brief Description of the LLViewerWindow class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//
+// A note about X,Y coordinates:
+//
+// X coordinates are in pixels, from the left edge of the window client area
+// Y coordinates are in pixels, from the BOTTOM edge of the window client area
+//
+// The Y coordinates therefore match OpenGL window coords, not Windows(tm) window coords.
+// If Y is from the top, the variable will be called "y_from_top"
+
+#ifndef LL_LLVIEWERWINDOW_H
+#define LL_LLVIEWERWINDOW_H
+
+#include "linked_lists.h"
+#include "v3dmath.h"
+#include "v2math.h"
+#include "llwindow.h"
+#include "lltimer.h"
+#include "llstat.h"
+#include "llalertdialog.h"
+
+class LLView;
+class LLViewerObject;
+class LLUUID;
+class LLMouseHandler;
+class LLProgressView;
+class LLTool;
+class LLVelocityBar;
+class LLViewerWindow;
+class LLTextBox;
+class LLImageRaw;
+class LLHUDIcon;
+
+class LLViewerWindow : public LLWindowCallbacks
+{
+public:
+ //
+ // CREATORS
+ //
+ LLViewerWindow(char* title, char* name, S32 x, S32 y, S32 width, S32 height, BOOL fullscreen, BOOL ignore_pixel_depth);
+ virtual ~LLViewerWindow();
+
+ void initGLDefaults();
+ void initBase();
+ void adjustRectanglesForFirstUse(const LLRect& full_window);
+ void initWorldUI();
+
+ //
+ // LLWindowCallback interface implementation
+ //
+ /*virtual*/ BOOL handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated);
+ /*virtual*/ BOOL handleTranslatedKeyUp(KEY key, MASK mask);
+ /*virtual*/ void handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level);
+ /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, MASK mask); // NOT going to handle extended
+ /*virtual*/ BOOL handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleCloseRequest(LLWindow *window);
+ /*virtual*/ void handleQuit(LLWindow *window);
+ /*virtual*/ BOOL handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ void handleMouseLeave(LLWindow *window);
+ /*virtual*/ void handleResize(LLWindow *window, S32 x, S32 y);
+ /*virtual*/ void handleFocus(LLWindow *window);
+ /*virtual*/ void handleFocusLost(LLWindow *window);
+ /*virtual*/ BOOL handleActivate(LLWindow *window, BOOL activated);
+ /*virtual*/ void handleMenuSelect(LLWindow *window, S32 menu_item);
+ /*virtual*/ BOOL handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height);
+ /*virtual*/ void handleScrollWheel(LLWindow *window, S32 clicks);
+ /*virtual*/ BOOL handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ void handleWindowBlock(LLWindow *window);
+ /*virtual*/ void handleWindowUnblock(LLWindow *window);
+ /*virtual*/ void handleDataCopy(LLWindow *window, S32 data_type, void *data);
+
+
+ //
+ // ACCESSORS
+ //
+ LLView* getRootView() const { return mRootView; }
+ const LLRect& getWindowRect() const { return mWindowRect; };
+ const LLRect& getVirtualWindowRect() const { return mVirtualWindowRect; };
+ S32 getWindowHeight() const;
+ S32 getWindowWidth() const;
+ S32 getWindowDisplayHeight() const;
+ S32 getWindowDisplayWidth() const;
+ LLWindow* getWindow() const { return mWindow; }
+ void* getPlatformWindow() const { return mWindow->getPlatformWindow(); }
+ void focusClient() const { return mWindow->focusClient(); };
+
+ LLCoordGL getLastMouse() const { return mLastMousePoint; }
+ S32 getLastMouseX() const { return mLastMousePoint.mX; }
+ S32 getLastMouseY() const { return mLastMousePoint.mY; }
+ LLCoordGL getCurrentMouse() const { return mCurrentMousePoint; }
+ S32 getCurrentMouseX() const { return mCurrentMousePoint.mX; }
+ S32 getCurrentMouseY() const { return mCurrentMousePoint.mY; }
+ S32 getCurrentMouseDX() const { return mCurrentMouseDelta.mX; }
+ S32 getCurrentMouseDY() const { return mCurrentMouseDelta.mY; }
+ LLCoordGL getCurrentMouseDelta() const { return mCurrentMouseDelta; }
+ LLStat * getMouseVelocityStat() { return &mMouseVelocityStat; }
+ BOOL getLeftMouseDown() const { return mLeftMouseDown; }
+ BOOL getRightMouseDown() const { return mRightMouseDown; }
+
+ LLView* getTopView() const;
+ BOOL hasTopView(LLView* view) const;
+
+ void setupViewport(S32 x_offset = 0, S32 y_offset = 0);
+ void setup3DRender();
+ void setup2DRender();
+
+ BOOL isPickPending() { return mPickPending; }
+
+ LLVector3 mouseDirectionGlobal(const S32 x, const S32 y) const;
+ LLVector3 mouseDirectionCamera(const S32 x, const S32 y) const;
+
+ // Is window of our application frontmost?
+ BOOL getActive() const { return mActive; }
+
+ void getTargetWindow(BOOL& fullscreen, S32& width, S32& height) const;
+ // The 'target' is where the user wants the window to be. It may not be
+ // there yet, because we may be supressing fullscreen prior to login.
+
+ //
+ // MANIPULATORS
+ //
+ void saveLastMouse(const LLCoordGL &point);
+
+ void setCursor( ECursorType c );
+ void showCursor();
+ void hideCursor();
+ void moveCursorToCenter(); // move to center of window
+
+ void setShowProgress(const BOOL show);
+ BOOL getShowProgress() const;
+ void moveProgressViewToFront();
+ void setProgressString(const LLString& string);
+ void setProgressPercent(const F32 percent);
+ void setProgressMessage(const LLString& msg);
+ void setProgressCancelButtonVisible( BOOL b, const LLString& label );
+ LLProgressView *getProgressView() const;
+
+ void updateObjectUnderCursor();
+
+ BOOL handlePerFrameHover(); // Once per frame, update UI based on mouse position
+
+ BOOL handleKey(KEY key, MASK mask);
+ void handleScrollWheel (S32 clicks);
+
+ // Handle the application becoming active (frontmost) or inactive
+ //BOOL handleActivate(BOOL activate);
+
+ void setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus)); // new_focus = NULL to release the focus.
+ LLUICtrl* getKeyboardFocus();
+ BOOL hasKeyboardFocus( const LLUICtrl* possible_focus ) const;
+ BOOL childHasKeyboardFocus( const LLView* parent ) const;
+
+ void setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor)); // new_captor = NULL to release the mouse.
+ BOOL hasMouseCapture(const LLMouseHandler* possible_captor ) const;
+ LLMouseHandler* getMouseCaptor() const;
+
+ void setTopView(LLView* new_top, void (*on_top_lost)(LLView* old_top)); // set new_top = NULL to release top_view.
+
+ void reshape(S32 width, S32 height);
+ void sendShapeToSim();
+
+ void draw();
+// void drawSelectedObjects();
+
+ static void loadUserImage(void **cb_data, const LLUUID &uuid);
+
+ // Save snapshot like Snapshot 1, 2, 3, etc.
+ static void saveMovieNumbered(void*);
+
+ static void movieSize(S32 new_width, S32 new_height);
+
+ typedef enum e_snapshot_type
+ {
+ SNAPSHOT_TYPE_COLOR,
+ SNAPSHOT_TYPE_DEPTH,
+ SNAPSHOT_TYPE_OBJECT_ID
+ } ESnapshotType;
+
+ BOOL saveSnapshot(const LLString& filename, S32 image_width, S32 image_height, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR);
+ BOOL rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, BOOL keep_window_aspect = TRUE,
+ BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR );
+ BOOL saveImageNumbered(LLImageRaw *raw);
+
+ void playSnapshotAnimAndSound();
+
+ // draws selection boxes around selected objects, must call displayObjects first
+ void renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud );
+ void performPick();
+
+ void hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent = FALSE, BOOL pick_parcel_walls = FALSE);
+ void hitObjectOrLandGlobalImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent);
+
+ void hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask));
+ void hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask));
+
+ LLViewerObject* getObjectUnderCursor(const F32 depth = 16.0f);
+
+ void requestFastFrame(LLView* view);
+ BOOL renderingFastFrame() { return mFastFrameTimer.getStarted() && !firstFastFrame(); }
+ void finishFastFrame() { mFastFrameTimer.stop(); mRenderFullFrame = FALSE; }
+ BOOL firstFastFrame() { return mRenderFullFrame; }
+ void finishFirstFastFrame() { mRenderFullFrame = FALSE; }
+
+ // Returns a pointer to the last object hit
+ LLViewerObject *lastObjectHit();
+ LLViewerObject *lastNonFloraObjectHit();
+
+ const LLVector3d& lastObjectHitOffset();
+ const LLVector3d& lastNonFloraObjectHitOffset();
+
+ // mousePointOnLand() returns true if found point
+ BOOL mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_pos_global);
+ BOOL mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y, const LLVector3d &plane_point, const LLVector3 &plane_normal);
+ LLVector3d clickPointInWorldGlobal(const S32 x, const S32 y_from_bot, LLViewerObject* clicked_object) const;
+ BOOL clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const;
+
+ // Prints window implementation details
+ void dumpState();
+
+ // Request display setting changes
+ void toggleFullscreen(BOOL show_progress);
+
+ // handle shutting down GL and bringing it back up
+ BOOL checkSettings();
+ void restartDisplay(BOOL show_progress_bar);
+ BOOL changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar);
+ BOOL getIgnoreDestroyWindow() { return mIgnoreActivate; }
+ F32 getDisplayAspectRatio() const;
+ const LLVector2& getDisplayScale() const { return mDisplayScale; }
+ void calcDisplayScale();
+
+ void drawPickBuffer() const;
+
+ LLAlertDialog* alertXml(const std::string& xml_filename,
+ LLAlertDialog::alert_callback_t callback = NULL, void* user_data = NULL);
+ LLAlertDialog* alertXml(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback = NULL, void* user_data = NULL);
+ LLAlertDialog* alertXmlEditText(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data,
+ LLAlertDialog::alert_text_callback_t text_callback, void *text_data,
+ const LLString::format_map_t& edit_args = LLString::format_map_t(),
+ BOOL draw_asterixes = FALSE);
+
+ static bool alertCallback(S32 modal);
+
+#ifdef SABINRIG
+ //Silly rig stuff
+ void printFeedback(); //RIG STUFF!
+#endif //SABINRIG
+
+private:
+ void switchToolByMask(MASK mask);
+ void destroyWindow();
+ void drawMouselookInstructions();
+ void stopGL(BOOL save_state = TRUE);
+ void restoreGL(const LLString& progress_message = LLString::null);
+ void initFonts(F32 zoom_factor = 1.f);
+
+ void analyzeHit(
+ S32 x, // input
+ S32 y_from_bot, // input
+ LLViewerObject* objectp, // input
+ U32 te_offset, // input
+ LLUUID* hit_object_id_p,// output
+ S32* hit_face_p, // output
+ LLVector3d* hit_pos_p, // output
+ BOOL* hit_land, // output
+ F32* hit_u_coord, // output
+ F32* hit_v_coord); // output
+
+public:
+ LLWindow* mWindow; // graphical window object
+
+protected:
+ BOOL mActive;
+ BOOL mWantFullscreen;
+ BOOL mShowFullscreenProgress;
+ LLRect mWindowRect;
+ LLRect mVirtualWindowRect;
+ LLView* mRootView; // a view of size mWindowRect, containing all child views
+ LLVector2 mDisplayScale;
+
+ LLCoordGL mCurrentMousePoint; // last mouse position in GL coords
+ LLCoordGL mLastMousePoint; // Mouse point at last frame.
+ LLCoordGL mCurrentMouseDelta; //amount mouse moved this frame
+ LLStat mMouseVelocityStat;
+ BOOL mLeftMouseDown;
+ BOOL mRightMouseDown;
+
+ LLProgressView *mProgressView;
+
+ LLTextBox* mToolTip;
+ BOOL mToolTipBlocked; // True after a key press or a mouse button event. False once the mouse moves again.
+ LLRect mToolTipStickyRect; // Once a tool tip is shown, it will stay visible until the mouse leaves this rect.
+
+ BOOL mMouseInWindow; // True if the mouse is over our window or if we have captured the mouse.
+ BOOL mFocusCycleMode;
+
+ // Variables used for tool override switching based on modifier keys. JC
+ MASK mLastMask; // used to detect changes in modifier mask
+ LLTool* mToolStored; // the tool we're overriding
+ BOOL mSuppressToolbox; // sometimes hide the toolbox, despite
+ // having a camera tool selected
+ BOOL mHideCursorPermanent; // true during drags, mouselook
+ LLCoordGL mPickPoint;
+ LLCoordGL mPickOffset;
+ MASK mPickMask;
+ BOOL mPickPending;
+ void (*mPickCallback)(S32 x, S32 y, MASK mask);
+
+ LLString mOverlayTitle; // Used for special titles such as "Second Life - Special E3 2003 Beta"
+
+ static char sSnapshotBaseName[LL_MAX_PATH];
+ static char sSnapshotDir[LL_MAX_PATH];
+
+ static char sMovieBaseName[LL_MAX_PATH];
+
+ BOOL mIgnoreActivate;
+ LLFrameTimer mFastFrameTimer;
+ BOOL mRenderFullFrame;
+ U8* mPickBuffer;
+};
+
+class LLBottomPanel : public LLPanel
+{
+public:
+ LLBottomPanel(const LLString& name, const LLRect& rect);
+ void setFocusIndicator(LLView * indicator);
+ LLView * getFocusIndicator() { return mIndicator; }
+ /*virtual*/ void draw();
+protected:
+ LLView * mIndicator;
+};
+extern LLBottomPanel * gBottomPanel;
+
+void toggle_flying(void*);
+void toggle_first_person();
+void toggle_build(void*);
+void reset_viewer_state_on_sim(void);
+
+//
+// Constants
+//
+
+
+//
+// Globals
+//
+
+extern LLVelocityBar* gVelocityBar;
+extern LLViewerWindow* gViewerWindow;
+extern BOOL gQuietSnapshot;
+
+extern LLFrameTimer gMouseIdleTimer; // how long has it been since the mouse last moved?
+extern LLFrameTimer gAwayTimer; // tracks time before setting the avatar away state to true
+extern LLFrameTimer gAwayTriggerTimer; // how long the avatar has been away
+
+extern LLVector3d gLastHitPosGlobal;
+extern LLVector3d gLastHitObjectOffset;
+extern LLUUID gLastHitObjectID;
+extern S32 gLastHitObjectFace;
+extern BOOL gLastHitLand;
+extern F32 gLastHitUCoord;
+extern F32 gLastHitVCoord;
+
+
+extern LLVector3d gLastHitNonFloraPosGlobal;
+extern LLVector3d gLastHitNonFloraObjectOffset;
+extern LLUUID gLastHitNonFloraObjectID;
+extern S32 gLastHitNonFloraObjectFace;
+
+extern S32 gLastHitUIElement;
+extern LLHUDIcon* gLastHitHUDIcon;
+extern BOOL gLastHitParcelWall;
+extern BOOL gDebugSelect;
+extern BOOL gPickFaces;
+extern BOOL gPickTransparent;
+
+extern BOOL gDebugFastUIRender;
+extern S32 CHAT_BAR_HEIGHT;
+#endif
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
new file mode 100644
index 0000000000..841b2cbb47
--- /dev/null
+++ b/indra/newview/llvlcomposition.cpp
@@ -0,0 +1,456 @@
+/**
+ * @file llvlcomposition.cpp
+ * @brief Viewer-side representation of a composition layer...
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvlcomposition.h"
+#include "llerror.h"
+#include "v3math.h"
+#include "llsurface.h"
+#include "lltextureview.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "noise.h"
+#include "llregionhandle.h" // for from_region_handle
+#include "llviewercontrol.h"
+
+
+
+F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac)
+{
+ // Not sure if this is the right math...
+ // Take weighted average of all four points (bilinear interpolation)
+ F32 result;
+
+ const F32 inv_x_frac = 1.f - x_frac;
+ const F32 inv_y_frac = 1.f - y_frac;
+ result = inv_x_frac*inv_y_frac*v00
+ + x_frac*inv_y_frac*v10
+ + inv_x_frac*y_frac*v01
+ + x_frac*y_frac*v11;
+
+ return result;
+}
+
+
+LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
+ LLViewerLayer(width, scale),
+ mParamsReady(FALSE)
+{
+ mSurfacep = surfacep;
+
+ // Load Terrain Textures - Original ones
+ LLUUID id;
+ // Dirt
+ id.set( gViewerArt.getString("terrain_dirt_detail.tga") );
+ setDetailTextureID(0, id);
+
+ // Grass
+ id.set( gViewerArt.getString("terrain_grass_detail.tga") );
+ setDetailTextureID(1, id);
+
+ // Rock mountain
+ id.set( gViewerArt.getString("terrain_mountain_detail.tga") );
+ setDetailTextureID(2, id);
+
+ // Rock face
+ id.set( gViewerArt.getString("terrain_rock_detail.tga") );
+ setDetailTextureID(3, id);
+
+ // Initialize the texture matrix to defaults.
+ for (S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ mStartHeight[i] = gSavedSettings.getF32("TerrainColorStartHeight");
+ mHeightRange[i] = gSavedSettings.getF32("TerrainColorHeightRange");
+ }
+ mTexScaleX = 16.f;
+ mTexScaleY = 16.f;
+ mTexturesLoaded = FALSE;
+}
+
+
+LLVLComposition::~LLVLComposition()
+{
+}
+
+
+void LLVLComposition::setSurface(LLSurface *surfacep)
+{
+ mSurfacep = surfacep;
+}
+
+
+void LLVLComposition::setDetailTextureID(S32 corner, const LLUUID& id)
+{
+ if(id.isNull())
+ {
+ return;
+ }
+ mDetailTextures[corner] = gImageList.getImage(id);
+}
+
+BOOL LLVLComposition::generateHeights(const F32 x, const F32 y,
+ const F32 width, const F32 height)
+{
+ if (!mParamsReady)
+ {
+ // All the parameters haven't been set yet (we haven't gotten the message from the sim)
+ return FALSE;
+ }
+
+ llassert(mSurfacep);
+ S32 x_begin, y_begin, x_end, y_end;
+
+ x_begin = llround( x * mScaleInv );
+ y_begin = llround( y * mScaleInv );
+ x_end = llround( (x + width) * mScaleInv );
+ y_end = llround( (y + width) * mScaleInv );
+
+ if (x_end > mWidth)
+ {
+ x_end = mWidth;
+ }
+ if (y_end > mWidth)
+ {
+ y_end = mWidth;
+ }
+
+ LLVector3d origin_global = from_region_handle(mSurfacep->getRegion()->getHandle());
+
+ // For perlin noise generation...
+ const F32 slope_squared = 1.5f*1.5f;
+ const F32 xyScale = 4.9215f; //0.93284f;
+ const F32 zScale = 4; //0.92165f;
+ const F32 z_offset = 0.f;
+ const F32 noise_magnitude = 2.f; // Degree to which noise modulates composition layer (versus
+ // simple height)
+
+ // Heights map into textures as 0-1 = first, 1-2 = second, etc.
+ // So we need to compress heights into this range.
+ const S32 NUM_TEXTURES = 4;
+
+ const F32 xyScaleInv = (1.f / xyScale);
+ const F32 zScaleInv = (1.f / zScale);
+
+ const F32 inv_width = 1.f/mWidth;
+
+ // OK, for now, just have the composition value equal the height at the point.
+ for (S32 j = y_begin; j < y_end; j++)
+ {
+ for (S32 i = x_begin; i < x_end; i++)
+ {
+
+ F32 vec[3];
+ F32 vec1[3];
+ F32 twiddle;
+
+ // Bilinearly interpolate the start height and height range of the textures
+ F32 start_height = bilinear(mStartHeight[SOUTHWEST],
+ mStartHeight[SOUTHEAST],
+ mStartHeight[NORTHWEST],
+ mStartHeight[NORTHEAST],
+ i*inv_width, j*inv_width); // These will be bilinearly interpolated
+ F32 height_range = bilinear(mHeightRange[SOUTHWEST],
+ mHeightRange[SOUTHEAST],
+ mHeightRange[NORTHWEST],
+ mHeightRange[NORTHEAST],
+ i*inv_width, j*inv_width); // These will be bilinearly interpolated
+
+ LLVector3 location(i*mScale, j*mScale, 0.f);
+
+ F32 height = mSurfacep->resolveHeightRegion(location) + z_offset;
+
+ // Step 0: Measure the exact height at this texel
+ vec[0] = (F32)(origin_global.mdV[VX]+location.mV[VX])*xyScaleInv; // Adjust to non-integer lattice
+ vec[1] = (F32)(origin_global.mdV[VY]+location.mV[VY])*xyScaleInv;
+ vec[2] = height*zScaleInv;
+ //
+ // Choose material value by adding to the exact height a random value
+ //
+ vec1[0] = vec[0]*(0.2222222222f);
+ vec1[1] = vec[1]*(0.2222222222f);
+ vec1[2] = vec[2]*(0.2222222222f);
+ twiddle = noise2(vec1)*6.5f; // Low freq component for large divisions
+
+ twiddle += turbulence2(vec, 2)*slope_squared; // High frequency component
+ twiddle *= noise_magnitude;
+
+ F32 scaled_noisy_height = (height + twiddle - start_height) * F32(NUM_TEXTURES) / height_range;
+
+ scaled_noisy_height = llmax(0.f, scaled_noisy_height);
+ scaled_noisy_height = llmin(3.f, scaled_noisy_height);
+ *(mDatap + i + j*mWidth) = scaled_noisy_height;
+ }
+ }
+ return TRUE;
+}
+
+static const S32 BASE_SIZE = 128;
+
+BOOL LLVLComposition::generateComposition()
+{
+
+ if (!mParamsReady)
+ {
+ // All the parameters haven't been set yet (we haven't gotten the message from the sim)
+ return FALSE;
+ }
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ if (mDetailTextures[i]->getDiscardLevel() < 0)
+ {
+ mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail
+ mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE);
+ return FALSE;
+ }
+ if ((mDetailTextures[i]->getDiscardLevel() != 0 &&
+ (mDetailTextures[i]->getWidth() < BASE_SIZE ||
+ mDetailTextures[i]->getHeight() < BASE_SIZE)))
+ {
+ S32 width = mDetailTextures[i]->getWidth(0);
+ S32 height = mDetailTextures[i]->getHeight(0);
+ S32 min_dim = llmin(width, height);
+ S32 ddiscard = 0;
+ while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
+ {
+ ddiscard++;
+ min_dim /= 2;
+ }
+ mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail
+ mDetailTextures[i]->setMinDiscardLevel(ddiscard);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
+ const F32 width, const F32 height)
+{
+ llassert(mSurfacep);
+ llassert(x >= 0.f);
+ llassert(y >= 0.f);
+
+ LLTimer gen_timer;
+
+ ///////////////////////////
+ //
+ // Generate raw data arrays for surface textures
+ //
+ //
+
+ // These have already been validated by generateComposition.
+ LLPointer<LLImageRaw> st_raw[4];
+ U8* st_data[4];
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ // Read back a raw image for this discard level, if it exists
+ st_raw[i] = new LLImageRaw;
+ S32 min_dim = llmin(mDetailTextures[i]->getWidth(0), mDetailTextures[i]->getHeight(0));
+ S32 ddiscard = 0;
+ while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
+ {
+ ddiscard++;
+ min_dim /= 2;
+ }
+ if (!mDetailTextures[i]->readBackRaw(ddiscard, st_raw[i]))
+ {
+ llwarns << "Unable to read raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl;
+ return FALSE;
+ }
+ if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE ||
+ mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE ||
+ mDetailTextures[i]->getComponents() != 3)
+ {
+ LLPointer<LLImageRaw> newraw = new LLImageRaw(BASE_SIZE, BASE_SIZE, 3);
+ newraw->composite(st_raw[i]);
+ st_raw[i] = newraw; // deletes old
+ }
+ st_data[i] = st_raw[i]->getData();
+ }
+
+ ///////////////////////////////////////
+ //
+ // Generate and clamp x/y bounding box.
+ //
+ //
+
+ S32 x_begin, y_begin, x_end, y_end;
+ x_begin = (S32)(x * mScaleInv);
+ y_begin = (S32)(y * mScaleInv);
+ x_end = llround( (x + width) * mScaleInv );
+ y_end = llround( (y + width) * mScaleInv );
+
+ if (x_end > mWidth)
+ {
+ llwarns << "x end > width" << llendl;
+ x_end = mWidth;
+ }
+ if (y_end > mWidth)
+ {
+ llwarns << "y end > width" << llendl;
+ y_end = mWidth;
+ }
+
+
+ ///////////////////////////////////////////
+ //
+ // Generate target texture information, stride ratios.
+ //
+ //
+
+ LLViewerImage *texturep;
+ U32 tex_width, tex_height, tex_comps;
+ U32 tex_stride;
+ F32 tex_x_scalef, tex_y_scalef;
+ S32 tex_x_begin, tex_y_begin, tex_x_end, tex_y_end;
+ F32 tex_x_ratiof, tex_y_ratiof;
+
+ texturep = mSurfacep->getSTexture();
+ tex_width = texturep->getWidth();
+ tex_height = texturep->getHeight();
+ tex_comps = texturep->getComponents();
+ tex_stride = tex_width * tex_comps;
+
+ S32 st_comps = 3;
+ S32 st_width = BASE_SIZE;
+ S32 st_height = BASE_SIZE;
+
+ if (tex_comps != st_comps)
+ {
+ llwarns << "Base texture comps != input texture comps" << llendl;
+ return FALSE;
+ }
+
+ tex_x_scalef = (F32)tex_width / (F32)mWidth;
+ tex_y_scalef = (F32)tex_height / (F32)mWidth;
+ tex_x_begin = (S32)((F32)x_begin * tex_x_scalef);
+ tex_y_begin = (S32)((F32)y_begin * tex_y_scalef);
+ tex_x_end = (S32)((F32)x_end * tex_x_scalef);
+ tex_y_end = (S32)((F32)y_end * tex_y_scalef);
+
+ tex_x_ratiof = (F32)mWidth*mScale / (F32)tex_width;
+ tex_y_ratiof = (F32)mWidth*mScale / (F32)tex_height;
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw(tex_width, tex_height, tex_comps);
+ U8 *rawp = raw->getData();
+
+ F32 tex_width_inv = 1.f/tex_width;
+ F32 tex_height_inv = 1.f/tex_height;
+
+ F32 st_x_stride, st_y_stride;
+ st_x_stride = ((F32)st_width / (F32)mTexScaleX)*((F32)mWidth / (F32)tex_width);
+ st_y_stride = ((F32)st_height / (F32)mTexScaleY)*((F32)mWidth / (F32)tex_height);
+
+ llassert(st_x_stride > 0.f);
+ llassert(st_y_stride > 0.f);
+ ////////////////////////////////
+ //
+ // Iterate through the target texture, striding through the
+ // subtextures and interpolating appropriately.
+ //
+ //
+
+ F32 sti, stj;
+ S32 st_offset;
+ sti = (tex_x_begin * st_x_stride) - st_width*(llfloor((tex_x_begin * st_x_stride)/st_width));
+ stj = (tex_y_begin * st_y_stride) - st_height*(llfloor((tex_y_begin * st_y_stride)/st_height));
+
+ st_offset = (llfloor(stj * st_width) + llfloor(sti)) * st_comps;
+ for (S32 j = tex_y_begin; j < tex_y_end; j++)
+ {
+ U32 offset = j * tex_stride + tex_x_begin * tex_comps;
+ sti = (tex_x_begin * st_x_stride) - st_width*((U32)(tex_x_begin * st_x_stride)/st_width);
+ for (S32 i = tex_x_begin; i < tex_x_end; i++)
+ {
+ S32 tex0, tex1;
+ F32 composition = getValueScaled(i*tex_x_ratiof, j*tex_y_ratiof);
+
+ tex0 = llfloor( composition );
+ tex0 = llclamp(tex0, 0, 3);
+ composition -= tex0;
+ tex1 = tex0 + 1;
+ tex1 = llclamp(tex1, 0, 3);
+
+ F32 xy_int_i, xy_int_j;
+
+ xy_int_i = i * tex_width_inv;
+ xy_int_j = j * tex_height_inv;
+
+ st_offset = (lltrunc(sti) + lltrunc(stj)*st_width) * st_comps;
+ for (U32 k = 0; k < tex_comps; k++)
+ {
+ // Linearly interpolate based on composition.
+ F32 a = *(st_data[tex0] + st_offset);
+ F32 b = *(st_data[tex1] + st_offset);
+ rawp[ offset ] = (U8)lltrunc( a + composition * (b - a) );
+ offset++;
+ st_offset++;
+ }
+
+ sti += st_x_stride;
+ if (sti >= st_width)
+ {
+ sti -= st_width;
+ }
+ }
+
+ stj += st_y_stride;
+ if (stj >= st_height)
+ {
+ stj -= st_height;
+ }
+ }
+
+ texturep->setSubImage(raw, tex_x_begin, tex_y_begin, tex_x_end - tex_x_begin, tex_y_end - tex_y_begin);
+ LLSurface::sTextureUpdateTime += gen_timer.getElapsedTimeF32();
+ LLSurface::sTexelsUpdated += (tex_x_end - tex_x_begin) * (tex_y_end - tex_y_begin);
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ // Un-boost detatil textures (will get re-boosted if rendering in high detail)
+ mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_NONE);
+ mDetailTextures[i]->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1);
+ }
+
+ return TRUE;
+}
+
+LLUUID LLVLComposition::getDetailTextureID(S32 corner)
+{
+ return mDetailTextures[corner]->getID();
+}
+
+LLViewerImage* LLVLComposition::getDetailTexture(S32 corner)
+{
+ return mDetailTextures[corner];
+}
+
+F32 LLVLComposition::getStartHeight(S32 corner)
+{
+ return mStartHeight[corner];
+}
+
+void LLVLComposition::setStartHeight(S32 corner, const F32 start_height)
+{
+ mStartHeight[corner] = start_height;
+}
+
+F32 LLVLComposition::getHeightRange(S32 corner)
+{
+ return mHeightRange[corner];
+}
+
+void LLVLComposition::setHeightRange(S32 corner, const F32 range)
+{
+ mHeightRange[corner] = range;
+}
diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h
new file mode 100644
index 0000000000..1e929f33dd
--- /dev/null
+++ b/indra/newview/llvlcomposition.h
@@ -0,0 +1,67 @@
+/**
+ * @file llvlcomposition.h
+ * @brief Viewer-side representation of a composition layer...
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVLCOMPOSITION_H
+#define LL_LLVLCOMPOSITION_H
+
+#include "llviewerlayer.h"
+#include "llviewerimage.h"
+
+class LLSurface;
+
+class LLVLComposition : public LLViewerLayer
+{
+public:
+ LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale);
+ /*virtual*/ ~LLVLComposition();
+
+ void setSurface(LLSurface *surfacep);
+
+ // Viewer side hack to generate composition values
+ BOOL generateHeights(const F32 x, const F32 y, const F32 width, const F32 height);
+ BOOL generateComposition();
+ // Generate texture from composition values.
+ BOOL generateTexture(const F32 x, const F32 y, const F32 width, const F32 height);
+
+ // Use these as indeces ito the get/setters below that use 'corner'
+ enum ECorner
+ {
+ SOUTHWEST = 0,
+ SOUTHEAST = 1,
+ NORTHWEST = 2,
+ NORTHEAST = 3,
+ CORNER_COUNT = 4
+ };
+ LLUUID getDetailTextureID(S32 corner);
+ LLViewerImage* getDetailTexture(S32 corner);
+ F32 getStartHeight(S32 corner);
+ F32 getHeightRange(S32 corner);
+
+ void setDetailTextureID(S32 corner, const LLUUID& id);
+ void setStartHeight(S32 corner, F32 start_height);
+ void setHeightRange(S32 corner, F32 range);
+
+ friend class LLVOSurfacePatch;
+ friend class LLDrawPoolTerrain;
+ void setParamsReady() { mParamsReady = TRUE; }
+ BOOL getParamsReady() const { return mParamsReady; }
+protected:
+ BOOL mParamsReady;
+ LLSurface *mSurfacep;
+ BOOL mTexturesLoaded;
+
+ LLPointer<LLViewerImage> mDetailTextures[CORNER_COUNT];
+
+ F32 mStartHeight[CORNER_COUNT];
+ F32 mHeightRange[CORNER_COUNT];
+
+ F32 mTexScaleX;
+ F32 mTexScaleY;
+};
+
+#endif //LL_LLVLCOMPOSITION_H
diff --git a/indra/newview/llvlmanager.cpp b/indra/newview/llvlmanager.cpp
new file mode 100644
index 0000000000..9a5c1e533b
--- /dev/null
+++ b/indra/newview/llvlmanager.cpp
@@ -0,0 +1,147 @@
+/**
+ * @file llvlmanager.cpp
+ * @brief LLVLManager class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvlmanager.h"
+
+#include "indra_constants.h"
+#include "bitpack.h"
+#include "patch_code.h"
+#include "patch_dct.h"
+#include "llviewerregion.h"
+#include "llframetimer.h"
+#include "llagent.h"
+#include "llsurface.h"
+
+LLVLManager gVLManager;
+
+LLVLManager::~LLVLManager()
+{
+ S32 i;
+ for (i = 0; i < mPacketData.count(); i++)
+ {
+ delete mPacketData[i];
+ }
+ mPacketData.reset();
+}
+
+void LLVLManager::addLayerData(LLVLData *vl_datap, const S32 mesg_size)
+{
+ if (LAND_LAYER_CODE == vl_datap->mType)
+ {
+ mLandBits += mesg_size * 8;
+ }
+ else if (WIND_LAYER_CODE == vl_datap->mType)
+ {
+ mWindBits += mesg_size * 8;
+ }
+ else if (CLOUD_LAYER_CODE == vl_datap->mType)
+ {
+ mCloudBits += mesg_size * 8;
+ }
+ else
+ {
+ llerrs << "Unknown layer type!" << (S32)vl_datap->mType << llendl;
+ }
+
+ mPacketData.put(vl_datap);
+}
+
+void LLVLManager::unpackData(const S32 num_packets)
+{
+ static LLFrameTimer decode_timer;
+
+ S32 i;
+ for (i = 0; i < mPacketData.count(); i++)
+ {
+ LLVLData *datap = mPacketData[i];
+
+ LLBitPack bit_pack(datap->mData, datap->mSize);
+ LLGroupHeader goph;
+
+ decode_patch_group_header(bit_pack, &goph);
+ if (LAND_LAYER_CODE == datap->mType)
+ {
+ datap->mRegionp->getLand().decompressDCTPatch(bit_pack, &goph, FALSE);
+ }
+ else if (WIND_LAYER_CODE == datap->mType)
+ {
+ datap->mRegionp->mWind.decompress(bit_pack, &goph);
+
+ }
+ else if (CLOUD_LAYER_CODE == datap->mType)
+ {
+ datap->mRegionp->mCloudLayer.decompress(bit_pack, &goph);
+ }
+ }
+
+ for (i = 0; i < mPacketData.count(); i++)
+ {
+ delete mPacketData[i];
+ }
+ mPacketData.reset();
+
+}
+
+void LLVLManager::resetBitCounts()
+{
+ mLandBits = mWindBits = mCloudBits = 0;
+}
+
+S32 LLVLManager::getLandBits() const
+{
+ return mLandBits;
+}
+
+S32 LLVLManager::getWindBits() const
+{
+ return mWindBits;
+}
+
+S32 LLVLManager::getCloudBits() const
+{
+ return mCloudBits;
+}
+
+S32 LLVLManager::getTotalBytes() const
+{
+ return mLandBits + mWindBits + mCloudBits;
+}
+
+void LLVLManager::cleanupData(LLViewerRegion *regionp)
+{
+ S32 cur = 0;
+ while (cur < mPacketData.count())
+ {
+ if (mPacketData[cur]->mRegionp == regionp)
+ {
+ delete mPacketData[cur];
+ mPacketData.remove(cur);
+ }
+ else
+ {
+ cur++;
+ }
+ }
+}
+
+LLVLData::LLVLData(LLViewerRegion *regionp, const S8 type, U8 *data, const S32 size)
+{
+ mType = type;
+ mData = data;
+ mRegionp = regionp;
+ mSize = size;
+}
+
+LLVLData::~LLVLData()
+{
+ delete [] mData;
+ mData = NULL;
+ mRegionp = NULL;
+}
diff --git a/indra/newview/llvlmanager.h b/indra/newview/llvlmanager.h
new file mode 100644
index 0000000000..98303fa68b
--- /dev/null
+++ b/indra/newview/llvlmanager.h
@@ -0,0 +1,61 @@
+/**
+ * @file llvlmanager.h
+ * @brief LLVLManager class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVLMANAGER_H
+#define LL_LLVLMANAGER_H
+
+// This class manages the data coming in for viewer layers from the network.
+
+#include "stdtypes.h"
+#include "lldarray.h"
+
+class LLVLData;
+class LLViewerRegion;
+
+class LLVLManager
+{
+public:
+ ~LLVLManager();
+
+ void addLayerData(LLVLData *vl_datap, const S32 mesg_size);
+
+ void unpackData(const S32 num_packets = 10);
+
+ S32 getTotalBytes() const;
+
+ S32 getLandBits() const;
+ S32 getWindBits() const;
+ S32 getCloudBits() const;
+
+ void resetBitCounts();
+
+ void cleanupData(LLViewerRegion *regionp);
+protected:
+
+ LLDynamicArray<LLVLData *> mPacketData;
+ U32 mLandBits;
+ U32 mWindBits;
+ U32 mCloudBits;
+};
+
+class LLVLData
+{
+public:
+ LLVLData(LLViewerRegion *regionp,
+ const S8 type, U8 *data, const S32 size);
+ ~LLVLData();
+
+ S8 mType;
+ U8 *mData;
+ S32 mSize;
+ LLViewerRegion *mRegionp;
+};
+
+extern LLVLManager gVLManager;
+
+#endif // LL_LLVIEWERLAYERMANAGER_H
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
new file mode 100644
index 0000000000..acec80c95a
--- /dev/null
+++ b/indra/newview/llvoavatar.cpp
@@ -0,0 +1,9426 @@
+/**
+ * @file llvoavatar.cpp
+ * @brief Implementation of LLVOAvatar class which is a derivation fo LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <algorithm>
+#include <vector>
+#include "llstl.h"
+
+#include "llvoavatar.h"
+
+#include "audioengine.h"
+#include "imageids.h"
+#include "indra_constants.h"
+#include "llchat.h"
+#include "llfontgl.h"
+#include "llprimitive.h"
+#include "lltextureentry.h"
+#include "message.h"
+#include "noise.h"
+#include "sound_ids.h"
+#include "lltimer.h"
+#include "timing.h"
+
+#include "llagent.h" // Get state values from here
+#include "llagparray.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolmedia.h"
+#include "lldrawpoolbump.h"
+#include "lldriverparam.h"
+#include "lleditingmotion.h"
+#include "llemote.h"
+#include "llface.h"
+#include "llfasttimer.h"
+#include "llfirstuse.h"
+#include "llfloatercustomize.h"
+#include "llfloatertools.h"
+#include "llgldbg.h"
+#include "llhandmotion.h"
+#include "llheadrotmotion.h"
+#include "llhudeffectbeam.h"
+#include "llhudeffectlookat.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llhudtext.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyframefallmotion.h"
+#include "llkeyframemotion.h"
+#include "llkeyframemotionparam.h"
+#include "llkeyframestandmotion.h"
+#include "llkeyframewalkmotion.h"
+#include "llmenugl.h"
+#include "llmutelist.h"
+#include "llnetmap.h"
+#include "llnotify.h"
+#include "llquantize.h"
+#include "llregionhandle.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llsprite.h"
+#include "llstatusbar.h"
+#include "lltargetingmotion.h"
+#include "lltexlayer.h"
+#include "lltoolbar.h"
+#include "lltoolgrab.h" // for needsRenderBeam
+#include "lltoolmgr.h" // for needsRenderBeam
+#include "lltoolmorph.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvosky.h"
+#include "llvovolume.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "lscript_byteformat.h"
+
+//#include "vtune/vtuneapi.h"
+
+// Direct imports, evil
+extern LLSky gSky;
+extern void set_avatar_character(void* charNameArg);
+extern BOOL gRenderForSelect;
+
+LLXmlTree LLVOAvatar::sXMLTree;
+LLXmlTree LLVOAvatar::sSkeletonXMLTree;
+LLVOAvatarSkeletonInfo* LLVOAvatar::sSkeletonInfo = NULL;
+LLVOAvatarInfo* LLVOAvatar::sAvatarInfo = NULL;
+
+BOOL gDebugAvatarRotation = FALSE;
+
+//extern BOOL gVelocityInterpolate;
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const F32 MIN_PIXEL_AREA_FOR_COMPOSITE = 200.f;
+
+F32 SHADOW_OFFSET_AMT = 0.03f;
+
+#define DELTA_TIME_MIN 0.01f // we clamp measured deltaTime to this
+#define DELTA_TIME_MAX 0.2f // range to insure stability of computations.
+
+const F32 PELVIS_LAG_FLYING = 0.22f;// pelvis follow half life while flying
+
+const F32 PELVIS_LAG_WALKING = 0.4f; // ...while walking
+
+const F32 PELVIS_LAG_MOUSELOOK = 0.15f;
+const F32 MOUSELOOK_PELVIS_FOLLOW_FACTOR = 0.5f;
+
+//Ventrella
+const F32 PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON = 0.0001f; // not zero! - something gets divided by this!
+//end Ventrella
+
+#define PELVIS_ROT_THRESHOLD_SLOW 60.0f // amount of deviation allowed between
+#define PELVIS_ROT_THRESHOLD_FAST 2.0f // the pelvis and the view direction
+ // when moving fast & slow
+
+const F32 MIN_SPEED_PELVIS_FOLLOW = 0.1f;
+
+#define TORSO_NOISE_AMOUNT 1.f // Amount of deviation from up-axis, in degrees
+#define TORSO_NOISE_SPEED 0.2f // Time scale factor on torso noise.
+
+const F32 BREATHE_ROT_MOTION_STRENGTH = 0.05f;
+
+const F32 BREATHE_SCALE_MOTION_STRENGTH = 0.005f;
+
+#define PELVIS_NOISE_FACTOR 0.5f // amount of random noise
+
+#define AUDIO_STEP_PRI 0xC0000000
+#define AUDIO_STEP_LO_SPEED 0.01f // as average speed goes from lo to hi,
+#define AUDIO_STEP_HI_SPEED 3.0f // from lo to hi
+#define AUDIO_STEP_LO_GAIN 0.15f // the resulting gain will ramp linearly
+#define AUDIO_STEP_HI_GAIN 0.15f
+
+const F32 DAMPED_MOTION_TIME_SCALE = 0.15f;
+
+const F32 LOOKAT_CAMERA_DIST_SQUARED = 25.f;
+
+#define AVATAR_HEADER "Linden Avatar 1.0"
+#define AVATAR_SECTION "[avatar]"
+
+#define AVATAR_DEFAULT_CHAR "avatar"
+
+const F32 MIN_SHADOW_HEIGHT = 0.f;
+const F32 MAX_SHADOW_HEIGHT = 0.3f;
+
+#define MIN_REQUIRED_PIXEL_AREA_BODY_NOISE (10000.f)
+#define MIN_REQUIRED_PIXEL_AREA_BREATHE (10000.f)
+#define MIN_REQUIRED_PIXEL_AREA_PELVIS_FIX (40.f)
+
+const S32 LOCTEX_IMAGE_SIZE_SELF = 512;
+const S32 LOCTEX_IMAGE_AREA_SELF = LOCTEX_IMAGE_SIZE_SELF * LOCTEX_IMAGE_SIZE_SELF;
+const S32 LOCTEX_IMAGE_SIZE_OTHER = LOCTEX_IMAGE_SIZE_SELF / 4; // The size of local textures for other (!mIsSelf) avatars
+const S32 LOCTEX_IMAGE_AREA_OTHER = LOCTEX_IMAGE_SIZE_OTHER * LOCTEX_IMAGE_SIZE_OTHER;
+
+const F32 HEAD_MOVEMENT_AVG_TIME = 0.9f;
+
+const S32 MORPH_MASK_REQUESTED_DISCARD = 0;
+const S32 MIN_PIXEL_AREA_BUMP = 500;
+
+// Discard level at which to switch to baked textures
+// Should probably be 4 or 3, but didn't want to change it while change other logic - SJB
+const S32 SWITCH_TO_BAKED_DISCARD = 5;
+
+const F32 FOOT_COLLIDE_FUDGE = 0.04f;
+
+const F32 HOVER_EFFECT_MAX_SPEED = 3.f;
+const F32 HOVER_EFFECT_STRENGTH = 0.f;
+F32 UNDERWATER_EFFECT_STRENGTH = 0.1f;
+const F32 UNDERWATER_FREQUENCY_DAMP = 0.33f;
+const F32 APPEARANCE_MORPH_TIME = 0.65f;
+const F32 CAMERA_SHAKE_ACCEL_THRESHOLD_SQUARED = 5.f * 5.f;
+const F32 TIME_BEFORE_MESH_CLEANUP = 5.f; // seconds
+const S32 AVATAR_AGP_RELEASE_THRESHOLD = 10; // number of avatar instances before releasing AGP memory
+const F32 FOOT_GROUND_COLLISION_TOLERANCE = 0.25f;
+const F32 AVATAR_LOD_TWEAK_RANGE = 0.7f;
+const S32 MAX_LOD_CHANGES_PER_FRAME = 2;
+const S32 MAX_BUBBLE_CHAT_LENGTH = 1023;
+const S32 MAX_BUBBLE_CHAT_UTTERANCES = 12;
+const F32 CHAT_FADE_TIME = 8.0;
+const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f;
+const S32 MAX_BUBBLES = 7;
+
+
+const bool USING_VENTRELLA_AVATAR_MOTION_TEST = false;
+
+
+S32 LLVOAvatar::sMaxVisible = 50;
+
+LLVOAvatar::ETextureIndex LLVOAvatar::sBakedTextureIndices[BAKED_TEXTURE_COUNT] =
+{
+ LLVOAvatar::TEX_HEAD_BAKED,
+ LLVOAvatar::TEX_UPPER_BAKED,
+ LLVOAvatar::TEX_LOWER_BAKED,
+ LLVOAvatar::TEX_EYES_BAKED,
+ LLVOAvatar::TEX_SKIRT_BAKED
+};
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+
+static F32 calc_bouncy_animation(F32 x)
+{
+ return -(cosf(x * F_PI * 2.5f - F_PI_BY_TWO))*(0.4f + x * -0.1f) + x * 1.3f;
+}
+
+//-----------------------------------------------------------------------------
+// Static Data
+//-----------------------------------------------------------------------------
+S32 LLVOAvatar::sMaxOtherAvatarsToComposite = 1; // Only this many avatars (other than yourself) can be composited at a time. Set in initClass().
+LLMap< LLGLenum, LLGLuint*> LLVOAvatar::sScratchTexNames;
+LLMap< LLGLenum, F32*> LLVOAvatar::sScratchTexLastBindTime;
+S32 LLVOAvatar::sScratchTexBytes = 0;
+S32 LLVOAvatar::sNumVisibleAvatars = 0;
+S32 LLVOAvatar::sNumLODChangesThisFrame = 0;
+
+LLUUID LLVOAvatar::sStepSoundOnLand = LLUUID("e8af4a28-aa83-4310-a7c4-c047e15ea0df");
+LLUUID LLVOAvatar::sStepSounds[LL_MCODE_END] =
+{
+ LLUUID(SND_STONE_RUBBER),
+ LLUUID(SND_METAL_RUBBER),
+ LLUUID(SND_GLASS_RUBBER),
+ LLUUID(SND_WOOD_RUBBER),
+ LLUUID(SND_FLESH_RUBBER),
+ LLUUID(SND_RUBBER_PLASTIC),
+ LLUUID(SND_RUBBER_RUBBER)
+};
+
+S32 LLVOAvatar::sRenderName = RENDER_NAME_ALWAYS;
+S32 LLVOAvatar::sNumVisibleChatBubbles = 0;
+BOOL LLVOAvatar::sDebugInvisible = FALSE;
+BOOL LLVOAvatar::sShowAttachmentPoints = FALSE;
+BOOL LLVOAvatar::sShowAnimationDebug = FALSE;
+BOOL LLVOAvatar::sShowFootPlane = FALSE;
+BOOL LLVOAvatar::sShowCollisionVolumes = FALSE;
+BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE;
+BOOL LLVOAvatar::sAvatarLoadTest = FALSE;
+F32 LLVOAvatar::sLODFactor = 1.f;
+BOOL LLVOAvatar::sJointDebug = FALSE;
+
+S32 LLVOAvatar::sCurJoint = 0;
+S32 LLVOAvatar::sCurVolume = 0;
+
+struct LLAvatarTexData
+{
+ LLAvatarTexData( const LLUUID& id, LLVOAvatar::ELocTexIndex index )
+ : mAvatarID(id), mIndex(index) {}
+ LLUUID mAvatarID;
+ LLVOAvatar::ELocTexIndex mIndex;
+};
+
+struct LLTextureMaskData
+{
+ LLTextureMaskData( const LLUUID& id )
+ : mAvatarID(id), mLastDiscardLevel(S32_MAX) {}
+ LLUUID mAvatarID;
+ S32 mLastDiscardLevel;
+};
+
+
+//-----------------------------------------------------------------------------
+// class LLBodyNoiseMotion
+//-----------------------------------------------------------------------------
+class LLBodyNoiseMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLBodyNoiseMotion(const LLUUID &id) : LLMotion(id) {mName = "body_noise";}
+
+ // Destructor
+ virtual ~LLBodyNoiseMotion() { }
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLBodyNoiseMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::HIGH_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_BODY_NOISE; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character)
+ {
+ if( !mTorsoState.setJoint( character->getJoint("mTorso") ))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setUsage(LLJointState::ROT);
+
+ addJointState( &mTorsoState );
+ return STATUS_SUCCESS;
+ }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ F32 nx[2];
+ nx[0]=time*TORSO_NOISE_SPEED;
+ nx[1]=0.0f;
+ F32 ny[2];
+ ny[0]=0.0f;
+ ny[1]=time*TORSO_NOISE_SPEED;
+ F32 noiseX = noise2(nx);
+ F32 noiseY = noise2(ny);
+
+ F32 rx = TORSO_NOISE_AMOUNT * DEG_TO_RAD * noiseX / 0.42f;
+ F32 ry = TORSO_NOISE_AMOUNT * DEG_TO_RAD * noiseY / 0.42f;
+ LLQuaternion tQn;
+ tQn.setQuat( rx, ry, 0.0f );
+ mTorsoState.setRotation( tQn );
+
+ return TRUE;
+ }
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mTorsoState;
+};
+
+//-----------------------------------------------------------------------------
+// class LLBreatheMotionRot
+//-----------------------------------------------------------------------------
+class LLBreatheMotionRot :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLBreatheMotionRot(const LLUUID &id) :
+ LLMotion(id),
+ mBreatheRate(1.f),
+ mCharacter(NULL)
+ {
+ mName = "breathe_rot";
+ }
+
+ // Destructor
+ virtual ~LLBreatheMotionRot() { }
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLBreatheMotionRot(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_BREATHE; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character)
+ {
+ //Ventrella
+ // I'm replacing the code below because I need to change
+ // the logic in order to add other body parts
+ /*
+ mCharacter = character;
+
+ if (!mChestState.setJoint( character->getJoint("mChest")))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mChestState.setUsage(LLJointState::ROT);
+
+ addJointState( &mChestState );
+ return STATUS_SUCCESS;
+ */
+
+
+ bool success = true;
+
+ if ( !mChestState.setJoint( character->getJoint( "mChest" ) ) ) { success = false; }
+
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ if ( !mNeckState.setJoint ( character->getJoint( "mNeck" )) ) { success = false; }
+
+ if ( !mCollarLeftState.setJoint ( character->getJoint( "mCollarLeft" )) ) { success = false; }
+ if ( !mShoulderLeftState.setJoint ( character->getJoint( "mShoulderLeft" )) ) { success = false; }
+ if ( !mElbowLeftState.setJoint ( character->getJoint( "mElbowLeft" )) ) { success = false; }
+ if ( !mWristLeftState.setJoint ( character->getJoint( "mWristLeft" )) ) { success = false; }
+
+ if ( !mCollarRightState.setJoint ( character->getJoint( "mCollarRight" )) ) { success = false; }
+ if ( !mShoulderRightState.setJoint ( character->getJoint( "mShoulderRight" )) ) { success = false; }
+ if ( !mElbowRightState.setJoint ( character->getJoint( "mElbowRight" )) ) { success = false; }
+ if ( !mWristRightState.setJoint ( character->getJoint( "mWristRight" )) ) { success = false; }
+
+ if ( !mHipLeftState.setJoint ( character->getJoint( "mHipLeft" )) ) { success = false; }
+ if ( !mKneeLeftState.setJoint ( character->getJoint( "mKneeLeft" )) ) { success = false; }
+ if ( !mAnkleLeftState.setJoint ( character->getJoint( "mAnkleLeft" )) ) { success = false; }
+
+ if ( !mHipRightState.setJoint ( character->getJoint( "mHipRight" )) ) { success = false; }
+ if ( !mKneeRightState.setJoint ( character->getJoint( "mKneeRight" )) ) { success = false; }
+ if ( !mAnkleRightState.setJoint ( character->getJoint( "mAnkleRight" )) ) { success = false; }
+ }
+
+ if ( success )
+ {
+ mChestState.setUsage(LLJointState::ROT);
+ addJointState( &mChestState );
+
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ //-------------------------------------------
+ // neck
+ //-------------------------------------------
+ mNeckState.setUsage(LLJointState::ROT);
+ addJointState( &mNeckState );
+
+ //-------------------------------------------
+ // left arm
+ //-------------------------------------------
+ mCollarLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mCollarLeftState );
+
+ mShoulderLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderLeftState );
+
+ mElbowLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mElbowLeftState );
+
+ mWristLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mWristLeftState );
+
+
+ //-------------------------------------------
+ // right arm
+ //-------------------------------------------
+ mCollarRightState.setUsage(LLJointState::ROT);
+ addJointState( &mCollarRightState );
+
+ mShoulderRightState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderRightState );
+
+ mElbowRightState.setUsage(LLJointState::ROT);
+ addJointState( &mElbowRightState );
+
+ mWristRightState.setUsage(LLJointState::ROT);
+ addJointState( &mWristRightState );
+
+ //-------------------------------------------
+ // left leg
+ //-------------------------------------------
+ mHipLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mHipLeftState );
+
+ mKneeLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mKneeLeftState );
+
+ mAnkleLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mAnkleLeftState );
+
+
+ //-------------------------------------------
+ // right leg
+ //-------------------------------------------
+ mHipRightState.setUsage(LLJointState::ROT);
+ addJointState( &mHipRightState );
+
+ mKneeRightState.setUsage(LLJointState::ROT);
+ addJointState( &mKneeRightState );
+
+ mAnkleRightState.setUsage(LLJointState::ROT);
+ addJointState( &mAnkleRightState );
+ }
+ }
+
+ if ( success )
+ {
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ return STATUS_FAILURE;
+ }
+ //end Ventrella
+ }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ mBreatheRate = 1.f;
+
+ F32 breathe_amt = (sinf(mBreatheRate * time) * BREATHE_ROT_MOTION_STRENGTH);
+
+ mChestState.setRotation(LLQuaternion(breathe_amt, LLVector3(0.f, 1.f, 0.f)));
+
+ //Ventrella
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ F32 wave = ( sinf ( time * 2.0f ) * 0.5f );
+
+ mChestState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
+
+ mCollarLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mShoulderLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mElbowLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
+ mWristLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+
+ mCollarRightState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
+ mShoulderRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mElbowRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
+ mWristRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+
+ mHipLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ mKneeLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
+ mAnkleLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+
+ mHipRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ mKneeRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
+ mAnkleRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ }
+ //end Ventrella
+
+ return TRUE;
+ }
+
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mChestState;
+
+ //Ventrella
+ LLJointState mNeckState;
+ LLJointState mCollarLeftState;
+ LLJointState mShoulderLeftState;
+ LLJointState mElbowLeftState;
+ LLJointState mWristLeftState;
+ LLJointState mCollarRightState;
+ LLJointState mShoulderRightState;
+ LLJointState mElbowRightState;
+ LLJointState mWristRightState;
+ LLJointState mHipLeftState;
+ LLJointState mKneeLeftState;
+ LLJointState mAnkleLeftState;
+ LLJointState mHipRightState;
+ LLJointState mKneeRightState;
+ LLJointState mAnkleRightState;
+ //end Ventrella
+
+ F32 mBreatheRate;
+ LLCharacter* mCharacter;
+};
+
+//-----------------------------------------------------------------------------
+// class LLPelvisFixMotion
+//-----------------------------------------------------------------------------
+class LLPelvisFixMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLPelvisFixMotion(const LLUUID &id) : LLMotion(id), mCharacter(NULL) {mName = "pelvis_fix";}
+
+ // Destructor
+ virtual ~LLPelvisFixMotion() { }
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID& id) { return new LLPelvisFixMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.5f; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.5f; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::LOW_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_PELVIS_FIX; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character)
+ {
+ mCharacter = character;
+
+ if (!mPelvisState.setJoint( character->getJoint("mPelvis")))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mPelvisState.setUsage(LLJointState::POS);
+
+ addJointState( &mPelvisState );
+ return STATUS_SUCCESS;
+ }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ mPelvisState.setPosition(LLVector3::zero);
+
+ return TRUE;
+ }
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mPelvisState;
+ LLCharacter* mCharacter;
+};
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar()
+//-----------------------------------------------------------------------------
+LLVOAvatar::LLVOAvatar(
+ const LLUUID& id,
+ const LLPCode pcode,
+ LLViewerRegion* regionp)
+ :
+ LLViewerObject(id, pcode, regionp),
+ mHUDTargetZoom(1.f),
+ mHUDCurZoom(1.f),
+ mLastHeadBakedID( IMG_DEFAULT_AVATAR ),
+ mLastUpperBodyBakedID( IMG_DEFAULT_AVATAR ),
+ mLastLowerBodyBakedID( IMG_DEFAULT_AVATAR ),
+ mLastEyesBakedID( IMG_DEFAULT_AVATAR ),
+ mLastSkirtBakedID( IMG_DEFAULT_AVATAR ),
+ mIsDummy(FALSE),
+ mSpecialRenderMode(0),
+ mPelvisToFoot(0.f),
+ mLastSkeletonSerialNum( 0 ),
+ mTurning(FALSE),
+ mHeadOffset(),
+ mIsSitting(FALSE),
+ mTimeVisible(),
+ mTyping(FALSE),
+ mMeshValid(FALSE),
+ mVisible(FALSE),
+ mWindFreq(0.f),
+ mRipplePhase( 0.f ),
+ mBelowWater(FALSE),
+ mAppearanceAnimSetByUser(FALSE),
+ mLastAppearanceBlendTime(0.f),
+ mAppearanceAnimating(FALSE),
+ mHeadLayerSet( NULL ),
+ mUpperBodyLayerSet( NULL ),
+ mLowerBodyLayerSet( NULL ),
+ mEyesLayerSet( NULL ),
+ mSkirtLayerSet( NULL ),
+ mRenderPriority(1.0f),
+ mNumAGPVertices(0),
+ mNameString(),
+ mTitle(),
+ mNameAway(FALSE),
+ mNameBusy(FALSE),
+ mNameMute(FALSE),
+ mNameAppearance(FALSE),
+ mLastRegionHandle(0),
+ mRegionCrossingCount(0),
+ mFirstTEMessageReceived( FALSE ),
+ mFirstAppearanceMessageReceived( FALSE ),
+ mHeadBakedLoaded(FALSE),
+ mHeadMaskDiscard(-1),
+ mUpperBakedLoaded(FALSE),
+ mUpperMaskDiscard(-1),
+ mLowerBakedLoaded(FALSE),
+ mLowerMaskDiscard(-1),
+ mEyesBakedLoaded(FALSE),
+ mSkirtBakedLoaded(FALSE),
+ mHeadMaskTexName(0),
+ mUpperMaskTexName(0),
+ mLowerMaskTexName(0),
+ mCulled( FALSE ),
+ mTexSkinColor( NULL ),
+ mTexHairColor( NULL ),
+ mTexEyeColor( NULL )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //VTResume(); // VTune
+
+ lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl;
+
+ mPelvisp = NULL;
+
+ for( S32 i=0; i<LOCTEX_NUM_ENTRIES; i++ )
+ {
+ mLocalTextureBaked[i] = FALSE;
+ mLocalTextureDiscard[i] = MAX_DISCARD_LEVEL+1;
+ }
+
+ mDirtyMesh = TRUE; // Dirty geometry, need to regenerate.
+ mShadow0Facep = NULL;
+ mShadow1Facep = NULL;
+ mHeadp = NULL;
+
+ mIsBuilt = FALSE;
+
+ mNumJoints = 0;
+ mSkeleton = NULL;
+ mScreenp = NULL;
+
+ mNumCollisionVolumes = 0;
+ mCollisionVolumes = NULL;
+
+ // set up animation variables
+ mSpeed = 0.f;
+ setAnimationData("Speed", &mSpeed);
+
+ strcpy(mAvatarDefinition, AVATAR_DEFAULT_CHAR);
+
+ if (id == gAgentID)
+ {
+ mIsSelf = TRUE;
+ gAgent.setAvatarObject(this);
+ lldebugs << "Marking avatar as self " << id << llendl;
+ }
+ else
+ {
+ mIsSelf = FALSE;
+ }
+
+ setNumTEs(TEX_NUM_ENTRIES);
+
+ mbCanSelect = TRUE;
+
+ mSignaledAnimations.clear();
+ mPlayingAnimations.clear();
+
+ mWasOnGroundLeft = FALSE;
+ mWasOnGroundRight = FALSE;
+
+ mTimeLast = 0.0f;
+ mSpeedAccum = 0.0f;
+
+ mRippleTimeLast = 0.f;
+
+ mShadowImageID = LLUUID( gViewerArt.getString("foot_shadow.tga"));
+ mShadowImagep = gImageList.getImage(mShadowImageID);
+ mShadowImagep->bind();
+ mShadowImagep->setClamp(TRUE, TRUE);
+
+ mInAir = FALSE;
+
+ mStepOnLand = TRUE;
+ mStepMaterial = 0;
+
+ //-------------------------------------------------------------------------
+ // initialize joint, mesh and shape members
+ //-------------------------------------------------------------------------
+ mRoot.setName( "mRoot" );
+
+ // skinned mesh objects
+ mHairLOD.setName("mHairLOD");
+ mHairMesh0.setName("mHairMesh0");
+ mHairMesh0.setMeshID(MESH_ID_HAIR);
+ mHairMesh1.setName("mHairMesh1");
+ mHairMesh2.setName("mHairMesh2");
+ mHairMesh3.setName("mHairMesh3");
+ mHairMesh4.setName("mHairMesh4");
+ mHairMesh5.setName("mHairMesh5");
+
+ mHairMesh0.setIsTransparent(TRUE);
+ mHairMesh1.setIsTransparent(TRUE);
+ mHairMesh2.setIsTransparent(TRUE);
+ mHairMesh3.setIsTransparent(TRUE);
+ mHairMesh4.setIsTransparent(TRUE);
+ mHairMesh5.setIsTransparent(TRUE);
+
+ mHeadLOD.setName("mHeadLOD");
+ mHeadMesh0.setName("mHeadMesh0");
+ mHeadMesh0.setMeshID(MESH_ID_HEAD);
+ mHeadMesh1.setName("mHeadMesh1");
+ mHeadMesh2.setName("mHeadMesh2");
+ mHeadMesh3.setName("mHeadMesh3");
+ mHeadMesh4.setName("mHeadMesh4");
+
+ mEyeLashLOD.setName("mEyeLashLOD");
+ mEyeLashMesh0.setName("mEyeLashMesh0");
+ mEyeLashMesh0.setMeshID(MESH_ID_HEAD);
+ mEyeLashMesh0.setIsTransparent(TRUE);
+
+ mUpperBodyLOD.setName("mUpperBodyLOD");
+ mUpperBodyMesh0.setName("mUpperBodyMesh0");
+ mUpperBodyMesh0.setMeshID(MESH_ID_UPPER_BODY);
+ mUpperBodyMesh1.setName("mUpperBodyMesh1");
+ mUpperBodyMesh2.setName("mUpperBodyMesh2");
+ mUpperBodyMesh3.setName("mUpperBodyMesh3");
+ mUpperBodyMesh4.setName("mUpperBodyMesh4");
+
+ mLowerBodyLOD.setName("mLowerBodyLOD");
+ mLowerBodyMesh0.setName("mLowerBodyMesh0");
+ mLowerBodyMesh0.setMeshID(MESH_ID_LOWER_BODY);
+ mLowerBodyMesh1.setName("mLowerBodyMesh1");
+ mLowerBodyMesh2.setName("mLowerBodyMesh2");
+ mLowerBodyMesh3.setName("mLowerBodyMesh3");
+ mLowerBodyMesh4.setName("mLowerBodyMesh4");
+
+ mEyeBallLeftLOD.setName("mEyeBallLeftLOD");
+ mEyeBallLeftMesh0.setName("mEyeBallLeftMesh0");
+ mEyeBallLeftMesh1.setName("mEyeBallLeftMesh1");
+
+ mEyeBallRightLOD.setName("mEyeBallRightLOD");
+ mEyeBallRightMesh0.setName("mEyeBallRightMesh0");
+ mEyeBallRightMesh1.setName("mEyeBallRightMesh1");
+
+ mSkirtLOD.setName("mSkirtLOD");
+ mSkirtMesh0.setName("mSkirtMesh0");
+ mSkirtMesh0.setMeshID(MESH_ID_SKIRT);
+ mSkirtMesh1.setName("mSkirtMesh1");
+ mSkirtMesh2.setName("mSkirtMesh2");
+ mSkirtMesh3.setName("mSkirtMesh3");
+ mSkirtMesh4.setName("mSkirtMesh4");
+
+ mSkirtMesh0.setIsTransparent(TRUE);
+ mSkirtMesh1.setIsTransparent(TRUE);
+ mSkirtMesh2.setIsTransparent(TRUE);
+ mSkirtMesh3.setIsTransparent(TRUE);
+ mSkirtMesh4.setIsTransparent(TRUE);
+
+ // set the pick names for the avatar
+ mHeadMesh0.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh1.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh2.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh3.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh4.setPickName( LLViewerJoint::PN_0 );
+ mEyeLashMesh0.setPickName( LLViewerJoint::PN_0 );
+
+ mUpperBodyMesh0.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh1.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh2.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh3.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh4.setPickName( LLViewerJoint::PN_1 );
+
+ mLowerBodyMesh0.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh1.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh2.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh3.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh4.setPickName( LLViewerJoint::PN_2 );
+
+ mEyeBallLeftMesh0.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallLeftMesh1.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallRightMesh0.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallRightMesh1.setPickName( LLViewerJoint::PN_3 );
+
+ mHairMesh0.setPickName( LLViewerJoint::PN_4);
+ mHairMesh1.setPickName( LLViewerJoint::PN_4);
+ mHairMesh2.setPickName( LLViewerJoint::PN_4);
+ mHairMesh3.setPickName( LLViewerJoint::PN_4);
+ mHairMesh4.setPickName( LLViewerJoint::PN_4);
+ mHairMesh5.setPickName( LLViewerJoint::PN_4);
+
+ mSkirtMesh0.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh1.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh2.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh3.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh4.setPickName( LLViewerJoint::PN_5 );
+
+ // material settings
+
+ mEyeBallLeftMesh0.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallLeftMesh1.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallRightMesh0.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallRightMesh1.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+
+ //-------------------------------------------------------------------------
+ // register motions
+ //-------------------------------------------------------------------------
+ if (LLCharacter::sInstances.getLength() == 1)
+ {
+ LLKeyframeMotion::setVFS(gStaticVFS);
+ addMotion( ANIM_AGENT_BUSY, LLNullMotion::create );
+ addMotion( ANIM_AGENT_CROUCH, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_CROUCHWALK, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_EXPRESS_AFRAID, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_ANGER, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_BORED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_CRY, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_DISDAIN, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_EMBARRASSED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_FROWN, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_KISS, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_LAUGH, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_OPEN_MOUTH, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_REPULSED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SAD, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SHRUG, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SMILE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SURPRISE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_TONGUE_OUT, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_TOOTHSMILE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_WINK, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_WORRY, LLEmote::create );
+ addMotion( ANIM_AGENT_RUN, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_STAND, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_1, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_2, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_3, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_4, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STANDUP, LLKeyframeFallMotion::create );
+ addMotion( ANIM_AGENT_TURNLEFT, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_TURNRIGHT, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_WALK, LLKeyframeWalkMotion::create );
+
+ // motions without a start/stop bit
+ addMotion( ANIM_AGENT_BODY_NOISE, LLBodyNoiseMotion::create );
+ addMotion( ANIM_AGENT_BREATHE_ROT, LLBreatheMotionRot::create );
+ addMotion( ANIM_AGENT_EDITING, LLEditingMotion::create );
+ addMotion( ANIM_AGENT_EYE, LLEyeMotion::create );
+ addMotion( ANIM_AGENT_FEMALE_WALK, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_FLY_ADJUST, LLFlyAdjustMotion::create );
+ addMotion( ANIM_AGENT_HAND_MOTION, LLHandMotion::create );
+ addMotion( ANIM_AGENT_HEAD_ROT, LLHeadRotMotion::create );
+ addMotion( ANIM_AGENT_PELVIS_FIX, LLPelvisFixMotion::create );
+ addMotion( ANIM_AGENT_SIT_FEMALE, LLKeyframeMotion::create );
+ addMotion( ANIM_AGENT_TARGET, LLTargetingMotion::create );
+ addMotion( ANIM_AGENT_WALK_ADJUST, LLWalkAdjustMotion::create );
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+ buildCharacter();
+
+ // preload specific motions here
+ createMotion( ANIM_AGENT_CUSTOMIZE);
+ createMotion( ANIM_AGENT_CUSTOMIZE_DONE);
+
+ //VTPause(); // VTune
+}
+
+
+//------------------------------------------------------------------------
+// LLVOAvatar::~LLVOAvatar()
+//------------------------------------------------------------------------
+LLVOAvatar::~LLVOAvatar()
+{
+ lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl;
+
+ if (mIsSelf)
+ {
+ gAgent.setAvatarObject(NULL);
+ }
+
+ mRoot.removeAllChildren();
+
+ delete [] mSkeleton;
+ mSkeleton = NULL;
+
+ delete mScreenp;
+ mScreenp = NULL;
+
+ delete [] mCollisionVolumes;
+ mCollisionVolumes = NULL;
+
+
+ mNumJoints = 0;
+
+ delete mHeadLayerSet;
+ mHeadLayerSet = NULL;
+
+ delete mUpperBodyLayerSet;
+ mUpperBodyLayerSet = NULL;
+
+ delete mLowerBodyLayerSet;
+ mLowerBodyLayerSet = NULL;
+
+ delete mEyesLayerSet;
+ mEyesLayerSet = NULL;
+
+ delete mSkirtLayerSet;
+ mSkirtLayerSet = NULL;
+
+ mAttachmentPoints.deleteAllData();
+
+ delete mTexSkinColor;
+ mTexSkinColor = NULL;
+ delete mTexHairColor;
+ mTexHairColor = NULL;
+ delete mTexEyeColor;
+ mTexEyeColor = NULL;
+
+ std::for_each(mMeshes.begin(), mMeshes.end(), DeletePairedPointer());
+
+ mDead = TRUE;
+
+ // Clean up class data
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+ mAnimationSources.clear();
+
+ lldebugs << "LLVOAvatar Destructor end" << llendl;
+}
+
+void LLVOAvatar::markDead()
+{
+ if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+
+ mBeam = NULL;
+ LLViewerObject::markDead();
+}
+
+
+BOOL LLVOAvatar::isFullyBaked()
+{
+ if (mIsDummy) return TRUE;
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if (isWearingWearableType(WT_SKIRT))
+ {
+ return head_baked && upper_baked && lower_baked && eyes_baked && skirt_baked;
+ }
+ else
+ {
+ return head_baked && upper_baked && lower_baked && eyes_baked;
+ }
+}
+
+void LLVOAvatar::deleteLayerSetCaches()
+{
+ if( mHeadLayerSet ) mHeadLayerSet->deleteCaches();
+ if( mUpperBodyLayerSet ) mUpperBodyLayerSet->deleteCaches();
+ if( mLowerBodyLayerSet ) mLowerBodyLayerSet->deleteCaches();
+ if( mEyesLayerSet ) mEyesLayerSet->deleteCaches();
+ if( mSkirtLayerSet ) mSkirtLayerSet->deleteCaches();
+}
+
+// static
+BOOL LLVOAvatar::areAllNearbyInstancesBaked()
+{
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ if( inst->isDead() )
+ {
+ continue;
+ }
+ else
+ if( inst->getPixelArea() < MIN_PIXEL_AREA_FOR_COMPOSITE )
+ {
+ return TRUE; // Assumes sInstances is sorted by pixel area.
+ }
+ else
+ if( !inst->isFullyBaked() )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// static
+void LLVOAvatar::dumpScratchTextureByteCount()
+{
+ llinfos << "Scratch Texture GL: " << (sScratchTexBytes/1024) << "KB" << llendl;
+}
+
+// static
+void LLVOAvatar::dumpBakedStatus()
+{
+ LLVector3d camera_pos_global = gAgent.getCameraPositionGlobal();
+
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ llinfos << "Avatar ";
+
+ LLNameValue* firstname = inst->getNVPair("FirstName");
+ LLNameValue* lastname = inst->getNVPair("LastName");
+
+ if( firstname )
+ {
+ llcont << firstname->getString();
+ }
+ if( lastname )
+ {
+ llcont << " " << lastname->getString();
+ }
+
+ llcont << " " << inst->mID;
+
+ if( inst->isDead() )
+ {
+ llcont << " DEAD ("<< inst->getNumRefs() << " refs)";
+ }
+
+ if( inst->mIsSelf )
+ {
+ llcont << " (self)";
+ }
+
+
+ F64 dist_to_camera = (inst->getPositionGlobal() - camera_pos_global).magVec();
+ llcont << " " << dist_to_camera << "m ";
+
+ llcont << " " << inst->mPixelArea << " pixels";
+
+ if( inst->isVisible() )
+ {
+ llcont << " (visible)";
+ }
+ else
+ {
+ llcont << " (not visible)";
+ }
+
+ if( inst->isFullyBaked() )
+ {
+ llcont << " Baked";
+ }
+ else
+ {
+ llcont << " Unbaked (";
+ if( inst->getTEImage( TEX_HEAD_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " head";
+ }
+
+ if( inst->getTEImage( TEX_UPPER_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " upper";
+ }
+
+ if( inst->getTEImage( TEX_LOWER_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " lower";
+ }
+
+ if( inst->getTEImage( TEX_EYES_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " eyes";
+ }
+
+ if (inst->isWearingWearableType(WT_SKIRT))
+ {
+ if( inst->getTEImage( TEX_SKIRT_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " skirt";
+ }
+ }
+
+ llcont << " ) " << inst->getUnbakedPixelAreaRank() << "/" << LLVOAvatar::sMaxOtherAvatarsToComposite;
+ if( inst->isCulled() )
+ {
+ llcont << " culled";
+ }
+ }
+ llcont << llendl;
+/*
+ if( inst->isDead() )
+ {
+ llinfos << "DEAD LIST " << llendl;
+
+
+ for( S32 i = 0; i < inst->mOwners.count(); i++ )
+ {
+ llinfos << i << llendl;
+ LLPointer<LLViewerObject>* owner = (LLPointer<LLViewerObject>*)(inst->mOwners[i]);
+ LLPointer<LLViewerObject>* cur;
+ if( !owner->mName.isEmpty() )
+ {
+ llinfos << " " << owner->mName << llendl;
+ }
+
+ LLViewerObject* key_vo;
+ for( key_vo = gObjectList.mActiveObjects.getFirstKey(); key_vo; key_vo = gObjectList.mActiveObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mActiveObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mActiveObjects" << llendl;
+ }
+ }
+
+ for( key_vo = gObjectList.mAvatarObjects.getFirstKey(); key_vo; key_vo = gObjectList.mAvatarObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mAvatarObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mAvatarObjects" << llendl;
+ }
+ }
+
+ LLUUID id;
+ for( id = gObjectList.mDeadObjects.getFirstKey(); id; id = gObjectList.mDeadObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mDeadObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mDeadObjects" << llendl;
+ }
+ }
+
+
+ for( id = gObjectList.mUUIDObjectMap.getFirstKey(); id; id = gObjectList.mUUIDObjectMap.getNextKey() )
+ {
+ cur = &(gObjectList.mUUIDObjectMap.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mUUIDObjectMap" << llendl;
+ }
+ }
+
+ S32 j;
+ S32 k;
+ for( j = 0; j < 16; j++ )
+ {
+ for( k = 0; k < 10; k++ )
+ {
+ cur = &(gObjectList.mCloseObjects[j][k]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mCloseObjects" << llendl;
+ }
+ }
+ }
+
+ for( j = 0; j < gObjectList.mObjects.count(); j++ )
+ {
+ cur = &(gObjectList.mObjects[j]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mObjects" << llendl;
+ }
+ }
+
+ for( j = 0; j < gObjectList.mMapObjects.count(); j++ )
+ {
+ cur = &(gObjectList.mMapObjects[j]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mMapObjects" << llendl;
+ }
+ }
+ }
+ }
+ */
+ }
+}
+
+//static
+void LLVOAvatar::cleanupVertexPrograms()
+{
+}
+
+//static
+void LLVOAvatar::initVertexPrograms()
+{
+}
+
+//static
+void LLVOAvatar::restoreGL()
+{
+ LLVOAvatar* inst;
+ for( inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ inst->setCompositeUpdatesEnabled( TRUE );
+ inst->invalidateComposite( inst->mHeadLayerSet, FALSE );
+ inst->invalidateComposite( inst->mLowerBodyLayerSet, FALSE );
+ inst->invalidateComposite( inst->mUpperBodyLayerSet, FALSE );
+ inst->invalidateComposite( inst->mEyesLayerSet, FALSE );
+ inst->invalidateComposite( inst->mSkirtLayerSet, FALSE );
+ inst->updateMeshTextures();
+ }
+}
+
+//static
+void LLVOAvatar::destroyGL()
+{
+ deleteCachedImages();
+ cleanupVertexPrograms();
+}
+
+// static
+void LLVOAvatar::deleteCachedImages()
+{
+ if (LLTexLayerSet::sHasCaches)
+ {
+ lldebugs << "Deleting layer set caches" << llendl;
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ inst->deleteLayerSetCaches();
+ }
+ LLTexLayerSet::sHasCaches = FALSE;
+ }
+
+ for( GLuint* namep = (GLuint*)sScratchTexNames.getFirstData();
+ namep;
+ namep = (GLuint*)sScratchTexNames.getNextData() )
+ {
+ glDeleteTextures(1, namep );
+ stop_glerror();
+ }
+
+ if( sScratchTexBytes )
+ {
+ lldebugs << "Clearing Scratch Textures " << (sScratchTexBytes/1024) << "KB" << llendl;
+
+ sScratchTexNames.deleteAllData();
+ LLVOAvatar::sScratchTexLastBindTime.deleteAllData();
+ LLImageGL::sGlobalTextureMemory -= sScratchTexBytes;
+ sScratchTexBytes = 0;
+ }
+
+ gTexStaticImageList.deleteCachedImages();
+}
+
+
+//------------------------------------------------------------------------
+// static
+// LLVOAvatar::initClass()
+//------------------------------------------------------------------------
+void LLVOAvatar::initClass()
+{
+ LLVOAvatar::sMaxOtherAvatarsToComposite = gSavedSettings.getS32("AvatarCompositeLimit");
+
+ char xmlFile[MAX_PATH];
+
+ sprintf(xmlFile, "%s_lad.xml", gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR).c_str());
+ BOOL success = sXMLTree.parseFile( xmlFile, FALSE );
+ if (!success)
+ {
+ llerrs << "Problem reading avatar configuration file:" << xmlFile << llendl;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar configuration file: " << xmlFile << llendl;
+ }
+
+ //-------------------------------------------------------------------------
+ // <linden_avatar version="1.0"> (root)
+ //-------------------------------------------------------------------------
+ if( !root->hasName( "linden_avatar" ) )
+ {
+ llerrs << "Invalid avatar file header: " << xmlFile << llendl;
+ }
+
+ LLString version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar file version: " << version << " in file: " << xmlFile << llendl;
+ }
+
+ S32 wearable_def_version = 1;
+ static LLStdStringHandle wearable_definition_version_string = LLXmlTree::addAttributeString("wearable_definition_version");
+ root->getFastAttributeS32( wearable_definition_version_string, wearable_def_version );
+ LLWearable::setCurrentDefinitionVersion( wearable_def_version );
+
+ LLString mesh_file_name;
+
+ LLXmlTreeNode* skeleton_node = root->getChildByName( "skeleton" );
+ if (!skeleton_node)
+ {
+ llerrs << "No skeleton in avatar configuration file: " << xmlFile << llendl;
+ }
+
+ LLString skeleton_file_name;
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if (!skeleton_node->getFastAttributeString(file_name_string, skeleton_file_name))
+ {
+ llerrs << "No file name in skeleton node in avatar config file: " << xmlFile << llendl;
+ }
+
+ std::string skeleton_path;
+ skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name);
+ if (!parseSkeletonFile(skeleton_path))
+ {
+ llerrs << "Error parsing skeleton file: " << skeleton_path << llendl;
+ }
+
+ // Process XML data
+
+ // avatar_skeleton.xml
+ llassert(!sSkeletonInfo);
+ sSkeletonInfo = new LLVOAvatarSkeletonInfo;
+ if (!sSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot()))
+ {
+ llerrs << "Error parsing skeleton XML file" << llendl;
+ }
+ // parse avatar_lad.xml
+ llassert(!sAvatarInfo);
+ sAvatarInfo = new LLVOAvatarInfo;
+ if (!sAvatarInfo->parseXmlSkeletonNode(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlMeshNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlColorNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlLayerNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlDriverNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+}
+
+
+void LLVOAvatar::cleanupClass()
+{
+ delete sAvatarInfo;
+ sAvatarInfo = NULL;
+ delete sSkeletonInfo;
+ sSkeletonInfo = NULL;
+ sSkeletonXMLTree.cleanup();
+ sXMLTree.cleanup();
+}
+
+
+//-----------------------------------------------------------------------------
+// parseSkeletonFile()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::parseSkeletonFile(const LLString& filename)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // parse the file
+ //-------------------------------------------------------------------------
+ BOOL success = sSkeletonXMLTree.parseFile( filename, FALSE );
+
+ if (!success)
+ {
+ llerrs << "Can't parse skeleton file: " << filename << llendl;
+ return FALSE;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sSkeletonXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar skeleton file: " << filename << llendl;
+ }
+
+ if( !root->hasName( "linden_skeleton" ) )
+ {
+ llerrs << "Invalid avatar skeleton file header: " << filename << llendl;
+ }
+
+ LLString version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar skeleton file version: " << version << " in file: " << filename << llendl;
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// setupBone()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::setupBone(LLVOAvatarBoneInfo* info, LLViewerJoint* parent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLViewerJoint* joint = NULL;
+
+ if (info->mIsJoint)
+ {
+ joint = (LLViewerJoint*)getCharacterJoint(sCurJoint);
+ if (!joint)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint->setName( info->mName );
+ }
+ else // collision volume
+ {
+ if (sCurVolume >= (S32)mNumCollisionVolumes)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint = (LLViewerJoint*)(&mCollisionVolumes[sCurVolume]);
+
+ joint->setName( info->mName );
+ }
+
+ // add to parent
+ if (parent)
+ {
+ parent->addChild( joint );
+ }
+
+ joint->setPosition(info->mPos);
+
+ joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY],
+ info->mRot.mV[VZ], LLQuaternion::XYZ));
+
+ joint->setScale(info->mScale);
+
+
+ if (info->mIsJoint)
+ {
+ joint->setSkinOffset( info->mPivot );
+ sCurJoint++;
+ }
+ else // collision volume
+ {
+ sCurVolume++;
+ }
+
+ // setup children
+ LLVOAvatarBoneInfo::child_list_t::iterator iter;
+ for (iter = info->mChildList.begin(); iter != info->mChildList.end(); iter++)
+ {
+ LLVOAvatarBoneInfo *child_info = *iter;
+ if (!setupBone(child_info, joint))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// buildSkeleton()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::buildSkeleton(LLVOAvatarSkeletonInfo *info)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // allocate joints
+ //-------------------------------------------------------------------------
+ if (!allocateCharacterJoints(info->mNumBones))
+ {
+ llerrs << "Can't allocate " << info->mNumBones << " joints" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // allocate volumes
+ //-------------------------------------------------------------------------
+ if (info->mNumCollisionVolumes)
+ {
+ if (!allocateCollisionVolumes(info->mNumCollisionVolumes))
+ {
+ llerrs << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << llendl;
+ return FALSE;
+ }
+ }
+
+ sCurJoint = 0;
+ sCurVolume = 0;
+
+ LLVOAvatarSkeletonInfo::bone_info_list_t::iterator iter;
+ for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); iter++)
+ {
+ LLVOAvatarBoneInfo *info = *iter;
+ if (!setupBone(info, NULL))
+ {
+ llerrs << "Error parsing bone in skeleton file" << llendl;
+ return FALSE;
+ }
+ }
+
+ // add special-purpose "screen" joint
+ mScreenp = new LLViewerJoint("mScreen", NULL);
+ // for now, put screen at origin, as it is only used during special
+ // HUD rendering mode
+ mScreenp->setWorldPosition(LLVector3::zero);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::buildCharacter()
+// Deferred initialization and rebuild of the avatar.
+//-----------------------------------------------------------------------------
+extern BOOL gPrintMessagesThisFrame;
+void LLVOAvatar::buildCharacter()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // remove all references to our existing skeleton
+ // so we can rebuild it
+ //-------------------------------------------------------------------------
+ flushAllMotions();
+
+ //-------------------------------------------------------------------------
+ // remove all of mRoot's children
+ //-------------------------------------------------------------------------
+ mRoot.removeAllChildren();
+ mIsBuilt = FALSE;
+
+ //-------------------------------------------------------------------------
+ // clear mesh data
+ //-------------------------------------------------------------------------
+ mHairMesh0.setMesh(NULL);
+ mHairMesh1.setMesh(NULL);
+ mHairMesh2.setMesh(NULL);
+ mHairMesh3.setMesh(NULL);
+ mHairMesh4.setMesh(NULL);
+ mHairMesh5.setMesh(NULL);
+
+ mHeadMesh0.setMesh(NULL);
+ mHeadMesh1.setMesh(NULL);
+ mHeadMesh2.setMesh(NULL);
+ mHeadMesh3.setMesh(NULL);
+ mHeadMesh4.setMesh(NULL);
+
+ mEyeLashMesh0.setMesh(NULL);
+
+ mUpperBodyMesh0.setMesh(NULL);
+ mUpperBodyMesh1.setMesh(NULL);
+ mUpperBodyMesh2.setMesh(NULL);
+ mUpperBodyMesh3.setMesh(NULL);
+ mUpperBodyMesh4.setMesh(NULL);
+
+ mLowerBodyMesh0.setMesh(NULL);
+ mLowerBodyMesh1.setMesh(NULL);
+ mLowerBodyMesh2.setMesh(NULL);
+ mLowerBodyMesh3.setMesh(NULL);
+ mLowerBodyMesh4.setMesh(NULL);
+
+ mEyeBallLeftMesh0.setMesh(NULL);
+ mEyeBallLeftMesh1.setMesh(NULL);
+ mEyeBallRightMesh0.setMesh(NULL);
+ mEyeBallRightMesh1.setMesh(NULL);
+
+ mSkirtMesh0.setMesh(NULL);
+ mSkirtMesh1.setMesh(NULL);
+ mSkirtMesh2.setMesh(NULL);
+ mSkirtMesh3.setMesh(NULL);
+ mSkirtMesh4.setMesh(NULL);
+
+ //-------------------------------------------------------------------------
+ // (re)load our skeleton and meshes
+ //-------------------------------------------------------------------------
+ LLTimer timer;
+
+ BOOL status = loadAvatar();
+ stop_glerror();
+
+ if (gNoRender)
+ {
+ // Still want to load the avatar skeleton so visual parameters work.
+ return;
+ }
+
+ gPrintMessagesThisFrame = TRUE;
+ lldebugs << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << llendl;
+
+ if ( ! status )
+ {
+ if ( mIsSelf )
+ {
+ llerrs << "Unable to load user's avatar" << llendl;
+ //set_avatar_character( &LLString(AVATAR_DEFAULT_CHAR));
+ }
+ else
+ {
+ llwarns << "Unable to load other's avatar" << llendl;
+ }
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize "well known" joint pointers
+ //-------------------------------------------------------------------------
+ mPelvisp = (LLViewerJoint*)mRoot.findJoint("mPelvis");
+ mTorsop = (LLViewerJoint*)mRoot.findJoint("mTorso");
+ mChestp = (LLViewerJoint*)mRoot.findJoint("mChest");
+ mNeckp = (LLViewerJoint*)mRoot.findJoint("mNeck");
+ mHeadp = (LLViewerJoint*)mRoot.findJoint("mHead");
+ mSkullp = (LLViewerJoint*)mRoot.findJoint("mSkull");
+ mHipLeftp = (LLViewerJoint*)mRoot.findJoint("mHipLeft");
+ mHipRightp = (LLViewerJoint*)mRoot.findJoint("mHipRight");
+ mKneeLeftp = (LLViewerJoint*)mRoot.findJoint("mKneeLeft");
+ mKneeRightp = (LLViewerJoint*)mRoot.findJoint("mKneeRight");
+ mAnkleLeftp = (LLViewerJoint*)mRoot.findJoint("mAnkleLeft");
+ mAnkleRightp = (LLViewerJoint*)mRoot.findJoint("mAnkleRight");
+ mFootLeftp = (LLViewerJoint*)mRoot.findJoint("mFootLeft");
+ mFootRightp = (LLViewerJoint*)mRoot.findJoint("mFootRight");
+ mWristLeftp = (LLViewerJoint*)mRoot.findJoint("mWristLeft");
+ mWristRightp = (LLViewerJoint*)mRoot.findJoint("mWristRight");
+ mEyeLeftp = (LLViewerJoint*)mRoot.findJoint("mEyeLeft");
+ mEyeRightp = (LLViewerJoint*)mRoot.findJoint("mEyeRight");
+
+ //-------------------------------------------------------------------------
+ // Make sure "well known" pointers exist
+ //-------------------------------------------------------------------------
+ if (!(mPelvisp &&
+ mTorsop &&
+ mChestp &&
+ mNeckp &&
+ mHeadp &&
+ mSkullp &&
+ mHipLeftp &&
+ mHipRightp &&
+ mKneeLeftp &&
+ mKneeRightp &&
+ mAnkleLeftp &&
+ mAnkleRightp &&
+ mFootLeftp &&
+ mFootRightp &&
+ mWristLeftp &&
+ mWristRightp &&
+ mEyeLeftp &&
+ mEyeRightp))
+ {
+ llerrs << "Failed to create avatar." << llendl;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize the pelvis
+ //-------------------------------------------------------------------------
+ mPelvisp->setPosition( LLVector3(0.0f, 0.0f, 0.0f) );
+
+ //-------------------------------------------------------------------------
+ // set head offset from pelvis
+ //-------------------------------------------------------------------------
+ updateHeadOffset();
+
+ //-------------------------------------------------------------------------
+ // start default motions
+ //-------------------------------------------------------------------------
+ startMotion( ANIM_AGENT_HEAD_ROT );
+ startMotion( ANIM_AGENT_EYE );
+ startMotion( ANIM_AGENT_BODY_NOISE );
+ startMotion( ANIM_AGENT_BREATHE_ROT );
+ startMotion( ANIM_AGENT_HAND_MOTION );
+ startMotion( ANIM_AGENT_PELVIS_FIX );
+
+ //-------------------------------------------------------------------------
+ // restart any currently active motions
+ //-------------------------------------------------------------------------
+ processAnimationStateChanges();
+
+ mIsBuilt = TRUE;
+ stop_glerror();
+
+ //-------------------------------------------------------------------------
+ // build the attach and detach menus
+ //-------------------------------------------------------------------------
+ if (mIsSelf)
+ {
+ gAttachBodyPartPieMenus[0] = NULL;
+ gAttachBodyPartPieMenus[1] = new LLPieMenu("Right Arm >");
+ gAttachBodyPartPieMenus[2] = new LLPieMenu("Head >");
+ gAttachBodyPartPieMenus[3] = new LLPieMenu("Left Arm >");
+ gAttachBodyPartPieMenus[4] = NULL;
+ gAttachBodyPartPieMenus[5] = new LLPieMenu("Left Leg >");
+ gAttachBodyPartPieMenus[6] = new LLPieMenu("Torso >");
+ gAttachBodyPartPieMenus[7] = new LLPieMenu("Right Leg >");
+
+ gDetachBodyPartPieMenus[0] = NULL;
+ gDetachBodyPartPieMenus[1] = new LLPieMenu("Right Arm >");
+ gDetachBodyPartPieMenus[2] = new LLPieMenu("Head >");
+ gDetachBodyPartPieMenus[3] = new LLPieMenu("Left Arm >");
+ gDetachBodyPartPieMenus[4] = NULL;
+ gDetachBodyPartPieMenus[5] = new LLPieMenu("Left Leg >");
+ gDetachBodyPartPieMenus[6] = new LLPieMenu("Torso >");
+ gDetachBodyPartPieMenus[7] = new LLPieMenu("Right Leg >");
+
+ for (S32 i = 0; i < 8; i++)
+ {
+ if (gAttachBodyPartPieMenus[i])
+ {
+ gAttachPieMenu->appendMenu( gAttachBodyPartPieMenus[i] );
+ }
+ else
+ {
+ BOOL attachment_found = FALSE;
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == i)
+ {
+ LLMenuItemCallGL* item;
+ item = new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar,
+ object_selected_and_point_valid,
+ attachment);
+ gAttachPieMenu->append(item);
+
+ attachment_found = TRUE;
+ break;
+
+ }
+ }
+
+ if (!attachment_found)
+ {
+ gAttachPieMenu->appendSeparator();
+ }
+ }
+
+ if (gDetachBodyPartPieMenus[i])
+ {
+ gDetachPieMenu->appendMenu( gDetachBodyPartPieMenus[i] );
+ }
+ else
+ {
+ BOOL attachment_found = FALSE;
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == i)
+ {
+ gDetachPieMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+
+ attachment_found = TRUE;
+ break;
+ }
+ }
+
+ if (!attachment_found)
+ {
+ gDetachPieMenu->appendSeparator();
+ }
+ }
+ }
+
+ // add screen attachments
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == 8)
+ {
+ LLMenuItemCallGL* item;
+ item = new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar,
+ object_selected_and_point_valid,
+ attachment);
+ gAttachScreenPieMenu->append(item);
+ gDetachScreenPieMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+ }
+ }
+
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() != (pass == 1))
+ {
+ continue;
+ }
+ gAttachSubMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar, object_selected_and_point_valid, &attach_label, attachment));
+ gDetachSubMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, &detach_label, attachment));
+
+ }
+ if (pass == 0)
+ {
+ // put separator between non-hud and hud attachments
+ gAttachSubMenu->appendSeparator();
+ gDetachSubMenu->appendSeparator();
+ }
+ }
+
+ for (S32 group = 0; group < 8; group++)
+ {
+ // skip over groups that don't have sub menus
+ if (!gAttachBodyPartPieMenus[group] || !gDetachBodyPartPieMenus[group])
+ {
+ continue;
+ }
+
+ std::multimap<S32, LLViewerJointAttachment*> attachment_pie_menu_map;
+
+ // gather up all attachment points assigned to this group, and throw into map sorted by pie slice number
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if(attachment->getGroup() == group)
+ {
+ // use multimap to provide a partial order off of the pie slice key
+ attachment_pie_menu_map.insert(std::pair<S32, LLViewerJointAttachment*>(attachment->getPieSlice(), attachment));
+ }
+ }
+
+ // add in requested order to pie menu, inserting separators as necessary
+ std::multimap<S32, LLViewerJointAttachment*>::iterator attach_it;
+ S32 cur_pie_slice = 0;
+ for (attach_it = attachment_pie_menu_map.begin(); attach_it != attachment_pie_menu_map.end(); ++attach_it)
+ {
+ S32 requested_pie_slice = attach_it->first;
+ while (cur_pie_slice < requested_pie_slice)
+ {
+ gAttachBodyPartPieMenus[group]->appendSeparator();
+ gDetachBodyPartPieMenus[group]->appendSeparator();
+ cur_pie_slice++;
+ }
+
+ LLViewerJointAttachment* attachment = attach_it->second;
+
+ gAttachBodyPartPieMenus[group]->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar, object_selected_and_point_valid, attachment));
+ gDetachBodyPartPieMenus[group]->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+
+ cur_pie_slice++;
+ }
+ }
+ }
+
+ mMeshValid = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// releaseMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::releaseMeshData()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ if (sInstances.getLength() < AVATAR_AGP_RELEASE_THRESHOLD || mIsDummy)
+ {
+ return;
+ }
+
+ //llinfos << "Releasing" << llendl;
+
+ // cleanup mesh data
+ mHairLOD.setValid(FALSE, TRUE);
+ mHeadLOD.setValid(FALSE, TRUE);
+ mEyeLashLOD.setValid(FALSE, TRUE);
+ mUpperBodyLOD.setValid(FALSE, TRUE);
+ mLowerBodyLOD.setValid(FALSE, TRUE);
+ mEyeBallLeftLOD.setValid(FALSE, TRUE);
+ mEyeBallRightLOD.setValid(FALSE, TRUE);
+ mSkirtLOD.setValid(FALSE, TRUE);
+
+ //cleanup AGP data
+ if (mDrawable.notNull())
+ {
+ LLFace* facep = mDrawable->getFace(0);
+ facep->setSize(0, 0);
+ mNumAGPVertices = 0;
+
+ // You need to reset the vertex data in order to guarantee
+ // that the data is deallocated, AND you start at the 0 index
+ // when you restore the face. We're assuming ONLY ONE FACE per
+ // avatar pool, this logic is broken if that isn't the case!
+ facep->getPool()->resetVertexData(0);
+ }
+
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (!attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(FALSE);
+ }
+ }
+ mMeshValid = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// restoreMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::restoreMeshData()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //llinfos << "Restoring" << llendl;
+ mMeshValid = TRUE;
+ updateJointLODs();
+
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+ }
+ else
+ {
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (!attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ }
+ }
+
+ // force mesh update as LOD might not have changed to trigger this
+ updateMeshData();
+}
+
+//-----------------------------------------------------------------------------
+// updateMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateMeshData()
+{
+ if (mDrawable.notNull())
+ {
+ LLFace* facep = mDrawable->getFace(0);
+
+ U32 num_vertices = 0;
+
+ // this order is determined by number of LODS
+ // if a mesh earlier in this list changed LODs while a later mesh doesn't,
+ // the later mesh's index offset will be inaccurate
+ mEyeBallLeftLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mEyeBallRightLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mEyeLashLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mHeadLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mLowerBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mSkirtLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mUpperBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mHairLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+
+ if (num_vertices != mNumAGPVertices)
+ {
+ // resize immediately
+// llinfos << "Resizing avatar AGP buffer!" << llendl;
+ facep->getPool()->resetVertexData(num_vertices);
+ facep->setSize(0,0);
+ facep->setSize(num_vertices, 0);
+ mNumAGPVertices = num_vertices;
+ }
+
+ // This is a hack! Avatars have their own pool, so we are detecting
+ // the case of more than one avatar in the pool (thus > 0 instead of >= 0)
+ if (facep->getGeomIndex() > 0)
+ {
+ llerrs << "non-zero geom index: " << facep->getGeomIndex() << " in LLVOAvatar::restoreMeshData" << llendl;
+ }
+
+ mEyeBallLeftLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mEyeBallRightLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mEyeLashLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mHeadLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mLowerBodyLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mSkirtLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mUpperBodyLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mHairLOD.updateFaceData(facep, mAdjustedPixelArea, TRUE);
+ }
+}
+
+//------------------------------------------------------------------------
+
+//------------------------------------------------------------------------
+// The viewer can only suggest a good size for the agent,
+// the simulator will keep it inside a reasonable range.
+void LLVOAvatar::computeBodySize()
+{
+ LLVector3 pelvis_scale = mPelvisp->getScale();
+
+ // some of the joints have not been cached
+ LLVector3 skull = mSkullp->getPosition();
+ LLVector3 skull_scale = mSkullp->getScale();
+
+ LLVector3 neck = mNeckp->getPosition();
+ LLVector3 neck_scale = mNeckp->getScale();
+
+ LLVector3 chest = mChestp->getPosition();
+ LLVector3 chest_scale = mChestp->getScale();
+
+ // the rest of the joints have been cached
+ LLVector3 head = mHeadp->getPosition();
+ LLVector3 head_scale = mHeadp->getScale();
+
+ LLVector3 torso = mTorsop->getPosition();
+ LLVector3 torso_scale = mTorsop->getScale();
+
+ LLVector3 hip = mHipLeftp->getPosition();
+ LLVector3 hip_scale = mHipLeftp->getScale();
+
+ LLVector3 knee = mKneeLeftp->getPosition();
+ LLVector3 knee_scale = mKneeLeftp->getScale();
+
+ LLVector3 ankle = mAnkleLeftp->getPosition();
+ LLVector3 ankle_scale = mAnkleLeftp->getScale();
+
+ LLVector3 foot = mFootLeftp->getPosition();
+
+ mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] -
+ knee.mV[VZ] * hip_scale.mV[VZ] -
+ ankle.mV[VZ] * knee_scale.mV[VZ] -
+ foot.mV[VZ] * ankle_scale.mV[VZ];
+
+ mBodySize.mV[VZ] = mPelvisToFoot +
+ // the sqrt(2) correction below is an approximate
+ // correction to get to the top of the head
+ F_SQRT2 * (skull.mV[VZ] * head_scale.mV[VZ]) +
+ head.mV[VZ] * neck_scale.mV[VZ] +
+ neck.mV[VZ] * chest_scale.mV[VZ] +
+ chest.mV[VZ] * torso_scale.mV[VZ] +
+ torso.mV[VZ] * pelvis_scale.mV[VZ];
+
+ // TODO -- measure the real depth and width
+ mBodySize.mV[VX] = DEFAULT_AGENT_DEPTH;
+ mBodySize.mV[VY] = DEFAULT_AGENT_WIDTH;
+
+/* debug spam
+ std::cout << "skull = " << skull << std::endl; // adebug
+ std::cout << "head = " << head << std::endl; // adebug
+ std::cout << "head_scale = " << head_scale << std::endl; // adebug
+ std::cout << "neck = " << neck << std::endl; // adebug
+ std::cout << "neck_scale = " << neck_scale << std::endl; // adebug
+ std::cout << "chest = " << chest << std::endl; // adebug
+ std::cout << "chest_scale = " << chest_scale << std::endl; // adebug
+ std::cout << "torso = " << torso << std::endl; // adebug
+ std::cout << "torso_scale = " << torso_scale << std::endl; // adebug
+ std::cout << std::endl; // adebug
+
+ std::cout << "pelvis_scale = " << pelvis_scale << std::endl;// adebug
+ std::cout << std::endl; // adebug
+
+ std::cout << "hip = " << hip << std::endl; // adebug
+ std::cout << "hip_scale = " << hip_scale << std::endl; // adebug
+ std::cout << "ankle = " << ankle << std::endl; // adebug
+ std::cout << "ankle_scale = " << ankle_scale << std::endl; // adebug
+ std::cout << "foot = " << foot << std::endl; // adebug
+ std::cout << "mBodySize = " << mBodySize << std::endl; // adebug
+ std::cout << "mPelvisToFoot = " << mPelvisToFoot << std::endl; // adebug
+ std::cout << std::endl; // adebug
+*/
+}
+
+//------------------------------------------------------------------------
+// LLVOAvatar::processUpdateMessage()
+//------------------------------------------------------------------------
+U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLVector3 old_vel = getVelocity();
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ //llinfos << getRotation() << llendl;
+ //llinfos << getPosition() << llendl;
+ if (update_type == OUT_FULL )
+ {
+ if( !mIsSelf || !mFirstTEMessageReceived )
+ {
+// dumpAvatarTEs( "PRE processUpdateMessage()" );
+ unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
+// dumpAvatarTEs( "POST processUpdateMessage()" );
+
+ if( !mFirstTEMessageReceived )
+ {
+ onFirstTEMessageReceived();
+ }
+
+ // Disable updates to composites. We'll decide whether we need to do
+ // any updates after we find out whether this update message has any
+ // "baked" (pre-composited) textures.
+ setCompositeUpdatesEnabled( FALSE );
+ updateMeshTextures();
+ setCompositeUpdatesEnabled( TRUE );
+ }
+ }
+
+ return retval;
+}
+
+// virtual
+S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid)
+{
+ // The core setTETexture() method requests images, so we need
+ // to redirect certain avatar texture requests to different sims.
+ if (isTextureIndexBaked(te))
+ {
+ LLHost target_host = getObjectHost();
+ return setTETextureCore(te, uuid, target_host);
+ }
+ else
+ {
+ return setTETextureCore(te, uuid, LLHost::invalid);
+ }
+}
+
+
+// setTEImage
+
+//------------------------------------------------------------------------
+// idleUpdate()
+//------------------------------------------------------------------------
+BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+ LLFastTimer t(LLFastTimer::FTM_AVATAR_UPDATE);
+
+ if (isDead())
+ {
+ llinfos << "Warning! Idle on dead avatar" << llendl;
+ return TRUE;
+ }
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)))
+ {
+ return TRUE;
+ }
+
+ // force immediate pixel area update on avatars using last frames data (before drawable or camera updates)
+ setPixelAreaAndAngle(gAgent);
+
+ // Update the LOD of the joints
+ static const F32 UPDATE_TIME = .5f;
+ if (mUpdateLODTimer.hasExpired())
+ {
+ mUpdateLODTimer.setTimerExpirySec(UPDATE_TIME * (.75f + frand(0.5f)));
+ updateJointLODs();
+ }
+
+ // force asynchronous drawable update
+ if(mDrawable.notNull() && !gNoRender)
+ {
+ LLFastTimer t(LLFastTimer::FTM_JOINT_UPDATE);
+
+ if (mIsSitting && getParent())
+ {
+ LLViewerObject *root_object = (LLViewerObject*)getRoot();
+ LLDrawable* drawablep = root_object->mDrawable;
+ // if this object hasn't already been updated by another avatar...
+ if (drawablep) // && !drawablep->isState(LLDrawable::EARLY_MOVE))
+ {
+ if (root_object->isSelected())
+ {
+ gPipeline.updateMoveNormalAsync(drawablep);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(drawablep);
+ }
+ }
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(mDrawable);
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // set alpha flag depending on state
+ //--------------------------------------------------------------------
+
+ if (mIsSelf)
+ {
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ // trigger fidget anims
+ if (isAnyAnimationSignaled(AGENT_STAND_ANIMS, NUM_AGENT_STAND_ANIMS))
+ {
+ agent.fidget();
+ }
+ }
+ else
+ {
+ // Should override the idleUpdate stuff and leave out the angular update part.
+ LLQuaternion rotation = getRotation();
+ LLViewerObject::idleUpdate(agent, world, time);
+ setRotation(rotation);
+ }
+
+ // attach objects that were waiting for a drawable
+ lazyAttach();
+
+ // animate the character
+ // store off last frame's root position to be consistent with camera position
+ LLVector3 root_pos_last = mRoot.getWorldPosition();
+
+ updateCharacter(agent);
+
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ // HACK!!!!
+ // this is necessary for the floating name text above your head
+ if (mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ // update attachments positions
+ {
+ LLFastTimer t(LLFastTimer::FTM_ATTACHMENT_UPDATE);
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attachment->getValid())
+ {
+ // if selecting any attachments, update all of them as non-damped
+ if (gSelectMgr->getObjectCount() && gSelectMgr->selectionIsAttachment())
+ {
+ gPipeline.updateMoveNormalAsync(attached_object->mDrawable);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(attached_object->mDrawable);
+ }
+ attached_object->updateText();
+ }
+ }
+ }
+
+ //force a move if sitting on an active object
+ if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive())
+ {
+ gPipeline.markMoved(mDrawable, TRUE);
+ }
+
+ // update morphing params
+ if (mAppearanceAnimating)
+ {
+ ESex avatar_sex = getSex();
+ F32 appearance_anim_time = mAppearanceMorphTimer.getElapsedTimeF32();
+ if (appearance_anim_time >= APPEARANCE_MORPH_TIME)
+ {
+ mAppearanceAnimating = FALSE;
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->stopAnimating(mAppearanceAnimSetByUser);
+ }
+ }
+ updateVisualParams();
+ if (mIsSelf)
+ {
+ gAgent.sendAgentSetAppearance();
+ }
+ }
+ else
+ {
+ F32 blend_frac = calc_bouncy_animation(appearance_anim_time / APPEARANCE_MORPH_TIME);
+ F32 last_blend_frac = calc_bouncy_animation(mLastAppearanceBlendTime / APPEARANCE_MORPH_TIME);
+ F32 morph_amt;
+ if (last_blend_frac == 1.f)
+ {
+ morph_amt = 1.f;
+ }
+ else
+ {
+ morph_amt = (blend_frac - last_blend_frac) / (1.f - last_blend_frac);
+ }
+
+ LLVisualParam *param;
+
+ // animate only top level params
+ for (param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->animate(morph_amt, mAppearanceAnimSetByUser);
+ }
+ }
+
+ // apply all params
+ for (param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ param->apply(avatar_sex);
+ }
+
+ mLastAppearanceBlendTime = appearance_anim_time;
+ }
+ dirtyMesh();
+ }
+
+ // update wind effect
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR) >= LLDrawPoolAvatar::SHADER_LEVEL_CLOTH))
+ {
+ F32 hover_strength = 0.f;
+ F32 time_delta = mRippleTimer.getElapsedTimeF32() - mRippleTimeLast;
+ mRippleTimeLast = mRippleTimer.getElapsedTimeF32();
+ LLVector3 velocity = getVelocity();
+ F32 speed = velocity.magVec();
+ //RN: velocity varies too much frame to frame for this to work
+ mRippleAccel.clearVec();//lerp(mRippleAccel, (velocity - mLastVel) * time_delta, LLCriticalDamp::getInterpolant(0.02f));
+ mLastVel = velocity;
+ LLVector4 wind;
+ wind.setVec(getRegion()->mWind.getVelocityNoisy(getPositionAgent(), 4.f) - velocity);
+
+ if (mInAir)
+ {
+ hover_strength = HOVER_EFFECT_STRENGTH * llmax(0.f, HOVER_EFFECT_MAX_SPEED - speed);
+ }
+
+ if (mBelowWater)
+ {
+ // TODO: make cloth flow more gracefully when underwater
+ hover_strength += UNDERWATER_EFFECT_STRENGTH;
+ }
+
+ wind.mV[VZ] += hover_strength;
+ wind.normVec();
+
+ wind.mV[VW] = llmin(0.025f + (speed * 0.015f) + hover_strength, 0.5f);
+ F32 interp;
+ if (wind.mV[VW] > mWindVec.mV[VW])
+ {
+ interp = LLCriticalDamp::getInterpolant(0.2f);
+ }
+ else
+ {
+ interp = LLCriticalDamp::getInterpolant(0.4f);
+ }
+ mWindVec = lerp(mWindVec, wind, interp);
+
+ F32 wind_freq = hover_strength + llclamp(8.f + (speed * 0.7f) + (noise1(mRipplePhase) * 4.f), 8.f, 25.f);
+ mWindFreq = lerp(mWindFreq, wind_freq, interp);
+
+ if (mBelowWater)
+ {
+ mWindFreq *= UNDERWATER_FREQUENCY_DAMP;
+ }
+
+ mRipplePhase += (time_delta * mWindFreq);
+ if (mRipplePhase > F_TWO_PI)
+ {
+ mRipplePhase = fmodf(mRipplePhase, F_TWO_PI);
+ }
+ }
+
+ // update chat bubble
+ //--------------------------------------------------------------------
+ // draw text label over characters head
+ //--------------------------------------------------------------------
+ if (mChatTimer.getElapsedTimeF32() > BUBBLE_CHAT_TIME)
+ {
+ mChats.clear();
+ }
+
+ const F32 time_visible = mTimeVisible.getElapsedTimeF32();
+ const F32 NAME_SHOW_TIME = gSavedSettings.getF32("RenderNameShowTime"); // seconds
+ const F32 FADE_DURATION = gSavedSettings.getF32("RenderNameFadeDuration"); // seconds
+ BOOL visible_chat = gSavedSettings.getBOOL("UseChatBubbles") && (mChats.size() || mTyping);
+ BOOL render_name = visible_chat ||
+ (mVisible &&
+ (sRenderName == RENDER_NAME_ALWAYS) ||
+ (sRenderName == RENDER_NAME_FADE && time_visible < NAME_SHOW_TIME));
+ // If it's your own avatar, don't draw in mouselook, and don't
+ // draw if we're specifically hiding our own name.
+ if (mIsSelf)
+ {
+ render_name = render_name
+ && !gAgent.cameraMouselook()
+ && (visible_chat || !gSavedSettings.getBOOL("RenderNameHideSelf"));
+ }
+
+ if ( render_name )
+ {
+ BOOL new_name = FALSE;
+ if (visible_chat != mVisibleChat)
+ {
+ mVisibleChat = visible_chat;
+ new_name = TRUE;
+ }
+
+ // First Calculate Alpha
+ // If alpha > 0, create mNameText if necessary, otherwise delete it
+ {
+ F32 alpha = 0.f;
+ if (mAppAngle > 5.f)
+ {
+ const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION;
+ if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME)
+ {
+ alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION;
+ }
+ else
+ {
+ // ...not fading, full alpha
+ alpha = 1.f;
+ }
+ }
+ else if (mAppAngle > 2.f)
+ {
+ // far away is faded out also
+ alpha = (mAppAngle-2.f)/3.f;
+ }
+
+ if (alpha > 0.f)
+ {
+ if (!mNameText)
+ {
+ mNameText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mNameText->setMass(10.f);
+ mNameText->setSourceObject(this);
+ mNameText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mNameText->setVisibleOffScreen(TRUE);
+ mNameText->setMaxLines(11);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+ mNameText->setUseBubble(TRUE);
+ sNumVisibleChatBubbles++;
+ new_name = TRUE;
+ }
+
+ LLColor4 avatar_name_color = gColors.getColor( "AvatarNameColor" );
+ avatar_name_color.setAlpha(alpha);
+ mNameText->setColor(avatar_name_color);
+
+ LLQuaternion root_rot = mRoot.getWorldRotation();
+ mNameText->setUsePixelSize(TRUE);
+ LLVector3 pixel_right_vec;
+ LLVector3 pixel_up_vec;
+ gCamera->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec);
+ LLVector3 camera_to_av = root_pos_last - gCamera->getOrigin();
+ camera_to_av.normVec();
+ LLVector3 local_camera_at = camera_to_av * ~root_rot;
+ LLVector3 local_camera_up = camera_to_av % gCamera->getLeftAxis();
+ local_camera_up.normVec();
+ local_camera_up = local_camera_up * ~root_rot;
+
+ local_camera_up.scaleVec(mBodySize * 0.5f);
+ local_camera_at.scaleVec(mBodySize * 0.5f);
+
+ LLVector3 name_position = mRoot.getWorldPosition() +
+ (local_camera_up * root_rot) -
+ (projected_vec(local_camera_at * root_rot, camera_to_av));
+ name_position += pixel_up_vec * 15.f;
+ mNameText->setPositionAgent(name_position);
+ }
+ else if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+ }
+
+ LLNameValue *title = getNVPair("Title");
+ LLNameValue* firstname = getNVPair("FirstName");
+ LLNameValue* lastname = getNVPair("LastName");
+
+ if (mNameText.notNull() && firstname && lastname)
+ {
+ BOOL is_away = mSignaledAnimations.find(ANIM_AGENT_AWAY) != mSignaledAnimations.end();
+ BOOL is_busy = mSignaledAnimations.find(ANIM_AGENT_BUSY) != mSignaledAnimations.end();
+ BOOL is_appearance = mSignaledAnimations.find(ANIM_AGENT_CUSTOMIZE) != mSignaledAnimations.end();
+ BOOL is_muted;
+ if (mIsSelf)
+ {
+ is_muted = FALSE;
+ }
+ else
+ {
+ is_muted = gMuteListp->isMuted(getID());
+ }
+
+ if (mNameString.empty() ||
+ new_name ||
+ (!title && !mTitle.empty()) ||
+ (title && mTitle != title->getString()) ||
+ (is_away != mNameAway || is_busy != mNameBusy || is_muted != mNameMute)
+ || is_appearance != mNameAppearance)
+ {
+ char line[MAX_STRING];
+ if (title && title->getString() && title->getString()[0] != '\0')
+ {
+ strcpy(line, title->getString() );
+ strcat(line, "\n");
+ strcat(line, firstname->getString() );
+ }
+ else
+ {
+ strcpy(line, firstname->getString() );
+ }
+
+ strcat(line, " ");
+ strcat(line, lastname->getString());
+ BOOL need_comma = FALSE;
+
+ if (is_away || is_muted || is_busy)
+ {
+ strcat(line, " (");
+ if (is_away)
+ {
+ strcat(line, "Away");
+ need_comma = TRUE;
+ }
+ if (is_busy)
+ {
+ if (need_comma)
+ {
+ strcat(line, ", ");
+ }
+ strcat(line, "Busy");
+ need_comma = TRUE;
+ }
+ if (is_muted)
+ {
+ if (need_comma)
+ {
+ strcat(line, ", ");
+ }
+ strcat(line, "Muted");
+ need_comma = TRUE;
+ }
+ strcat(line,")");
+ }
+ if (is_appearance)
+ {
+ strcat(line, "\n(Editing Appearance)");
+ }
+ mNameAway = is_away;
+ mNameBusy = is_busy;
+ mNameMute = is_muted;
+ mNameAppearance = is_appearance;
+ mTitle = title ? title->getString() : "";
+ mNameString = utf8str_to_wstring(line);
+ new_name = TRUE;
+ }
+
+ if (visible_chat)
+ {
+ mNameText->setDropShadow(TRUE);
+ mNameText->setFont(LLFontGL::sSansSerif);
+ mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_LEFT);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f);
+ if (new_name)
+ {
+ mNameText->setLabel(mNameString);
+ }
+
+ char line[MAX_STRING];
+ line[0] = '\0';
+ std::deque<LLChat>::iterator chat_iter = mChats.begin();
+ mNameText->clearString();
+
+ LLColor4 new_chat = gColors.getColor( "AvatarNameColor" );
+ LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f);
+ LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f);
+ if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES)
+ {
+ ++chat_iter;
+ }
+
+ for(; chat_iter != mChats.end(); ++chat_iter)
+ {
+ F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f);
+ LLFontGL::StyleFlags style;
+ switch(chat_iter->mChatType)
+ {
+ case CHAT_TYPE_WHISPER:
+ style = LLFontGL::ITALIC;
+ break;
+ case CHAT_TYPE_SHOUT:
+ style = LLFontGL::BOLD;
+ break;
+ default:
+ style = LLFontGL::NORMAL;
+ break;
+ }
+ if (chat_fade_amt < 1.f)
+ {
+ F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f);
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(new_chat, normal_chat, u), style);
+ }
+ else if (chat_fade_amt < 2.f)
+ {
+ F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f);
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(normal_chat, old_chat, u), style);
+ }
+ else if (chat_fade_amt < 3.f)
+ {
+ //FIXME: only remove lines down to minimum number
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), old_chat, style);
+ }
+ }
+ mNameText->setVisibleOffScreen(TRUE);
+
+ if (mTyping)
+ {
+ S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1;
+ switch(dot_count)
+ {
+ case 1:
+ mNameText->addLine(".", new_chat);
+ break;
+ case 2:
+ mNameText->addLine("..", new_chat);
+ break;
+ case 3:
+ mNameText->addLine("...", new_chat);
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ if (gSavedSettings.getBOOL("SmallAvatarNames"))
+ {
+ mNameText->setFont(LLFontGL::sSansSerif);
+ }
+ else
+ {
+ mNameText->setFont(LLFontGL::sSansSerifBig);
+ }
+ mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_CENTER);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+ mNameText->setVisibleOffScreen(FALSE);
+ if (new_name)
+ {
+ mNameText->setLabel("");
+ mNameText->setString(mNameString);
+ }
+ }
+ }
+ }
+ else if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+
+ //--------------------------------------------------------------------
+ // draw tractor beam when editing objects
+ //--------------------------------------------------------------------
+ if (!mIsSelf)
+ {
+ return TRUE;
+ }
+
+ // This is only done for yourself (maybe it should be in the agent?)
+ if (!needsRenderBeam() || !mIsBuilt)
+ {
+ mBeam = NULL;
+ }
+ else if (!mBeam || mBeam->isDead())
+ {
+ // VEFFECT: Tractor Beam
+ mBeam = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM);
+ mBeam->setColor(LLColor4U(gAgent.getEffectColor()));
+ mBeam->setSourceObject(this);
+ mBeamTimer.reset();
+ }
+
+ if (!mBeam.isNull())
+ {
+ if (gAgent.mPointAt.notNull())
+ {
+ // get point from pointat effect
+ mBeam->setPositionGlobal(gAgent.mPointAt->getPointAtPosGlobal());
+ mBeam->triggerLocal();
+ }
+ else if (gSelectMgr->getFirstRootObject() &&
+ gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ mBeam->setTargetObject(objectp);
+ }
+ else
+ {
+ mBeam->setTargetObject(NULL);
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (tool->isEditing())
+ {
+ if (tool->getEditingObject())
+ {
+ mBeam->setTargetObject(tool->getEditingObject());
+ }
+ else
+ {
+ mBeam->setPositionGlobal(tool->getEditingPointGlobal());
+ }
+ }
+ else
+ {
+ mBeam->setPositionGlobal(gLastHitNonFloraPosGlobal + gLastHitNonFloraObjectOffset);
+ }
+
+ }
+ if (mBeamTimer.getElapsedTimeF32() > 0.25f)
+ {
+ mBeam->setColor(LLColor4U(gAgent.getEffectColor()));
+ mBeam->setNeedsSendToSim(TRUE);
+ mBeamTimer.reset();
+ }
+ }
+
+ F32 avatar_height = (F32)(getPositionGlobal().mdV[VZ]);
+
+ F32 water_height;
+ water_height = getRegion()->getWaterHeight();
+
+ mBelowWater = avatar_height < water_height;
+
+ return TRUE;
+}
+
+void LLVOAvatar::slamPosition()
+{
+ gAgent.setPositionAgent(getPositionAgent());
+ mRoot.setWorldPosition(getPositionAgent()); // teleport
+ setChanged(TRANSLATED);
+ if (mDrawable.notNull())
+ {
+ gPipeline.updateMoveNormalAsync(mDrawable);
+ }
+ mRoot.updateWorldMatrixChildren();
+}
+
+//------------------------------------------------------------------------
+// updateCharacter()
+// called on both your avatar and other avatars
+//------------------------------------------------------------------------
+void LLVOAvatar::updateCharacter(LLAgent &agent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ // clear debug text
+ mDebugText.clear();
+
+ if (LLVOAvatar::sShowAnimationDebug)
+ {
+ LLString playing_anims;
+ for (LLMotion* motionp = mMotionController.getFirstActiveMotion();
+ motionp;
+ motionp = mMotionController.getNextActiveMotion())
+ {
+ if (motionp->getMinPixelArea() < getPixelArea())
+ {
+ char output[256];
+ if (motionp->getName().empty())
+ {
+ sprintf(output, "%s - %d", motionp->getID().getString().c_str(), (U32)motionp->getPriority());
+ }
+ else
+ {
+ sprintf(output, "%s - %d", motionp->getName().c_str(), (U32)motionp->getPriority());
+ }
+ addDebugText(output);
+ }
+ }
+ }
+
+ if (LLVOAvatar::sJointDebug)
+ {
+ llinfos << getNVPair("FirstName")->getString() << ": joint touches: " << LLJoint::sNumTouches << " updates: " << LLJoint::sNumUpdates << llendl;
+
+ LLJoint::sNumUpdates = 0;
+ LLJoint::sNumTouches = 0;
+ }
+// llinfos << mPixelArea << llendl;
+ if (gNoRender)
+ {
+ // Hack if we're running drones...
+ if (mIsSelf)
+ {
+ gAgent.setPositionAgent(getPositionAgent());
+ }
+ return;
+ }
+
+
+ LLVector3d root_pos_global;
+
+ if (!mIsBuilt)
+ {
+ return;
+ }
+
+ // change animation time quanta based on avatar render load
+ if (!mIsSelf)
+ {
+ F32 time_quantum = clamp_rescale((F32)sInstances.getLength(), 10.f, 35.f, 0.f, 0.25f);
+ F32 pixel_area_scale = clamp_rescale(mPixelArea, 100, 5000, 1.f, 0.f);
+ F32 time_step = time_quantum * pixel_area_scale;
+ if (time_step != 0.f)
+ {
+ // disable walk motion servo controller as it doesn't work with motion timesteps
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ removeAnimationData("Walk Speed");
+ }
+ mMotionController.setTimeStep(time_step);
+// llinfos << "Setting timestep to " << time_quantum * pixel_area_scale << llendl;
+ }
+
+ if (getParent() && !mIsSitting)
+ {
+ sitOnObject((LLViewerObject*)getParent());
+ }
+ else if (!getParent() && mIsSitting && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED))
+ {
+ getOffObject();
+ }
+
+ // For fading out the names above heads, only let the timer
+ // run if we're visible.
+ if (mDrawable.notNull() && !mDrawable->isVisible())
+ {
+ mTimeVisible.reset();
+ }
+
+ // update screen joint size
+ mScreenp->setScale(LLVector3(1.f, gCamera->getAspect(), 1.f));
+ mScreenp->updateWorldMatrixChildren();
+
+ //--------------------------------------------------------------------
+ // create local variables in world coords for region position values
+ //--------------------------------------------------------------------
+ F32 speed;
+ LLVector3 normal;
+
+ LLVector3 xyVel = getVelocity();
+ xyVel.mV[VZ] = 0.0f;
+ speed = xyVel.magVec();
+
+ if (!(mIsSitting && getParent()))
+ {
+ //--------------------------------------------------------------------
+ // get timing info
+ // handle initial condition case
+ //--------------------------------------------------------------------
+ F32 animation_time = mAnimTimer.getElapsedTimeF32();
+ if (mTimeLast == 0.0f)
+ {
+ mTimeLast = animation_time;
+
+ // put the pelvis at slaved position/mRotation
+ mRoot.setWorldPosition( getPositionAgent() ); // first frame
+ mRoot.setWorldRotation( getRotation() );
+ }
+
+ //--------------------------------------------------------------------
+ // dont' let dT get larger than 1/5th of a second
+ //--------------------------------------------------------------------
+ F32 deltaTime = animation_time - mTimeLast;
+
+ deltaTime = llclamp( deltaTime, DELTA_TIME_MIN, DELTA_TIME_MAX );
+ mTimeLast = animation_time;
+
+ mSpeedAccum = (mSpeedAccum * 0.95f) + (speed * 0.05f);
+
+ //--------------------------------------------------------------------
+ // compute the position of the avatar's root
+ //--------------------------------------------------------------------
+ LLVector3d root_pos;
+ LLVector3d ground_under_pelvis;
+
+ if (mIsSelf)
+ {
+ gAgent.setPositionAgent(getRenderPosition());
+ }
+
+ root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition());
+
+ resolveHeightGlobal(root_pos, ground_under_pelvis, normal);
+ F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]);
+ BOOL in_air = ( (!gWorldPointer->getRegionFromPosGlobal(ground_under_pelvis)) ||
+ foot_to_ground > FOOT_GROUND_COLLISION_TOLERANCE);
+
+ if (in_air && !mInAir)
+ {
+ mTimeInAir.reset();
+ }
+ mInAir = in_air;
+
+ // correct for the fact that the pelvis is not necessarily the center
+ // of the agent's physical representation
+ root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot;
+ mRoot.setWorldPosition(gAgent.getPosAgentFromGlobal(root_pos) ); // regular update
+
+ //--------------------------------------------------------------------
+ // Propagate viewer object rotation to root of avatar
+ //--------------------------------------------------------------------
+ if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))
+ {
+ LLQuaternion iQ;
+ LLVector3 upDir( 0.0f, 0.0f, 1.0f );
+
+ // Compute a forward direction vector derived from the primitive rotation
+ // and the velocity vector. When walking or jumping, don't let body deviate
+ // more than 90 from the view, if necessary, flip the velocity vector.
+
+ LLVector3 primDir;
+ if (mIsSelf)
+ {
+ primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector());
+ primDir.normVec();
+ }
+ else
+ {
+ primDir = getRotation().getMatrix3().getFwdRow();
+ }
+ LLVector3 velDir = getVelocity();
+ velDir.normVec();
+ if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end())
+ {
+ F32 vpD = velDir * primDir;
+ if (vpD < -0.5f)
+ {
+ velDir *= -1.0f;
+ }
+ }
+ LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f));
+ if (mIsSelf && gAgent.cameraMouselook())
+ {
+ // make sure fwdDir stays in same general direction as primdir
+ if (gAgent.getFlying())
+ {
+ fwdDir = gCamera->getAtAxis();
+ }
+ else
+ {
+ LLVector3 at_axis = gCamera->getAtAxis();
+ LLVector3 up_vector = gAgent.getReferenceUpVector();
+ at_axis -= up_vector * (at_axis * up_vector);
+ at_axis.normVec();
+
+ F32 dot = fwdDir * at_axis;
+ if (dot < 0.f)
+ {
+ fwdDir -= 2.f * at_axis * dot;
+ fwdDir.normVec();
+ }
+ }
+
+ }
+
+ LLQuaternion root_rotation = mRoot.getWorldMatrix().quaternion();
+ F32 root_roll, root_pitch, root_yaw;
+ root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw);
+
+ if (gDebugAvatarRotation)
+ {
+ llinfos << "root_roll " << RAD_TO_DEG * root_roll
+ << " root_pitch " << RAD_TO_DEG * root_pitch
+ << " root_yaw " << RAD_TO_DEG * root_yaw
+ << llendl;
+ }
+
+ // When moving very slow, the pelvis is allowed to deviate from the
+ // forward direction to allow it to hold it's position while the torso
+ // and head turn. Once in motion, it must conform however.
+ BOOL self_in_mouselook = mIsSelf && gAgent.cameraMouselook();
+
+ LLVector3 pelvisDir( mRoot.getWorldMatrix().getFwdRow4().mV );
+ F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, PELVIS_ROT_THRESHOLD_SLOW, PELVIS_ROT_THRESHOLD_FAST);
+
+ //Ventrella
+ //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
+ //{
+ // pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, 1.0f, 1.0f);
+ //}
+ //end Ventrella
+
+ if (self_in_mouselook)
+ {
+ pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR;
+ }
+ pelvis_rot_threshold *= DEG_TO_RAD;
+
+ F32 angle = angle_between( pelvisDir, fwdDir );
+
+ // The avatar's root is allowed to have a yaw that deviates widely
+ // from the forward direction, but if roll or pitch are off even
+ // a little bit we need to correct the rotation.
+ if(root_roll < 1.f * DEG_TO_RAD
+ && root_pitch < 5.f * DEG_TO_RAD)
+ {
+ // smaller correction vector means pelvis follows prim direction more closely
+ if (!mTurning && angle > pelvis_rot_threshold*0.75f)
+ {
+ mTurning = TRUE;
+ }
+
+ // use tighter threshold when turning
+ if (mTurning)
+ {
+ pelvis_rot_threshold *= 0.4f;
+ }
+
+ // am I done turning?
+ if (angle < pelvis_rot_threshold)
+ {
+ mTurning = FALSE;
+ }
+
+ LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f);
+ fwdDir += correction_vector;
+ }
+ else
+ {
+ mTurning = FALSE;
+ }
+
+ // Now compute the full world space rotation for the whole body (wQv)
+ LLVector3 leftDir = upDir % fwdDir;
+ leftDir.normVec();
+ fwdDir = leftDir % upDir;
+ LLQuaternion wQv( fwdDir, leftDir, upDir );
+
+ if (mIsSelf && mTurning)
+ {
+ if ((fwdDir % pelvisDir) * upDir > 0.f)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT);
+ }
+ else
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT);
+ }
+ }
+
+ // Set the root rotation, but do so incrementally so that it
+ // lags in time by some fixed amount.
+ //F32 u = LLCriticalDamp::getInterpolant(PELVIS_LAG);
+ F32 pelvis_lag_time = 0.f;
+ if (self_in_mouselook)
+ {
+ pelvis_lag_time = PELVIS_LAG_MOUSELOOK;
+ }
+ else if (mInAir)
+ {
+ pelvis_lag_time = PELVIS_LAG_FLYING;
+ // increase pelvis lag time when moving slowly
+ pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f);
+ }
+ else
+ {
+ pelvis_lag_time = PELVIS_LAG_WALKING;
+ }
+
+ //Ventrella
+ //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
+ //{
+ // pelvis_lag_time = PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON;
+ //}
+ //end Ventrella
+
+ F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);
+
+ mRoot.setWorldRotation( slerp(u, mRoot.getWorldRotation(), wQv) );
+ }
+ }
+ else if (mDrawable.notNull())
+ {
+ mRoot.setPosition(mDrawable->getPosition());
+ mRoot.setRotation(mDrawable->getRotation());
+ }
+
+ //--------------------------------------------------------------------
+ // the rest should only be done when close enough to see it
+ //--------------------------------------------------------------------
+ if ( !(mIsSitting && getParent()) &&
+ ((mPixelArea < 12.0f) ||
+ (!isVisible() && (mPixelArea < MIN_PIXEL_AREA_FOR_COMPOSITE))) )
+ {
+ mRoot.setWorldRotation( getRotation() );
+ mRoot.updateWorldMatrixChildren();
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // Update character motions
+ //-------------------------------------------------------------------------
+ // store data relevant to motions
+ mSpeed = speed;
+
+ // update animations
+ {
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_ANIMATION);
+ updateMotion();
+ }
+
+ // update head position
+ updateHeadOffset();
+
+ //-------------------------------------------------------------------------
+ // Find the ground under each foot, these are used for a variety
+ // of things that follow
+ //-------------------------------------------------------------------------
+ LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition();
+
+ LLVector3 ankle_left_ground_agent = ankle_left_pos_agent;
+ LLVector3 ankle_right_ground_agent = ankle_right_pos_agent;
+ resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal);
+ resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal);
+
+ F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]);
+ F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]);
+
+ if (!mIsSitting)
+ {
+ //-------------------------------------------------------------------------
+ // Figure out which foot is on ground
+ //-------------------------------------------------------------------------
+ if (!mInAir)
+ {
+ if ((leftElev < 0.0f) || (rightElev < 0.0f))
+ {
+ ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ ankle_right_pos_agent = mFootRightp->getWorldPosition();
+ leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ];
+ rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ];
+ }
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Generate footstep sounds when feet hit the ground
+ //-------------------------------------------------------------------------
+ const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND};
+ const S32 NUM_AGENT_FOOTSTEP_ANIMS = sizeof(AGENT_FOOTSTEP_ANIMS) / sizeof(LLUUID);
+
+ if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) )
+ {
+ BOOL playSound = FALSE;
+ LLVector3 foot_pos_agent;
+
+ BOOL onGroundLeft = (leftElev <= 0.05f);
+ BOOL onGroundRight = (rightElev <= 0.05f);
+
+ // did left foot hit the ground?
+ if ( onGroundLeft && !mWasOnGroundLeft )
+ {
+ foot_pos_agent = ankle_left_pos_agent;
+ playSound = TRUE;
+ }
+
+ // did right foot hit the ground?
+ if ( onGroundRight && !mWasOnGroundRight )
+ {
+ foot_pos_agent = ankle_right_pos_agent;
+ playSound = TRUE;
+ }
+
+ mWasOnGroundLeft = onGroundLeft;
+ mWasOnGroundRight = onGroundRight;
+
+ if ( playSound )
+ {
+// F32 gain = clamp_rescale( mSpeedAccum,
+// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED,
+// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN );
+
+ F32 gain = gSavedSettings.getF32("AudioLevelFootsteps");
+ LLUUID& step_sound_id = getStepSound();
+
+ LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent);
+
+ if (gParcelMgr && gParcelMgr->canHearSound(foot_pos_global)
+ && gMuteListp && !gMuteListp->isMuted(getID()))
+ {
+ gAudiop->triggerSound(step_sound_id, getID(), gain, foot_pos_global);
+ }
+ }
+ }
+
+ mRoot.updateWorldMatrixChildren();
+
+ if (!mDebugText.size() && mText.notNull())
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+ else if (mDebugText.size())
+ {
+ setDebugText(mDebugText);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateHeadOffset()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateHeadOffset()
+{
+ // since we only care about Z, just grab one of the eyes
+ LLVector3 midEyePt = mEyeLeftp->getWorldPosition();
+ midEyePt -= mDrawable.notNull() ? mDrawable->getWorldPosition() : mRoot.getWorldPosition();
+ midEyePt.mV[VZ] = llmax(-mPelvisToFoot + gCamera->getNear(), midEyePt.mV[VZ]);
+
+ if (mDrawable.notNull())
+ {
+ midEyePt = midEyePt * ~mDrawable->getWorldRotation();
+ }
+ if (mIsSitting)
+ {
+ mHeadOffset = midEyePt;
+ }
+ else
+ {
+ F32 u = llmax(0.f, HEAD_MOVEMENT_AVG_TIME - (1.f / gFPSClamped));
+ mHeadOffset = lerp(midEyePt, mHeadOffset, u);
+ }
+}
+
+//------------------------------------------------------------------------
+// updateVisibility()
+//------------------------------------------------------------------------
+void LLVOAvatar::updateVisibility(BOOL force_invisible)
+{
+ BOOL visible = FALSE;
+
+ if (mIsDummy)
+ {
+ visible = TRUE;
+ }
+ else if (mDrawable.isNull())
+ {
+ visible = FALSE;
+ }
+ else if (!force_invisible)
+ {
+ // calculate avatar distance wrt head
+ LLVector3 pos = mDrawable->getPositionAgent();
+ pos -= gCamera->getOrigin();
+ mDrawable->mDistanceWRTCamera = pos.magVec();
+
+ if (!mDrawable->getSpatialGroup() || mDrawable->getSpatialGroup()->isVisible())
+ {
+ visible = TRUE;
+ }
+ else
+ {
+ visible = FALSE;
+ }
+
+ if( mIsSelf )
+ {
+ if( !gAgent.areWearablesLoaded())
+ {
+ visible = FALSE;
+ }
+ }
+ else
+ if( !mFirstAppearanceMessageReceived )
+ {
+ visible = FALSE;
+ }
+
+ if (sDebugInvisible)
+ {
+ LLNameValue* firstname = getNVPair("FirstName");
+ if (firstname)
+ {
+ llinfos << "Avatar " << firstname->getString() << " updating visiblity" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar " << this << " updating visiblity" << llendl;
+ }
+
+ if (visible)
+ {
+ llinfos << "Visible" << llendl;
+ }
+ else
+ {
+ llinfos << "Not visible" << llendl;
+ }
+
+ /*if (avatar_in_frustum)
+ {
+ llinfos << "Avatar in frustum" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar not in frustum" << llendl;
+ }*/
+
+ /*if (gCamera->sphereInFrustum(sel_pos_agent, 2.0f))
+ {
+ llinfos << "Sel pos visible" << llendl;
+ }
+ if (gCamera->sphereInFrustum(wrist_right_pos_agent, 0.2f))
+ {
+ llinfos << "Wrist pos visible" << llendl;
+ }
+ if (gCamera->sphereInFrustum(getPositionAgent(), getMaxScale()*2.f))
+ {
+ llinfos << "Agent visible" << llendl;
+ }*/
+ llinfos << "PA: " << getPositionAgent() << llendl;
+ /*llinfos << "SPA: " << sel_pos_agent << llendl;
+ llinfos << "WPA: " << wrist_right_pos_agent << llendl;*/
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getObject(0))
+ {
+ if(attachment->getObject(0)->mDrawable->isVisible())
+ {
+ llinfos << attachment->getName() << " visible" << llendl;
+ }
+ else
+ {
+ llinfos << attachment->getName() << " not visible at " << mDrawable->getWorldPosition() << " and radius " << mDrawable->getRadius() << llendl;
+ }
+ }
+ }
+ }
+ }
+
+ if (!visible && mVisible)
+ {
+ mMeshInvisibleTime.reset();
+ }
+
+ if (visible)
+ {
+ if (!mMeshValid)
+ {
+ restoreMeshData();
+ }
+ gPipeline.markVisible(mDrawable);
+ }
+ else
+ {
+ if (mMeshValid && mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP)
+ {
+ releaseMeshData();
+ }
+ // this breaks off-screen chat bubbles
+ //if (mNameText)
+ //{
+ // mNameText->markDead();
+ // mNameText = NULL;
+ // sNumVisibleChatBubbles--;
+ //}
+ }
+
+ mVisible = visible;
+}
+
+//------------------------------------------------------------------------
+// updateAllVisibility()
+//------------------------------------------------------------------------
+//static
+void LLVOAvatar::updateAllAvatarVisiblity()
+{
+ LLVOAvatar::sNumVisibleAvatars = 0;
+ LLVOAvatar *avatarp;
+
+ F32 render_priority = (F32)LLVOAvatar::sMaxVisible;
+ for (avatarp = (LLVOAvatar*)sInstances.getFirstData(); avatarp; avatarp = (LLVOAvatar*)sInstances.getNextData())
+ {
+ if (avatarp->isDead())
+ {
+ continue;
+ }
+ if (avatarp->isSelf())
+ {
+ avatarp->mRenderPriority = 1000.f;
+ }
+ else
+ {
+ avatarp->mRenderPriority = render_priority * 10.f; // 500 -> 10
+ if (render_priority > 0.f)
+ {
+ render_priority -= 1.f;
+ }
+ }
+ avatarp->updateVisibility(LLVOAvatar::sNumVisibleAvatars > LLVOAvatar::sMaxVisible);
+
+ if (avatarp->mDrawable.isNull())
+ {
+ llwarns << "Avatar with no drawable" << llendl;
+ }
+ else if (avatarp->mDrawable->isVisible())
+ {
+ LLVOAvatar::sNumVisibleAvatars++;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// needsRenderBeam()
+//------------------------------------------------------------------------
+BOOL LLVOAvatar::needsRenderBeam()
+{
+ if (gNoRender)
+ {
+ return FALSE;
+ }
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+
+ BOOL is_touching_or_grabbing = (tool == gToolGrab && gToolGrab->isEditing());
+ if (gToolGrab->getEditingObject() &&
+ gToolGrab->getEditingObject()->isAttachment())
+ {
+ // don't render selection beam on hud objects
+ is_touching_or_grabbing = FALSE;
+ }
+ return is_touching_or_grabbing || (mState & AGENT_STATE_EDITING && gSelectMgr->shouldShowSelection());
+}
+
+//-----------------------------------------------------------------------------
+// renderSkinned()
+//-----------------------------------------------------------------------------
+U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
+{
+ U32 num_indices = 0;
+
+ if (!mIsBuilt)
+ {
+ return num_indices;
+ }
+
+ if (sDebugInvisible)
+ {
+ LLNameValue* firstname = getNVPair("FirstName");
+ if (firstname)
+ {
+ llinfos << "Avatar " << firstname->getString() << " in render" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar " << this << " in render" << llendl;
+ }
+ if (!mIsBuilt)
+ {
+ llinfos << "Not built!" << llendl;
+ }
+ else if (!gAgent.needsRenderAvatar())
+ {
+ llinfos << "Doesn't need avatar render!" << llendl;
+ }
+ else
+ {
+ llinfos << "Rendering!" << llendl;
+ }
+ }
+
+ if (!mIsBuilt)
+ {
+ return num_indices;
+ }
+
+ if (mIsSelf && !gAgent.needsRenderAvatar())
+ {
+ return num_indices;
+ }
+
+ // render collision normal
+ if (sShowFootPlane && mDrawable.notNull())
+ {
+ LLVector3 slaved_pos = mDrawable->getPositionAgent();
+ LLVector3 foot_plane_normal(mFootPlane.mV[VX], mFootPlane.mV[VY], mFootPlane.mV[VZ]);
+ F32 dist_from_plane = (slaved_pos * foot_plane_normal) - mFootPlane.mV[VW];
+ LLVector3 collide_point = slaved_pos;
+ collide_point.mV[VZ] -= foot_plane_normal.mV[VZ] * (dist_from_plane + COLLISION_TOLERANCE - FOOT_COLLIDE_FUDGE);
+
+ glBegin(GL_LINES);
+ {
+ F32 SQUARE_SIZE = 0.2f;
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX], collide_point.mV[VY], collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + mFootPlane.mV[VX], collide_point.mV[VY] + mFootPlane.mV[VY], collide_point.mV[VZ] + mFootPlane.mV[VZ]);
+
+ }glEnd();
+ }
+ //--------------------------------------------------------------------
+ // render all geomety attached to the skeleton
+ //--------------------------------------------------------------------
+ static LLStat render_stat;
+
+ LLViewerJointMesh::sRenderPass = pass;
+
+ if (pass == AVATAR_RENDER_PASS_SINGLE)
+ {
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mHeadLOD.render(mAdjustedPixelArea);
+ }
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ if( isWearingWearableType( WT_SKIRT ) )
+ {
+ glAlphaFunc(GL_GREATER,0.25f);
+ num_indices += mSkirtLOD.render(mAdjustedPixelArea);
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mEyeLashLOD.render(mAdjustedPixelArea);
+ num_indices += mHairLOD.render(mAdjustedPixelArea);
+ }
+ }
+ else if (pass == AVATAR_RENDER_PASS_CLOTHING_INNER)
+ {
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mHeadLOD.render(mAdjustedPixelArea);
+ }
+ LLViewerJointMesh::sClothingInnerColor = mTexSkinColor->getColor() * 0.5f;
+ LLViewerJointMesh::sClothingMaskImageName = mUpperMaskTexName;
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = mLowerMaskTexName;
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = 0;
+ if( isWearingWearableType( WT_SKIRT ) )
+ {
+ glAlphaFunc(GL_GREATER,0.25f);
+ num_indices += mSkirtLOD.render(mAdjustedPixelArea);
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mEyeLashLOD.render(mAdjustedPixelArea);
+ num_indices += mHairLOD.render(mAdjustedPixelArea);
+ }
+ }
+ else if (pass == AVATAR_RENDER_PASS_CLOTHING_OUTER)
+ {
+ LLViewerJointMesh::sClothingInnerColor = mTexSkinColor->getColor() * 0.5f;
+ LLViewerJointMesh::sClothingMaskImageName = mUpperMaskTexName;
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = mLowerMaskTexName;
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = 0;
+ }
+
+ LLViewerJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
+
+ //llinfos << "Avatar render: " << render_timer.getElapsedTimeF32() << llendl;
+
+ //render_stat.addValue(render_timer.getElapsedTimeF32()*1000.f);
+
+ return num_indices;
+}
+
+//-----------------------------------------------------------------------------
+// renderRigid()
+//-----------------------------------------------------------------------------
+U32 LLVOAvatar::renderRigid()
+{
+ U32 num_indices = 0;
+
+ if (!mIsBuilt)
+ {
+ return 0;
+ }
+
+ if (mIsSelf && (!gAgent.needsRenderAvatar() || !gAgent.needsRenderHead()))
+ {
+ return 0;
+ }
+
+ if (!mIsBuilt)
+ {
+ return 0;
+ }
+
+ num_indices += mEyeBallLeftLOD.render(mAdjustedPixelArea);
+ num_indices += mEyeBallRightLOD.render(mAdjustedPixelArea);
+
+ return num_indices;
+}
+
+//-----------------------------------------------------------------------------
+// renderCollisionVolumes()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::renderCollisionVolumes()
+{
+ for (S32 i = 0; i < mNumCollisionVolumes; i++)
+ {
+ mCollisionVolumes[i].render();
+ }
+}
+
+//------------------------------------------------------------------------
+// LLVOAvatar::updateTextures()
+//------------------------------------------------------------------------
+void LLVOAvatar::updateTextures(LLAgent &agent)
+{
+ BOOL render_avatar = TRUE;
+
+ if (mIsDummy || gNoRender)
+ {
+ return;
+ }
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if( mIsSelf )
+ {
+ render_avatar = TRUE;
+ }
+ else
+ {
+ render_avatar = isVisible() && !mCulled;
+ }
+
+ // bind the texture so that they'll be decoded
+ // slightly inefficient, we can short-circuit this
+ // if we have to
+ if( render_avatar && !gGLManager.mIsDisabled )
+ {
+ if( head_baked && ! mHeadBakedLoaded )
+ {
+ getTEImage( TEX_HEAD_BAKED )->bind();
+ }
+ if( upper_baked && ! mUpperBakedLoaded )
+ {
+ getTEImage( TEX_UPPER_BAKED )->bind();
+ }
+ if( lower_baked && ! mLowerBakedLoaded )
+ {
+ getTEImage( TEX_LOWER_BAKED )->bind();
+ }
+ if( eyes_baked && ! mEyesBakedLoaded )
+ {
+ getTEImage( TEX_EYES_BAKED )->bind();
+ }
+ if( skirt_baked && ! mSkirtBakedLoaded )
+ {
+ getTEImage( TEX_SKIRT_BAKED )->bind();
+ }
+ }
+
+ /*
+ // JAMESDEBUG
+ if (mIsSelf)
+ {
+ S32 null_count = 0;
+ S32 default_count = 0;
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ const LLTextureEntry* te = getTE(i);
+ if (te)
+ {
+ if (te->getID() == LLUUID::null)
+ {
+ null_count++;
+ }
+ else if (te->getID() == IMG_DEFAULT_AVATAR)
+ {
+ default_count++;
+ }
+ }
+ }
+ llinfos << "JAMESDEBUG my avatar TE null " << null_count << " default " << default_count << llendl;
+ }
+ */
+
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ LLViewerImage *imagep = getTEImage(i);
+ if (imagep)
+ {
+ // Debugging code - maybe non-self avatars are downloading textures?
+ //llinfos << "avatar self " << mIsSelf << " tex " << i
+ // << " decode " << imagep->getDecodePriority()
+ // << " boost " << boost_avatar
+ // << " size " << imagep->getWidth() << "x" << imagep->getHeight()
+ // << " discard " << imagep->getDiscardLevel()
+ // << " desired " << imagep->getDesiredDiscardLevel()
+ // << llendl;
+
+ const LLTextureEntry *te = getTE(i);
+ F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT);
+// BOOL boost_aux = (imagep->needsAux() && (!imagep->mFullWidth || !imagep->mFullHeight));
+ S32 boost_level = mIsSelf ? LLViewerImage::BOOST_AVATAR_BAKED_SELF : LLViewerImage::BOOST_AVATAR_BAKED;
+
+ // Spam if this is a baked texture, not set to default image, without valid host info
+ if (isTextureIndexBaked(i)
+ && imagep->getID() != IMG_DEFAULT_AVATAR
+ && !imagep->getTargetHost().isOk())
+ {
+ llwarns << "LLVOAvatar::updateTextures No host for texture " << imagep->getID()
+ << " for avatar " << (mIsSelf ? "<myself>" : getID().getString())
+ << " on host " << getRegion()->getHost() << llendl;
+ }
+
+ switch( i )
+ {
+ // Head
+ case TEX_HEAD_BODYPAINT:
+ addLocalTextureStats( LOCTEX_HEAD_BODYPAINT, imagep, texel_area_ratio, render_avatar, head_baked );
+ break;
+
+ // Upper
+ case TEX_UPPER_JACKET:
+ addLocalTextureStats( LOCTEX_UPPER_JACKET, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_SHIRT:
+ addLocalTextureStats( LOCTEX_UPPER_SHIRT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_GLOVES:
+ addLocalTextureStats( LOCTEX_UPPER_GLOVES, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_UNDERSHIRT:
+ addLocalTextureStats( LOCTEX_UPPER_UNDERSHIRT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_BODYPAINT:
+ addLocalTextureStats( LOCTEX_UPPER_BODYPAINT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ // Lower
+ case TEX_LOWER_JACKET:
+ addLocalTextureStats( LOCTEX_LOWER_JACKET, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_PANTS:
+ addLocalTextureStats( LOCTEX_LOWER_PANTS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_SHOES:
+ addLocalTextureStats( LOCTEX_LOWER_SHOES, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_SOCKS:
+ addLocalTextureStats( LOCTEX_LOWER_SOCKS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_UNDERPANTS:
+ addLocalTextureStats( LOCTEX_LOWER_UNDERPANTS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_BODYPAINT:
+ addLocalTextureStats( LOCTEX_LOWER_BODYPAINT, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ // Eyes
+ case TEX_EYES_IRIS:
+ addLocalTextureStats( LOCTEX_EYES_IRIS, imagep, texel_area_ratio, render_avatar, eyes_baked );
+ break;
+
+ // Skirt
+ case TEX_SKIRT:
+ addLocalTextureStats( LOCTEX_SKIRT, imagep, texel_area_ratio, render_avatar, skirt_baked );
+ break;
+
+ // Baked
+ case TEX_HEAD_BAKED:
+ if (head_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_UPPER_BAKED:
+ if (upper_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_LOWER_BAKED:
+ if (lower_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_EYES_BAKED:
+ if (eyes_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_SKIRT_BAKED:
+ if (skirt_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_HAIR:
+ // Hair is neither a local texture used for baking, nor the output
+ // of the baking process. It's just a texture that happens to be
+ // used to draw avatars. Hence BOOST_AVATAR. JC
+ if (mIsSelf)
+ {
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF);
+ }
+ else
+ {
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR);
+ }
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+ }
+ }
+
+ if( render_avatar )
+ {
+ mShadowImagep->addTextureStats(mPixelArea, 1.f);
+ }
+}
+
+
+void LLVOAvatar::addLocalTextureStats( LLVOAvatar::ELocTexIndex idx, LLViewerImage* imagep,
+ F32 texel_area_ratio, BOOL render_avatar, BOOL covered_by_baked )
+{
+ if (!covered_by_baked &&
+ render_avatar && // always true if mIsSelf
+ mLocalTexture[ idx ].notNull() && mLocalTexture[idx]->getID() != IMG_DEFAULT_AVATAR)
+ {
+ F32 desired_pixels;
+ if( mIsSelf )
+ {
+ desired_pixels = llmin(mPixelArea, (F32)LOCTEX_IMAGE_AREA_SELF );
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF);
+ }
+ else
+ {
+ desired_pixels = llmin(mPixelArea, (F32)LOCTEX_IMAGE_AREA_OTHER );
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR);
+ }
+ imagep->addTextureStats( desired_pixels, texel_area_ratio );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resolveHeight()
+//-----------------------------------------------------------------------------
+
+void LLVOAvatar::resolveHeightAgent(const LLVector3 &in_pos_agent, LLVector3 &out_pos_agent, LLVector3 &out_norm)
+{
+ LLVector3d in_pos_global, out_pos_global;
+
+ in_pos_global = gAgent.getPosGlobalFromAgent(in_pos_agent);
+ resolveHeightGlobal(in_pos_global, out_pos_global, out_norm);
+ out_pos_agent = gAgent.getPosAgentFromGlobal(out_pos_global);
+}
+
+
+void LLVOAvatar::resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm)
+{
+ LLViewerObject *obj;
+ gWorldPointer->resolveStepHeightGlobal(this, start_pt, end_pt, out_pos, out_norm, &obj);
+}
+
+
+void LLVOAvatar::resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm)
+{
+ LLVector3d zVec(0.0f, 0.0f, 0.5f);
+ LLVector3d p0 = inPos + zVec;
+ LLVector3d p1 = inPos - zVec;
+ LLViewerObject *obj;
+ gWorldPointer->resolveStepHeightGlobal(this, p0, p1, outPos, outNorm, &obj);
+ if (!obj)
+ {
+ mStepOnLand = TRUE;
+ mStepMaterial = 0;
+ mStepObjectVelocity.setVec(0.0f, 0.0f, 0.0f);
+ }
+ else
+ {
+ mStepOnLand = FALSE;
+ mStepMaterial = obj->getMaterial();
+
+ // We want the primitive velocity, not our velocity... (which actually subtracts the
+ // step object velocity)
+ LLVector3 angularVelocity = obj->getAngularVelocity();
+ LLVector3 relativePos = gAgent.getPosAgentFromGlobal(outPos) - obj->getPositionAgent();
+
+ LLVector3 linearComponent = angularVelocity % relativePos;
+// llinfos << "Linear Component of Rotation Velocity " << linearComponent << llendl;
+ mStepObjectVelocity = obj->getVelocity() + linearComponent;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getStepSound()
+//-----------------------------------------------------------------------------
+LLUUID& LLVOAvatar::getStepSound()
+{
+ if ( mStepOnLand )
+ {
+ return sStepSoundOnLand;
+ }
+
+ return sStepSounds[mStepMaterial];
+}
+
+
+//-----------------------------------------------------------------------------
+// processAnimationStateChanges()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::processAnimationStateChanges()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if ( isAnyAnimationSignaled(AGENT_WALK_ANIMS, NUM_AGENT_WALK_ANIMS) )
+ {
+ startMotion(ANIM_AGENT_WALK_ADJUST);
+ stopMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+ else if (mInAir && !mIsSitting)
+ {
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ startMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+ else
+ {
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ stopMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+
+ if ( isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
+ {
+ startMotion(ANIM_AGENT_TARGET);
+ stopMotion(ANIM_AGENT_BODY_NOISE);
+ }
+ else
+ {
+ stopMotion(ANIM_AGENT_TARGET);
+ startMotion(ANIM_AGENT_BODY_NOISE);
+ }
+
+ // clear all current animations
+ AnimIterator anim_it;
+ for (anim_it = mPlayingAnimations.begin(); anim_it != mPlayingAnimations.end();)
+ {
+ AnimIterator found_anim = mSignaledAnimations.find(anim_it->first);
+
+ // playing, but not signaled, so stop
+ if (found_anim == mSignaledAnimations.end())
+ {
+ processSingleAnimationStateChange(anim_it->first, FALSE);
+ mPlayingAnimations.erase(anim_it++);
+ continue;
+ }
+
+ ++anim_it;
+ }
+
+ // start up all new anims
+ for (anim_it = mSignaledAnimations.begin(); anim_it != mSignaledAnimations.end();)
+ {
+ AnimIterator found_anim = mPlayingAnimations.find(anim_it->first);
+
+ // signaled but not playing, or different sequence id, start motion
+ if (found_anim == mPlayingAnimations.end() || found_anim->second != anim_it->second)
+ {
+ if (processSingleAnimationStateChange(anim_it->first, TRUE))
+ {
+ mPlayingAnimations[anim_it->first] = anim_it->second;
+ ++anim_it;
+ continue;
+ }
+ }
+
+ ++anim_it;
+ }
+
+ // clear source information for animations which have been stopped
+ if (mIsSelf)
+ {
+ AnimSourceIterator source_it = mAnimationSources.begin();
+
+ for (source_it = mAnimationSources.begin(); source_it != mAnimationSources.end();)
+ {
+ if (mSignaledAnimations.find(source_it->second) == mSignaledAnimations.end())
+ {
+ mAnimationSources.erase(source_it++);
+ }
+ else
+ {
+ ++source_it;
+ }
+ }
+ }
+
+ stop_glerror();
+}
+
+
+//-----------------------------------------------------------------------------
+// processSingleAnimationStateChange();
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::processSingleAnimationStateChange( const LLUUID& anim_id, BOOL start )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ BOOL result = FALSE;
+
+ if ( start ) // start animation
+ {
+ if (anim_id == ANIM_AGENT_TYPE)
+ {
+ if (gAudiop)
+ {
+ LLVector3d char_pos_global = gAgent.getPosGlobalFromAgent(getCharacterPosition());
+ if (gParcelMgr && gParcelMgr->canHearSound(char_pos_global)
+ && gMuteListp && !gMuteListp->isMuted(getID()))
+ {
+ // RN: uncomment this to play on typing sound at fixed volume once sound engine is fixed
+ // to support both spatialized and non-spatialized instances of the same sound
+ //if (mIsSelf)
+ //{
+ // gAudiop->triggerSound(LLUUID(gSavedSettings.getString("UISndTyping")), 0.8f);
+ //}
+ //else
+ {
+ LLUUID sound_id = LLUUID(gSavedSettings.getString("UISndTyping"));
+ gAudiop->triggerSound(sound_id, getID(), 1.f, char_pos_global);
+ }
+ }
+ }
+ }
+ else if (anim_id == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
+ {
+ mIsSitting = TRUE;
+ }
+
+
+ if (startMotion(anim_id))
+ {
+ result = TRUE;
+ }
+ else
+ {
+ llwarns << "Failed to start motion!" << llendl;
+ }
+ }
+ else //stop animation
+ {
+ if (anim_id == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
+ {
+ mIsSitting = FALSE;
+ }
+ stopMotion(anim_id);
+ result = TRUE;
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// isAnyAnimationSignaled()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims)
+{
+ for (S32 i = 0; i < num_anims; i++)
+ {
+ if(mSignaledAnimations.find(anim_array[i]) != mSignaledAnimations.end())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// resetAnimations()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::resetAnimations()
+{
+ LLKeyframeMotion::flushKeyframeCache();
+ flushAllMotions();
+}
+
+//-----------------------------------------------------------------------------
+// startMotion()
+// id is the asset if of the animation to start
+// time_offset is the offset into the animation at which to start playing
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::startMotion(const LLUUID& id, F32 time_offset)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ // start special case female walk for female avatars
+ if (getSex() == SEX_FEMALE)
+ {
+ if (id == ANIM_AGENT_WALK)
+ {
+ return LLCharacter::startMotion(ANIM_AGENT_FEMALE_WALK, time_offset);
+ }
+ else if (id == ANIM_AGENT_SIT)
+ {
+ return LLCharacter::startMotion(ANIM_AGENT_SIT_FEMALE, time_offset);
+ }
+ }
+
+ if (mIsSelf && id == ANIM_AGENT_AWAY)
+ {
+ gAgent.setAFK();
+ }
+
+ return LLCharacter::startMotion(id, time_offset);
+}
+
+//-----------------------------------------------------------------------------
+// stopMotion()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::stopMotion(const LLUUID& id, BOOL stop_immediate)
+{
+ if (mIsSelf)
+ {
+ if (id == ANIM_AGENT_STAND)
+ {
+ LLAgent::stopFidget();
+ }
+ else if (id == ANIM_AGENT_AWAY)
+ {
+ gAgent.clearAFK();
+ }
+ }
+
+ if (id == ANIM_AGENT_WALK)
+ {
+ LLCharacter::stopMotion(ANIM_AGENT_FEMALE_WALK, stop_immediate);
+ }
+ else if (id == ANIM_AGENT_SIT)
+ {
+ LLCharacter::stopMotion(ANIM_AGENT_SIT_FEMALE, stop_immediate);
+ }
+
+ return LLCharacter::stopMotion(id, stop_immediate);
+}
+
+//-----------------------------------------------------------------------------
+// stopMotionFromSource()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::stopMotionFromSource(const LLUUID& source_id)
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+ AnimSourceIterator motion_it;
+
+ for(motion_it = mAnimationSources.find(source_id); motion_it != mAnimationSources.end();)
+ {
+ gAgent.sendAnimationRequest( motion_it->second, ANIM_REQUEST_STOP );
+ mAnimationSources.erase(motion_it++);
+ }
+
+ LLViewerObject* object = gObjectList.findObject(source_id);
+ if (object)
+ {
+ object->mFlags &= ~FLAGS_ANIM_SOURCE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getVolumePos()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getVolumePos(S32 joint_index, LLVector3& volume_offset)
+{
+ if (joint_index > mNumCollisionVolumes)
+ {
+ return LLVector3::zero;
+ }
+
+ return mCollisionVolumes[joint_index].getVolumePos(volume_offset);
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+LLJoint* LLVOAvatar::findCollisionVolume(U32 volume_id)
+{
+ if ((S32)volume_id > mNumCollisionVolumes)
+ {
+ return NULL;
+ }
+
+ return &mCollisionVolumes[volume_id];
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+S32 LLVOAvatar::getCollisionVolumeID(std::string &name)
+{
+ for (S32 i = 0; i < mNumCollisionVolumes; i++)
+ {
+ if (mCollisionVolumes[i].getName() == name)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// addDebugText()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::addDebugText(const char* text)
+{
+ mDebugText.append(1, '\n');
+ mDebugText.append(text);
+}
+
+//-----------------------------------------------------------------------------
+// getID()
+//-----------------------------------------------------------------------------
+const LLUUID& LLVOAvatar::getID()
+{
+ return mID;
+}
+
+//-----------------------------------------------------------------------------
+// getJoint()
+//-----------------------------------------------------------------------------
+// RN: avatar joints are multi-rooted to include screen-based attachments
+LLJoint *LLVOAvatar::getJoint( const std::string &name )
+{
+ LLJoint* jointp = NULL;
+ if (mScreenp)
+ {
+ jointp = mScreenp->findJoint(name);
+ }
+ if (!jointp)
+ {
+ jointp = mRoot.findJoint(name);
+ }
+ return jointp;
+}
+
+//-----------------------------------------------------------------------------
+// getCharacterPosition()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterPosition()
+{
+ if (mDrawable.notNull())
+ {
+ return mDrawable->getPositionAgent();
+ }
+ else
+ {
+ return getPositionAgent();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterRotation()
+//-----------------------------------------------------------------------------
+LLQuaternion LLVOAvatar::getCharacterRotation()
+{
+ return getRotation();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterVelocity()
+{
+ return getVelocity() - mStepObjectVelocity;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterAngularVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterAngularVelocity()
+{
+ return getAngularVelocity();
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getGround()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::getGround(const LLVector3 &in_pos_agent, LLVector3 &out_pos_agent, LLVector3 &outNorm)
+{
+ LLVector3d z_vec(0.0f, 0.0f, 1.0f);
+ LLVector3d p0_global, p1_global;
+
+ if (gNoRender || mIsDummy)
+ {
+ outNorm.setVec(z_vec);
+ out_pos_agent = in_pos_agent;
+ return;
+ }
+
+ p0_global = gAgent.getPosGlobalFromAgent(in_pos_agent) + z_vec;
+ p1_global = gAgent.getPosGlobalFromAgent(in_pos_agent) - z_vec;
+ LLViewerObject *obj;
+ LLVector3d out_pos_global;
+ gWorldPointer->resolveStepHeightGlobal(this, p0_global, p1_global, out_pos_global, outNorm, &obj);
+ out_pos_agent = gAgent.getPosAgentFromGlobal(out_pos_global);
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getTimeDilation()
+//-----------------------------------------------------------------------------
+F32 LLVOAvatar::getTimeDilation()
+{
+ return mTimeDilation;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getPixelArea()
+//-----------------------------------------------------------------------------
+F32 LLVOAvatar::getPixelArea()
+{
+ if (mIsDummy)
+ {
+ return 100000.f;
+ }
+ return mPixelArea;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getHeadMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLVOAvatar::getHeadMesh()
+{
+ return mHeadMesh0.getMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getUpperBodyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLVOAvatar::getUpperBodyMesh()
+{
+ return mUpperBodyMesh0.getMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getPosGlobalFromAgent()
+//-----------------------------------------------------------------------------
+LLVector3d LLVOAvatar::getPosGlobalFromAgent(const LLVector3 &position)
+{
+ return gAgent.getPosGlobalFromAgent(position);
+}
+
+//-----------------------------------------------------------------------------
+// getPosAgentFromGlobal()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getPosAgentFromGlobal(const LLVector3d &position)
+{
+ return gAgent.getPosAgentFromGlobal(position);
+}
+
+//-----------------------------------------------------------------------------
+// allocateCharacterJoints()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::allocateCharacterJoints( U32 num )
+{
+ delete [] mSkeleton;
+ mSkeleton = NULL;
+ mNumJoints = 0;
+
+ mSkeleton = new LLViewerJoint[num];
+
+ for(S32 joint_num = 0; joint_num < (S32)num; joint_num++)
+ {
+ mSkeleton[joint_num].setJointNum(joint_num);
+ }
+
+ if (!mSkeleton)
+ {
+ return FALSE;
+ }
+
+ mNumJoints = num;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// allocateCollisionVolumes()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::allocateCollisionVolumes( U32 num )
+{
+ delete [] mCollisionVolumes;
+ mCollisionVolumes = NULL;
+ mNumCollisionVolumes = 0;
+
+ mCollisionVolumes = new LLViewerJointCollisionVolume[num];
+ if (!mCollisionVolumes)
+ {
+ return FALSE;
+ }
+
+ mNumCollisionVolumes = num;
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// getCharacterJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLVOAvatar::getCharacterJoint( U32 num )
+{
+ if ((S32)num >= mNumJoints
+ || num < 0)
+ {
+ return NULL;
+ }
+ return (LLJoint*)&mSkeleton[num];
+}
+
+
+//-----------------------------------------------------------------------------
+// requestStopMotion()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestStopMotion( LLMotion* motion )
+{
+ // Only agent avatars should handle the stop motion notifications.
+ if ( mIsSelf )
+ {
+ // Notify agent that motion has stopped
+ gAgent.requestStopMotion( motion );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// loadAvatar()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadAvatar()
+{
+// LLFastTimer t(LLFastTimer::FTM_LOAD_AVATAR);
+
+ // avatar_skeleton.xml
+ if( !buildSkeleton(sSkeletonInfo) )
+ {
+ llwarns << "avatar file: buildSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <skeleton>
+ if( !loadSkeletonNode() )
+ {
+ llwarns << "avatar file: loadNodeSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <mesh>
+ if( !loadMeshNodes() )
+ {
+ llwarns << "avatar file: loadNodeMesh() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <global_color>
+ if( sAvatarInfo->mTexSkinColorInfo )
+ {
+ mTexSkinColor = new LLTexGlobalColor( this );
+ if( !mTexSkinColor->setInfo( sAvatarInfo->mTexSkinColorInfo ) )
+ {
+ llwarns << "avatar file: mTexSkinColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"skin_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarInfo->mTexHairColorInfo )
+ {
+ mTexHairColor = new LLTexGlobalColor( this );
+ if( !mTexHairColor->setInfo( sAvatarInfo->mTexHairColorInfo ) )
+ {
+ llwarns << "avatar file: mTexHairColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"hair_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarInfo->mTexEyeColorInfo )
+ {
+ mTexEyeColor = new LLTexGlobalColor( this );
+ if( !mTexEyeColor->setInfo( sAvatarInfo->mTexEyeColorInfo ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"eye_color\" not found" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <layer_set>
+ if (sAvatarInfo->mLayerInfoList.empty())
+ {
+ llwarns << "avatar file: missing <layer_set> node" << llendl;
+ }
+ else
+ {
+ LLVOAvatarInfo::layer_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mLayerInfoList.begin();
+ iter != sAvatarInfo->mLayerInfoList.end(); iter++)
+ {
+ LLTexLayerSetInfo *info = *iter;
+ LLTexLayerSet* layer_set = new LLTexLayerSet( this );
+ if (!layer_set->setInfo(info))
+ {
+ stop_glerror();
+ delete layer_set;
+ llwarns << "avatar file: layer_set->parseData() failed" << llendl;
+ return FALSE;
+ }
+ if( layer_set->isBodyRegion( "head" ) )
+ {
+ mHeadLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "upper_body" ) )
+ {
+ mUpperBodyLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "lower_body" ) )
+ {
+ mLowerBodyLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "eyes" ) )
+ {
+ mEyesLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "skirt" ) )
+ {
+ mSkirtLayerSet = layer_set;
+ }
+ else
+ {
+ llwarns << "<layer_set> has invalid body_region attribute" << llendl;
+ delete layer_set;
+ return FALSE;
+ }
+ }
+ }
+
+ // avatar_lad.xml : <driver_parameters>
+ {
+ LLVOAvatarInfo::driver_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mDriverInfoList.begin();
+ iter != sAvatarInfo->mDriverInfoList.end(); iter++)
+ {
+ LLDriverParamInfo *info = *iter;
+ LLDriverParam* driver_param = new LLDriverParam( this );
+ if (driver_param->setInfo(info))
+ {
+ addVisualParam( driver_param );
+ }
+ else
+ {
+ delete driver_param;
+ llwarns << "avatar file: driver_param->parseData() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadSkeletonNode(): loads <skeleton> node from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadSkeletonNode ()
+{
+ mRoot.addChild( &mSkeleton[0] );
+
+ mRoot.addChild( &mHeadLOD );
+ mHeadLOD.mUpdateXform = FALSE;
+ mHeadLOD.addChild( &mHeadMesh0 );
+ mHeadLOD.addChild( &mHeadMesh1 );
+ mHeadLOD.addChild( &mHeadMesh2 );
+ mHeadLOD.addChild( &mHeadMesh3 );
+ mHeadLOD.addChild( &mHeadMesh4 );
+
+ mRoot.addChild( &mEyeLashLOD );
+ mEyeLashLOD.mUpdateXform = FALSE;
+ mEyeLashLOD.addChild( &mEyeLashMesh0 );
+
+ mRoot.addChild( &mUpperBodyLOD );
+ mUpperBodyLOD.mUpdateXform = FALSE;
+ mUpperBodyLOD.addChild( &mUpperBodyMesh0 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh1 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh2 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh3 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh4 );
+
+ mRoot.addChild( &mLowerBodyLOD );
+ mLowerBodyLOD.mUpdateXform = FALSE;
+ mLowerBodyLOD.addChild( &mLowerBodyMesh0 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh1 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh2 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh3 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh4 );
+
+ mRoot.addChild( &mSkirtLOD );
+ mSkirtLOD.mUpdateXform = FALSE;
+ mSkirtLOD.addChild( &mSkirtMesh0 );
+ mSkirtLOD.addChild( &mSkirtMesh1 );
+ mSkirtLOD.addChild( &mSkirtMesh2 );
+ mSkirtLOD.addChild( &mSkirtMesh3 );
+ mSkirtLOD.addChild( &mSkirtMesh4 );
+
+ LLViewerJoint *skull = (LLViewerJoint*)mRoot.findJoint("mSkull");
+ if (skull)
+ {
+ skull->addChild( &mHairLOD );
+ mHairLOD.mUpdateXform = FALSE;
+ mHairLOD.addChild( &mHairMesh0 );
+ mHairLOD.addChild( &mHairMesh1 );
+ mHairLOD.addChild( &mHairMesh2 );
+ mHairLOD.addChild( &mHairMesh3 );
+ mHairLOD.addChild( &mHairMesh4 );
+ mHairLOD.addChild( &mHairMesh5 );
+ }
+
+ LLViewerJoint *eyeL = (LLViewerJoint*)mRoot.findJoint("mEyeLeft");
+ if (eyeL)
+ {
+ eyeL->addChild( &mEyeBallLeftLOD );
+ mEyeBallLeftLOD.mUpdateXform = FALSE;
+ mEyeBallLeftLOD.addChild( &mEyeBallLeftMesh0 );
+ mEyeBallLeftLOD.addChild( &mEyeBallLeftMesh1 );
+ }
+
+ LLViewerJoint *eyeR = (LLViewerJoint*)mRoot.findJoint("mEyeRight");
+ if (eyeR)
+ {
+ eyeR->addChild( &mEyeBallRightLOD );
+ mEyeBallRightLOD.mUpdateXform = FALSE;
+ mEyeBallRightLOD.addChild( &mEyeBallRightMesh0 );
+ mEyeBallRightLOD.addChild( &mEyeBallRightMesh1 );
+ }
+
+ // SKELETAL DISTORTIONS
+ {
+ LLVOAvatarInfo::skeletal_distortion_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mSkeletalDistortionInfoList.begin();
+ iter != sAvatarInfo->mSkeletalDistortionInfoList.end(); iter++)
+ {
+ LLPolySkeletalDistortionInfo *info = *iter;
+ LLPolySkeletalDistortion *param = new LLPolySkeletalDistortion(this);
+ if (!param->setInfo(info))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ addVisualParam(param);
+ }
+ }
+ }
+
+ // ATTACHMENTS
+ {
+ LLVOAvatarInfo::attachment_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mAttachmentInfoList.begin();
+ iter != sAvatarInfo->mAttachmentInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarAttachmentInfo *info = *iter;
+ LLViewerJointAttachment* attachment = new LLViewerJointAttachment();
+
+ attachment->setName(info->mName);
+ LLJoint *parentJoint = getJoint(info->mJointName);
+ if (!parentJoint)
+ {
+ llwarns << "No parent joint by name " << info->mJointName << " found for attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+
+ if (info->mHasPosition)
+ {
+ attachment->setOriginalPosition(info->mPosition);
+ }
+
+ if (info->mHasRotation)
+ {
+ LLQuaternion rotation;
+ rotation.setQuat(info->mRotationEuler.mV[VX] * DEG_TO_RAD,
+ info->mRotationEuler.mV[VY] * DEG_TO_RAD,
+ info->mRotationEuler.mV[VZ] * DEG_TO_RAD);
+ attachment->setRotation(rotation);
+ }
+
+ int group = info->mGroup;
+ if (group >= 0)
+ {
+ if (group < 0 || group >= 9)
+ {
+ llwarns << "Invalid group number (" << group << ") for attachment point " << info->mName << llendl;
+ }
+ else
+ {
+ attachment->setGroup(group);
+ }
+ }
+
+ S32 attachmentID = info->mAttachmentID;
+ if (attachmentID < 1 || attachmentID > 255)
+ {
+ llwarns << "Attachment point out of range [1-255]: " << attachmentID << " on attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+ if (mAttachmentPoints.checkData(attachmentID))
+ {
+ llwarns << "Attachment point redefined with id " << attachmentID << " on attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+
+ attachment->setPieSlice(info->mPieMenuSlice);
+ attachment->setVisibleInFirstPerson(info->mVisibleFirstPerson);
+ attachment->setIsHUDAttachment(info->mIsHUDAttachment);
+
+ mAttachmentPoints[attachmentID] = attachment;
+
+ // now add attachment joint
+ parentJoint->addChild(attachment);
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadMeshNodes(): loads <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadMeshNodes()
+{
+ LLVOAvatarInfo::mesh_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mMeshInfoList.begin();
+ iter != sAvatarInfo->mMeshInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarMeshInfo *info = *iter;
+ LLString &type = info->mType;
+ S32 lod = info->mLOD;
+
+ LLViewerJointMesh* mesh = NULL;
+ if (type == "hairMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mHairMesh0;
+ break;
+ case 1:
+ mesh = &mHairMesh1;
+ break;
+ case 2:
+ mesh = &mHairMesh2;
+ break;
+ case 3:
+ mesh = &mHairMesh3;
+ break;
+ case 4:
+ mesh = &mHairMesh4;
+ break;
+ case 5:
+ mesh = &mHairMesh5;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "headMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mHeadMesh0;
+ break;
+ case 1:
+ mesh = &mHeadMesh1;
+ break;
+ case 2:
+ mesh = &mHeadMesh2;
+ break;
+ case 3:
+ mesh = &mHeadMesh3;
+ break;
+ case 4:
+ mesh = &mHeadMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "upperBodyMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mUpperBodyMesh0;
+ break;
+ case 1:
+ mesh = &mUpperBodyMesh1;
+ break;
+ case 2:
+ mesh = &mUpperBodyMesh2;
+ break;
+ case 3:
+ mesh = &mUpperBodyMesh3;
+ break;
+ case 4:
+ mesh = &mUpperBodyMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "lowerBodyMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mLowerBodyMesh0;
+ break;
+ case 1:
+ mesh = &mLowerBodyMesh1;
+ break;
+ case 2:
+ mesh = &mLowerBodyMesh2;
+ break;
+ case 3:
+ mesh = &mLowerBodyMesh3;
+ break;
+ case 4:
+ mesh = &mLowerBodyMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "skirtMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mSkirtMesh0;
+ break;
+ case 1:
+ mesh = &mSkirtMesh1;
+ break;
+ case 2:
+ mesh = &mSkirtMesh2;
+ break;
+ case 3:
+ mesh = &mSkirtMesh3;
+ break;
+ case 4:
+ mesh = &mSkirtMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "eyelashMesh")
+ {
+ mesh = &mEyeLashMesh0;
+ }
+ else if (type == "eyeBallLeftMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mEyeBallLeftMesh0;
+ break;
+ case 1:
+ mesh = &mEyeBallLeftMesh1;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "eyeBallRightMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mEyeBallRightMesh0;
+ break;
+ case 1:
+ mesh = &mEyeBallRightMesh1;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+
+ if( !mesh )
+ {
+ llwarns << "Ignoring unrecognized mesh type: " << type << llendl;
+ return FALSE;
+ }
+
+ // llinfos << "Parsing mesh data for " << type << "..." << llendl;
+
+ mesh->setColor( 0.8f, 0.8f, 0.8f, 1.0f );
+
+ LLPolyMesh *poly_mesh = NULL;
+
+ if (!info->mReferenceMeshName.empty())
+ {
+ mesh_map_t::iterator iter = mMeshes.find(info->mReferenceMeshName);
+ if (iter != mMeshes.end())
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName, iter->second);
+ poly_mesh->setAvatar(this);
+ }
+ else
+ {
+ // This should never happen
+ }
+ }
+ else
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName);
+ poly_mesh->setAvatar(this);
+ }
+
+ if( !poly_mesh )
+ {
+ llwarns << "Failed to load mesh of type " << type << llendl;
+ return FALSE;
+ }
+
+ // Multimap insert
+ mMeshes.insert(std::pair<LLString, LLPolyMesh*>(info->mMeshFileName, poly_mesh));
+
+ mesh->setMesh( poly_mesh );
+
+ mesh->setLOD( info->mMinPixelArea );
+
+ LLVOAvatarInfo::LLVOAvatarMeshInfo::morph_info_list_t::iterator iter;
+ for (iter = info->mPolyMorphTargetInfoList.begin();
+ iter != info->mPolyMorphTargetInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarMeshInfo::morph_info_pair_t *info_pair = &(*iter);
+ LLPolyMorphTarget *param = new LLPolyMorphTarget(mesh->getMesh());
+ if (!param->setInfo(info_pair->first))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ if (info_pair->second)
+ {
+ addSharedVisualParam(param);
+ }
+ else
+ {
+ addVisualParam(param);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// updateVisualParams()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateVisualParams()
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ setSex( (getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE );
+
+ LLCharacter::updateVisualParams();
+
+ if (mLastSkeletonSerialNum != mSkeletonSerialNum)
+ {
+ computeBodySize();
+ mLastSkeletonSerialNum = mSkeletonSerialNum;
+ mRoot.updateWorldMatrixChildren();
+ }
+
+ dirtyMesh();
+ updateHeadOffset();
+}
+
+//-----------------------------------------------------------------------------
+// isActive()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isActive() const
+{
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// setPixelAreaAndAngle()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent;
+
+ pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 max_scale = getMaxScale();
+ F32 mid_scale = getMidScale();
+ F32 min_scale = llmin( getScale().mV[VX], llmin( getScale().mV[VY], getScale().mV[VZ] ) );
+
+ // IW: esitmate - when close to large objects, computing range based on distance from center is no good
+ // to try to get a min distance from face, subtract min_scale/2 from the range.
+ // This means we'll load too much detail sometimes, but that's better than not enough
+ // I don't think there's a better way to do this without calculating distance per-poly
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2;
+
+ if (range < 0.001f) // range == zero
+ {
+ mAppAngle = 180.f;
+ mPixelArea = gCamera->getViewHeightInPixels() *
+ gCamera->getViewHeightInPixels() *
+ gCamera->getAspect();
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ F32 pixels_per_meter = gCamera->getPixelMeterRatio() / range;
+
+ mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale);
+// if( !mIsSelf )
+// {
+// llinfos << "range " << range << llendl;
+// llinfos << "pixels_per_meter " << pixels_per_meter << llendl;
+// llinfos << "scale " << max_scale << "x" << mid_scale << llendl;
+// llinfos << "pixel area " << mPixelArea << llendl;
+// }
+ }
+
+ // We always want to look good to ourselves
+ if( mIsSelf )
+ {
+ mPixelArea = llmax( mPixelArea, F32(LOCTEX_IMAGE_SIZE_SELF / 16) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateJointLODs()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateJointLODs()
+{
+ if (!mMeshValid)
+ {
+ return;
+ }
+
+ F32 lod_factor = (sLODFactor * AVATAR_LOD_TWEAK_RANGE + (1.f - AVATAR_LOD_TWEAK_RANGE));
+ F32 avatar_num_min_factor = clamp_rescale(sLODFactor, 0.f, 1.f, 0.25f, 0.6f);
+ F32 avatar_num_factor = clamp_rescale((F32)sNumVisibleAvatars, 8, 25, 1.f, avatar_num_min_factor);
+
+ {
+ if (mIsSelf)
+ {
+ if(gAgent.cameraCustomizeAvatar() || gAgent.cameraMouselook())
+ {
+ mAdjustedPixelArea = 1000000;
+ }
+ else
+ {
+ mAdjustedPixelArea = mPixelArea;
+ }
+ }
+ else if (mIsDummy)
+ {
+ mAdjustedPixelArea = 1000000;
+ }
+ else
+ {
+ // reported avatar pixel area is dependent on avatar render load, based on number of visible avatars
+ mAdjustedPixelArea = (F32)mPixelArea * lod_factor * lod_factor * avatar_num_factor * avatar_num_factor;
+ }
+
+ // now select meshes to render based on adjusted pixel area, and perform AGP data push as necessary
+ BOOL res = mRoot.updateLOD(mAdjustedPixelArea, TRUE);
+ if (res)
+ {
+ sNumLODChangesThisFrame++;
+ updateMeshData();
+ }
+ }
+
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// createDrawable()
+//-----------------------------------------------------------------------------
+LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_AVATAR);
+
+ // Only a single face (one per avatar)
+ mDrawable->setState(LLDrawable::ACTIVE);
+ mDrawable->addFace(poolp, NULL);
+
+ poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+
+ LLFace *facep;
+
+ // Add faces for the foot shadows
+ facep = mDrawable->addFace(poolp, mShadowImagep);
+ mShadow0Facep = facep;
+
+ facep = mDrawable->addFace(poolp, mShadowImagep);
+ mShadow1Facep = facep;
+
+ gPipeline.markMaterialed(mDrawable);
+ dirtyMesh();
+ return mDrawable;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateGeometry()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)))
+ {
+ return TRUE;
+ }
+
+ if (!mMeshValid)
+ {
+ return TRUE;
+ }
+
+ if (!drawable)
+ {
+ llerrs << "LLVOAvatar::updateGeometry() called with NULL drawable" << llendl;
+ }
+
+ // Update the shadow, tractor, and text label geometry.
+
+ updateShadowFaces();
+
+ if (!drawable->isVisible())
+ {
+ return TRUE;
+ }
+
+ LLFace* facep = drawable->getFace(0);
+ if (!mDirtyMesh && !facep->getDirty())
+ {
+ return TRUE;
+ }
+
+ // U32 num_vertices = 0;
+
+ updateMeshData();
+
+ mDirtyMesh = FALSE;
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateShadowFaces()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateShadowFaces()
+{
+ LLFace *face0p = mShadow0Facep;
+ LLFace *face1p = mShadow1Facep;
+ //
+ // render avatar shadows
+ //
+ if (mInAir)
+ {
+ face0p->setSize(0, 0);
+ face1p->setSize(0, 0);
+ return;
+ }
+
+ LLSprite sprite(mShadowImageID);
+ sprite.setFollow(FALSE);
+ const F32 cos_angle = gSky.getSunDirection().mV[2];
+ F32 cos_elev = sqrt(1 - cos_angle * cos_angle);
+ if (cos_angle < 0) cos_elev = -cos_elev;
+ sprite.setSize(0.4f + cos_elev * 0.8f, 0.3f);
+ LLVector3 sun_vec = gSky.mVOSkyp->getToSun();
+
+ if (mShadowImagep->getHasGLTexture())
+ {
+ LLVector3 normal;
+ LLVector3d shadow_pos;
+ LLVector3 shadow_pos_agent;
+ F32 foot_height;
+
+ if (mFootLeftp)
+ {
+ LLVector3 joint_world_pos = mFootLeftp->getWorldPosition();
+ // this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+ // but we make an explicit ray trace call in expectation of future improvements
+ resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),
+ gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+ shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+ foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+ // Pull sprite in direction of surface normal
+ shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+ // Render sprite
+ sprite.setNormal(normal);
+ if (mIsSelf && gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+ {
+ sprite.setColor(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+ }
+ sprite.setPosition(shadow_pos_agent);
+
+ LLVector3 foot_to_knee = mKneeLeftp->getWorldPosition() - joint_world_pos;
+ //foot_to_knee.normVec();
+ foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+ sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+
+ sprite.updateFace(*face0p);
+ }
+
+ if (mFootRightp)
+ {
+ LLVector3 joint_world_pos = mFootRightp->getWorldPosition();
+ // this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+ // but we make an explicit ray trace call in expectation of future improvements
+ resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),
+ gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+ shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+ foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+ // Pull sprite in direction of surface normal
+ shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+ // Render sprite
+ sprite.setNormal(normal);
+ if (mIsSelf && gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+ {
+ sprite.setColor(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+ }
+ sprite.setPosition(shadow_pos_agent);
+
+ LLVector3 foot_to_knee = mKneeRightp->getWorldPosition() - joint_world_pos;
+ //foot_to_knee.normVec();
+ foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+ sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+
+ sprite.updateFace(*face1p);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateSexDependentLayerSets()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateSexDependentLayerSets( BOOL set_by_user )
+{
+ invalidateComposite( mHeadLayerSet, set_by_user );
+ invalidateComposite( mLowerBodyLayerSet, set_by_user );
+ invalidateComposite( mUpperBodyLayerSet, set_by_user );
+ updateMeshTextures();
+}
+
+//-----------------------------------------------------------------------------
+// dirtyMesh()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::dirtyMesh()
+{
+ mDirtyMesh = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// requestLayerSetUpdate()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestLayerSetUpdate( LLVOAvatar::ELocTexIndex i )
+{
+ switch( i )
+ {
+ case LOCTEX_HEAD_BODYPAINT:
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_UPPER_BODYPAINT:
+ case LOCTEX_UPPER_SHIRT:
+ case LOCTEX_UPPER_GLOVES:
+ case LOCTEX_UPPER_UNDERSHIRT:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_LOWER_BODYPAINT:
+ case LOCTEX_LOWER_PANTS:
+ case LOCTEX_LOWER_SHOES:
+ case LOCTEX_LOWER_SOCKS:
+ case LOCTEX_LOWER_UNDERPANTS:
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_EYES_IRIS:
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->requestUpdate();
+ }
+ break;
+
+
+ case LOCTEX_SKIRT:
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->requestUpdate();
+ }
+ break;
+
+
+ case LOCTEX_LOWER_JACKET:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpdate();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpdate();
+ }
+ break;
+ }
+
+}
+
+void LLVOAvatar::setParent(LLViewerObject* parent)
+{
+ if (parent == NULL)
+ {
+ getOffObject();
+ LLViewerObject::setParent(parent);
+ if (isSelf())
+ {
+ gAgent.resetCamera();
+ }
+ }
+ else
+ {
+ LLViewerObject::setParent(parent);
+ sitOnObject(parent);
+ }
+}
+
+void LLVOAvatar::addChild(LLViewerObject *childp)
+{
+ LLViewerObject::addChild(childp);
+ attachObject(childp);
+}
+
+void LLVOAvatar::removeChild(LLViewerObject *childp)
+{
+ LLViewerObject::removeChild(childp);
+ detachObject(childp);
+}
+
+//-----------------------------------------------------------------------------
+// attachObject()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object)
+{
+ S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getState());
+ //clamp((S32)(viewer_object->getState() & AGENT_ATTACH_MASK) >> AGENT_ATTACH_OFFSET, 1, 0xff);
+
+ //if (mIsSelf)
+ //{
+ // gSelectMgr->deselectObjectAndFamily(viewer_object);
+ //}
+
+ LLViewerJointAttachment* attachment = mAttachmentPoints.getIfThere(attachmentID);
+
+ if (!attachment)
+ {
+ llwarns << "Tried to attach object to invalid attachment point: " << attachmentID << llendl;
+ return FALSE;
+ }
+
+// LLQuaternion object_world_rot = viewer_object->getWorldRotation();
+
+ if (!attachment->addObject(viewer_object))
+ {
+ return FALSE;
+ }
+
+ if (viewer_object->isSelected())
+ {
+ gSelectMgr->updateSelectionCenter();
+ gSelectMgr->updatePointAt();
+ }
+
+// LLQuaternion desired_rot = (object_world_rot * ~attachment->getWorldRotation());
+
+ lldebugs << "Attaching object (" << attachmentID << ") item_id=" << attachment->getItemID() << " task_id=" << viewer_object->getID() << "to " << attachment->getName() << llendl;
+
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, attachment->getItemID() );
+ gInventory.notifyObservers();
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// lazyAttach()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::lazyAttach()
+{
+ for(LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getAttachmentDirty())
+ {
+ attachment->lazyAttach();
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// detachObject()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
+{
+ for(LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ // only one object per attachment point for now
+ if (attachment->getObject(0) == viewer_object)
+ {
+ LLUUID item_id = attachment->getItemID();
+ attachment->removeObject(viewer_object);
+ if (mIsSelf)
+ {
+ // the simulator should automatically handle
+ // permissiosn revokation
+
+ stopMotionFromSource(viewer_object->getID());
+ LLFollowCamMgr::setCameraActive(viewer_object->getID(), FALSE);
+
+ for (S32 i = 0; i < (S32)viewer_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = viewer_object->mChildList[i];
+ // the simulator should automatically handle
+ // permissions revokation
+
+ stopMotionFromSource(child_objectp->getID());
+ LLFollowCamMgr::setCameraActive(child_objectp->getID(), FALSE);
+ }
+
+ }
+ lldebugs << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << llendl;
+ if (mIsSelf)
+ {
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ gInventory.notifyObservers();
+ }
+ return TRUE;
+ }
+ }
+
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// sitOnObject()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::sitOnObject(LLViewerObject *sit_object)
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+ LLQuaternion inv_obj_rot = ~sit_object->getRenderRotation();
+ LLVector3 obj_pos = sit_object->getRenderPosition();
+
+ LLVector3 rel_pos = getRenderPosition() - obj_pos;
+ rel_pos.rotVec(inv_obj_rot);
+
+ mDrawable->mXform.setPosition(rel_pos);
+ mDrawable->mXform.setRotation(mDrawable->getWorldRotation() * inv_obj_rot);
+
+ gPipeline.markMoved(mDrawable, TRUE);
+ mIsSitting = TRUE;
+ mRoot.getXform()->setParent(&sit_object->mDrawable->mXform); // LLVOAvatar::sitOnObject
+ mRoot.setPosition(getPosition());
+ mRoot.updateWorldMatrixChildren();
+
+ stopMotion(ANIM_AGENT_BODY_NOISE);
+
+ if (mIsSelf)
+ {
+ // Might be first sit
+ LLFirstUse::useSit();
+
+ gAgent.setFlying(FALSE);
+ gAgent.setThirdPersonHeadOffset(LLVector3::zero);
+ //interpolate to new camera position
+ gAgent.startCameraAnimation();
+ // make sure we are not trying to autopilot
+ gAgent.stopAutoPilot();
+ gAgent.setupSitCamera();
+ if (gAgent.mForceMouselook) gAgent.changeCameraToMouselook();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getOffObject()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::getOffObject()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLViewerObject* sit_object = (LLViewerObject*)getParent();
+
+ if (sit_object)
+ {
+ stopMotionFromSource(sit_object->getID());
+ LLFollowCamMgr::setCameraActive(sit_object->getID(), FALSE);
+
+ for (S32 i = 0; i < (S32)sit_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = sit_object->mChildList[i];
+
+ stopMotionFromSource(child_objectp->getID());
+ LLFollowCamMgr::setCameraActive(child_objectp->getID(), FALSE);
+ }
+ }
+
+ // assumes that transform will not be updated with drawable still having a parent
+ LLVector3 cur_position_world = mDrawable->getWorldPosition();
+ LLQuaternion cur_rotation_world = mDrawable->getWorldRotation();
+
+ // set *local* position based on last *world* position, since we're unparenting the avatar
+ mDrawable->mXform.setPosition(cur_position_world);
+ mDrawable->mXform.setRotation(cur_rotation_world);
+
+ gPipeline.markMoved(mDrawable, TRUE);
+
+ mIsSitting = FALSE;
+ mRoot.getXform()->setParent(NULL); // LLVOAvatar::getOffObject
+ mRoot.setPosition(cur_position_world);
+ mRoot.setRotation(cur_rotation_world);
+ mRoot.getXform()->update();
+
+ startMotion(ANIM_AGENT_BODY_NOISE);
+
+ if (mIsSelf)
+ {
+ LLQuaternion av_rot = gAgent.getFrameAgent().getQuaternion();
+ LLQuaternion obj_rot = sit_object ? sit_object->getRenderRotation() : LLQuaternion::DEFAULT;
+ av_rot = av_rot * obj_rot;
+ LLVector3 at_axis = LLVector3::x_axis;
+ at_axis = at_axis * av_rot;
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ gAgent.resetAxes(at_axis);
+
+ //reset orientation
+// mRoot.setRotation(avWorldRot);
+ gAgent.setThirdPersonHeadOffset(LLVector3(0.f, 0.f, 1.f));
+
+ gAgent.setSitCamera(LLUUID::null);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// findAvatarFromAttachment()
+//-----------------------------------------------------------------------------
+// static
+LLVOAvatar* LLVOAvatar::findAvatarFromAttachment( LLViewerObject* obj )
+{
+ if( obj->isAttachment() )
+ {
+ do
+ {
+ obj = (LLViewerObject*) obj->getParent();
+ }
+ while( obj && !obj->isAvatar() );
+
+ if( obj && !obj->isDead() )
+ {
+ return (LLVOAvatar*)obj;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// isWearingAttachment()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isWearingAttachment( const LLUUID& inv_item_id )
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// getWornAttachment()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLVOAvatar::getWornAttachment( const LLUUID& inv_item_id )
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return attachment_point->getObject(0);
+ }
+ }
+ return NULL;
+}
+
+const LLString LLVOAvatar::getAttachedPointName(const LLUUID& inv_item_id)
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return (LLString)attachment_point->getName();
+ }
+ }
+
+ return LLString::null;
+}
+
+
+//-----------------------------------------------------------------------------
+// static
+// onLocalTextureLoaded()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onLocalTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src_raw, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ //llinfos << "onLocalTextureLoaded: " << src_vi->getID() << llendl;
+
+ const LLUUID& src_id = src_vi->getID();
+ LLAvatarTexData *data = (LLAvatarTexData *)userdata;
+ if (success)
+ {
+ LLVOAvatar *self = (LLVOAvatar *)gObjectList.findObject(data->mAvatarID);
+ LLVOAvatar::ELocTexIndex idx = data->mIndex;
+ if( self &&
+ (!self->mLocalTextureBaked[ idx ]) &&
+ (self->mLocalTexture[ idx ].notNull()) &&
+ (self->mLocalTexture[ idx ]->getID() == src_id) &&
+ (discard_level < self->mLocalTextureDiscard[idx]))
+ {
+ self->mLocalTextureDiscard[idx] = discard_level;
+ self->requestLayerSetUpdate( idx );
+ if( self->mIsSelf && gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+ self->updateMeshTextures();
+ }
+ }
+
+ if( final || !success )
+ {
+ delete data;
+ }
+}
+
+void LLVOAvatar::updateComposites()
+{
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->updateComposite();
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->updateComposite();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->updateComposite();
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->updateComposite();
+ }
+
+ if( mSkirtLayerSet && isWearingWearableType( WT_SKIRT ))
+ {
+ mSkirtLayerSet->updateComposite();
+ }
+}
+
+LLColor4 LLVOAvatar::getGlobalColor( const LLString& color_name )
+{
+ if( color_name=="skin_color" && mTexSkinColor )
+ {
+ return mTexSkinColor->getColor();
+ }
+ else
+ if( color_name=="hair_color" && mTexHairColor )
+ {
+ return mTexHairColor->getColor();
+ }
+ if( color_name=="eye_color" && mTexEyeColor )
+ {
+ return mTexEyeColor->getColor();
+ }
+ else
+ {
+// return LLColor4( .5f, .5f, .5f, .5f );
+ return LLColor4( 0.f, 1.f, 1.f, 1.f ); // good debugging color
+ }
+}
+
+
+void LLVOAvatar::invalidateComposite( LLTexLayerSet* layerset, BOOL set_by_user )
+{
+ if( !layerset || !layerset->getUpdatesEnabled() )
+ {
+ return;
+ }
+
+ /* Debug spam. JC
+ const char* layer_name = "";
+ if (layerset == mHeadLayerSet)
+ {
+ layer_name = "head";
+ }
+ else if (layerset == mUpperBodyLayerSet)
+ {
+ layer_name = "upperbody";
+ }
+ else if (layerset == mLowerBodyLayerSet)
+ {
+ layer_name = "lowerbody";
+ }
+ else if (layerset == mEyesLayerSet)
+ {
+ layer_name = "eyes";
+ }
+ else if (layerset == mSkirtLayerSet)
+ {
+ layer_name = "skirt";
+ }
+ else
+ {
+ layer_name = "unknown";
+ }
+ llinfos << "LLVOAvatar::invalidComposite() " << layer_name << llendl;
+ */
+
+ layerset->requestUpdate();
+
+ if( set_by_user )
+ {
+ llassert( mIsSelf );
+
+ ETextureIndex baked_te = getBakedTE( layerset );
+ if( gAgent.cameraCustomizeAvatar() )
+ {
+ mSavedTE[ baked_te ].setNull();
+ }
+ else
+ {
+ setTEImage( baked_te, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ layerset->requestUpload();
+ }
+ }
+}
+
+
+void LLVOAvatar::onGlobalColorChanged( LLTexGlobalColor* global_color, BOOL set_by_user )
+{
+ if( global_color == mTexSkinColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( skin color )" << llendl;
+ invalidateComposite( mHeadLayerSet, set_by_user );
+ invalidateComposite( mUpperBodyLayerSet, set_by_user );
+ invalidateComposite( mLowerBodyLayerSet, set_by_user );
+ }
+ else
+ if( global_color == mTexHairColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( hair color )" << llendl;
+ invalidateComposite( mHeadLayerSet, set_by_user );
+
+ LLColor4 color = mTexHairColor->getColor();
+ mHairMesh0.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh1.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh2.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh3.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh4.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh5.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ }
+ else
+ if( global_color == mTexEyeColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( eyecolor )" << llendl;
+ invalidateComposite( mEyesLayerSet, set_by_user );
+ }
+ updateMeshTextures();
+}
+
+void LLVOAvatar::forceBakeAllTextures(bool slam_for_debug)
+{
+ llinfos << "TAT: forced full rebake. " << llendl;
+
+ for (S32 i = 0; i < BAKED_TEXTURE_COUNT; i++)
+ {
+ ETextureIndex baked_index = sBakedTextureIndices[i];
+ LLTexLayerSet* layer_set = getLayerSet(baked_index);
+ if (layer_set)
+ {
+ if (slam_for_debug)
+ {
+ layer_set->setUpdatesEnabled(TRUE);
+ layer_set->cancelUpload();
+ }
+
+ BOOL set_by_user = TRUE;
+ invalidateComposite(layer_set, set_by_user);
+ gViewerStats->incStat(LLViewerStats::ST_TEX_REBAKES);
+ }
+ else
+ {
+ llwarns << "TAT: NO LAYER SET FOR " << (S32)baked_index << llendl;
+ }
+ }
+
+ // Don't know if this is needed
+ updateMeshTextures();
+}
+
+
+// static
+void LLVOAvatar::processRebakeAvatarTextures(LLMessageSystem* msg, void**)
+{
+ LLUUID texture_id;
+ msg->getUUID("TextureData", "TextureID", texture_id);
+
+ LLVOAvatar* self = gAgent.getAvatarObject();
+ if (!self) return;
+
+ // If this is a texture corresponding to one of our baked entries,
+ // just rebake that layer set.
+ BOOL found = FALSE;
+ for (S32 i = 0; i < BAKED_TEXTURE_COUNT; i++)
+ {
+ ETextureIndex baked_index = sBakedTextureIndices[i];
+ if (texture_id == self->getTEImage(baked_index)->getID())
+ {
+ LLTexLayerSet* layer_set = self->getLayerSet(baked_index);
+ if (layer_set)
+ {
+ llinfos << "TAT: rebake - matched entry " << (S32)baked_index << llendl;
+ // Apparently set_by_user == force upload
+ BOOL set_by_user = TRUE;
+ self->invalidateComposite(layer_set, set_by_user);
+ found = TRUE;
+ gViewerStats->incStat(LLViewerStats::ST_TEX_REBAKES);
+ }
+ }
+ }
+
+ // If texture not found, rebake all entries.
+ if (!found)
+ {
+ self->forceBakeAllTextures();
+ }
+ else
+ {
+ // Not sure if this is necessary, but forceBakeAllTextures() does it.
+ self->updateMeshTextures();
+ }
+}
+
+
+BOOL LLVOAvatar::getLocalTextureRaw(S32 index, LLImageRaw* image_raw)
+{
+ BOOL success = FALSE;
+
+ if( (0 <= index) && (index < LOCTEX_NUM_ENTRIES) )
+ {
+ if (mLocalTexture[ index ].isNull() || mLocalTexture[ index ]->getID() == IMG_DEFAULT_AVATAR )
+ {
+ success = TRUE;
+ }
+ else
+ {
+ if( mLocalTexture[ index ]->readBackRaw(-1, image_raw) )
+ {
+ success = TRUE;
+ }
+ else
+ {
+ // No data loaded yet
+ setLocalTexture( (ELocTexIndex)index, getTEImage( index ), FALSE );
+ }
+ }
+ }
+ return success;
+}
+
+BOOL LLVOAvatar::getLocalTextureGL(S32 index, LLImageGL** image_gl_pp)
+{
+ BOOL success = FALSE;
+ *image_gl_pp = NULL;
+
+ if( (0 <= index) && (index < LOCTEX_NUM_ENTRIES) )
+ {
+ if( mLocalTexture[ index ].isNull() || mLocalTexture[ index ]->getID() == IMG_DEFAULT_AVATAR)
+ {
+ success = TRUE;
+ }
+ else
+ {
+ *image_gl_pp = mLocalTexture[ index ];
+ success = TRUE;
+ }
+ }
+
+ if( !success )
+ {
+// llinfos << "getLocalTextureGL(" << index << ") had no data" << llendl;
+ }
+ return success;
+}
+
+const LLUUID& LLVOAvatar::getLocalTextureID( S32 index )
+{
+ if (index >= 0 && mLocalTexture[index].notNull())
+ {
+ return mLocalTexture[index]->getID();
+ }
+ else
+ {
+ return IMG_DEFAULT_AVATAR;
+ }
+}
+
+// static
+void LLVOAvatar::dumpTotalLocalTextureByteCount()
+{
+ S32 total_gl_bytes = 0;
+ for( LLVOAvatar* cur = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ cur;
+ cur = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ S32 gl_bytes = 0;
+ cur->getLocalTextureByteCount(&gl_bytes );
+ total_gl_bytes += gl_bytes;
+ }
+ llinfos << "Total Avatar LocTex GL:" << (total_gl_bytes/1024) << "KB" << llendl;
+}
+
+BOOL LLVOAvatar::isVisible()
+{
+ return mDrawable.notNull() && mDrawable->isVisible();
+}
+
+
+//-----------------------------------------------------------------------------
+// findMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLVOAvatar::findMotion(const LLUUID& id)
+{
+ return mMotionController.findMotion(id);
+}
+
+// Counts the memory footprint of local textures.
+void LLVOAvatar::getLocalTextureByteCount( S32* gl_bytes )
+{
+ *gl_bytes = 0;
+ for( S32 i = 0; i < LOCTEX_NUM_ENTRIES; i++ )
+ {
+ LLViewerImage* image_gl = mLocalTexture[i];
+ if( image_gl )
+ {
+ S32 bytes = (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents();
+
+ if( image_gl->getHasGLTexture() )
+ {
+ *gl_bytes += bytes;
+ }
+ }
+ }
+}
+
+
+BOOL LLVOAvatar::bindScratchTexture( LLGLenum format )
+{
+ U32 texture_bytes = 0;
+ GLuint gl_name = getScratchTexName( format, &texture_bytes );
+ if( gl_name )
+ {
+ LLImageGL::bindExternalTexture( gl_name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ F32* last_bind_time = LLVOAvatar::sScratchTexLastBindTime.getIfThere( format );
+ if( last_bind_time )
+ {
+ if( *last_bind_time != LLImageGL::sLastFrameTime )
+ {
+ *last_bind_time = LLImageGL::sLastFrameTime;
+ LLImageGL::updateBoundTexMem(texture_bytes);
+ }
+ }
+ else
+ {
+ LLImageGL::updateBoundTexMem(texture_bytes);
+ LLVOAvatar::sScratchTexLastBindTime.addData( format, new F32(LLImageGL::sLastFrameTime) );
+ }
+
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+LLGLuint LLVOAvatar::getScratchTexName( LLGLenum format, U32* texture_bytes )
+{
+ S32 components;
+ GLenum internal_format;
+ switch( format )
+ {
+ case GL_LUMINANCE: components = 1; internal_format = GL_LUMINANCE8; break;
+ case GL_ALPHA: components = 1; internal_format = GL_ALPHA8; break;
+ case GL_COLOR_INDEX: components = 1; internal_format = GL_COLOR_INDEX8_EXT; break;
+ case GL_LUMINANCE_ALPHA: components = 2; internal_format = GL_LUMINANCE8_ALPHA8; break;
+ case GL_RGB: components = 3; internal_format = GL_RGB8; break;
+ case GL_RGBA: components = 4; internal_format = GL_RGBA8; break;
+ default: llassert(0); components = 4; internal_format = GL_RGBA8; break;
+ }
+
+ *texture_bytes = components * VOAVATAR_SCRATCH_TEX_WIDTH * VOAVATAR_SCRATCH_TEX_HEIGHT;
+
+ if( LLVOAvatar::sScratchTexNames.checkData( format ) )
+ {
+ return *( LLVOAvatar::sScratchTexNames.getData( format ) );
+ }
+ else
+ {
+
+ LLGLSUIDefault gls_ui;
+
+ GLuint name = 0;
+ glGenTextures(1, &name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, internal_format,
+ VOAVATAR_SCRATCH_TEX_WIDTH, VOAVATAR_SCRATCH_TEX_HEIGHT,
+ 0, format, GL_UNSIGNED_BYTE, NULL );
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ stop_glerror();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ stop_glerror();
+
+ LLVOAvatar::sScratchTexNames.addData( format, new LLGLuint( name ) );
+
+ LLVOAvatar::sScratchTexBytes += *texture_bytes;
+ LLImageGL::sGlobalTextureMemory += *texture_bytes;
+ return name;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// setLocalTextureTE()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setLocTexTE( U8 te, LLViewerImage* image, BOOL set_by_user )
+{
+ if( !mIsSelf )
+ {
+ llassert( 0 );
+ return;
+ }
+
+ if( te >= TEX_NUM_ENTRIES )
+ {
+ llassert(0);
+ return;
+ }
+
+ if( getTEImage( te )->getID() == image->getID() )
+ {
+ return;
+ }
+
+ if (isTextureIndexBaked(te))
+ {
+ llassert(0);
+ return;
+ }
+
+ LLTexLayerSet* layer_set = getLayerSet((ETextureIndex)te);
+ if (layer_set)
+ {
+ invalidateComposite(layer_set, set_by_user);
+ }
+
+ setTEImage( te, image );
+ updateMeshTextures();
+
+ if( gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+}
+
+void LLVOAvatar::setupComposites()
+{
+ // Don't invalidate the baked textures we had on start-up.
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if (mHeadLayerSet)
+ {
+ mHeadLayerSet->setUpdatesEnabled( !head_baked );
+ }
+ if (mUpperBodyLayerSet)
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( !upper_baked );
+ }
+ if (mLowerBodyLayerSet)
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( !lower_baked );
+ }
+ if (mEyesLayerSet)
+ {
+ mEyesLayerSet->setUpdatesEnabled( !eyes_baked );
+ }
+ if (mSkirtLayerSet)
+ {
+ mSkirtLayerSet->setUpdatesEnabled( !skirt_baked );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateMeshTextures()
+// Uses the current TE values to set the meshes' and layersets' textures.
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateMeshTextures()
+{
+// llinfos << "updateMeshTextures" << llendl;
+ if (gNoRender)
+ {
+ return;
+ }
+ // if user has never specified a texture, assign the default
+ LLViewerImage* default_tex = gImageList.getImage(IMG_DEFAULT);
+ U8 num_TEs = getNumTEs();
+ for (U32 i=0; i<num_TEs; i++)
+ {
+ LLViewerImage* te_image = getTEImage(i);
+ if( (NULL == te_image) || te_image->getID().isNull() || (te_image->getID() == IMG_DEFAULT) )
+ {
+ if( TEX_HAIR == i )
+ {
+ setTEImage(i, default_tex );
+ }
+ else
+ {
+ setTEImage(i, gImageList.getImage(IMG_DEFAULT_AVATAR)); // a special texture that's never rendered.
+ }
+ }
+ }
+
+ // During face edit mode, we don't use baked textures
+ BOOL self_customize = mIsSelf && gAgent.cameraCustomizeAvatar();
+
+ BOOL head_baked = (getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = (getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = (getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = (getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = (getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ // Nothing should be baked if we're in customize avatar mode.
+ llassert( !( self_customize &&
+ ( head_baked || upper_baked || lower_baked || eyes_baked ) ) );
+
+ BOOL use_lkg_head_baked = FALSE;
+ BOOL use_lkg_upper_baked = FALSE;
+ BOOL use_lkg_lower_baked = FALSE;
+ BOOL use_lkg_eyes_baked = FALSE;
+ BOOL use_lkg_skirt_baked = FALSE;
+
+ BOOL other_culled = !mIsSelf && mCulled;
+ if( other_culled )
+ {
+ use_lkg_head_baked = !head_baked && (mLastHeadBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_upper_baked = !upper_baked && (mLastUpperBodyBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_lower_baked = !lower_baked && (mLastLowerBodyBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_eyes_baked = !eyes_baked && (mLastEyesBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_skirt_baked = !skirt_baked && (mLastSkirtBakedID != IMG_DEFAULT_AVATAR);
+
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->destroyComposite();
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->destroyComposite();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->destroyComposite();
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->destroyComposite();
+ }
+
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->destroyComposite();
+ }
+
+ }
+ else
+ if( !self_customize )
+ {
+ // When you're changing clothes and you're not in Appearance mode,
+ // use the last-known good baked texture until you finish the first
+ // render of the new layerset.
+ use_lkg_head_baked = !head_baked && (mLastHeadBakedID != IMG_DEFAULT_AVATAR) && mHeadLayerSet && !mHeadLayerSet->getComposite()->isInitialized();
+ use_lkg_upper_baked = !upper_baked && (mLastUpperBodyBakedID != IMG_DEFAULT_AVATAR) && mUpperBodyLayerSet && !mUpperBodyLayerSet->getComposite()->isInitialized();
+ use_lkg_lower_baked = !lower_baked && (mLastLowerBodyBakedID != IMG_DEFAULT_AVATAR) && mLowerBodyLayerSet && !mLowerBodyLayerSet->getComposite()->isInitialized();
+ use_lkg_eyes_baked = !eyes_baked && (mLastEyesBakedID != IMG_DEFAULT_AVATAR) && mEyesLayerSet && !mEyesLayerSet->getComposite()->isInitialized();
+ use_lkg_skirt_baked = !skirt_baked && (mLastSkirtBakedID != IMG_DEFAULT_AVATAR) && mSkirtLayerSet && !mSkirtLayerSet->getComposite()->isInitialized();
+
+ if( use_lkg_head_baked )
+ {
+ mHeadLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_upper_baked )
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_lower_baked )
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_eyes_baked )
+ {
+ mEyesLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_skirt_baked )
+ {
+ mSkirtLayerSet->setUpdatesEnabled( TRUE );
+ }
+ }
+
+ // Baked textures should be requested from the sim this avatar is on. JC
+ LLHost target_host = getObjectHost();
+ if (!target_host.isOk())
+ {
+ llwarns << "updateMeshTextures: invalid host for object: " << getID() << llendl;
+ }
+
+ // Head
+ if( use_lkg_head_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastHeadBakedID, target_host );
+ mHeadMesh0.setTexture( baked );
+ mHeadMesh1.setTexture( baked );
+ mHeadMesh2.setTexture( baked );
+ mHeadMesh3.setTexture( baked );
+ mHeadMesh4.setTexture( baked );
+ mEyeLashMesh0.setTexture( baked );
+ }
+ else
+ if( !self_customize && head_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_HEAD_BAKED );
+ if( baked->getID() == mLastHeadBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mHeadBakedLoaded = FALSE;
+ mHeadMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mHeadLayerSet && !other_culled )
+ {
+ mHeadLayerSet->createComposite();
+ mHeadLayerSet->setUpdatesEnabled( TRUE );
+ mHeadMesh0.setLayerSet( mHeadLayerSet );
+ mHeadMesh1.setLayerSet( mHeadLayerSet );
+ mHeadMesh2.setLayerSet( mHeadLayerSet );
+ mHeadMesh3.setLayerSet( mHeadLayerSet );
+ mHeadMesh4.setLayerSet( mHeadLayerSet );
+ mEyeLashMesh0.setLayerSet( mHeadLayerSet );
+ }
+ else
+ {
+ mHeadMesh0.setTexture( default_tex );
+ mHeadMesh1.setTexture( default_tex );
+ mHeadMesh2.setTexture( default_tex );
+ mHeadMesh3.setTexture( default_tex );
+ mHeadMesh4.setTexture( default_tex );
+ mEyeLashMesh0.setTexture( default_tex );
+ }
+
+ // Upper body
+ if( use_lkg_upper_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastUpperBodyBakedID, target_host );
+ mUpperBodyMesh0.setTexture( baked );
+ mUpperBodyMesh1.setTexture( baked );
+ mUpperBodyMesh2.setTexture( baked );
+ mUpperBodyMesh3.setTexture( baked );
+ mUpperBodyMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && upper_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_UPPER_BAKED );
+
+ if( baked->getID() == mLastUpperBodyBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mUpperBakedLoaded = FALSE;
+ mUpperMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mUpperBodyLayerSet && !other_culled )
+ {
+ mUpperBodyLayerSet->createComposite();
+ mUpperBodyLayerSet->setUpdatesEnabled( TRUE );
+ mUpperBodyMesh0.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh1.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh2.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh3.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh4.setLayerSet( mUpperBodyLayerSet );
+ }
+ else
+ {
+ mUpperBodyMesh0.setTexture( default_tex );
+ mUpperBodyMesh1.setTexture( default_tex );
+ mUpperBodyMesh2.setTexture( default_tex );
+ mUpperBodyMesh3.setTexture( default_tex );
+ mUpperBodyMesh4.setTexture( default_tex );
+ }
+
+ // Lower body
+ if( use_lkg_lower_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastLowerBodyBakedID, target_host );
+ mLowerBodyMesh0.setTexture( baked );
+ mLowerBodyMesh1.setTexture( baked );
+ mLowerBodyMesh2.setTexture( baked );
+ mLowerBodyMesh3.setTexture( baked );
+ mLowerBodyMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && lower_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_LOWER_BAKED );
+ if( baked->getID() == mLastLowerBodyBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mLowerBakedLoaded = FALSE;
+ mLowerMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mLowerBodyLayerSet && !other_culled )
+ {
+ mLowerBodyLayerSet->createComposite();
+ mLowerBodyLayerSet->setUpdatesEnabled( TRUE );
+ mLowerBodyMesh0.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh1.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh2.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh3.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh4.setLayerSet( mLowerBodyLayerSet );
+ }
+ else
+ {
+ mLowerBodyMesh0.setTexture( default_tex );
+ mLowerBodyMesh1.setTexture( default_tex );
+ mLowerBodyMesh2.setTexture( default_tex );
+ mLowerBodyMesh3.setTexture( default_tex );
+ mLowerBodyMesh4.setTexture( default_tex );
+ }
+
+ // Eyes
+ if( use_lkg_eyes_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastEyesBakedID, target_host );
+ mEyeBallLeftMesh0.setTexture( baked );
+ mEyeBallLeftMesh1.setTexture( baked );
+ mEyeBallRightMesh0.setTexture( baked );
+ mEyeBallRightMesh1.setTexture( baked );
+ }
+ else
+ if( !self_customize && eyes_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_EYES_BAKED );
+ if( baked->getID() == mLastEyesBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mEyesBakedLoaded = FALSE;
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mEyesLayerSet && !other_culled )
+ {
+ mEyesLayerSet->createComposite();
+ mEyesLayerSet->setUpdatesEnabled( TRUE );
+ mEyeBallLeftMesh0.setLayerSet( mEyesLayerSet );
+ mEyeBallLeftMesh1.setLayerSet( mEyesLayerSet );
+ mEyeBallRightMesh0.setLayerSet( mEyesLayerSet );
+ mEyeBallRightMesh1.setLayerSet( mEyesLayerSet );
+ }
+ else
+ {
+ mEyeBallLeftMesh0.setTexture( default_tex );
+ mEyeBallLeftMesh1.setTexture( default_tex );
+ mEyeBallRightMesh0.setTexture( default_tex );
+ mEyeBallRightMesh1.setTexture( default_tex );
+ }
+
+ // Skirt
+ if( use_lkg_skirt_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastSkirtBakedID, target_host );
+ mSkirtMesh0.setTexture( baked );
+ mSkirtMesh1.setTexture( baked );
+ mSkirtMesh2.setTexture( baked );
+ mSkirtMesh3.setTexture( baked );
+ mSkirtMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && skirt_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_SKIRT_BAKED );
+ if( baked->getID() == mLastSkirtBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mSkirtBakedLoaded = FALSE;
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mSkirtLayerSet && !other_culled)
+ {
+ mSkirtLayerSet->createComposite();
+ mSkirtLayerSet->setUpdatesEnabled( TRUE );
+ mSkirtMesh0.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh1.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh2.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh3.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh4.setLayerSet( mSkirtLayerSet );
+ }
+ else
+ {
+ mSkirtMesh0.setTexture( default_tex );
+ mSkirtMesh1.setTexture( default_tex );
+ mSkirtMesh2.setTexture( default_tex );
+ mSkirtMesh3.setTexture( default_tex );
+ mSkirtMesh4.setTexture( default_tex );
+ }
+
+ mHairMesh0.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh1.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh2.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh3.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh4.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh5.setTexture( getTEImage( TEX_HAIR ) );
+
+ if( mTexHairColor )
+ {
+ LLColor4 color = mTexHairColor->getColor();
+ mHairMesh0.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh1.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh2.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh3.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh4.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh5.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ }
+
+ // Head
+ BOOL head_baked_ready = (head_baked && mHeadBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_HEAD_BODYPAINT, getTEImage( TEX_HEAD_BODYPAINT ), head_baked_ready );
+
+ // Upper body
+ BOOL upper_baked_ready = (upper_baked && mUpperBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_UPPER_SHIRT, getTEImage( TEX_UPPER_SHIRT ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_BODYPAINT, getTEImage( TEX_UPPER_BODYPAINT ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_JACKET, getTEImage( TEX_UPPER_JACKET ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_GLOVES, getTEImage( TEX_UPPER_GLOVES ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_UNDERSHIRT, getTEImage( TEX_UPPER_UNDERSHIRT ), upper_baked_ready );
+
+ // Lower body
+ BOOL lower_baked_ready = (lower_baked && mLowerBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_LOWER_PANTS, getTEImage( TEX_LOWER_PANTS ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_BODYPAINT, getTEImage( TEX_LOWER_BODYPAINT ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_SHOES, getTEImage( TEX_LOWER_SHOES ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_SOCKS, getTEImage( TEX_LOWER_SOCKS ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_JACKET, getTEImage( TEX_LOWER_JACKET ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_UNDERPANTS, getTEImage( TEX_LOWER_UNDERPANTS ), lower_baked_ready );
+
+ // Eyes
+ BOOL eyes_baked_ready = (eyes_baked && mEyesBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_EYES_IRIS, getTEImage( TEX_EYES_IRIS ), eyes_baked_ready );
+
+ // Skirt
+ BOOL skirt_baked_ready = (skirt_baked && mSkirtBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_SKIRT, getTEImage( TEX_SKIRT ), skirt_baked_ready );
+
+ removeMissingBakedTextures();
+}
+
+//-----------------------------------------------------------------------------
+// setLocalTexture()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setLocalTexture( ELocTexIndex idx, LLViewerImage* tex, BOOL baked_version_ready )
+{
+ S32 desired_discard = mIsSelf ? 0 : 2;
+ if (!baked_version_ready)
+ {
+ if (tex != mLocalTexture[idx] || mLocalTextureBaked[idx])
+ {
+ mLocalTextureDiscard[idx] = MAX_DISCARD_LEVEL+1;
+ }
+ if (tex->getID() != IMG_DEFAULT_AVATAR)
+ {
+ if (mLocalTextureDiscard[idx] > desired_discard)
+ {
+ S32 tex_discard = tex->getDiscardLevel();
+ if (tex_discard >= 0 && tex_discard <= desired_discard)
+ {
+ mLocalTextureDiscard[idx] = tex_discard;
+ requestLayerSetUpdate( idx );
+ if( mIsSelf && gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+ }
+ else
+ {
+ tex->setLoadedCallback( onLocalTextureLoaded, desired_discard, TRUE, new LLAvatarTexData(getID(), idx) );
+ }
+ }
+ tex->setMinDiscardLevel(desired_discard);
+ }
+ }
+ mLocalTextureBaked[idx] = baked_version_ready;
+ mLocalTexture[idx] = tex;
+}
+
+//-----------------------------------------------------------------------------
+// requestLayerSetUploads()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestLayerSetUploads()
+{
+ BOOL upper_baked = (getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = (getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL head_baked = (getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = (getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = (getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if( !head_baked && mHeadLayerSet )
+ {
+ mHeadLayerSet->requestUpload();
+ }
+
+ if( !upper_baked && mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpload();
+ }
+
+ if( !lower_baked && mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpload();
+ }
+
+ if( !eyes_baked && mEyesLayerSet )
+ {
+ mEyesLayerSet->requestUpload();
+ }
+
+ if( !skirt_baked && mSkirtLayerSet )
+ {
+ mSkirtLayerSet->requestUpload();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setCompositeUpdatesEnabled()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setCompositeUpdatesEnabled( BOOL b )
+{
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->setUpdatesEnabled( b );
+ }
+
+}
+
+void LLVOAvatar::addChat(const LLChat& chat)
+{
+ std::deque<LLChat>::iterator chat_iter;
+
+ mChats.push_back(chat);
+
+ S32 chat_length = 0;
+ for( chat_iter = mChats.begin(); chat_iter != mChats.end(); ++chat_iter)
+ {
+ chat_length += chat_iter->mText.size();
+ }
+
+ // remove any excess chat
+ chat_iter = mChats.begin();
+ while ((chat_length > MAX_BUBBLE_CHAT_LENGTH || mChats.size() > MAX_BUBBLE_CHAT_UTTERANCES) && chat_iter != mChats.end())
+ {
+ chat_length -= chat_iter->mText.size();
+ mChats.pop_front();
+ chat_iter = mChats.begin();
+ }
+
+ mChatTimer.reset();
+}
+
+void LLVOAvatar::clearChat()
+{
+ mChats.clear();
+}
+
+S32 LLVOAvatar::getLocalDiscardLevel( S32 index )
+{
+ if (index >= 0 && mLocalTexture[index].notNull() && mLocalTexture[index]->getID() != IMG_DEFAULT_AVATAR)
+ {
+ return mLocalTexture[index]->getDiscardLevel();
+ }
+ else
+ {
+ // We don't care about this (no image associated with the layer) treat as fully loaded.
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// isLocalTextureDataFinal()
+// Returns true is the highest quality discard level exists for every texture
+// in the layerset.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isLocalTextureDataFinal( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_HEAD_BODYPAINT ) == 0;
+ }
+ else if( layerset == mUpperBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_UPPER_SHIRT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_BODYPAINT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_JACKET ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_GLOVES ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_UNDERSHIRT ) == 0;
+ }
+ else if( layerset == mLowerBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_LOWER_PANTS ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_BODYPAINT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SHOES ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SOCKS ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_JACKET ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_UNDERPANTS ) == 0;
+ }
+ else if( layerset == mEyesLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_EYES_IRIS ) == 0;
+ }
+ else if( layerset == mSkirtLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_SKIRT ) == 0;
+ }
+
+ llassert(0);
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// isLocalTextureDataAvailable()
+// Returns true is at least the lowest quality discard level exists for every texture
+// in the layerset.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isLocalTextureDataAvailable( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_HEAD_BODYPAINT ) >= 0;
+ }
+ else if( layerset == mUpperBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_UPPER_SHIRT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_BODYPAINT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_JACKET ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_GLOVES ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_UNDERSHIRT ) >= 0;
+ }
+ else if( layerset == mLowerBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_LOWER_PANTS ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_BODYPAINT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SHOES ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SOCKS ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_JACKET ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_UNDERPANTS ) >= 0;
+ }
+ else if( layerset == mEyesLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_EYES_IRIS ) >= 0;
+ }
+ else if( layerset == mSkirtLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_SKIRT ) >= 0;
+ }
+
+ llassert(0);
+ return FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// getBakedTE()
+// Used by the LayerSet. (Layer sets don't in general know what textures depend on them.)
+//-----------------------------------------------------------------------------
+LLVOAvatar::ETextureIndex LLVOAvatar::getBakedTE( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return TEX_HEAD_BAKED;
+ }
+ else
+ if( layerset == mUpperBodyLayerSet )
+ {
+ return TEX_UPPER_BAKED;
+ }
+ else
+ if( layerset == mLowerBodyLayerSet )
+ {
+ return TEX_LOWER_BAKED;
+ }
+ else
+ if( layerset == mEyesLayerSet )
+ {
+ return TEX_EYES_BAKED;
+ }
+ else
+ if( layerset == mSkirtLayerSet )
+ {
+ return TEX_SKIRT_BAKED;
+ }
+
+ llassert(0);
+ return TEX_HEAD_BAKED;
+}
+
+//-----------------------------------------------------------------------------
+// setNewBakedTexture()
+// A new baked texture has been successfully uploaded and we can start using it now.
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+ // Baked textures live on other sims.
+ LLHost target_host = getObjectHost();
+ setTEImage( te, gImageList.getImageFromHost( uuid, target_host ) );
+ updateMeshTextures();
+ dirtyMesh();
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+ switch( te )
+ {
+ case TEX_HEAD_BAKED:
+ llinfos << "New baked texture: HEAD" << llendl;
+ break;
+ case TEX_UPPER_BAKED:
+ llinfos << "New baked texture: UPPER" << llendl;
+ break;
+ case TEX_LOWER_BAKED:
+ llinfos << "New baked texture: LOWER" << llendl;
+ break;
+ case TEX_EYES_BAKED:
+ llinfos << "New baked texture: EYES" << llendl;
+ break;
+ case TEX_SKIRT_BAKED:
+ llinfos << "New baked texture: SKIRT" << llendl;
+ break;
+ default:
+ llwarns << "New baked texture: unknown te " << te << llendl;
+ break;
+ }
+
+ // dumpAvatarTEs( "setNewBakedTexture() send" );
+ // RN: throttle uploads
+ if (!hasPendingBakedUploads())
+ {
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+bool LLVOAvatar::hasPendingBakedUploads()
+{
+ bool head_pending = (mHeadLayerSet && mHeadLayerSet->getComposite()->uploadPending());
+ bool upper_pending = (mUpperBodyLayerSet && mUpperBodyLayerSet->getComposite()->uploadPending());
+ bool lower_pending = (mLowerBodyLayerSet && mLowerBodyLayerSet->getComposite()->uploadPending());
+ bool eyes_pending = (mEyesLayerSet && mEyesLayerSet->getComposite()->uploadPending());
+ bool skirt_pending = (mSkirtLayerSet && mSkirtLayerSet->getComposite()->uploadPending());
+
+ //llinfos << "TAT: LLVOAvatar::hasPendingBakedUploads()"
+ // << " head_pending " << head_pending
+ // << " upper_pending " << upper_pending
+ // << " lower_pending " << lower_pending
+ // << " eyes_pending " << eyes_pending
+ // << " skirt_pending " << skirt_pending
+ // << llendl;
+
+ if (head_pending || upper_pending || lower_pending || eyes_pending || skirt_pending)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setCachedBakedTexture()
+// A baked texture id was received from a cache query, make it active
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setCachedBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+ setTETexture( te, uuid );
+
+ switch(te)
+ {
+ case TEX_HEAD_BAKED:
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_UPPER_BAKED:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_LOWER_BAKED:
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_EYES_BAKED:
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_SKIRT_BAKED:
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->cancelUpload();
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// static
+// onCustomizeStart()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onCustomizeStart()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 tex_index = sBakedTextureIndices[i];
+ avatar->mSavedTE[ tex_index ] = avatar->getTEImage(tex_index)->getID();
+ avatar->setTEImage( tex_index, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ }
+
+ avatar->updateMeshTextures();
+
+// avatar->dumpAvatarTEs( "onCustomizeStart() send" );
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// static
+// onCustomizeEnd()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onCustomizeEnd()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar ) return;
+
+ LLHost target_host = avatar->getObjectHost();
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 tex_index = sBakedTextureIndices[i];
+ const LLUUID& saved = avatar->mSavedTE[ tex_index ];
+ if( !saved.isNull() )
+ {
+ avatar->setTEImage( tex_index, gImageList.getImageFromHost( saved, target_host ) );
+ }
+ }
+
+ avatar->updateMeshTextures();
+
+ if( !gQuit )
+ {
+ avatar->requestLayerSetUploads();
+ }
+
+ gAgent.sendAgentSetAppearance();
+ }
+
+BOOL LLVOAvatar::teToColorParams( ETextureIndex te, const char* param_name[3] )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT:
+ param_name[0] = "shirt_red";
+ param_name[1] = "shirt_green";
+ param_name[2] = "shirt_blue";
+ break;
+
+ case TEX_LOWER_PANTS:
+ param_name[0] = "pants_red";
+ param_name[1] = "pants_green";
+ param_name[2] = "pants_blue";
+ break;
+
+ case TEX_LOWER_SHOES:
+ param_name[0] = "shoes_red";
+ param_name[1] = "shoes_green";
+ param_name[2] = "shoes_blue";
+ break;
+
+ case TEX_LOWER_SOCKS:
+ param_name[0] = "socks_red";
+ param_name[1] = "socks_green";
+ param_name[2] = "socks_blue";
+ break;
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET:
+ param_name[0] = "jacket_red";
+ param_name[1] = "jacket_green";
+ param_name[2] = "jacket_blue";
+ break;
+
+ case TEX_UPPER_GLOVES:
+ param_name[0] = "gloves_red";
+ param_name[1] = "gloves_green";
+ param_name[2] = "gloves_blue";
+ break;
+
+ case TEX_UPPER_UNDERSHIRT:
+ param_name[0] = "undershirt_red";
+ param_name[1] = "undershirt_green";
+ param_name[2] = "undershirt_blue";
+ break;
+
+ case TEX_LOWER_UNDERPANTS:
+ param_name[0] = "underpants_red";
+ param_name[1] = "underpants_green";
+ param_name[2] = "underpants_blue";
+ break;
+
+ case TEX_SKIRT:
+ param_name[0] = "skirt_red";
+ param_name[1] = "skirt_green";
+ param_name[2] = "skirt_blue";
+ break;
+
+ default:
+ llassert(0);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void LLVOAvatar::setClothesColor( ETextureIndex te, const LLColor4& new_color, BOOL set_by_user )
+{
+ const char* param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ setVisualParamWeight( param_name[0], new_color.mV[VX], set_by_user );
+ setVisualParamWeight( param_name[1], new_color.mV[VY], set_by_user );
+ setVisualParamWeight( param_name[2], new_color.mV[VZ], set_by_user );
+ }
+}
+
+LLColor4 LLVOAvatar::getClothesColor( ETextureIndex te )
+{
+ LLColor4 color;
+ const char* param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ color.mV[VX] = getVisualParamWeight( param_name[0] );
+ color.mV[VY] = getVisualParamWeight( param_name[1] );
+ color.mV[VZ] = getVisualParamWeight( param_name[2] );
+ }
+ return color;
+}
+
+
+
+
+void LLVOAvatar::dumpAvatarTEs( const char* context )
+{
+ llinfos << (mIsSelf ? "Self: " : "Other: ") << context << llendl;
+ for( S32 i=0; i<TEX_NUM_ENTRIES; i++ )
+ {
+ const char* te_name[] = {
+ "TEX_HEAD_BODYPAINT ",
+ "TEX_UPPER_SHIRT ",
+ "TEX_LOWER_PANTS ",
+ "TEX_EYES_IRIS ",
+ "TEX_HAIR ",
+ "TEX_UPPER_BODYPAINT ",
+ "TEX_LOWER_BODYPAINT ",
+ "TEX_LOWER_SHOES ",
+ "TEX_HEAD_BAKED ",
+ "TEX_UPPER_BAKED ",
+ "TEX_LOWER_BAKED ",
+ "TEX_EYES_BAKED ",
+ "TEX_LOWER_SOCKS ",
+ "TEX_UPPER_JACKET ",
+ "TEX_LOWER_JACKET ",
+ "TEX_UPPER_GLOVES ",
+ "TEX_UPPER_UNDERSHIRT ",
+ "TEX_LOWER_UNDERPANTS ",
+ "TEX_SKIRT ",
+ "TEX_SKIRT_BAKED "
+ };
+
+ LLViewerImage* te_image = getTEImage(i);
+ if( !te_image )
+ {
+ llinfos << " " << te_name[i] << ": null ptr" << llendl;
+ }
+ else
+ if( te_image->getID().isNull() )
+ {
+ llinfos << " " << te_name[i] << ": null UUID" << llendl;
+ }
+ else
+ if( te_image->getID() == IMG_DEFAULT )
+ {
+ llinfos << " " << te_name[i] << ": IMG_DEFAULT" << llendl;
+ }
+ else
+ if( te_image->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llinfos << " " << te_name[i] << ": IMG_DEFAULT_AVATAR" << llendl;
+ }
+ else
+ {
+ llinfos << " " << te_name[i] << ": " << te_image->getID() << llendl;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateAttachmentVisibility()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateAttachmentVisibility(U32 camera_mode)
+{
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ else
+ {
+ switch (camera_mode)
+ {
+ case CAMERA_MODE_MOUSELOOK:
+ if (LLVOAvatar::sVisibleInFirstPerson && attachmentPoint->getVisibleInFirstPerson())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ else
+ {
+ attachmentPoint->setAttachmentVisibility(FALSE);
+ }
+ break;
+ default:
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ break;
+ }
+ }
+ }
+}
+
+// Given a texture entry, determine which wearable type owns it.
+// static
+LLUUID LLVOAvatar::getDefaultTEImageID( S32 te )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT: return LLUUID( gSavedSettings.getString("UIImgDefaultShirtUUID") );
+ case TEX_LOWER_PANTS: return LLUUID( gSavedSettings.getString("UIImgDefaultPantsUUID") );
+ case TEX_EYES_IRIS: return LLUUID( gSavedSettings.getString("UIImgDefaultEyesUUID") );
+ case TEX_HAIR: return LLUUID( gSavedSettings.getString("UIImgDefaultHairUUID") );
+ case TEX_LOWER_SHOES: return LLUUID( gSavedSettings.getString("UIImgDefaultShoesUUID") );
+ case TEX_LOWER_SOCKS: return LLUUID( gSavedSettings.getString("UIImgDefaultSocksUUID") );
+ case TEX_UPPER_GLOVES: return LLUUID( gSavedSettings.getString("UIImgDefaultGlovesUUID") );
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET: return LLUUID( gSavedSettings.getString("UIImgDefaultJacketUUID") );
+
+ case TEX_UPPER_UNDERSHIRT:
+ case TEX_LOWER_UNDERPANTS: return LLUUID( gSavedSettings.getString("UIImgDefaultUnderwearUUID") );
+
+ case TEX_SKIRT: return LLUUID( gSavedSettings.getString("UIImgDefaultSkirtUUID") );
+
+ default: return IMG_DEFAULT_AVATAR;
+ }
+}
+
+
+
+// Given a texture entry, determine which wearable type owns it.
+// static
+EWearableType LLVOAvatar::getTEWearableType( S32 te )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT:
+ return WT_SHIRT;
+
+ case TEX_LOWER_PANTS:
+ return WT_PANTS;
+
+ case TEX_EYES_IRIS:
+ return WT_EYES;
+
+ case TEX_HAIR:
+ return WT_HAIR;
+
+ case TEX_HEAD_BODYPAINT:
+ case TEX_UPPER_BODYPAINT:
+ case TEX_LOWER_BODYPAINT:
+ return WT_SKIN;
+
+ case TEX_LOWER_SHOES:
+ return WT_SHOES;
+
+ case TEX_LOWER_SOCKS:
+ return WT_SOCKS;
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET:
+ return WT_JACKET;
+
+ case TEX_UPPER_GLOVES:
+ return WT_GLOVES;
+
+ case TEX_UPPER_UNDERSHIRT:
+ return WT_UNDERSHIRT;
+
+ case TEX_LOWER_UNDERPANTS:
+ return WT_UNDERPANTS;
+
+ case TEX_SKIRT:
+ return WT_SKIRT;
+
+ default:
+ return WT_INVALID;
+ }
+}
+
+// Unlike most wearable functions, this works for both self and other.
+BOOL LLVOAvatar::isWearingWearableType( EWearableType type )
+{
+ if (mIsDummy) return TRUE;
+
+ ETextureIndex indicator_te;
+ switch( type )
+ {
+ case WT_SHIRT:
+ indicator_te = TEX_UPPER_SHIRT;
+ break;
+
+ case WT_PANTS:
+ indicator_te = TEX_LOWER_PANTS;
+ break;
+
+ case WT_SHOES:
+ indicator_te = TEX_LOWER_SHOES;
+ break;
+
+ case WT_SOCKS:
+ indicator_te = TEX_LOWER_SOCKS;
+ break;
+
+ case WT_JACKET:
+ indicator_te = TEX_UPPER_JACKET;
+ // Note: no need to test both upper and lower jacket
+ break;
+
+ case WT_GLOVES:
+ indicator_te = TEX_UPPER_GLOVES;
+ break;
+
+ case WT_UNDERSHIRT:
+ indicator_te = TEX_UPPER_UNDERSHIRT;
+ break;
+
+ case WT_UNDERPANTS:
+ indicator_te = TEX_LOWER_UNDERPANTS;
+ break;
+
+ case WT_SKIRT:
+ indicator_te = TEX_SKIRT;
+ break;
+
+ case WT_SHAPE:
+ case WT_SKIN:
+ case WT_HAIR:
+ case WT_EYES:
+ return TRUE; // everyone has all bodyparts
+
+ default:
+ return FALSE;
+ }
+
+ return ( getTEImage(indicator_te)->getID() != IMG_DEFAULT_AVATAR );
+}
+
+
+//-----------------------------------------------------------------------------
+// clampAttachmentPositions()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::clampAttachmentPositions()
+{
+ if (isDead()) return;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment)
+ {
+ attachment->clampObjectPosition();
+ }
+ }
+}
+
+BOOL LLVOAvatar::hasHUDAttachment()
+{
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+LLBBox LLVOAvatar::getHUDBBox()
+{
+ LLBBox bbox;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ LLViewerObject* hud_object = attachment->getObject(0);
+
+ // initialize bounding box to contain identity orientation and center point for attached object
+ bbox.addPointLocal(hud_object->getPosition());
+ // add rotated bounding box for attached object
+ bbox.addBBoxAgent(hud_object->getBoundingBoxAgent());
+ for (U32 i = 0; i < hud_object->mChildList.size(); i++)
+ {
+ bbox.addBBoxAgent(hud_object->mChildList[i]->getBoundingBoxAgent());
+ }
+ }
+ }
+
+ return bbox;
+}
+
+void LLVOAvatar::rebuildHUD()
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ LLViewerObject* hud_object = attachment->getObject(0);
+ LLDrawable* hud_drawable = hud_object->mDrawable;
+
+ if (hud_drawable)
+ {
+ // this assumes that an AGP sync will happen because face has been backlisted,
+ // so that pool has been rebuilt this frame and is scheduled for a sync
+ for(S32 face_index = 0; face_index < hud_drawable->getNumFaces(); ++face_index)
+ {
+ LLFace* facep = hud_drawable->getFace(face_index);
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ facep->restore();
+ }
+ }
+ }
+ for (U32 child_num = 0; child_num < hud_object->mChildList.size(); ++child_num)
+ {
+ LLViewerObject* childp = hud_object->mChildList[child_num];
+ LLDrawable* child_drawable = childp->mDrawable;
+
+ if (child_drawable)
+ {
+ for(S32 face_index = 0; face_index < child_drawable->getNumFaces(); ++face_index)
+ {
+ LLFace* facep = child_drawable->getFace(face_index);
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ facep->restore();
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onFirstTEMessageReceived()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onFirstTEMessageReceived()
+{
+ if( !mFirstTEMessageReceived )
+ {
+ mFirstTEMessageReceived = TRUE;
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ // Use any baked textures that we have even if they haven't downloaded yet.
+ // (That is, don't do a transition from unbaked to baked.)
+ if( head_baked )
+ {
+ mLastHeadBakedID = getTEImage( TEX_HEAD_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_HEAD_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( upper_baked )
+ {
+ mLastUpperBodyBakedID = getTEImage( TEX_UPPER_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_UPPER_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( lower_baked )
+ {
+ mLastLowerBodyBakedID = getTEImage( TEX_LOWER_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_LOWER_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( eyes_baked )
+ {
+ mLastEyesBakedID = getTEImage( TEX_EYES_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_EYES_BAKED );
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( skirt_baked )
+ {
+ mLastSkirtBakedID = getTEImage( TEX_SKIRT_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_SKIRT_BAKED );
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ updateMeshTextures();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// processAvatarAppearance()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+// llinfos << "processAvatarAppearance start " << mID << llendl;
+ BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived;
+
+ mFirstAppearanceMessageReceived = TRUE;
+
+ if( mIsSelf )
+ {
+ llwarns << "Received AvatarAppearance for self" << llendl;
+ if( mFirstTEMessageReceived )
+ {
+// llinfos << "processAvatarAppearance end " << mID << llendl;
+ return;
+ }
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ ESex old_sex = getSex();
+
+// llinfos << "ady LLVOAvatar::processAvatarAppearance()" << llendl;
+// dumpAvatarTEs( "PRE processAvatarAppearance()" );
+ unpackTEMessage(mesgsys, _PREHASH_ObjectData);
+// dumpAvatarTEs( "POST processAvatarAppearance()" );
+
+// llinfos << "Received AvatarAppearance: " << (mIsSelf ? "(self): " : "(other): " ) <<
+// (( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "HEAD " : "head " ) <<
+// (( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "UPPER " : "upper " ) <<
+// (( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "LOWER " : "lower " ) <<
+// (( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "EYES" : "eyes" ) << llendl;
+
+ if( !mFirstTEMessageReceived )
+ {
+ onFirstTEMessageReceived();
+ }
+
+ setCompositeUpdatesEnabled( FALSE );
+ updateMeshTextures(); // enables updates for laysets without baked textures.
+
+ // parse visual params
+ S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam);
+ if( num_blocks > 1 )
+ {
+ BOOL params_changed = FALSE;
+ BOOL interp_params = FALSE;
+
+ LLVisualParam* param = getFirstVisualParam();
+ if (!param)
+ {
+ llwarns << "No visual params!" << llendl;
+ }
+ else
+ {
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ param = getNextVisualParam();
+ }
+
+ if( !param )
+ {
+ llwarns << "Number of params in AvatarAppearance msg does not match number of params in avatar xml file." << llendl;
+ return;
+ }
+
+ U8 value;
+ mesgsys->getU8Fast(_PREHASH_VisualParam, _PREHASH_ParamValue, value, i);
+ F32 newWeight = U8_to_F32(value, param->getMinWeight(), param->getMaxWeight());
+
+ if (is_first_appearance_message || (param->getWeight() != newWeight))
+ {
+ //llinfos << "Received update for param " << param->getDisplayName() << " at value " << newWeight << llendl;
+ params_changed = TRUE;
+ if(is_first_appearance_message)
+ {
+ param->setWeight(newWeight, FALSE);
+ }
+ else
+ {
+ interp_params = TRUE;
+ param->setAnimationTarget(newWeight, FALSE);
+ }
+ }
+
+ param = getNextVisualParam();
+ }
+ }
+
+ while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ param = getNextVisualParam();
+ }
+ if( param )
+ {
+ llwarns << "Number of params in AvatarAppearance msg does not match number of params in avatar xml file." << llendl;
+ return;
+ }
+
+ if (params_changed)
+ {
+ if (interp_params)
+ {
+ startAppearanceAnimation(FALSE, FALSE);
+ }
+ updateVisualParams();
+
+ ESex new_sex = getSex();
+ if( old_sex != new_sex )
+ {
+ updateSexDependentLayerSets( FALSE );
+ }
+ }
+ }
+ else
+ {
+ llwarns << "AvatarAppearance msg received without any parameters" << llendl;
+ }
+
+ setCompositeUpdatesEnabled( TRUE );
+
+ llassert( getSex() == ((getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE) );
+
+ // If all of the avatars are completely baked, release the global image caches to conserve memory.
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+// llinfos << "processAvatarAppearance end " << mID << llendl;
+}
+
+// static
+void LLVOAvatar::getAnimLabels( LLDynamicArray<const char*>* labels )
+{
+ S32 i;
+ for( i = 0; i < gUserAnimStatesCount; i++ )
+ {
+ labels->put( gUserAnimStates[i].mLabel );
+ }
+
+ // Special case to trigger away (AFK) state
+ labels->put( "Away From Keyboard" );
+}
+
+// static
+void LLVOAvatar::getAnimNames( LLDynamicArray<const char*>* names )
+{
+ S32 i;
+
+ for( i = 0; i < gUserAnimStatesCount; i++ )
+ {
+ names->put( gUserAnimStates[i].mName );
+ }
+
+ // Special case to trigger away (AFK) state
+ names->put( "enter_away_from_keyboard_state" );
+}
+
+void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ //llinfos << "onBakedTextureMasksLoaded: " << src_vi->getID() << llendl;
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLUUID id = src_vi->getID();
+
+ if (!userdata)
+ {
+ return;
+ }
+
+ LLTextureMaskData* maskData = (LLTextureMaskData*) userdata;
+ LLVOAvatar* self = (LLVOAvatar*) gObjectList.findObject( maskData->mAvatarID );
+
+ // if discard level is 2 less than last discard level we processed, or we hit 0,
+ // then generate morph masks
+ if( self && success && (discard_level < maskData->mLastDiscardLevel - 2 || discard_level == 0) )
+ {
+ LLViewerImage* head_baked = self->getTEImage( TEX_HEAD_BAKED );
+ LLViewerImage* upper_baked = self->getTEImage( TEX_UPPER_BAKED );
+ LLViewerImage* lower_baked = self->getTEImage( TEX_LOWER_BAKED );
+
+ if( aux_src && aux_src->getComponents() == 1 )
+ {
+ if (!aux_src->getData())
+ {
+ llwarns << "No auxiliary source data for onBakedTextureMasksLoaded" << llendl;
+ src_vi->startImageDecode();
+ return;
+ }
+
+ U32 gl_name;
+ glGenTextures(1, (GLuint*) &gl_name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( gl_name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, GL_ALPHA8,
+ aux_src->getWidth(), aux_src->getHeight(),
+ 0, GL_ALPHA, GL_UNSIGNED_BYTE, aux_src->getData());
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if( id == head_baked->getID() )
+ {
+ if (self->mHeadLayerSet)
+ {
+ //llinfos << "onBakedTextureMasksLoaded for head " << id << " discard = " << discard_level << llendl;
+ self->mHeadLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mHeadMaskDiscard = discard_level;
+ if (self->mHeadMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mHeadMaskTexName);
+ }
+ self->mHeadMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ if( id == upper_baked->getID() )
+ {
+ if ( self->mUpperBodyLayerSet)
+ {
+ //llinfos << "onBakedTextureMasksLoaded for upper body " << id << " discard = " << discard_level << llendl;
+ self->mUpperBodyLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mUpperMaskDiscard = discard_level;
+ if (self->mUpperMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mUpperMaskTexName);
+ }
+ self->mUpperMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ if( id == lower_baked->getID() )
+ {
+ if ( self->mLowerBodyLayerSet )
+ {
+ //llinfos << "onBakedTextureMasksLoaded for lower body " << id << " discard = " << discard_level << llendl;
+ self->mLowerBodyLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mLowerMaskDiscard = discard_level;
+ if (self->mLowerMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mLowerMaskTexName);
+ }
+ self->mLowerMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "onBakedTextureMasksLoaded(): unexpected image id: " << id << llendl;
+ }
+
+ self->dirtyMesh();
+ }
+ else
+ {
+ // this can happen when someone uses an old baked texture possibly provided by
+ // viewer-side baked texture caching
+ llwarns << "Masks loaded callback but NO aux source!" << llendl;
+ }
+ }
+
+ if (final || !success)
+ {
+ delete maskData;
+ }
+
+}
+
+// static
+void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ LLUUID *avatar_idp = (LLUUID *)userdata;
+ LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+
+ if (!success && selfp)
+ {
+ selfp->removeMissingBakedTextures();
+ }
+ if (final || !success )
+ {
+ delete avatar_idp;
+ }
+}
+
+void LLVOAvatar::onBakedTextureLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata)
+{
+ //llinfos << "onBakedTextureLoaded: " << src_vi->getID() << llendl;
+
+ LLUUID id = src_vi->getID();
+ LLUUID *avatar_idp = (LLUUID *)userdata;
+ LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+
+ if (selfp && !success)
+ {
+ selfp->removeMissingBakedTextures();
+ }
+
+ if( final || !success )
+ {
+ delete avatar_idp;
+ }
+
+ if( selfp && success && final )
+ {
+ selfp->useBakedTexture( id );
+ }
+}
+
+
+// Called when baked texture is loaded and also when we start up with a baked texture
+void LLVOAvatar::useBakedTexture( const LLUUID& id )
+{
+// llinfos << "useBakedTexture" << llendl;
+ LLViewerImage* head_baked = getTEImage( TEX_HEAD_BAKED );
+ LLViewerImage* upper_baked = getTEImage( TEX_UPPER_BAKED );
+ LLViewerImage* lower_baked = getTEImage( TEX_LOWER_BAKED );
+ LLViewerImage* eyes_baked = getTEImage( TEX_EYES_BAKED );
+ LLViewerImage* skirt_baked = getTEImage( TEX_SKIRT_BAKED );
+
+ if( id == head_baked->getID() )
+ {
+ mHeadBakedLoaded = TRUE;
+
+ mLastHeadBakedID = id;
+ mHeadMesh0.setTexture( head_baked );
+ mHeadMesh1.setTexture( head_baked );
+ mHeadMesh2.setTexture( head_baked );
+ mHeadMesh3.setTexture( head_baked );
+ mHeadMesh4.setTexture( head_baked );
+ mEyeLashMesh0.setTexture( head_baked );
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->destroyComposite();
+ }
+ setLocalTexture( LOCTEX_HEAD_BODYPAINT, getTEImage( TEX_HEAD_BODYPAINT ), TRUE );
+ }
+ else
+ if( id == upper_baked->getID() )
+ {
+ mUpperBakedLoaded = TRUE;
+
+ mLastUpperBodyBakedID = id;
+ mUpperBodyMesh0.setTexture( upper_baked );
+ mUpperBodyMesh1.setTexture( upper_baked );
+ mUpperBodyMesh2.setTexture( upper_baked );
+ mUpperBodyMesh3.setTexture( upper_baked );
+ mUpperBodyMesh4.setTexture( upper_baked );
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_UPPER_SHIRT, getTEImage( TEX_UPPER_SHIRT ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_BODYPAINT, getTEImage( TEX_UPPER_BODYPAINT ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_JACKET, getTEImage( TEX_UPPER_JACKET ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_GLOVES, getTEImage( TEX_UPPER_GLOVES ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_UNDERSHIRT, getTEImage( TEX_UPPER_UNDERSHIRT ), TRUE );
+ }
+ else
+ if( id == lower_baked->getID() )
+ {
+ mLowerBakedLoaded = TRUE;
+
+ mLastLowerBodyBakedID = id;
+ mLowerBodyMesh0.setTexture( lower_baked );
+ mLowerBodyMesh1.setTexture( lower_baked );
+ mLowerBodyMesh2.setTexture( lower_baked );
+ mLowerBodyMesh3.setTexture( lower_baked );
+ mLowerBodyMesh4.setTexture( lower_baked );
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_LOWER_PANTS, getTEImage( TEX_LOWER_PANTS ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_BODYPAINT, getTEImage( TEX_LOWER_BODYPAINT ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_SHOES, getTEImage( TEX_LOWER_SHOES ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_SOCKS, getTEImage( TEX_LOWER_SOCKS ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_JACKET, getTEImage( TEX_LOWER_JACKET ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_UNDERPANTS, getTEImage( TEX_LOWER_UNDERPANTS ), TRUE );
+ }
+ else
+ if( id == eyes_baked->getID() )
+ {
+ mEyesBakedLoaded = TRUE;
+
+ mLastEyesBakedID = id;
+ mEyeBallLeftMesh0.setTexture( eyes_baked );
+ mEyeBallLeftMesh1.setTexture( eyes_baked );
+ mEyeBallRightMesh0.setTexture( eyes_baked );
+ mEyeBallRightMesh1.setTexture( eyes_baked );
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_EYES_IRIS, getTEImage( TEX_EYES_IRIS ), TRUE );
+ }
+ else
+ if( id == skirt_baked->getID() )
+ {
+ mSkirtBakedLoaded = TRUE;
+
+ mLastSkirtBakedID = id;
+ mSkirtMesh0.setTexture( skirt_baked );
+ mSkirtMesh1.setTexture( skirt_baked );
+ mSkirtMesh2.setTexture( skirt_baked );
+ mSkirtMesh3.setTexture( skirt_baked );
+ mSkirtMesh4.setTexture( skirt_baked );
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_SKIRT, getTEImage( TEX_SKIRT ), TRUE );
+ }
+
+ dirtyMesh();
+}
+
+// static
+void LLVOAvatar::dumpArchetypeXML( void* )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ apr_file_t* file = ll_apr_file_open(gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"new archetype.xml"), LL_APR_WB );
+ if( !file )
+ {
+ return;
+ }
+
+ apr_file_printf( file, "<?xml version=\"1.0\" encoding=\"US-ASCII\" standalone=\"yes\"?>\n" );
+ apr_file_printf( file, "<linden_genepool version=\"1.0\">\n" );
+ apr_file_printf( file, "\n\t<archetype name=\"???\">\n" );
+
+ // only body parts, not clothing.
+ for( S32 type = WT_SHAPE; type <= WT_EYES; type++ )
+ {
+ const char* wearable_name = LLWearable::typeToTypeName( (EWearableType) type );
+ apr_file_printf( file, "\n\t\t<!-- wearable: %s -->\n", wearable_name );
+
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ LLViewerVisualParam* viewer_param = (LLViewerVisualParam*)param;
+ if( (viewer_param->getWearableType() == type) &&
+ (viewer_param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ apr_file_printf( file, "\t\t<param id=\"%d\" name=\"%s\" value=\"%.3f\"/>\n",
+ viewer_param->getID(), viewer_param->getName().c_str(), viewer_param->getWeight() );
+ }
+ }
+
+ for( S32 te = 0; te < TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == type )
+ {
+ LLViewerImage* te_image = avatar->getTEImage( te );
+ if( te_image )
+ {
+ char uuid_str[UUID_STR_LENGTH];
+ te_image->getID().toString( uuid_str );
+ apr_file_printf( file, "\t\t<texture te=\"%i\" uuid=\"%s\"/>\n", te, uuid_str);
+ }
+ }
+ }
+ }
+ apr_file_printf( file, "\t</archetype>\n" );
+ apr_file_printf( file, "\n</linden_genepool>\n" );
+ apr_file_close( file );
+}
+
+
+// Assumes LLVOAvatar::sInstances has already been sorted.
+S32 LLVOAvatar::getUnbakedPixelAreaRank()
+{
+ S32 rank = 1;
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ if( inst == this )
+ {
+ return rank;
+ }
+ else
+ if( !inst->isDead() && !inst->isFullyBaked() )
+ {
+ rank++;
+ }
+ }
+
+ llassert(0);
+ return 0;
+}
+
+// static
+void LLVOAvatar::cullAvatarsByPixelArea()
+{
+ LLVOAvatar::sInstances.bubbleSortList();
+
+
+ // Update the avatars that have changed status
+ S32 rank = 1;
+
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ BOOL culled;
+ if( inst->isDead() )
+ {
+ culled = TRUE;
+ }
+ else if( inst->isSelf() || inst->isFullyBaked() )
+ {
+ culled = FALSE;
+ }
+ else
+ {
+ culled = (rank > LLVOAvatar::sMaxOtherAvatarsToComposite) || (inst->mPixelArea < MIN_PIXEL_AREA_FOR_COMPOSITE);
+ rank++;
+ }
+
+ if( inst->mCulled != culled )
+ {
+ inst->mCulled = culled;
+
+ lldebugs << "avatar " << inst->getID() << (culled ? " start culled" : " start not culled" ) << llendl;
+
+ inst->updateMeshTextures();
+ }
+ }
+
+ if( LLVOAvatar::areAllNearbyInstancesBaked() )
+ {
+ LLVOAvatar::deleteCachedImages();
+ }
+}
+
+const LLUUID& LLVOAvatar::grabLocalTexture(ETextureIndex index)
+{
+ if (canGrabLocalTexture(index))
+ {
+ return getTEImage( index )->getID();
+ }
+ return LLUUID::null;
+}
+
+BOOL LLVOAvatar::canGrabLocalTexture(ETextureIndex index)
+{
+ // Check if the texture hasn't been baked yet.
+ if ( getTEImage( index )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ lldebugs << "getTEImage( " << (U32) index << " )->getID() == IMG_DEFAULT_AVATAR" << llendl;
+ return FALSE;
+ }
+
+ // Check permissions of textures that show up in the
+ // baked texture. We don't want people copying people's
+ // work via baked textures.
+ std::vector<ETextureIndex> textures;
+ switch (index)
+ {
+ case TEX_EYES_BAKED:
+ textures.push_back(TEX_EYES_IRIS);
+ break;
+ case TEX_HEAD_BAKED:
+ textures.push_back(TEX_HEAD_BODYPAINT);
+ break;
+ case TEX_UPPER_BAKED:
+ textures.push_back(TEX_UPPER_BODYPAINT);
+ textures.push_back(TEX_UPPER_UNDERSHIRT);
+ textures.push_back(TEX_UPPER_SHIRT);
+ textures.push_back(TEX_UPPER_JACKET);
+ textures.push_back(TEX_UPPER_GLOVES);
+ break;
+ case TEX_LOWER_BAKED:
+ textures.push_back(TEX_LOWER_BODYPAINT);
+ textures.push_back(TEX_LOWER_UNDERPANTS);
+ textures.push_back(TEX_LOWER_PANTS);
+ textures.push_back(TEX_LOWER_JACKET);
+ textures.push_back(TEX_LOWER_SOCKS);
+ textures.push_back(TEX_LOWER_SHOES);
+ break;
+ case TEX_SKIRT_BAKED:
+ textures.push_back(TEX_SKIRT);
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ std::vector<ETextureIndex>::iterator iter = textures.begin();
+ std::vector<ETextureIndex>::iterator end = textures.end();
+ for (; iter != end; ++iter)
+ {
+ ETextureIndex t_index = (*iter);
+ lldebugs << "Checking index " << (U32) t_index << llendl;
+ const LLUUID& texture_id = getTEImage( t_index )->getID();
+ if (texture_id != IMG_DEFAULT_AVATAR)
+ {
+ // Search inventory for this texture.
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLAssetIDMatches asset_id_matches(texture_id);
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cats,
+ items,
+ LLInventoryModel::INCLUDE_TRASH,
+ asset_id_matches);
+
+ BOOL can_grab = FALSE;
+ lldebugs << "item count for asset " << texture_id << ": " << items.count() << llendl;
+ if (items.count())
+ {
+ // search for full permissions version
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ LLInventoryItem* itemp = items[i];
+ LLPermissions item_permissions = itemp->getPermissions();
+ if ( item_permissions.allowOperationBy(
+ PERM_MODIFY, gAgent.getID(), gAgent.getGroupID()) &&
+ item_permissions.allowOperationBy(
+ PERM_COPY, gAgent.getID(), gAgent.getGroupID()) &&
+ item_permissions.allowOperationBy(
+ PERM_TRANSFER, gAgent.getID(), gAgent.getGroupID()) )
+ {
+ can_grab = TRUE;
+ break;
+ }
+ }
+ }
+ if (!can_grab) return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLVOAvatar::dumpLocalTextures()
+{
+ llinfos << "Local Textures:" << llendl;
+
+ char* names[] = {
+ "Shirt ",
+ "UpperTatoo",
+ "Pants ",
+ "LowerTatoo",
+ "Head Tatoo",
+ "Shoes ",
+ "Socks ",
+ "Upper Jckt",
+ "Lower Jckt",
+ "Gloves ",
+ "Undershirt",
+ "Underpants",
+ "Iris ",
+ "Skirt "};
+
+ ETextureIndex baked_equiv[] = {
+ TEX_UPPER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_HEAD_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_EYES_BAKED,
+ TEX_SKIRT_BAKED };
+
+
+ for( S32 i = 0; i < LOCTEX_NUM_ENTRIES; i++ )
+ {
+ if( getTEImage( baked_equiv[i] )->getID() != IMG_DEFAULT_AVATAR )
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ // End users don't get to trivially see avatar texture IDs, makes textures
+ // easier to steal. JC
+ llinfos << "LocTex " << names[i] << ": Baked " << llendl;
+#else
+ llinfos << "LocTex " << names[i] << ": Baked " << getTEImage( baked_equiv[i] )->getID() << llendl;
+#endif
+ }
+ else if (mLocalTexture[i].notNull())
+ {
+ if( mLocalTexture[i]->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llinfos << "LocTex " << names[i] << ": None" << llendl;
+ }
+ else
+ {
+ LLViewerImage* image = mLocalTexture[i];
+ F32 data_progress = 0.0f;
+ F32 decode_progress = image->getDecodeProgress(&data_progress);
+
+ llinfos << "LocTex " << names[i] << ": "
+ << "Discard " << image->getDiscardLevel() << ", "
+ << "(" << image->getWidth() << ", " << image->getHeight() << ") "
+#if !LL_RELEASE_FOR_DOWNLOAD
+ // End users don't get to trivially see avatar texture IDs,
+ // makes textures easier to steal
+ << image->getID() << " "
+#endif
+ << "Data: " << (data_progress * 100) << "% "
+ << "Decode: " << (decode_progress * 100) << "% "
+ << "Priority: " << image->getDecodePriority() << " "
+ << (image->needsDecode() ? "pending decode" : "not pending decode")
+ << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "LocTex " << names[i] << ": No LLViewerImage" << llendl;
+ }
+ }
+}
+
+void LLVOAvatar::startAppearanceAnimation(BOOL set_by_user, BOOL play_sound)
+{
+ if(!mAppearanceAnimating)
+ {
+ mAppearanceAnimSetByUser = set_by_user;
+ mAppearanceAnimating = TRUE;
+ mAppearanceMorphTimer.reset();
+ mLastAppearanceBlendTime = 0.f;
+ }
+}
+
+
+void LLVOAvatar::removeMissingBakedTextures()
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+ BOOL removed = FALSE;
+
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 te = sBakedTextureIndices[i];
+
+ if( getTEImage( te )->isMissingAsset() )
+ {
+ setTEImage( te, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ removed = TRUE;
+ }
+ }
+
+ if( removed )
+ {
+ invalidateComposite( mEyesLayerSet, FALSE );
+ invalidateComposite( mHeadLayerSet, FALSE );
+ invalidateComposite( mUpperBodyLayerSet, FALSE );
+ invalidateComposite( mLowerBodyLayerSet, FALSE );
+ invalidateComposite( mSkirtLayerSet, FALSE );
+ updateMeshTextures();
+ requestLayerSetUploads();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarInfo
+//-----------------------------------------------------------------------------
+
+LLVOAvatarInfo::LLVOAvatarInfo()
+ : mTexSkinColorInfo(0), mTexHairColorInfo(0), mTexEyeColorInfo(0)
+{
+}
+
+LLVOAvatarInfo::~LLVOAvatarInfo()
+{
+ std::for_each(mMeshInfoList.begin(), mMeshInfoList.end(), DeletePointer());
+ std::for_each(mSkeletalDistortionInfoList.begin(), mSkeletalDistortionInfoList.end(), DeletePointer());
+ std::for_each(mAttachmentInfoList.begin(), mAttachmentInfoList.end(), DeletePointer());
+ delete mTexSkinColorInfo;
+ delete mTexHairColorInfo;
+ delete mTexEyeColorInfo;
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+ std::for_each(mDriverInfoList.begin(), mDriverInfoList.end(), DeletePointer());
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarBoneInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
+{
+ if (node->hasName("bone"))
+ {
+ mIsJoint = TRUE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ llwarns << "Bone without name" << llendl;
+ return FALSE;
+ }
+ }
+ else if (node->hasName("collision_volume"))
+ {
+ mIsJoint = FALSE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ mName = "Collision Volume";
+ }
+ }
+ else
+ {
+ llerrs << "Invalid node " << node->getName() << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
+ if (!node->getFastAttributeVector3(pos_string, mPos))
+ {
+ llwarns << "Bone without position" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot");
+ if (!node->getFastAttributeVector3(rot_string, mRot))
+ {
+ llwarns << "Bone without rotation" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ if (!node->getFastAttributeVector3(scale_string, mScale))
+ {
+ llwarns << "Bone without scale" << llendl;
+ return FALSE;
+ }
+
+ if (mIsJoint)
+ {
+ static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
+ if (!node->getFastAttributeVector3(pivot_string, mPivot))
+ {
+ llwarns << "Bone without pivot" << llendl;
+ return FALSE;
+ }
+ }
+
+ // parse children
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo;
+ if (!child_info->parseXml(child))
+ {
+ delete child_info;
+ return FALSE;
+ }
+ mChildList.push_back(child_info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarSkeletonInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
+{
+ static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones");
+ if (!node->getFastAttributeS32(num_bones_string, mNumBones))
+ {
+ llerrs << "Couldn't find number of bones." << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes");
+ node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes);
+
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ llerrs << "Error parsing bone in skeleton file" << llendl;
+ }
+ mBoneInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlSkeletonNode(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* node = root->getChildByName( "skeleton" );
+ if( !node )
+ {
+ llwarns << "avatar file: missing <skeleton>" << llendl;
+ return FALSE;
+ }
+
+ LLXmlTreeNode* child;
+
+ // SKELETON DISTORTIONS
+ for (child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_skeleton"))
+ {
+ if (child->getChildByName("param_morph"))
+ {
+ llwarns << "Can't specify morph param in skeleton definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolySkeletalDistortionInfo *info = new LLPolySkeletalDistortionInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+
+ mSkeletalDistortionInfoList.push_back(info);
+ }
+
+ // ATTACHMENT POINTS
+ for (child = node->getChildByName( "attachment_point" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLVOAvatarAttachmentInfo* info = new LLVOAvatarAttachmentInfo();
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!child->getFastAttributeString(name_string, info->mName))
+ {
+ llwarns << "No name supplied for attachment point." << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle joint_string = LLXmlTree::addAttributeString("joint");
+ if (!child->getFastAttributeString(joint_string, info->mJointName))
+ {
+ llwarns << "No bone declared in attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle position_string = LLXmlTree::addAttributeString("position");
+ if (child->getFastAttributeVector3(position_string, info->mPosition))
+ {
+ info->mHasPosition = TRUE;
+ }
+
+ static LLStdStringHandle rotation_string = LLXmlTree::addAttributeString("rotation");
+ if (child->getFastAttributeVector3(rotation_string, info->mRotationEuler))
+ {
+ info->mHasRotation = TRUE;
+ }
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if (child->getFastAttributeS32(group_string, info->mGroup))
+ {
+ if (info->mGroup == -1)
+ info->mGroup = -1111; // -1 = none parsed, < -1 = bad value
+ }
+
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ if (!child->getFastAttributeS32(id_string, info->mAttachmentID))
+ {
+ llwarns << "No id supplied for attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle slot_string = LLXmlTree::addAttributeString("pie_slice");
+ child->getFastAttributeS32(slot_string, info->mPieMenuSlice);
+
+ static LLStdStringHandle visible_in_first_person_string = LLXmlTree::addAttributeString("visible_in_first_person");
+ child->getFastAttributeBOOL(visible_in_first_person_string, info->mVisibleFirstPerson);
+
+ static LLStdStringHandle hud_attachment_string = LLXmlTree::addAttributeString("hud");
+ child->getFastAttributeBOOL(hud_attachment_string, info->mIsHUDAttachment);
+
+ mAttachmentInfoList.push_back(info);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlMeshNodes(): parses <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlMeshNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* node = root->getChildByName( "mesh" );
+ node;
+ node = root->getNextNamedChild())
+ {
+ LLVOAvatarMeshInfo *info = new LLVOAvatarMeshInfo;
+
+ // attribute: type
+ static LLStdStringHandle type_string = LLXmlTree::addAttributeString("type");
+ if( !node->getFastAttributeString( type_string, info->mType ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing type attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle lod_string = LLXmlTree::addAttributeString("lod");
+ if (!node->getFastAttributeS32( lod_string, info->mLOD ))
+ {
+ llwarns << "Avatar file: <mesh> is missing lod attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if( !node->getFastAttributeString( file_name_string, info->mMeshFileName ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing file_name attribute. Ignoring: " << info->mType << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle reference_string = LLXmlTree::addAttributeString("reference");
+ node->getFastAttributeString( reference_string, info->mReferenceMeshName );
+
+ // attribute: min_pixel_area
+ static LLStdStringHandle min_pixel_area_string = LLXmlTree::addAttributeString("min_pixel_area");
+ static LLStdStringHandle min_pixel_width_string = LLXmlTree::addAttributeString("min_pixel_width");
+ if (!node->getFastAttributeF32( min_pixel_area_string, info->mMinPixelArea ))
+ {
+ F32 min_pixel_area = 0.1f;
+ if (node->getFastAttributeF32( min_pixel_width_string, min_pixel_area ))
+ {
+ // this is square root of pixel area (sensible to use linear space in defining lods)
+ min_pixel_area = min_pixel_area * min_pixel_area;
+ }
+ info->mMinPixelArea = min_pixel_area;
+ }
+
+ // Parse visual params for this node only if we haven't already
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_morph"))
+ {
+ if (child->getChildByName("param_skeleton"))
+ {
+ llwarns << "Can't specify skeleton param in a mesh definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolyMorphTargetInfo *morphinfo = new LLPolyMorphTargetInfo();
+ if (!morphinfo->parseXml(child))
+ {
+ delete morphinfo;
+ delete info;
+ return -1;
+ }
+ BOOL shared = FALSE;
+ static LLStdStringHandle shared_string = LLXmlTree::addAttributeString("shared");
+ child->getFastAttributeBOOL(shared_string, shared);
+
+ info->mPolyMorphTargetInfoList.push_back(LLVOAvatarMeshInfo::morph_info_pair_t(morphinfo, shared));
+ }
+
+ mMeshInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlColorNodes(): parses <global_color> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlColorNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* color_node = root->getChildByName( "global_color" );
+ color_node;
+ color_node = root->getNextNamedChild())
+ {
+ LLString global_color_name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (color_node->getFastAttributeString( name_string, global_color_name ) )
+ {
+ if( global_color_name == "skin_color" )
+ {
+ if (mTexSkinColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of skin_color" << llendl;
+ return FALSE;
+ }
+ mTexSkinColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexSkinColorInfo->parseXml( color_node ) )
+ {
+ delete mTexSkinColorInfo; mTexSkinColorInfo = 0;
+ llwarns << "avatar file: mTexSkinColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "hair_color" )
+ {
+ if (mTexHairColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of hair_color" << llendl;
+ return FALSE;
+ }
+ mTexHairColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexHairColorInfo->parseXml( color_node ) )
+ {
+ delete mTexHairColorInfo; mTexHairColorInfo = 0;
+ llwarns << "avatar file: mTexHairColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "eye_color" )
+ {
+ if (mTexEyeColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of eye_color" << llendl;
+ return FALSE;
+ }
+ mTexEyeColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexEyeColorInfo->parseXml( color_node ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlLayerNodes(): parses <layer_set> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlLayerNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* layer_node = root->getChildByName( "layer_set" );
+ layer_node;
+ layer_node = root->getNextNamedChild())
+ {
+ LLTexLayerSetInfo* layer_info = new LLTexLayerSetInfo();
+ if( layer_info->parseXml( layer_node ) )
+ {
+ mLayerInfoList.push_back(layer_info);
+ }
+ else
+ {
+ delete layer_info;
+ llwarns << "avatar file: layer_set->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlDriverNodes(): parses <driver_parameters> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlDriverNodes(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* driver = root->getChildByName( "driver_parameters" );
+ if( driver )
+ {
+ for (LLXmlTreeNode* grand_child = driver->getChildByName( "param" );
+ grand_child;
+ grand_child = driver->getNextNamedChild())
+ {
+ if( grand_child->getChildByName( "param_driver" ) )
+ {
+ LLDriverParamInfo* driver_info = new LLDriverParamInfo();
+ if( driver_info->parseXml( grand_child ) )
+ {
+ mDriverInfoList.push_back(driver_info);
+ }
+ else
+ {
+ delete driver_info;
+ llwarns << "avatar file: driver_param->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+void LLVOAvatar::writeCAL3D(std::string& path, std::string& file_base)
+{
+ char filename[MAX_PATH];
+
+ // reset animated morphs
+ setVisualParamWeight("Blink_Left", 0.f);
+ setVisualParamWeight("Blink_Right", 0.f);
+ setVisualParamWeight("Hands_Relaxed", 1.f);
+ setVisualParamWeight("Hands_Point", 0.f);
+ setVisualParamWeight("Hands_Fist", 0.f);
+ setVisualParamWeight("Hands_Relaxed_L", 0.f);
+ setVisualParamWeight("Hands_Point_L", 0.f);
+ setVisualParamWeight("Hands_Fist_L", 0.f);
+ setVisualParamWeight("Hands_Relaxed_R", 0.f);
+ setVisualParamWeight("Hands_Point_R", 0.f);
+ setVisualParamWeight("Hands_Fist_R", 0.f);
+ setVisualParamWeight("Hands_Salute_R", 0.f);
+ setVisualParamWeight("Hands_Typing", 0.f);
+ setVisualParamWeight("Hands_Peace_R", 0.f);
+ setVisualParamWeight("Hands_Spread_R", 0.f);
+ updateVisualParams();
+
+ sprintf(filename, "%s\\%s_skeleton.xsf", path.c_str(), file_base.c_str());
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar file " << filename << llendl;
+ return;
+ }
+ apr_file_printf(fp, "<SKELETON VERSION=\"1000\" NUMBONES=\"%d\">\n", sSkeletonInfo->getNumBones() - sSkeletonInfo->getNumCollisionVolumes());
+ mRoot.writeCAL3D(fp);
+ apr_file_printf(fp, "</SKELETON>\n");
+ apr_file_close(fp);
+
+ sprintf(filename, "%s\\%s_mesh_body.xmf", path.c_str(), file_base.c_str());
+ //gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"avatar.cal").c_str()
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar file " << filename << llendl;
+ return;
+ }
+
+ BOOL has_skirt = isWearingWearableType(WT_SKIRT);
+
+ apr_file_printf(fp, "<MESH VERSION=\"1000\" NUMSUBMESH=\"%d\">\n", has_skirt ? 8 : 7);
+ mHairMesh0.writeCAL3D(fp, 5, this);
+ mHeadMesh0.writeCAL3D(fp, 0, this);
+ mEyeLashMesh0.writeCAL3D(fp, 0, this);
+ mUpperBodyMesh0.writeCAL3D(fp, 1, this);
+ mLowerBodyMesh0.writeCAL3D(fp, 2, this);
+ mEyeBallLeftMesh0.writeCAL3D(fp, 3, this);
+ mEyeBallRightMesh0.writeCAL3D(fp, 3, this);
+ if (has_skirt)
+ {
+ mSkirtMesh0.writeCAL3D(fp, 4, this);
+ }
+ apr_file_printf(fp, "</MESH>\n");
+ apr_file_close(fp);
+
+ // write out material files
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ for (S32 i = 0; i < (has_skirt ? BAKED_TEXTURE_COUNT : BAKED_TEXTURE_COUNT - 1); i++)
+ {
+ sprintf(filename, "%s\\%s_material_tex_%d.tga", path.c_str(), file_base.c_str(), i);
+
+ LLViewerImage* viewer_imagep = mTEImages[sBakedTextureIndices[i]];
+ if (!viewer_imagep->getHasGLTexture())
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ continue;
+ }
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ viewer_imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ // output image for hair
+ sprintf(filename, "%s\\%s_material_tex_5.tga", path.c_str(), file_base.c_str());
+ LLViewerImage* viewer_imagep = mTEImages[TEX_HAIR];
+ if (!viewer_imagep->getHasGLTexture())
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ }
+ else
+ {
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ viewer_imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ // save out attachments
+ sprintf(filename, "%s\\%s_mesh_attachments.xmf", path.c_str(), file_base.c_str());
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write attachments file " << filename << llendl;
+ return;
+ }
+
+ typedef std::multimap<LLUUID, LLMaterialExportInfo*>::iterator material_it_t;
+ std::multimap<LLUUID, LLMaterialExportInfo*> material_map;
+
+ S32 num_attachment_objects = 0;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull() &&
+ attached_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ num_attachment_objects += attached_object->mDrawable->getNumFaces();
+ for (U32 i = 0; i < attached_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_object = attached_object->mChildList[i];
+ num_attachment_objects += child_object->mDrawable->getNumFaces();
+ }
+ }
+ }
+
+ apr_file_printf(fp, "<MESH VERSION=\"1000\" NUMSUBMESH=\"%d\">\n", num_attachment_objects);
+
+ S32 material_index = 6;
+ S32 texture_index = 6;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ LLVOVolume* attached_volume = (LLVOVolume*)attached_object;
+ LLVector3 pos = attachment->getPosition();
+ LLJoint* cur_joint = attachment->getParent();
+ while (cur_joint)
+ {
+ pos += cur_joint->getSkinOffset();
+ cur_joint = (LLViewerJoint*)cur_joint->getParent();
+ }
+ pos *= 100.f;
+ S32 attached_joint_num = attachment->getParent()->mJointNum;
+ LLQuaternion rot = attachment->getRotation();
+ attached_volume->writeCAL3D(fp, path, file_base, attached_joint_num, pos, rot, material_index, texture_index, material_map);
+ }
+ }
+ apr_file_printf(fp, "</MESH>\n");
+ apr_file_close(fp);
+
+ // now dump sample animation
+ LLKeyframeMotion* walk_motion =
+ getSex() == SEX_MALE ? (LLKeyframeMotion*)findMotion(ANIM_AGENT_WALK) : (LLKeyframeMotion*)findMotion(ANIM_AGENT_FEMALE_WALK);
+ if (FALSE)//(walk_motion)
+ {
+ sprintf(filename, "%s\\%s_anim.xaf", path.c_str(), file_base.c_str());
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar animation file " << filename << llendl;
+ return;
+ }
+
+ walk_motion->writeCAL3D(fp);
+
+ apr_file_close(fp);
+ }
+
+ // finally, write out .cfg file
+ sprintf(filename, "%s\\%s_avatar.cfg", path.c_str(), file_base.c_str());
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar config file " << filename << llendl;
+ return;
+ }
+
+ // this version exports animation
+ //apr_file_printf(fp, "#\n# cal3d model configuration file\n#\n# model: %s_avatar\n#\n\nscale=1.0\n\nskeleton=%s_skeleton.xsf\n\nanimation=%s_anim.xaf\n\n", file_base.c_str(), file_base.c_str(), file_base.c_str());
+ apr_file_printf(fp, "#\n# cal3d model configuration file\n#\n# model: %s_avatar\n#\n\nscale=1.0\n\nskeleton=%s_skeleton.xsf\n\n", file_base.c_str(), file_base.c_str());
+ apr_file_printf(fp, "mesh=%s_mesh_body.xmf\nmesh=%s_mesh_attachments.xmf\n", file_base.c_str(), file_base.c_str());
+
+ for (S32 i = 0; i < material_index; i++)
+ {
+ apr_file_printf(fp, "material=%s_material_%d.xrf\n", file_base.c_str(), i);
+ }
+ apr_file_close(fp);
+
+ for(S32 i = 0; i < 6; i++)
+ {
+ sprintf(filename, "%s\\%s_material_%d.xrf", path.c_str(), file_base.c_str(), i);
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write material definition file " << filename << llendl;
+ return;
+ }
+
+ // for hair material, use hair color...otherwise use white for entire body
+ LLColor4U material_color = (i == 5) ? mTexHairColor->getColor() : LLColor4U::white;
+
+ apr_file_printf(fp, "<HEADER MAGIC=\"XRF\" VERSION=\"900\" />\n<MATERIAL NUMMAPS=\"1\">\n");
+ apr_file_printf(fp, " <AMBIENT>%d %d %d %d</AMBIENT>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <DIFFUSE>%d %d %d %d</DIFFUSE>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <SPECULAR>0 0 0 0</SPECULAR>\n");
+ apr_file_printf(fp, " <SHININESS>1.0</SHININESS>\n");
+ apr_file_printf(fp, " <MAP>%s_material_tex_%d.tga</MAP>\n", file_base.c_str(), i);
+ apr_file_printf(fp, "</MATERIAL>\n");
+
+ apr_file_close(fp);
+ }
+
+ // write out material files
+ for(material_it_t material_it = material_map.begin(); material_it != material_map.end(); ++material_it)
+ {
+ LLMaterialExportInfo* export_info = material_it->second;
+
+ sprintf(filename, "%s\\%s_material_%d.xrf", path.c_str(), file_base.c_str(), export_info->mMaterialIndex);
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write material definition file " << filename << llendl;
+ return;
+ }
+
+ LLColor4U material_color = export_info->mColor;
+
+ apr_file_printf(fp, "<HEADER MAGIC=\"XRF\" VERSION=\"900\" />\n<MATERIAL NUMMAPS=\"1\">\n");
+ apr_file_printf(fp, " <AMBIENT>%d %d %d %d</AMBIENT>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <DIFFUSE>%d %d %d %d</DIFFUSE>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <SPECULAR>0 0 0 0</SPECULAR>\n");
+ apr_file_printf(fp, " <SHININESS>1.0</SHININESS>\n");
+ apr_file_printf(fp, " <MAP>%s_material_tex_%d.tga</MAP>\n", file_base.c_str(), export_info->mTextureIndex);
+ apr_file_printf(fp, "</MATERIAL>\n");
+
+ apr_file_close(fp);
+ }
+
+
+ std::for_each(material_map.begin(), material_map.end(), DeletePairedPointer());
+ material_map.clear();
+}
+
+// warning: order(N) not order(1)
+S32 LLVOAvatar::getAttachmentCount()
+{
+ S32 count = mAttachmentPoints.getLength();
+ return count;
+}
+
+//virtual
+void LLVOAvatar::updateRegion(LLViewerRegion *regionp)
+{
+ if (mIsSelf)
+ {
+ if (regionp->getHandle() != mLastRegionHandle)
+ {
+ if (mLastRegionHandle != 0)
+ {
+ ++mRegionCrossingCount;
+ F64 delta = (F64)mRegionCrossingTimer.getElapsedTimeF32();
+ F64 avg = (mRegionCrossingCount == 1) ? 0 : gViewerStats->getStat(LLViewerStats::ST_CROSSING_AVG);
+ F64 delta_avg = (delta + avg*(mRegionCrossingCount-1)) / mRegionCrossingCount;
+ gViewerStats->setStat(LLViewerStats::ST_CROSSING_AVG, delta_avg);
+
+ F64 max = (mRegionCrossingCount == 1) ? 0 : gViewerStats->getStat(LLViewerStats::ST_CROSSING_MAX);
+ max = llmax(delta, max);
+ gViewerStats->setStat(LLViewerStats::ST_CROSSING_MAX, max);
+ }
+ mLastRegionHandle = regionp->getHandle();
+ }
+ mRegionCrossingTimer.reset();
+ }
+}
+
+LLString LLVOAvatar::getFullname() const
+{
+ LLString name;
+
+ LLNameValue* first = getNVPair("FirstName");
+ LLNameValue* last = getNVPair("LastName");
+ if (first && last)
+ {
+ name += first->getString();
+ name += " ";
+ name += last->getString();
+ }
+
+ return name;
+}
+
+LLTexLayerSet* LLVOAvatar::getLayerSet(ETextureIndex index) const
+{
+ switch( index )
+ {
+ case TEX_HEAD_BAKED:
+ case TEX_HEAD_BODYPAINT:
+ return mHeadLayerSet;
+
+ case TEX_UPPER_BAKED:
+ case TEX_UPPER_SHIRT:
+ case TEX_UPPER_BODYPAINT:
+ case TEX_UPPER_JACKET:
+ case TEX_UPPER_GLOVES:
+ case TEX_UPPER_UNDERSHIRT:
+ return mUpperBodyLayerSet;
+
+ case TEX_LOWER_BAKED:
+ case TEX_LOWER_PANTS:
+ case TEX_LOWER_BODYPAINT:
+ case TEX_LOWER_SHOES:
+ case TEX_LOWER_SOCKS:
+ case TEX_LOWER_JACKET:
+ case TEX_LOWER_UNDERPANTS:
+ return mLowerBodyLayerSet;
+
+ case TEX_EYES_BAKED:
+ case TEX_EYES_IRIS:
+ return mEyesLayerSet;
+
+ case TEX_SKIRT_BAKED:
+ case TEX_SKIRT:
+ return mSkirtLayerSet;
+
+ case TEX_HAIR:
+ default:
+ return NULL;
+ }
+}
+
+LLHost LLVOAvatar::getObjectHost() const
+{
+ LLViewerRegion* region = getRegion();
+ if (region && !isDead())
+ {
+ return region->getHost();
+ }
+ else
+ {
+ return LLHost::invalid;
+ }
+}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
new file mode 100644
index 0000000000..a47321cb90
--- /dev/null
+++ b/indra/newview/llvoavatar.h
@@ -0,0 +1,897 @@
+/**
+ * @file llvoavatar.h
+ * @brief Declaration of LLVOAvatar class which is a derivation fo
+ * LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOAVATAR_H
+#define LL_LLVOAVATAR_H
+
+// May help Visual Studio avoid opening this file.
+
+#include <set>
+#include <map>
+#include <algorithm>
+#include <deque>
+
+#include "llchat.h"
+#include "llviewerobject.h"
+#include "lljointsolverrp3.h"
+#include "llviewerjointshape.h"
+#include "llviewerjointmesh.h"
+#include "llviewerjointattachment.h"
+#include "llcharacter.h"
+#include "material_codes.h"
+#include "llanimationstates.h"
+#include "v4coloru.h"
+#include "llstring.h"
+#include "llframetimer.h"
+#include "llxmltree.h"
+#include "llwearable.h"
+
+const S32 VOAVATAR_SCRATCH_TEX_WIDTH = 512;
+const S32 VOAVATAR_SCRATCH_TEX_HEIGHT = 512;
+
+const LLUUID ANIM_AGENT_BODY_NOISE = LLUUID("9aa8b0a6-0c6f-9518-c7c3-4f41f2c001ad"); //"body_noise"
+const LLUUID ANIM_AGENT_BREATHE_ROT = LLUUID("4c5a103e-b830-2f1c-16bc-224aa0ad5bc8"); //"breathe_rot"
+const LLUUID ANIM_AGENT_EDITING = LLUUID("2a8eba1d-a7f8-5596-d44a-b4977bf8c8bb"); //"editing"
+const LLUUID ANIM_AGENT_EYE = LLUUID("5c780ea8-1cd1-c463-a128-48c023f6fbea"); //"eye"
+const LLUUID ANIM_AGENT_FLY_ADJUST = LLUUID("db95561f-f1b0-9f9a-7224-b12f71af126e"); //"fly_adjust"
+const LLUUID ANIM_AGENT_HAND_MOTION = LLUUID("ce986325-0ba7-6e6e-cc24-b17c4b795578"); //"hand_motion"
+const LLUUID ANIM_AGENT_HEAD_ROT = LLUUID("e6e8d1dd-e643-fff7-b238-c6b4b056a68d"); //"head_rot"
+const LLUUID ANIM_AGENT_PELVIS_FIX = LLUUID("0c5dd2a2-514d-8893-d44d-05beffad208b"); //"pelvis_fix"
+const LLUUID ANIM_AGENT_TARGET = LLUUID("0e4896cb-fba4-926c-f355-8720189d5b55"); //"target"
+const LLUUID ANIM_AGENT_WALK_ADJUST = LLUUID("829bc85b-02fc-ec41-be2e-74cc6dd7215d"); //"walk_adjust"
+
+class LLChat;
+class LLXmlTreeNode;
+class LLTexLayerSet;
+class LLTexGlobalColor;
+class LLTexGlobalColorInfo;
+class LLTexLayerSetInfo;
+class LLDriverParamInfo;
+
+class LLHUDText;
+class LLHUDEffectSpiral;
+
+typedef enum e_mesh_id
+{
+ MESH_ID_HAIR,
+ MESH_ID_HEAD,
+ MESH_ID_UPPER_BODY,
+ MESH_ID_LOWER_BODY,
+ MESH_ID_SKIRT
+} eMeshID;
+
+typedef enum e_render_name
+{
+ RENDER_NAME_NEVER,
+ RENDER_NAME_FADE,
+ RENDER_NAME_ALWAYS
+} eRenderName;
+
+const S32 BAKED_TEXTURE_COUNT = 5; // number of values in ETextureIndex that are pre-composited
+
+//------------------------------------------------------------------------
+// LLVOAvatar Support classes
+//------------------------------------------------------------------------
+
+class LLVOAvatarBoneInfo
+{
+ friend class LLVOAvatar;
+ friend class LLVOAvatarSkeletonInfo;
+public:
+ LLVOAvatarBoneInfo() : mIsJoint(FALSE) {}
+ ~LLVOAvatarBoneInfo()
+ {
+ std::for_each(mChildList.begin(), mChildList.end(), DeletePointer());
+ }
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mName;
+ BOOL mIsJoint;
+ LLVector3 mPos;
+ LLVector3 mRot;
+ LLVector3 mScale;
+ LLVector3 mPivot;
+ typedef std::vector<LLVOAvatarBoneInfo*> child_list_t;
+ child_list_t mChildList;
+};
+
+class LLVOAvatarSkeletonInfo
+{
+ friend class LLVOAvatar;
+public:
+ LLVOAvatarSkeletonInfo() :
+ mNumBones(0), mNumCollisionVolumes(0) {}
+ ~LLVOAvatarSkeletonInfo()
+ {
+ std::for_each(mBoneInfoList.begin(), mBoneInfoList.end(), DeletePointer());
+ }
+ BOOL parseXml(LLXmlTreeNode* node);
+ S32 getNumBones() { return mNumBones; }
+ S32 getNumCollisionVolumes() { return mNumCollisionVolumes; }
+
+protected:
+ S32 mNumBones;
+ S32 mNumCollisionVolumes;
+ typedef std::vector<LLVOAvatarBoneInfo*> bone_info_list_t;
+ bone_info_list_t mBoneInfoList;
+};
+
+
+//------------------------------------------------------------------------
+// LLVOAvatarInfo
+// One instance (in LLVOAvatar) with common data parsed from the XML files
+//------------------------------------------------------------------------
+class LLVOAvatarInfo
+{
+ friend class LLVOAvatar;
+public:
+ LLVOAvatarInfo();
+ ~LLVOAvatarInfo();
+
+protected:
+ BOOL parseXmlSkeletonNode(LLXmlTreeNode* root);
+ BOOL parseXmlMeshNodes(LLXmlTreeNode* root);
+ BOOL parseXmlColorNodes(LLXmlTreeNode* root);
+ BOOL parseXmlLayerNodes(LLXmlTreeNode* root);
+ BOOL parseXmlDriverNodes(LLXmlTreeNode* root);
+
+ struct LLVOAvatarMeshInfo
+ {
+ typedef std::pair<LLPolyMorphTargetInfo*,BOOL> morph_info_pair_t;
+ typedef std::vector<morph_info_pair_t> morph_info_list_t;
+
+ LLVOAvatarMeshInfo() : mLOD(0), mMinPixelArea(.1f) {}
+ ~LLVOAvatarMeshInfo()
+ {
+ morph_info_list_t::iterator iter;
+ for (iter = mPolyMorphTargetInfoList.begin(); iter != mPolyMorphTargetInfoList.end(); iter++)
+ {
+ delete iter->first;
+ }
+ mPolyMorphTargetInfoList.clear();
+ }
+
+ LLString mType;
+ S32 mLOD;
+ LLString mMeshFileName;
+ LLString mReferenceMeshName;
+ F32 mMinPixelArea;
+ morph_info_list_t mPolyMorphTargetInfoList;
+ };
+ typedef std::vector<LLVOAvatarMeshInfo*> mesh_info_list_t;
+ mesh_info_list_t mMeshInfoList;
+
+ typedef std::vector<LLPolySkeletalDistortionInfo*> skeletal_distortion_info_list_t;
+ skeletal_distortion_info_list_t mSkeletalDistortionInfoList;
+
+ struct LLVOAvatarAttachmentInfo
+ {
+ LLVOAvatarAttachmentInfo()
+ : mGroup(-1), mAttachmentID(-1), mPieMenuSlice(-1), mVisibleFirstPerson(FALSE),
+ mIsHUDAttachment(FALSE), mHasPosition(FALSE), mHasRotation(FALSE) {}
+ LLString mName;
+ LLString mJointName;
+ LLVector3 mPosition;
+ LLVector3 mRotationEuler;
+ S32 mGroup;
+ S32 mAttachmentID;
+ S32 mPieMenuSlice;
+ BOOL mVisibleFirstPerson;
+ BOOL mIsHUDAttachment;
+ BOOL mHasPosition;
+ BOOL mHasRotation;
+ };
+ typedef std::vector<LLVOAvatarAttachmentInfo*> attachment_info_list_t;
+ attachment_info_list_t mAttachmentInfoList;
+
+ LLTexGlobalColorInfo *mTexSkinColorInfo;
+ LLTexGlobalColorInfo *mTexHairColorInfo;
+ LLTexGlobalColorInfo *mTexEyeColorInfo;
+
+ typedef std::vector<LLTexLayerSetInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+
+ typedef std::vector<LLDriverParamInfo*> driver_info_list_t;
+ driver_info_list_t mDriverInfoList;
+};
+
+//------------------------------------------------------------------------
+// LLVOAvatar
+//------------------------------------------------------------------------
+class LLVOAvatar :
+ public LLViewerObject,
+ public LLCharacter
+{
+protected:
+ virtual ~LLVOAvatar();
+
+public:
+ LLVOAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ /*virtual*/ void markDead();
+
+ //--------------------------------------------------------------------
+ // LLViewerObject interface
+ //--------------------------------------------------------------------
+ static void initClass(); // Initialize data that's only inited once per class.
+ static void cleanupClass(); // Cleanup data that's only inited once per class.
+ static BOOL parseSkeletonFile(const LLString& filename);
+ virtual U32 processUpdateMessage( LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ void setFootPlane(const LLVector4 &plane) { mFootPlane = plane; }
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ // Graphical stuff for objects - maybe broken out into render class later?
+
+ U32 renderSkinned(EAvatarRenderPass pass);
+ U32 renderRigid();
+
+ void renderCollisionVolumes();
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ // If setting a baked texture, need to request it from a non-local sim.
+ /*virtual*/ S32 setTETexture(const U8 te, const LLUUID& uuid);
+
+ void updateVisibility(BOOL force_invisible);
+ void updateAttachmentVisibility(U32 camera_mode);
+ void clampAttachmentPositions();
+ S32 getAttachmentCount(); // Warning: order(N) not order(1)
+
+ BOOL hasHUDAttachment();
+ LLBBox getHUDBBox();
+// void renderHUD(BOOL for_select); // old
+ void rebuildHUD();
+
+ static void updateAllAvatarVisiblity();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ void updateShadowFaces();
+
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
+ void updateJointLODs();
+
+ void writeCAL3D(std::string& path, std::string& file_base);
+
+ virtual void updateRegion(LLViewerRegion *regionp);
+
+ //--------------------------------------------------------------------
+ // texture entry assignment
+ //--------------------------------------------------------------------
+ enum ETextureIndex
+ {
+ TEX_HEAD_BODYPAINT = 0,
+ TEX_UPPER_SHIRT = 1,
+ TEX_LOWER_PANTS = 2,
+ TEX_EYES_IRIS = 3,
+ TEX_HAIR = 4,
+ TEX_UPPER_BODYPAINT = 5,
+ TEX_LOWER_BODYPAINT = 6,
+ TEX_LOWER_SHOES = 7,
+ TEX_HEAD_BAKED = 8, // Pre-composited
+ TEX_UPPER_BAKED = 9, // Pre-composited
+ TEX_LOWER_BAKED = 10, // Pre-composited
+ TEX_EYES_BAKED = 11, // Pre-composited
+ TEX_LOWER_SOCKS = 12,
+ TEX_UPPER_JACKET = 13,
+ TEX_LOWER_JACKET = 14,
+ TEX_UPPER_GLOVES = 15,
+ TEX_UPPER_UNDERSHIRT = 16,
+ TEX_LOWER_UNDERPANTS = 17,
+ TEX_SKIRT = 18,
+ TEX_SKIRT_BAKED = 19, // Pre-composited
+ TEX_NUM_ENTRIES = 20
+ };
+ // Note: if TEX_NUM_ENTRIES changes, update AGENT_TEXTURES in llagentinfo.h, mTextureIndexBaked, and BAKED_TEXTURE_COUNT
+
+ static BOOL isTextureIndexBaked( S32 i )
+ {
+ switch(i)
+ {
+ case TEX_HEAD_BAKED:
+ case TEX_UPPER_BAKED:
+ case TEX_LOWER_BAKED:
+ case TEX_EYES_BAKED:
+ case TEX_SKIRT_BAKED:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // LLCharacter interface
+ //--------------------------------------------------------------------
+ virtual const char *getAnimationPrefix() { return "avatar"; }
+ virtual LLJoint *getRootJoint() { return &mRoot; }
+ virtual LLVector3 getCharacterPosition();
+ virtual LLQuaternion getCharacterRotation();
+ virtual LLVector3 getCharacterVelocity();
+ virtual LLVector3 getCharacterAngularVelocity();
+ virtual F32 getTimeDilation();
+ virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ virtual BOOL allocateCharacterJoints( U32 num );
+ virtual LLJoint *getCharacterJoint( U32 num );
+ virtual void requestStopMotion( LLMotion* motion );
+ virtual F32 getPixelArea();
+ virtual LLPolyMesh* getHeadMesh();
+ virtual LLPolyMesh* getUpperBodyMesh();
+ virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position);
+ virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position);
+ virtual void updateVisualParams();
+ virtual BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f);
+ virtual BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE);
+ virtual void stopMotionFromSource(const LLUUID& source_id);
+ virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset);
+ virtual LLJoint* findCollisionVolume(U32 volume_id);
+ virtual S32 getCollisionVolumeID(std::string &name);
+ virtual void addDebugText(const char* text);
+ virtual const LLUUID& getID();
+ virtual LLJoint *getJoint( const std::string &name );
+
+ //--------------------------------------------------------------------
+ // Other public functions
+ //--------------------------------------------------------------------
+ BOOL allocateCollisionVolumes( U32 num );
+
+ static void getAnimLabels( LLDynamicArray<const char*>* labels );
+ static void getAnimNames( LLDynamicArray<const char*>* names );
+
+ static void onCustomizeStart();
+ static void onCustomizeEnd();
+
+ void getLocalTextureByteCount( S32* gl_byte_count );
+ static void dumpTotalLocalTextureByteCount();
+ LLMotion* findMotion(const LLUUID& id);
+
+ BOOL isVisible();
+ BOOL isSelf() { return mIsSelf; }
+ BOOL isCulled() { return mCulled; }
+
+ S32 getUnbakedPixelAreaRank();
+ static void cullAvatarsByPixelArea();
+
+ void dumpLocalTextures();
+ const LLUUID& grabLocalTexture(ETextureIndex index);
+ BOOL canGrabLocalTexture(ETextureIndex index);
+ void startAppearanceAnimation(BOOL set_by_user, BOOL play_sound);
+
+ void setCompositeUpdatesEnabled(BOOL b);
+
+ void addChat(const LLChat& chat);
+ void clearChat();
+ void startTyping() { mTyping = TRUE; mTypingTimer.reset(); }
+ void stopTyping() { mTyping = FALSE; }
+
+ // Returns "FirstName LastName"
+ LLString getFullname() const;
+
+ //--------------------------------------------------------------------
+ // internal (pseudo-private) functions
+ //--------------------------------------------------------------------
+ F32 getPelvisToFoot() { return mPelvisToFoot; }
+
+ void buildCharacter();
+ void releaseMeshData();
+ void restoreMeshData();
+ void updateMeshData();
+
+ void computeBodySize();
+
+ void updateCharacter(LLAgent &agent);
+ void updateHeadOffset();
+
+ LLUUID& getStepSound();
+ void processAnimationStateChanges();
+ BOOL processSingleAnimationStateChange(const LLUUID &anim_id, BOOL start);
+ void resetAnimations();
+ BOOL isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims);
+
+ BOOL needsRenderBeam();
+
+ // Utility functions
+ void resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm);
+ void resolveHeightAgent(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ void resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm);
+
+ void slamPosition(); // Slam position to transmitted position (for teleport);
+
+ BOOL loadAvatar();
+ BOOL setupBone(LLVOAvatarBoneInfo* info, LLViewerJoint* parent);
+ BOOL buildSkeleton(LLVOAvatarSkeletonInfo *info);
+
+ // morph targets and such
+ void processAvatarAppearance( LLMessageSystem* mesgsys );
+ void onFirstTEMessageReceived();
+ void updateSexDependentLayerSets( BOOL set_by_user );
+ void dirtyMesh(); // Dirty the avatar mesh
+
+ virtual void setParent(LLViewerObject* parent);
+ virtual void addChild(LLViewerObject *childp);
+ virtual void removeChild(LLViewerObject *childp);
+
+ BOOL attachObject(LLViewerObject *viewer_object);
+ BOOL detachObject(LLViewerObject *viewer_object);
+ void lazyAttach();
+
+ void sitOnObject(LLViewerObject *sit_object);
+ void getOffObject();
+
+ BOOL isWearingAttachment( const LLUUID& inv_item_id );
+ LLViewerObject* getWornAttachment( const LLUUID& inv_item_id );
+ const LLString getAttachedPointName(const LLUUID& inv_item_id);
+
+ static LLVOAvatar* findAvatarFromAttachment( LLViewerObject* obj );
+
+ void updateMeshTextures();
+
+ //--------------------------------------------------------------------
+ // local textures for compositing.
+ //--------------------------------------------------------------------
+ enum ELocTexIndex
+ {
+ LOCTEX_UPPER_SHIRT = 0,
+ LOCTEX_UPPER_BODYPAINT = 1,
+ LOCTEX_LOWER_PANTS = 2,
+ LOCTEX_LOWER_BODYPAINT = 3,
+ LOCTEX_HEAD_BODYPAINT = 4,
+ LOCTEX_LOWER_SHOES = 5,
+ LOCTEX_LOWER_SOCKS = 6,
+ LOCTEX_UPPER_JACKET = 7,
+ LOCTEX_LOWER_JACKET = 8,
+ LOCTEX_UPPER_GLOVES = 9,
+ LOCTEX_UPPER_UNDERSHIRT = 10,
+ LOCTEX_LOWER_UNDERPANTS = 11,
+ LOCTEX_EYES_IRIS = 12,
+ LOCTEX_SKIRT = 13,
+ LOCTEX_NUM_ENTRIES = 14
+ };
+
+ //--------------------------------------------------------------------
+ // texture compositing (used only by the LLTexLayer series of classes)
+ //--------------------------------------------------------------------
+ LLColor4 getGlobalColor( const LLString& color_name );
+ BOOL isLocalTextureDataAvailable( LLTexLayerSet* layerset );
+ BOOL isLocalTextureDataFinal( LLTexLayerSet* layerset );
+ ETextureIndex getBakedTE( LLTexLayerSet* layerset );
+ void updateComposites();
+ void onGlobalColorChanged( LLTexGlobalColor* global_color, BOOL set_by_user );
+ BOOL getLocalTextureRaw( S32 index, LLImageRaw* image_raw_pp );
+ BOOL getLocalTextureGL( S32 index, LLImageGL** image_gl_pp );
+ const LLUUID& getLocalTextureID( S32 index );
+ LLGLuint getScratchTexName( LLGLenum format, U32* texture_bytes );
+ BOOL bindScratchTexture( LLGLenum format );
+ void invalidateComposite( LLTexLayerSet* layerset, BOOL set_by_user );
+ void forceBakeAllTextures(bool slam_for_debug = false);
+ static void processRebakeAvatarTextures(LLMessageSystem* msg, void**);
+ void setNewBakedTexture( ETextureIndex i, const LLUUID& uuid );
+ void setCachedBakedTexture( ETextureIndex i, const LLUUID& uuid );
+ void requestLayerSetUploads();
+ bool hasPendingBakedUploads();
+ static void onLocalTextureLoaded( BOOL succcess, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+ static void dumpArchetypeXML( void* );
+ static void dumpScratchTextureByteCount();
+ static void dumpBakedStatus();
+ static void deleteCachedImages();
+ static void destroyGL();
+ static void restoreGL();
+ static void initVertexPrograms();
+ static void cleanupVertexPrograms();
+ static enum EWearableType getTEWearableType( S32 te );
+ static LLUUID getDefaultTEImageID( S32 te );
+
+ //--------------------------------------------------------------------
+ // Clothing colors (conventience functions to access visual parameters
+ //--------------------------------------------------------------------
+ void setClothesColor( ETextureIndex te, const LLColor4& new_color, BOOL set_by_user );
+ LLColor4 getClothesColor( ETextureIndex te );
+ BOOL teToColorParams( ETextureIndex te, const char* param_name[3] );
+
+ BOOL isWearingWearableType( EWearableType type );
+
+ //--------------------------------------------------------------------
+ // texture compositing
+ //--------------------------------------------------------------------
+ void setLocTexTE( U8 te, LLViewerImage* image, BOOL set_by_user );
+ void setupComposites();
+
+ //--------------------------------------------------------------------
+ // member variables
+ //--------------------------------------------------------------------
+
+ BOOL mDirtyMesh;
+
+ LLFace *mShadow0Facep;
+ LLFace *mShadow1Facep;
+
+ LLFrameTimer mUpdateLODTimer; // controls frequency of LOD change calculations
+
+ //--------------------------------------------------------------------
+ // State of deferred character building
+ //--------------------------------------------------------------------
+ BOOL mIsBuilt;
+
+ //--------------------------------------------------------------------
+ // avatar definition name
+ //--------------------------------------------------------------------
+ char mAvatarDefinition[64];
+
+ //--------------------------------------------------------------------
+ // skeleton for skinned avatar
+ //--------------------------------------------------------------------
+ S32 mNumJoints;
+ LLViewerJoint *mSkeleton;
+
+ S32 mNumCollisionVolumes;
+ LLViewerJointCollisionVolume* mCollisionVolumes;
+
+ //--------------------------------------------------------------------
+ // cached pointers to well known joints
+ //--------------------------------------------------------------------
+ LLViewerJoint *mPelvisp;
+ LLViewerJoint *mTorsop;
+ LLViewerJoint *mChestp;
+ LLViewerJoint *mNeckp;
+ LLViewerJoint *mHeadp;
+ LLViewerJoint *mSkullp;
+ LLViewerJoint *mEyeLeftp;
+ LLViewerJoint *mEyeRightp;
+ LLViewerJoint *mHipLeftp;
+ LLViewerJoint *mHipRightp;
+ LLViewerJoint *mKneeLeftp;
+ LLViewerJoint *mKneeRightp;
+ LLViewerJoint *mAnkleLeftp;
+ LLViewerJoint *mAnkleRightp;
+ LLViewerJoint *mFootLeftp;
+ LLViewerJoint *mFootRightp;
+ LLViewerJoint *mWristLeftp;
+ LLViewerJoint *mWristRightp;
+
+ //--------------------------------------------------------------------
+ // special purpose joint for HUD attachments
+ //--------------------------------------------------------------------
+ LLViewerJoint *mScreenp;
+ F32 mHUDTargetZoom;
+ F32 mHUDCurZoom;
+
+ //--------------------------------------------------------------------
+ // mesh objects for skinned avatar
+ //--------------------------------------------------------------------
+ LLViewerJoint mHairLOD;
+ LLViewerJointMesh mHairMesh0;
+ LLViewerJointMesh mHairMesh1;
+ LLViewerJointMesh mHairMesh2;
+ LLViewerJointMesh mHairMesh3;
+ LLViewerJointMesh mHairMesh4;
+ LLViewerJointMesh mHairMesh5;
+
+ LLViewerJoint mHeadLOD;
+ LLViewerJointMesh mHeadMesh0;
+ LLViewerJointMesh mHeadMesh1;
+ LLViewerJointMesh mHeadMesh2;
+ LLViewerJointMesh mHeadMesh3;
+ LLViewerJointMesh mHeadMesh4;
+
+ LLViewerJoint mEyeLashLOD;
+ LLViewerJointMesh mEyeLashMesh0;
+
+ LLViewerJoint mUpperBodyLOD;
+ LLViewerJointMesh mUpperBodyMesh0;
+ LLViewerJointMesh mUpperBodyMesh1;
+ LLViewerJointMesh mUpperBodyMesh2;
+ LLViewerJointMesh mUpperBodyMesh3;
+ LLViewerJointMesh mUpperBodyMesh4;
+
+ LLViewerJoint mLowerBodyLOD;
+ LLViewerJointMesh mLowerBodyMesh0;
+ LLViewerJointMesh mLowerBodyMesh1;
+ LLViewerJointMesh mLowerBodyMesh2;
+ LLViewerJointMesh mLowerBodyMesh3;
+ LLViewerJointMesh mLowerBodyMesh4;
+
+ LLViewerJoint mEyeBallLeftLOD;
+ LLViewerJointMesh mEyeBallLeftMesh0;
+ LLViewerJointMesh mEyeBallLeftMesh1;
+
+ LLViewerJoint mEyeBallRightLOD;
+ LLViewerJointMesh mEyeBallRightMesh0;
+ LLViewerJointMesh mEyeBallRightMesh1;
+
+ LLViewerJoint mSkirtLOD;
+ LLViewerJointMesh mSkirtMesh0;
+ LLViewerJointMesh mSkirtMesh1;
+ LLViewerJointMesh mSkirtMesh2;
+ LLViewerJointMesh mSkirtMesh3;
+ LLViewerJointMesh mSkirtMesh4;
+
+ typedef std::multimap<LLString, LLPolyMesh*> mesh_map_t;
+ mesh_map_t mMeshes;
+
+ //--------------------------------------------------------------------
+ // true if this avatar is for this viewers agent
+ //--------------------------------------------------------------------
+ BOOL mIsSelf;
+
+ //--------------------------------------------------------------------
+ // texture ids and pointers
+ //--------------------------------------------------------------------
+ LLUUID mShadowImageID;
+ LLPointer<LLViewerImage> mShadowImagep;
+
+ LLUUID mLastHeadBakedID;
+ LLUUID mLastUpperBodyBakedID;
+ LLUUID mLastLowerBodyBakedID;
+ LLUUID mLastEyesBakedID;
+ LLUUID mLastSkirtBakedID;
+
+ //--------------------------------------------------------------------
+ // Misc Render State
+ //--------------------------------------------------------------------
+ BOOL mIsDummy; // For special views
+ S32 mSpecialRenderMode; // Special lighting
+
+ //--------------------------------------------------------------------
+ // Animation timer
+ //--------------------------------------------------------------------
+ LLTimer mAnimTimer;
+ F32 mTimeLast;
+
+ //--------------------------------------------------------------------
+ // Measures speed (for diagnostics mostly).
+ //--------------------------------------------------------------------
+ F32 mSpeedAccum;
+
+ //--------------------------------------------------------------------
+ // animation state data
+ //--------------------------------------------------------------------
+ typedef std::map<LLUUID, S32>::iterator AnimIterator;
+
+ std::map<LLUUID, S32> mSignaledAnimations; // requested state of Animation name/value
+ std::map<LLUUID, S32> mPlayingAnimations; // current state of Animation name/value
+
+ typedef std::multimap<LLUUID, LLUUID> AnimationSourceMap;
+ typedef AnimationSourceMap::iterator AnimSourceIterator;
+ AnimationSourceMap mAnimationSources; // object ids that triggered anim ids
+
+ BOOL mTurning; // controls hysteresis on avatar rotation
+
+ //--------------------------------------------------------------------
+ // misc. animation related state
+ //--------------------------------------------------------------------
+ F32 mSpeed;
+
+ //
+ // Shadow stuff
+ //
+ LLDrawable* mShadow;
+ BOOL mInAir;
+ LLFrameTimer mTimeInAir;
+
+ //--------------------------------------------------------------------
+ // Keeps track of foot step state for generating sounds
+ //--------------------------------------------------------------------
+ BOOL mWasOnGroundLeft;
+ BOOL mWasOnGroundRight;
+ LLVector4 mFootPlane;
+
+ //--------------------------------------------------------------------
+ // Keep track of the material being stepped on
+ //--------------------------------------------------------------------
+ BOOL mStepOnLand;
+ U8 mStepMaterial;
+ LLVector3 mStepObjectVelocity;
+
+ //--------------------------------------------------------------------
+ // Pelvis height adjustment members.
+ //--------------------------------------------------------------------
+ F32 mPelvisToFoot;
+ LLVector3 mBodySize;
+ S32 mLastSkeletonSerialNum;
+
+ //--------------------------------------------------------------------
+ // current head position
+ //--------------------------------------------------------------------
+ LLVector3 mHeadOffset;
+
+ //--------------------------------------------------------------------
+ // avatar skeleton
+ //--------------------------------------------------------------------
+ LLViewerJoint mRoot;
+
+ //--------------------------------------------------------------------
+ // sitting state
+ //--------------------------------------------------------------------
+ BOOL mIsSitting;
+
+ //--------------------------------------------------------------------
+ // Display the name, then optionally fade it out
+ //--------------------------------------------------------------------
+ LLFrameTimer mTimeVisible;
+ LLPointer<LLHUDText> mNameText;
+ std::deque<LLChat> mChats;
+ LLFrameTimer mChatTimer;
+ BOOL mTyping;
+ LLFrameTimer mTypingTimer;
+
+ //--------------------------------------------------------------------
+ // destroy mesh data after being invisible for a while
+ //--------------------------------------------------------------------
+ BOOL mMeshValid;
+ BOOL mVisible;
+ LLFrameTimer mMeshInvisibleTime;
+
+ //--------------------------------------------------------------------
+ // wind rippling in clothes
+ //--------------------------------------------------------------------
+ LLVector4 mWindVec;
+ F32 mWindFreq;
+ F32 mRipplePhase;
+ LLFrameTimer mRippleTimer;
+ F32 mRippleTimeLast;
+ LLVector3 mRippleAccel;
+ LLVector3 mLastVel;
+ BOOL mBelowWater;
+
+ //--------------------------------------------------------------------
+ // appearance morphing
+ //--------------------------------------------------------------------
+ LLFrameTimer mAppearanceMorphTimer;
+ BOOL mAppearanceAnimSetByUser;
+ F32 mLastAppearanceBlendTime;
+ BOOL mAppearanceAnimating;
+
+ //--------------------------------------------------------------------
+ // static members
+ //--------------------------------------------------------------------
+ static S32 sMaxVisible;
+ static S32 sCurJoint;
+ static S32 sCurVolume;
+ static BOOL sShowAnimationDebug; // show animation debug info
+ static BOOL sShowFootPlane; // show foot collision plane reported by server
+ static BOOL sShowCollisionVolumes; // show skeletal collision volumes
+ static BOOL sVisibleInFirstPerson;
+
+ static S32 sMaxOtherAvatarsToComposite;
+
+ static S32 sNumLODChangesThisFrame;
+
+ // global table of sound ids per material, and the ground
+ static LLUUID sStepSounds[LL_MCODE_END];
+ static LLUUID sStepSoundOnLand;
+
+ static S32 sRenderName;
+ static S32 sNumVisibleChatBubbles;
+ static BOOL sDebugInvisible;
+ static BOOL sShowAttachmentPoints;
+
+ // Number of instances of this class
+ static S32 sNumVisibleAvatars;
+
+ // Scratch textures used for compositing
+ static LLMap< LLGLenum, LLGLuint*> sScratchTexNames;
+ static LLMap< LLGLenum, F32*> sScratchTexLastBindTime;
+ static S32 sScratchTexBytes;
+
+ // map of attachment points, by ID
+ LLPtrSkipMap<S32, LLViewerJointAttachment*> mAttachmentPoints;
+
+ // xml parse tree of avatar config file
+ static LLXmlTree sXMLTree;
+ // xml parse tree of avatar skeleton file
+ static LLXmlTree sSkeletonXMLTree;
+
+ // number of avatar duplicates, for debugging purposes
+ static BOOL sAvatarLoadTest;
+
+ // user-settable LOD factor
+ static F32 sLODFactor;
+
+ // output total number of joints being touched for each avatar
+ static BOOL sJointDebug;
+ static ETextureIndex sBakedTextureIndices[BAKED_TEXTURE_COUNT];
+
+ //--------------------------------------------------------------------
+ // Texture Layer Sets and Global Colors
+ //--------------------------------------------------------------------
+ LLTexLayerSet* mHeadLayerSet;
+ LLTexLayerSet* mUpperBodyLayerSet;
+ LLTexLayerSet* mLowerBodyLayerSet;
+ LLTexLayerSet* mEyesLayerSet;
+ LLTexLayerSet* mSkirtLayerSet;
+
+
+protected:
+ LLPointer<LLHUDEffectSpiral> mBeam;
+ LLFrameTimer mBeamTimer;
+ LLFrameTimer mEditEffectTimer;
+
+ F32 mRenderPriority;
+ F32 mAdjustedPixelArea;
+ S32 mNumAGPVertices;
+
+ LLWString mNameString;
+ LLString mTitle;
+ BOOL mNameAway;
+ BOOL mNameBusy;
+ BOOL mNameMute;
+ BOOL mNameAppearance;
+ BOOL mVisibleChat;
+
+ LLString mDebugText;
+ U64 mLastRegionHandle;
+ LLFrameTimer mRegionCrossingTimer;
+ S32 mRegionCrossingCount;
+
+ //--------------------------------------------------------------------
+ // local textures for compositing.
+ //--------------------------------------------------------------------
+
+ LLPointer<LLViewerImage> mLocalTexture[ LOCTEX_NUM_ENTRIES ];
+ BOOL mLocalTextureBaked[ LOCTEX_NUM_ENTRIES ]; // Texture is covered by a baked texture
+ S32 mLocalTextureDiscard[ LOCTEX_NUM_ENTRIES ];
+ LLUUID mSavedTE[ TEX_NUM_ENTRIES ];
+ BOOL mFirstTEMessageReceived;
+ BOOL mFirstAppearanceMessageReceived;
+
+ BOOL mHeadBakedLoaded;
+ S32 mHeadMaskDiscard;
+ BOOL mUpperBakedLoaded;
+ S32 mUpperMaskDiscard;
+ BOOL mLowerBakedLoaded;
+ S32 mLowerMaskDiscard;
+ BOOL mEyesBakedLoaded;
+ BOOL mSkirtBakedLoaded;
+
+ //RN: testing 2 pass rendering
+ U32 mHeadMaskTexName;
+ U32 mUpperMaskTexName;
+ U32 mLowerMaskTexName;
+
+ BOOL mCulled;
+
+ //--------------------------------------------------------------------
+ // Global Colors
+ //--------------------------------------------------------------------
+ LLTexGlobalColor* mTexSkinColor;
+ LLTexGlobalColor* mTexHairColor;
+ LLTexGlobalColor* mTexEyeColor;
+
+ static LLVOAvatarSkeletonInfo* sSkeletonInfo;
+ static LLVOAvatarInfo* sAvatarInfo;
+
+protected:
+
+ BOOL loadSkeletonNode();
+ BOOL loadMeshNodes();
+
+ BOOL isFullyBaked();
+ void deleteLayerSetCaches();
+ static BOOL areAllNearbyInstancesBaked();
+
+ static void onBakedTextureMasksLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+
+ void setLocalTexture(ELocTexIndex i, LLViewerImage* tex, BOOL baked_version_exits);
+
+ void requestLayerSetUpdate(LLVOAvatar::ELocTexIndex i);
+ void addLocalTextureStats(LLVOAvatar::ELocTexIndex i, LLViewerImage* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked);
+ static void onInitialBakedTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+ static void onBakedTextureLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ void useBakedTexture(const LLUUID& id);
+ void dumpAvatarTEs(const char* context);
+ void removeMissingBakedTextures();
+ LLTexLayerSet* getLayerSet(ETextureIndex index) const;
+ LLHost getObjectHost() const;
+ S32 getLocalDiscardLevel( S32 index);
+};
+
+#endif // LL_VO_AVATAR_H
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
new file mode 100644
index 0000000000..611f5ddab0
--- /dev/null
+++ b/indra/newview/llvocache.cpp
@@ -0,0 +1,134 @@
+/**
+ * @file llvocache.cpp
+ * @brief Cache of objects on the viewer.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvocache.h"
+
+#include "llerror.h"
+
+//---------------------------------------------------------------------------
+// LLVOCacheEntry
+//---------------------------------------------------------------------------
+
+LLVOCacheEntry::LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp)
+{
+ mLocalID = local_id;
+ mCRC = crc;
+ mHitCount = 0;
+ mDupeCount = 0;
+ mCRCChangeCount = 0;
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+}
+
+LLVOCacheEntry::LLVOCacheEntry()
+{
+ mLocalID = 0;
+ mCRC = 0;
+ mHitCount = 0;
+ mDupeCount = 0;
+ mCRCChangeCount = 0;
+ mBuffer = NULL;
+ mDP.assignBuffer(mBuffer, 0);
+}
+
+
+LLVOCacheEntry::LLVOCacheEntry(FILE *fp)
+{
+ S32 size;
+ fread(&mLocalID, 1, sizeof(U32), fp);
+ fread(&mCRC, 1, sizeof(U32), fp);
+ fread(&mHitCount, 1, sizeof(S32), fp);
+ fread(&mDupeCount, 1, sizeof(S32), fp);
+ fread(&mCRCChangeCount, 1, sizeof(S32), fp);
+
+ fread(&size, 1, sizeof(S32), fp);
+
+ // Corruption in the cache entries
+ if ((size > 10000) || (size < 1))
+ {
+ // We've got a bogus size, skip reading it.
+ // We won't bother seeking, because the rest of this file
+ // is likely bogus, and will be tossed anyway.
+ llwarns << "Bogus cache entry, size " << size << ", aborting!" << llendl;
+ mLocalID = 0;
+ mCRC = 0;
+ mBuffer = NULL;
+ return;
+ }
+
+ mBuffer = new U8[size];
+ fread(mBuffer, 1, size, fp);
+ mDP.assignBuffer(mBuffer, size);
+}
+
+LLVOCacheEntry::~LLVOCacheEntry()
+{
+ delete [] mBuffer;
+}
+
+
+// New CRC means the object has changed.
+void LLVOCacheEntry::assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ mCRC = crc;
+ mHitCount = 0;
+ mCRCChangeCount++;
+
+ mDP.freeBuffer();
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+ }
+}
+
+LLDataPackerBinaryBuffer *LLVOCacheEntry::getDP(U32 crc)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ //llinfos << "Not getting cache entry, invalid!" << llendl;
+ return NULL;
+ }
+ mHitCount++;
+ return &mDP;
+}
+
+
+void LLVOCacheEntry::recordHit()
+{
+ mHitCount++;
+}
+
+
+void LLVOCacheEntry::dump() const
+{
+ llinfos << "local " << mLocalID
+ << " crc " << mCRC
+ << " hits " << mHitCount
+ << " dupes " << mDupeCount
+ << " change " << mCRCChangeCount
+ << llendl;
+}
+
+void LLVOCacheEntry::writeToFile(FILE *fp) const
+{
+ fwrite(&mLocalID, 1, sizeof(U32), fp);
+ fwrite(&mCRC, 1, sizeof(U32), fp);
+ fwrite(&mHitCount, 1, sizeof(S32), fp);
+ fwrite(&mDupeCount, 1, sizeof(S32), fp);
+ fwrite(&mCRCChangeCount, 1, sizeof(S32), fp);
+ S32 size = mDP.getBufferSize();
+ fwrite(&size, 1, sizeof(S32), fp);
+ fwrite(mBuffer, 1, size, fp);
+}
diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h
new file mode 100644
index 0000000000..7dc06a0b49
--- /dev/null
+++ b/indra/newview/llvocache.h
@@ -0,0 +1,51 @@
+/**
+ * @file llvocache.h
+ * @brief Cache of objects on the viewer.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOCACHE_H
+#define LL_LLVOCACHE_H
+
+#include "lluuid.h"
+#include "lldatapacker.h"
+#include "lldlinked.h"
+
+
+//---------------------------------------------------------------------------
+// Cache entries
+class LLVOCacheEntry;
+
+class LLVOCacheEntry : public LLDLinked<LLVOCacheEntry>
+{
+public:
+ LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp);
+ LLVOCacheEntry(FILE *fp);
+ LLVOCacheEntry();
+ ~LLVOCacheEntry();
+
+ U32 getLocalID() const { return mLocalID; }
+ U32 getCRC() const { return mCRC; }
+ S32 getHitCount() const { return mHitCount; }
+ S32 getCRCChangeCount() const { return mCRCChangeCount; }
+
+ void dump() const;
+ void writeToFile(FILE *fp) const;
+ void assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp);
+ LLDataPackerBinaryBuffer *getDP(U32 crc);
+ void recordHit();
+ void recordDupe() { mDupeCount++; }
+
+protected:
+ U32 mLocalID;
+ U32 mCRC;
+ S32 mHitCount;
+ S32 mDupeCount;
+ S32 mCRCChangeCount;
+ LLDataPackerBinaryBuffer mDP;
+ U8 *mBuffer;
+};
+
+#endif
diff --git a/indra/newview/llvoclouds.cpp b/indra/newview/llvoclouds.cpp
new file mode 100644
index 0000000000..b4924a45f1
--- /dev/null
+++ b/indra/newview/llvoclouds.cpp
@@ -0,0 +1,188 @@
+/**
+ * @file llvoclouds.cpp
+ * @brief Implementation of LLVOClouds class which is a derivation fo LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvoclouds.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h" // to get camera position
+#include "lldrawable.h"
+#include "llface.h"
+#include "llprimitive.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llvosky.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+LLVOClouds::LLVOClouds(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, LL_VO_CLOUDS, regionp)
+{
+ mCloudGroupp = NULL;
+ mbCanSelect = FALSE;
+ setNumTEs(1);
+ LLViewerImage* image = gImageList.getImage(gCloudTextureID);
+ image->setBoostLevel(LLViewerImage::BOOST_CLOUDS);
+ setTEImage(0, image);
+}
+
+
+LLVOClouds::~LLVOClouds()
+{
+}
+
+
+BOOL LLVOClouds::isActive() const
+{
+ return TRUE;
+}
+
+
+BOOL LLVOClouds::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
+ return TRUE;
+
+ // Set dirty flag (so renderer will rebuild primitive)
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+
+void LLVOClouds::setPixelAreaAndAngle(LLAgent &agent)
+{
+ mAppAngle = 50;
+ mPixelArea = 1500*100;
+}
+
+void LLVOClouds::updateTextures(LLAgent &agent)
+{
+ getTEImage(0)->addTextureStats(mPixelArea);
+}
+
+LLDrawable* LLVOClouds::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_CLOUDS);
+
+ LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_CLOUDS);
+
+ mDrawable->setNumFaces(1, pool, getTEImage(0));
+
+ return mDrawable;
+}
+
+BOOL LLVOClouds::updateGeometry(LLDrawable *drawable)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
+ return TRUE;
+
+ LLVector3 at;
+ LLVector3 up;
+ LLVector3 right;
+ LLVector3 normal(0.f,0.f,-1.f);
+ LLVector3 position_agent;
+ //LLVector3 v[4];
+ LLFace *facep;
+ const LLVector3 region_pos_agent = mRegionp->getOriginAgent();
+ const LLVector3 camera_agent = gAgent.getCameraPositionAgent();
+ LLVector3 center_offset = getPositionRegion();
+ LLVector2 uvs[4];
+
+ uvs[0].setVec(0.f, 1.f);
+ uvs[1].setVec(0.f, 0.f);
+ uvs[2].setVec(1.f, 1.f);
+ uvs[3].setVec(1.f, 0.f);
+
+ LLVector3 vtx[4];
+
+ S32 num_faces = mCloudGroupp->getNumPuffs();
+
+ drawable->setNumFacesFast(num_faces, gPipeline.getPool(LLDrawPool::POOL_CLOUDS), getTEImage(0));
+
+ S32 face_indx = 0;
+ for ( ; face_indx < num_faces; face_indx++)
+ {
+ facep = drawable->getFace(face_indx);
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ facep->setPrimType(LLTriangles);
+ facep->setSize(4, 6);
+ index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ const LLCloudPuff &puff = mCloudGroupp->getPuff(face_indx);
+ const LLVector3 puff_pos_agent = gAgent.getPosAgentFromGlobal(puff.getPositionGlobal());
+ facep->mCenterAgent = puff_pos_agent;
+
+ LLVector3 from_camera_vec = gCamera->getAtAxis();//puff_pos_agent - camera_agent;
+ at = from_camera_vec;
+ right = at % LLVector3(0.f, 0.f, 1.f);
+ right.normVec();
+ up = right % at;
+ up.normVec();
+ right *= 0.5f*CLOUD_PUFF_WIDTH;
+ up *= 0.5f*CLOUD_PUFF_HEIGHT;;
+
+ facep->mCenterAgent = puff_pos_agent;
+
+ LLColor4 color(1.f, 1.f, 1.f, puff.getAlpha());
+ facep->setFaceColor(color);
+
+ vtx[0] = puff_pos_agent - right + up;
+ vtx[1] = puff_pos_agent - right - up;
+ vtx[2] = puff_pos_agent + right + up;
+ vtx[3] = puff_pos_agent + right - up;
+
+ *verticesp++ = vtx[0];
+ *verticesp++ = vtx[1];
+ *verticesp++ = vtx[2];
+ *verticesp++ = vtx[3];
+
+ *texCoordsp++ = uvs[0];
+ *texCoordsp++ = uvs[1];
+ *texCoordsp++ = uvs[2];
+ *texCoordsp++ = uvs[3];
+
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+ }
+ for ( ; face_indx < drawable->getNumFaces(); face_indx++)
+ {
+ drawable->getFace(face_indx)->setSize(0,0);
+ }
+
+ return TRUE;
+}
diff --git a/indra/newview/llvoclouds.h b/indra/newview/llvoclouds.h
new file mode 100644
index 0000000000..0debe20730
--- /dev/null
+++ b/indra/newview/llvoclouds.h
@@ -0,0 +1,48 @@
+/**
+ * @file llvoclouds.h
+ * @brief Description of LLVOClouds class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOCLOUDS_H
+#define LL_LLVOCLOUDS_H
+
+#include "llviewerobject.h"
+#include "lltable.h"
+#include "v4coloru.h"
+
+class LLViewerImage;
+class LLViewerCloudGroup;
+
+class LLCloudGroup;
+
+
+class LLVOClouds : public LLViewerObject
+{
+public:
+ LLVOClouds(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp );
+ virtual ~LLVOClouds();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ void setCloudGroup(LLCloudGroup *cgp) { mCloudGroupp = cgp; }
+protected:
+ LLCloudGroup *mCloudGroupp;
+};
+
+extern LLUUID gCloudTextureID;
+
+#endif // LL_VO_CLOUDS_H
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
new file mode 100644
index 0000000000..a1bfe31f1e
--- /dev/null
+++ b/indra/newview/llvograss.cpp
@@ -0,0 +1,499 @@
+/**
+ * @file llvograss.cpp
+ * @brief Not a blade, but a clump of grass
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvograss.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llvosky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+#include "llworld.h"
+#include "lldir.h"
+#include "llxmltree.h"
+
+const S32 GRASS_MAX_BLADES = 32;
+const F32 GRASS_BLADE_BASE = 0.25f; // Width of grass at base
+const F32 GRASS_BLADE_TOP = 0.25f; // Width of grass at top
+const F32 GRASS_BLADE_HEIGHT = 0.5f; // meters
+const F32 GRASS_DISTRIBUTION_SD = 0.15f; // empirically defined
+
+F32 exp_x[GRASS_MAX_BLADES];
+F32 exp_y[GRASS_MAX_BLADES];
+F32 rot_x[GRASS_MAX_BLADES];
+F32 rot_y[GRASS_MAX_BLADES];
+F32 dz_x [GRASS_MAX_BLADES];
+F32 dz_y [GRASS_MAX_BLADES];
+
+F32 w_mod[GRASS_MAX_BLADES]; // Factor to modulate wind movement by to randomize appearance
+
+LLVOGrass::SpeciesMap LLVOGrass::sSpeciesTable;
+S32 LLVOGrass::sMaxGrassSpecies = 0;
+
+
+LLVOGrass::LLVOGrass(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp)
+{
+ mPatch = NULL;
+ mLastPatchUpdateTime = 0;
+ mGrassVel.clearVec();
+ mGrassBend.clearVec();
+ mbCanSelect = TRUE;
+
+ mBladeWindAngle = 35.f;
+ mBWAOverlap = 2.f;
+
+ setNumTEs(1);
+
+ setTEColor(0, LLColor4(1.0f, 1.0f, 1.0f, 1.f));
+ mNumBlades = GRASS_MAX_BLADES;
+}
+
+LLVOGrass::~LLVOGrass()
+{
+}
+
+
+void LLVOGrass::updateSpecies()
+{
+ mSpecies = mState;
+
+ if (!sSpeciesTable.count(mSpecies))
+ {
+ llinfos << "Unknown grass type, substituting grass type." << llendl;
+ SpeciesMap::const_iterator it = sSpeciesTable.begin();
+ mSpecies = (*it).first;
+ }
+ setTEImage(0, gImageList.getImage(sSpeciesTable[mSpecies]->mTextureID));
+}
+
+
+void alert_done(S32 option, void* user_data)
+{
+ return;
+}
+
+
+void LLVOGrass::initClass()
+{
+ LLVector3 pos(0.0f, 0.0f, 0.0f);
+ // Create nifty list of exponential distribution 0-1
+ F32 x = 0.f;
+ F32 y = 0.f;
+ F32 rot;
+
+ std::string xml_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"grass.xml");
+
+ LLXmlTree grass_def_grass;
+
+ if (!grass_def_grass.parseFile(xml_filename))
+ {
+ llerrs << "Failed to parse grass file." << llendl;
+ return;
+ }
+
+ LLXmlTreeNode* rootp = grass_def_grass.getRoot();
+
+ for (LLXmlTreeNode* grass_def = rootp->getFirstChild();
+ grass_def;
+ grass_def = rootp->getNextChild())
+ {
+ if (!grass_def->hasName("grass"))
+ {
+ llwarns << "Invalid grass definition node " << grass_def->getName() << llendl;
+ continue;
+ }
+ F32 F32_val;
+ LLUUID id;
+
+ BOOL success = TRUE;
+
+ S32 species;
+ static LLStdStringHandle species_id_string = LLXmlTree::addAttributeString("species_id");
+ if (!grass_def->getFastAttributeS32(species_id_string, species))
+ {
+ llwarns << "No species id defined" << llendl;
+ continue;
+ }
+
+ if (species < 0)
+ {
+ llwarns << "Invalid species id " << species << llendl;
+ continue;
+ }
+
+ GrassSpeciesData* newGrass = new GrassSpeciesData();
+
+
+ static LLStdStringHandle texture_id_string = LLXmlTree::addAttributeString("texture_id");
+ grass_def->getFastAttributeUUID(texture_id_string, id);
+ newGrass->mTextureID = id;
+
+ if (newGrass->mTextureID.isNull())
+ {
+ LLString textureName;
+
+ static LLStdStringHandle texture_name_string = LLXmlTree::addAttributeString("texture_name");
+ success &= grass_def->getFastAttributeString(texture_name_string, textureName);
+ newGrass->mTextureID.set( gViewerArt.getString(textureName) );
+ }
+
+ static LLStdStringHandle blade_sizex_string = LLXmlTree::addAttributeString("blade_size_x");
+ success &= grass_def->getFastAttributeF32(blade_sizex_string, F32_val);
+ newGrass->mBladeSizeX = F32_val;
+
+ static LLStdStringHandle blade_sizey_string = LLXmlTree::addAttributeString("blade_size_y");
+ success &= grass_def->getFastAttributeF32(blade_sizey_string, F32_val);
+ newGrass->mBladeSizeY = F32_val;
+
+ if (sSpeciesTable.count(species))
+ {
+ llinfos << "Grass species " << species << " already defined! Duplicate discarded." << llendl;
+ delete newGrass;
+ continue;
+ }
+ else
+ {
+ sSpeciesTable[species] = newGrass;
+ }
+
+ if (species >= sMaxGrassSpecies) sMaxGrassSpecies = species + 1;
+
+ if (!success)
+ {
+ LLString name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ grass_def->getFastAttributeString(name_string, name);
+ llwarns << "Incomplete definition of grass " << name << llendl;
+ }
+ }
+
+ BOOL have_all_grass = TRUE;
+ LLString err;
+ char buffer[10];
+
+ for (S32 i=0;i<sMaxGrassSpecies;++i)
+ {
+ if (!sSpeciesTable.count(i))
+ {
+ snprintf(buffer,10," %d",i);
+ err.append(buffer);
+ have_all_grass = FALSE;
+ }
+ }
+
+ if (!have_all_grass)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[SPECIES]"] = err;
+ gViewerWindow->alertXml("ErrorUndefinedGrasses", args, alert_done );
+ }
+
+ for (S32 i = 0; i < GRASS_MAX_BLADES; ++i)
+ {
+ if (1) //(i%2 == 0) Uncomment for X blading
+ {
+ F32 u = sqrt(-2.0f * log(frand(1.0)));
+ F32 v = 2.0f * F_PI * frand(1.0);
+
+ x = u * sin(v) * GRASS_DISTRIBUTION_SD;
+ y = u * cos(v) * GRASS_DISTRIBUTION_SD;
+
+ rot = frand(F_PI);
+ }
+ else
+ {
+ rot += (F_PI*0.4f + frand(0.2f*F_PI));
+ }
+
+ exp_x[i] = x;
+ exp_y[i] = y;
+ rot_x[i] = sin(rot);
+ rot_y[i] = cos(rot);
+ dz_x[i] = frand(GRASS_BLADE_BASE * 0.25f);
+ dz_y[i] = frand(GRASS_BLADE_BASE * 0.25f);
+ w_mod[i] = 0.5f + frand(1.f); // Degree to which blade is moved by wind
+
+ }
+}
+
+void LLVOGrass::cleanupClass()
+{
+ for_each(sSpeciesTable.begin(), sSpeciesTable.end(), DeletePairedPointer());
+}
+
+U32 LLVOGrass::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ updateSpecies();
+
+ if ( (getVelocity().magVecSquared() > 0.f)
+ ||(getAcceleration().magVecSquared() > 0.f)
+ ||(getAngularVelocity().magVecSquared() > 0.f))
+ {
+ llinfos << "ACK! Moving grass!" << llendl;
+ setVelocity(LLVector3::zero);
+ setAcceleration(LLVector3::zero);
+ setAngularVelocity(LLVector3::zero);
+ }
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ return retval;
+}
+
+BOOL LLVOGrass::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GRASS)))
+ {
+ return TRUE;
+ }
+
+ if (!mDrawable)
+ {
+ // So drones work.
+ return TRUE;
+ }
+
+ if (mPatch && (mLastPatchUpdateTime != mPatch->getLastUpdateTime()))
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ return TRUE;
+}
+
+
+void LLVOGrass::setPixelAreaAndAngle(LLAgent &agent)
+{
+ // This should be the camera's center, as soon as we move to all region-local.
+ LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 range = relative_position.magVec(); // ugh, square root
+
+ F32 max_scale = getMaxScale();
+
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ // Compute pixels per meter at the given range
+ F32 pixels_per_meter = gCamera->getViewHeightInPixels() /
+ (tan(gCamera->getView()) * range);
+
+ // Assume grass texture is a 1 meter by 1 meter sprite at the grass object's center
+ mPixelArea = (pixels_per_meter) * (pixels_per_meter);
+}
+
+
+// BUG could speed this up by caching the relative_position and range calculations
+void LLVOGrass::updateTextures(LLAgent &agent)
+{
+ // dot_product = A B cos T
+ // BUT at_axis is unit, so dot_product = B cos T
+ LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 dot_product = relative_position * agent.getFrameAgent().getAtAxis();
+ F32 cos_angle = dot_product / relative_position.magVec();
+
+ if (getTEImage(0))
+ {
+ getTEImage(0)->addTextureStats(mPixelArea*20.f, 1.f, cos_angle);
+ }
+}
+
+BOOL LLVOGrass::updateLOD()
+{
+ F32 tan_angle = 0.f;
+ S32 num_blades = 0;
+
+ tan_angle = (mScale.mV[0]*mScale.mV[1])/mDrawable->mDistanceWRTCamera;
+ num_blades = llmin(GRASS_MAX_BLADES, lltrunc(tan_angle * 5));
+ num_blades = llmax(1, num_blades);
+ if (num_blades >= (mNumBlades << 1))
+ {
+ while (mNumBlades < num_blades)
+ {
+ mNumBlades <<= 1;
+ }
+
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ else if (num_blades <= (mNumBlades >> 1))
+ {
+ while (mNumBlades > num_blades)
+ {
+ mNumBlades >>=1;
+ }
+
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+LLDrawable* LLVOGrass::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+// mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_GRASS);
+
+ LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+
+ mDrawable->setNumFaces(1, pool, getTEImage(0));
+
+ return mDrawable;
+}
+
+BOOL LLVOGrass::updateGeometry(LLDrawable *drawable)
+{
+ plantBlades();
+ return TRUE;
+}
+
+void LLVOGrass::plantBlades()
+{
+ mPatch = mRegionp->getLand().resolvePatchRegion(getPositionRegion());
+ mLastPatchUpdateTime = mPatch->getLastUpdateTime();
+
+ LLVector3 position;
+ // Create random blades of grass with gaussian distribution
+ F32 x,y,xf,yf,dzx,dzy;
+
+ LLVector3 normal(0,0,1);
+ LLColor4U color(0,0,0,1);
+
+ LLFace *face = mDrawable->getFace(0);
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ LLStrider<LLColor4U> colorsp;
+
+ U32 *indicesp;
+
+ face->setPool(face->getPool(), getTEImage(0));
+ face->setState(LLFace::GLOBAL);
+ face->setSize(mNumBlades * 4, mNumBlades * 12);
+ face->setPrimType(LLTriangles);
+ S32 index_offset = face->getGeometryColors(verticesp,normalsp,texCoordsp,colorsp,indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ // It is possible that the species of a grass is not defined
+ // This is bad, but not the end of the world.
+ if (!sSpeciesTable.count(mSpecies))
+ {
+ llinfos << "Unknown grass species " << mSpecies << llendl;
+ return;
+ }
+
+ F32 width = sSpeciesTable[mSpecies]->mBladeSizeX;
+ F32 height = sSpeciesTable[mSpecies]->mBladeSizeY;
+
+ for (S32 i = 0; i < mNumBlades; i++)
+ {
+ x = exp_x[i] * mScale.mV[VX];
+ y = exp_y[i] * mScale.mV[VY];
+ xf = rot_x[i] * GRASS_BLADE_BASE * width * w_mod[i];
+ yf = rot_y[i] * GRASS_BLADE_BASE * width * w_mod[i];
+ dzx = dz_x [i];
+ dzy = dz_y [i];
+
+ F32 blade_height= GRASS_BLADE_HEIGHT * height * w_mod[i];
+
+ *texCoordsp++ = LLVector2(0, 0);
+ *texCoordsp++ = LLVector2(0, 0.98f);
+ *texCoordsp++ = LLVector2(1, 0);
+ *texCoordsp++ = LLVector2(1, 0.98f);
+
+ position.mV[0] = mPosition.mV[VX] + x + xf;
+ position.mV[1] = mPosition.mV[VY] + y + yf;
+ position.mV[2] = 0.f;
+ position.mV[2] = mRegionp->getLand().resolveHeightRegion(position);
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ position.mV[0] += dzx;
+ position.mV[1] += dzy;
+ position.mV[2] += blade_height;
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ position.mV[0] = mPosition.mV[VX] + x - xf;
+ position.mV[1] = mPosition.mV[VY] + y - xf;
+ position.mV[2] = 0.f;
+ position.mV[2] = mRegionp->getLand().resolveHeightRegion(position);
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ position.mV[0] += dzx;
+ position.mV[1] += dzy;
+ position.mV[2] += blade_height;
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+
+ *(colorsp++) = color;
+ *(colorsp++) = color;
+ *(colorsp++) = color;
+ *(colorsp++) = color;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+ index_offset += 4;
+ }
+
+ LLPipeline::sCompiles++;
+
+ face->mCenterLocal = mPosition;
+
+}
+
+
+
+
+
diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h
new file mode 100644
index 0000000000..ef3d30c5ea
--- /dev/null
+++ b/indra/newview/llvograss.h
@@ -0,0 +1,84 @@
+/**
+ * @file llvograss.h
+ * @brief Description of LLVOGrass class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOGRASS_H
+#define LL_LLVOGRASS_H
+
+#include "llviewerobject.h"
+#include "lldarray.h"
+#include <map>
+
+class LLSurfacePatch;
+class LLViewerImage;
+
+
+class LLVOGrass : public LLViewerObject
+{
+public:
+ LLVOGrass(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOGrass();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+ static void cleanupClass();
+
+ /*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ static void import(FILE *file, LLMessageSystem *mesgsys, const LLVector3 &pos);
+ /*virtual*/ void exportFile(FILE *file, const LLVector3 &position);
+
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ BOOL updateLOD();
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ void plantBlades();
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ static S32 sMaxGrassSpecies;
+
+ struct GrassSpeciesData
+ {
+ LLUUID mTextureID;
+
+ F32 mBladeSizeX;
+ F32 mBladeSizeY;
+ };
+
+ typedef std::map<U32, GrassSpeciesData*> SpeciesMap;
+
+ U8 mSpecies; // Species of grass
+ F32 mBladeSizeX;
+ F32 mBladeSizeY;
+
+ LLSurfacePatch *mPatch; // Stores the land patch where the grass is centered
+
+ U64 mLastPatchUpdateTime;
+
+ LLVector3 mGrassBend; // Accumulated wind (used for blowing trees)
+ LLVector3 mGrassVel;
+ LLVector3 mWind;
+ F32 mBladeWindAngle;
+ F32 mBWAOverlap;
+
+private:
+ void updateSpecies();
+ F32 mLastHeight; // For cheap update hack
+ S32 mNumBlades;
+
+ static SpeciesMap sSpeciesTable;
+};
+#endif // LL_VO_GRASS_
diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp
new file mode 100644
index 0000000000..dd3ef61173
--- /dev/null
+++ b/indra/newview/llvoground.cpp
@@ -0,0 +1,165 @@
+/**
+ * @file llvoground.cpp
+ * @brief LLVOGround class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvoground.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+
+LLVOGround::LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp)
+{
+ mbCanSelect = FALSE;
+}
+
+
+LLVOGround::~LLVOGround()
+{
+}
+
+BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GROUND)))
+ {
+ return TRUE;
+ }
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+
+void LLVOGround::updateTextures(LLAgent &agent)
+{
+}
+
+
+LLDrawable *LLVOGround::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_GROUND);
+
+ mDrawable->addFace(poolp, NULL);
+
+ return mDrawable;
+}
+
+BOOL LLVOGround::updateGeometry(LLDrawable *drawable)
+{
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face;
+
+ if (drawable->getNumFaces() < 1)
+ drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_GROUND), NULL);
+ face = drawable->getFace(0);
+ face->setPrimType(LLTriangles);
+ face->setSize(6, 12);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ ///////////////////////////////////////
+ //
+ //
+ //
+ LLVector3 at_dir = gCamera->getAtAxis();
+ at_dir.mV[VZ] = 0.f;
+ if (at_dir.normVec() < 0.01)
+ {
+ // We really don't care, as we're not looking anywhere near the horizon.
+ }
+ LLVector3 left_dir = gCamera->getLeftAxis();
+ left_dir.mV[VZ] = 0.f;
+ left_dir.normVec();
+
+ // Our center top point
+ LLVector3 center_top, center_bottom;
+
+ LLColor4 ground_color = gSky.getFogColor();
+ ground_color.mV[3] = 1.f;
+ face->setFaceColor(ground_color);
+
+ if (gCamera->getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight())
+ {
+ // Underwater
+ //center_top = gCamera->getOrigin() + at_dir*gCamera->getFar();
+ center_top = gCamera->getOrigin() - LLVector3(0, 0, 5);
+ center_bottom = gCamera->getOrigin() + at_dir*gCamera->getFar();;
+ //center_top.mV[VZ] = gAgent.getRegion()->getWaterHeight() + 0.5f;
+ center_bottom.mV[VZ] = -100.f;
+ }
+ else
+ {
+ // Above water
+ center_top = gCamera->getOrigin() - LLVector3(0, 0, 30);
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) > 0))
+ {
+ center_top.mV[VZ] = gAgent.getRegion()->getWaterHeight();
+ }
+ //center_top = gCamera->getOrigin() + at_dir*9000.f;
+ center_bottom = gCamera->getOrigin() - at_dir*gCamera->getFar();
+ //center_top.mV[VZ] = 0.f;
+ //center_bottom.mV[VZ] = gAgent.getRegion()->getWaterHeight();
+ }
+
+ *(verticesp++) = center_top + LLVector3(6000, 6000, 0);
+ *(verticesp++) = center_top + LLVector3(-6000, 6000, 0);
+ *(verticesp++) = center_top + LLVector3(-6000, -6000, 0);
+
+ *(verticesp++) = center_top + LLVector3(-6000, -6000, 0);
+ *(verticesp++) = center_top + LLVector3(6000, -6000, 0);
+ *(verticesp++) = center_top + LLVector3(6000, 6000, 0);
+
+
+ // Triangles for each side
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 5;
+
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 5;
+ *indicesp++ = index_offset + 4;
+
+ *(texCoordsp++) = LLVector2(0.f, 0.f);
+ *(texCoordsp++) = LLVector2(1.f, 0.f);
+ *(texCoordsp++) = LLVector2(0.f, 1.f);
+ *(texCoordsp++) = LLVector2(1.f, 1.f);
+ *(texCoordsp++) = LLVector2(0.f, 2.f);
+ *(texCoordsp++) = LLVector2(1.f, 2.f);
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h
new file mode 100644
index 0000000000..228d872eaf
--- /dev/null
+++ b/indra/newview/llvoground.h
@@ -0,0 +1,36 @@
+/**
+ * @file llvoground.h
+ * @brief LLVOGround class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOGROUND_H
+#define LL_LLVOGROUND_H
+
+#include "stdtypes.h"
+#include "v3color.h"
+#include "v4coloru.h"
+#include "llviewerimage.h"
+#include "llviewerobject.h"
+
+class LLVOGround : public LLViewerObject
+{
+protected:
+public:
+ LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOGround();
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ // Graphical stuff for objects - maybe broken out into render class
+ // later?
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void cleanupGL();
+};
+
+#endif // LL_LLVOGROUND_H
diff --git a/indra/newview/llvoinventorylistener.cpp b/indra/newview/llvoinventorylistener.cpp
new file mode 100644
index 0000000000..a0b623db71
--- /dev/null
+++ b/indra/newview/llvoinventorylistener.cpp
@@ -0,0 +1,45 @@
+/**
+ * @file llvoinventorylistener.cpp
+ * @brief Interface for classes that wish to receive updates about viewer object inventory
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvoinventorylistener.h"
+#include "llviewerobject.h"
+
+void LLVOInventoryListener::removeVOInventoryListener()
+{
+ if (mListenerVObject != NULL)
+ {
+ mListenerVObject->removeInventoryListener(this);
+ mListenerVObject = NULL;
+ }
+}
+
+void LLVOInventoryListener::registerVOInventoryListener(LLViewerObject* object, void* user_data)
+{
+ removeVOInventoryListener();
+ if (object != NULL)
+ {
+ mListenerVObject = object;
+ object->registerInventoryListener(this,user_data);
+ }
+}
+
+void LLVOInventoryListener::requestVOInventory()
+{
+ if (mListenerVObject != NULL)
+ {
+ mListenerVObject->requestInventory();
+ }
+}
+
+// This assumes mListenerVObject is clearing it's own lists
+void LLVOInventoryListener::clearVOInventoryListener()
+{
+ mListenerVObject = NULL;
+}
diff --git a/indra/newview/llvoinventorylistener.h b/indra/newview/llvoinventorylistener.h
new file mode 100644
index 0000000000..1cca42fc93
--- /dev/null
+++ b/indra/newview/llvoinventorylistener.h
@@ -0,0 +1,43 @@
+/**
+ * @file llvoinventorylistener.h
+ * @brief Interface for classes that wish to receive updates about viewer object inventory
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Description of LLVOInventoryListener class, which is an interface
+// for windows that are interested in updates to a ViewerObject's inventory.
+
+#ifndef LL_LLVOINVENTORYLISTENER_H
+#define LL_LLVOINVENTORYLISTENER_H
+
+#include "llviewerobject.h"
+
+class LLVOInventoryListener
+{
+public:
+ virtual void inventoryChanged(LLViewerObject* object,
+ InventoryObjectList* inventory,
+ S32 serial_num,
+ void* user_data) = 0;
+
+ // Remove the listener from the object and clear this listener
+ void removeVOInventoryListener();
+
+ // Just clear this listener, don't worry about the object.
+ void clearVOInventoryListener();
+
+protected:
+ LLVOInventoryListener() : mListenerVObject(NULL) { }
+ virtual ~LLVOInventoryListener() { removeVOInventoryListener(); }
+
+ void registerVOInventoryListener(LLViewerObject* object, void* user_data);
+ void requestVOInventory();
+
+private:
+ LLViewerObject* mListenerVObject;
+};
+
+#endif
+
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
new file mode 100644
index 0000000000..caba9e3aef
--- /dev/null
+++ b/indra/newview/llvopartgroup.cpp
@@ -0,0 +1,266 @@
+/**
+ * @file llvopartgroup.cpp
+ * @brief Group of particle systems
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvopartgroup.h"
+
+#include "llfasttimer.h"
+#include "message.h"
+#include "v2math.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerpartsim.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+
+const F32 MAX_PART_LIFETIME = 120.f;
+
+extern U64 gFrameTime;
+
+LLVOPartGroup::LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+ : LLViewerObject(id, pcode, regionp),
+ mViewerPartGroupp(NULL)
+{
+ setNumTEs(1);
+ setTETexture(0, LLUUID::null);
+ mbCanSelect = FALSE; // users can't select particle systems
+ mDebugColor = LLColor4(frand(1.f), frand(1.f), frand(1.f), 1.f);
+}
+
+
+LLVOPartGroup::~LLVOPartGroup()
+{
+}
+
+BOOL LLVOPartGroup::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES))
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ // Particle groups don't need any of default idleUpdate velocity interpolation stuff.
+ //LLViewerObject::idleUpdate(agent, world, time);
+ return TRUE;
+}
+
+
+void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent)
+{
+ // mPixelArea is calculated during render
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 mid_scale = getMidScale();
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz);
+
+ if (range < 0.001f || isHUDAttachment()) // range == zero
+ {
+ mAppAngle = 180.f;
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( mid_scale, range) * RAD_TO_DEG;
+ }
+}
+
+void LLVOPartGroup::updateTextures(LLAgent &agent)
+{
+ // Texture stats for particles will need to be updated in a different way...
+}
+
+
+LLDrawable* LLVOPartGroup::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
+
+ LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ mDrawable->setNumFaces(mViewerPartGroupp->getCount(), pool, getTEImage(0));
+ return mDrawable;
+}
+
+ const F32 MAX_PARTICLE_AREA_SCALE = 0.02f; // some tuned constant, limits on how much particle area to draw
+
+BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
+{
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_PARTICLES);
+
+ LLVector3 at;
+ LLVector3 up;
+ LLVector3 right;
+ LLVector3 position_agent;
+ LLVector3 camera_agent = gAgent.getCameraPositionAgent();
+ LLVector2 uvs[4];
+
+ uvs[0].setVec(0.f, 1.f);
+ uvs[1].setVec(0.f, 0.f);
+ uvs[2].setVec(1.f, 1.f);
+ uvs[3].setVec(1.f, 0.f);
+
+ LLDrawPool *drawpool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ S32 num_parts = mViewerPartGroupp->getCount();
+ LLFace *facep;
+
+ if (!num_parts)
+ {
+ drawable->setNumFaces(0, drawpool, getTEImage(0));
+ LLPipeline::sCompiles++;
+ return TRUE;
+ }
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
+ {
+ return TRUE;
+ }
+
+// drawable->setNumFaces(num_parts, drawpool, getTEImage(0));
+ drawable->setNumFacesFast(num_parts, drawpool, getTEImage(0));
+
+ LLVector3 normal(-gCamera->getXAxis()); // make point agent face camera
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp = 0;
+
+ S32 vert_offset;
+ F32 tot_area = 0;
+ F32 max_area = LLViewerPartSim::getMaxPartCount() * MAX_PARTICLE_AREA_SCALE;
+
+ S32 count=0;
+ for (S32 i = 0; i < num_parts; i++)
+ {
+ const LLViewerPart &part = mViewerPartGroupp->mParticles[i];
+
+ LLVector3 part_pos_agent(part.mPosAgent);
+ at = part_pos_agent - camera_agent;
+
+ //F32 invcamdist = 1.0f / at.magVec();
+ //area += (part.mScale.mV[0]*invcamdist)*(part.mScale.mV[1]*invcamdist);
+ F32 camera_dist_squared = at.magVecSquared();
+ F32 inv_camera_dist_squared;
+ if (camera_dist_squared > 1.f)
+ inv_camera_dist_squared = 1.f / camera_dist_squared;
+ else
+ inv_camera_dist_squared = 1.f;
+ F32 area = part.mScale.mV[0] * part.mScale.mV[1] * inv_camera_dist_squared;
+ tot_area += area;
+ if (tot_area > max_area)
+ {
+ break;
+ }
+
+ count++;
+
+ facep = drawable->getFace(i);
+ const F32 NEAR_PART_DIST_SQ = 5.f*5.f; // Only discard particles > 5 m from the camera
+ const F32 MIN_PART_AREA = .005f*.005f; // only less than 5 mm x 5 mm at 1 m from camera
+ if (camera_dist_squared > NEAR_PART_DIST_SQ && area < MIN_PART_AREA)
+ {
+ facep->setSize(0, 0);
+ continue;
+ }
+ facep->setSize(4, 6);
+ facep->mCenterAgent = part_pos_agent;
+ vert_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == vert_offset)
+ {
+ return TRUE;
+ }
+
+ right = at % LLVector3(0.f, 0.f, 1.f);
+ right.normVec();
+ up = right % at;
+ up.normVec();
+
+ if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK)
+ {
+ LLVector3 normvel = part.mVelocity;
+ normvel.normVec();
+ LLVector2 up_fracs;
+ up_fracs.mV[0] = normvel*right;
+ up_fracs.mV[1] = normvel*up;
+ up_fracs.normVec();
+
+ LLVector3 new_up;
+ LLVector3 new_right;
+ new_up = up_fracs.mV[0] * right + up_fracs.mV[1]*up;
+ new_right = up_fracs.mV[1] * right - up_fracs.mV[0]*up;
+ up = new_up;
+ right = new_right;
+ up.normVec();
+ right.normVec();
+ }
+
+ right *= 0.5f*part.mScale.mV[0];
+ up *= 0.5f*part.mScale.mV[1];
+
+ *verticesp++ = part_pos_agent + up - right;
+ *verticesp++ = part_pos_agent - up - right;
+ *verticesp++ = part_pos_agent + up + right;
+ *verticesp++ = part_pos_agent - up + right;
+
+ *texCoordsp++ = uvs[0];
+ *texCoordsp++ = uvs[1];
+ *texCoordsp++ = uvs[2];
+ *texCoordsp++ = uvs[3];
+
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+
+ if (part.mFlags & LLPartData::LL_PART_EMISSIVE_MASK)
+ {
+ facep->setState(LLFace::FULLBRIGHT);
+ }
+ else
+ {
+ facep->clearState(LLFace::FULLBRIGHT);
+ }
+ facep->setFaceColor(part.mColor);
+
+ *indicesp++ = vert_offset + 0;
+ *indicesp++ = vert_offset + 1;
+ *indicesp++ = vert_offset + 2;
+
+ *indicesp++ = vert_offset + 1;
+ *indicesp++ = vert_offset + 3;
+ *indicesp++ = vert_offset + 2;
+ }
+ for (S32 j = count; j < drawable->getNumFaces(); j++)
+ {
+ drawable->getFace(j)->setSize(0,0);
+ }
+
+ mPixelArea = tot_area * gCamera->getPixelMeterRatio() * gCamera->getPixelMeterRatio();
+ const F32 area_scale = 10.f; // scale area to increase priority a bit
+ getTEImage(0)->addTextureStats(mPixelArea * area_scale);
+
+ LLPipeline::sCompiles++;
+
+ return TRUE;
+}
+
diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h
new file mode 100644
index 0000000000..657d1824d0
--- /dev/null
+++ b/indra/newview/llvopartgroup.h
@@ -0,0 +1,42 @@
+/**
+ * @file llvopartgroup.h
+ * @brief Group of particle systems
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOPARTGROUP_H
+#define LL_LLVOPARTGROUP_H
+
+#include "llviewerobject.h"
+#include "v3math.h"
+#include "v3color.h"
+#include "llframetimer.h"
+
+class LLViewerPartGroup;
+
+class LLVOPartGroup : public LLViewerObject
+{
+public:
+ LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+
+ ~LLVOPartGroup();
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void setViewerPartGroup(LLViewerPartGroup *part_groupp) { mViewerPartGroupp = part_groupp; }
+protected:
+ LLViewerPartGroup *mViewerPartGroupp;
+
+ LLColor4 mDebugColor;
+};
+
+#endif // LL_LLVOPARTGROUP_H
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
new file mode 100644
index 0000000000..5c76dc5a87
--- /dev/null
+++ b/indra/newview/llvosky.cpp
@@ -0,0 +1,2429 @@
+/**
+ * @file llvosky.cpp
+ * @brief LLVOSky class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvosky.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+#include "llframetimer.h"
+#include "timing.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llcubemap.h"
+#include "lldrawpoolsky.h"
+#include "lldrawpoolwater.h"
+#include "llglheaders.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h" // for gSunTextureID
+
+const S32 NUM_TILES_X = 8;
+const S32 NUM_TILES_Y = 4;
+const S32 NUM_TILES = NUM_TILES_X * NUM_TILES_Y;
+
+// Heavenly body constants
+const F32 SUN_DISK_RADIUS = 0.5f;
+const F32 MOON_DISK_RADIUS = SUN_DISK_RADIUS * 0.9f;
+const F32 SUN_INTENSITY = 1e5;
+const F32 SUN_DISK_INTENSITY = 24.f;
+
+
+// Texture coordinates:
+const LLVector2 TEX00 = LLVector2(0.f, 0.f);
+const LLVector2 TEX01 = LLVector2(0.f, 1.f);
+const LLVector2 TEX10 = LLVector2(1.f, 0.f);
+const LLVector2 TEX11 = LLVector2(1.f, 1.f);
+
+//static
+LLColor3 LLHaze::sAirScaSeaLevel;
+
+class LLFastLn
+{
+public:
+ LLFastLn()
+ {
+ mTable[0] = 0;
+ for( S32 i = 1; i < 257; i++ )
+ {
+ mTable[i] = log((F32)i);
+ }
+ }
+
+ F32 ln( F32 x )
+ {
+ const F32 OO_255 = 0.003921568627450980392156862745098f;
+ const F32 LN_255 = 5.5412635451584261462455391880218f;
+
+ if( x < OO_255 )
+ {
+ return log(x);
+ }
+ else
+ if( x < 1 )
+ {
+ x *= 255.f;
+ S32 index = llfloor(x);
+ F32 t = x - index;
+ F32 low = mTable[index];
+ F32 high = mTable[index + 1];
+ return low + t * (high - low) - LN_255;
+ }
+ else
+ if( x <= 255 )
+ {
+ S32 index = llfloor(x);
+ F32 t = x - index;
+ F32 low = mTable[index];
+ F32 high = mTable[index + 1];
+ return low + t * (high - low);
+ }
+ else
+ {
+ return log( x );
+ }
+ }
+
+ F32 pow( F32 x, F32 y )
+ {
+ return (F32)LL_FAST_EXP(y * ln(x));
+ }
+
+
+private:
+ F32 mTable[257]; // index 0 is unused
+};
+
+LLFastLn gFastLn;
+
+
+// Functions used a lot.
+
+inline F32 LLHaze::calcPhase(const F32 cos_theta) const
+{
+ const F32 g2 = mG * mG;
+ const F32 den = 1 + g2 - 2 * mG * cos_theta;
+ return (1 - g2) * gFastLn.pow(den, -1.5);
+}
+
+inline void color_pow(LLColor3 &col, const F32 e)
+{
+ col.mV[0] = gFastLn.pow(col.mV[0], e);
+ col.mV[1] = gFastLn.pow(col.mV[1], e);
+ col.mV[2] = gFastLn.pow(col.mV[2], e);
+}
+
+inline LLColor3 color_norm(const LLColor3 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 1.f)
+ {
+ return 1.f/m * col;
+ }
+ else return col;
+}
+
+inline LLColor3 color_norm_fog(const LLColor3 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 0.75f)
+ {
+ return 0.75f/m * col;
+ }
+ else return col;
+}
+
+
+inline LLColor4 color_norm_abs(const LLColor4 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 1e-6)
+ {
+ return 1.f/m * col;
+ }
+ else
+ {
+ return col;
+ }
+}
+
+
+inline F32 color_intens ( const LLColor4 &col )
+{
+ return col.mV[0] + col.mV[1] + col.mV[2];
+}
+
+
+inline F32 color_avg ( const LLColor3 &col )
+{
+ return color_intens(col) / 3;
+}
+
+inline void color_gamma_correct(LLColor3 &col)
+{
+ const F32 gamma_inv = 1.f/1.2f;
+ if (col.mV[0] != 0.f)
+ {
+ col.mV[0] = gFastLn.pow(col.mV[0], gamma_inv);
+ }
+ if (col.mV[1] != 0.f)
+ {
+ col.mV[1] = gFastLn.pow(col.mV[1], gamma_inv);
+ }
+ if (col.mV[2] != 0.f)
+ {
+ col.mV[2] = gFastLn.pow(col.mV[2], gamma_inv);
+ }
+}
+
+inline F32 min_intens_factor( LLColor3& col, F32 min_intens, BOOL postmultiply = FALSE);
+inline F32 min_intens_factor( LLColor3& col, F32 min_intens, BOOL postmultiply)
+{
+ const F32 intens = color_intens(col);
+ F32 factor = 1;
+ if (0 == intens)
+ {
+ return 0;
+ }
+
+ if (intens < min_intens)
+ {
+ factor = min_intens / intens;
+ if (postmultiply)
+ col *= factor;
+ }
+ return factor;
+}
+
+inline LLVector3 move_vec(const LLVector3& v, const F32 cos_max_angle)
+{
+ LLVector3 v_norm = v;
+ v_norm.normVec();
+
+ LLVector2 v_norm_proj(v_norm.mV[0], v_norm.mV[1]);
+ const F32 projection2 = v_norm_proj.magVecSquared();
+ const F32 scale = sqrt((1 - cos_max_angle * cos_max_angle) / projection2);
+ return LLVector3(scale * v_norm_proj.mV[0], scale * v_norm_proj.mV[1], cos_max_angle);
+}
+
+
+/***************************************
+ Transparency Map
+***************************************/
+
+void LLTranspMap::init(const F32 elev, const F32 step, const F32 h, const LLHaze* const haze)
+{
+ mHaze = haze;
+ mAtmHeight = h;
+ mElevation = elev;
+ mStep = step;
+ mStepInv = 1.f / step;
+ F32 sin_angle = EARTH_RADIUS/(EARTH_RADIUS + mElevation);
+ mCosMaxAngle = -sqrt(1 - sin_angle * sin_angle);
+ mMapSize = S32(ceil((1 - mCosMaxAngle) * mStepInv + 1) + 0.5);
+ delete mT;
+ mT = new LLColor3[mMapSize];
+
+ for (S32 i = 0; i < mMapSize; ++i)
+ {
+ const F32 cos_a = 1 - i*mStep;
+ const LLVector3 dir(0, sqrt(1-cos_a*cos_a), cos_a);
+ mT[i] = calcAirTranspDir(mElevation, dir);
+ }
+}
+
+
+
+LLColor3 LLTranspMap::calcAirTranspDir(const F32 elevation, const LLVector3 &dir) const
+{
+ LLColor3 opt_depth(0, 0, 0);
+ const LLVector3 point(0, 0, EARTH_RADIUS + elevation);
+ F32 dist = -dir * point;
+ LLVector3 cur_point;
+ S32 s;
+
+ if (dist > 0)
+ {
+ cur_point = point + dist * dir;
+// const F32 K = log(dist * INV_FIRST_STEP + 1) * INV_NO_STEPS;
+// const F32 e_pow_k = LL_FAST_EXP(K);
+ const F32 e_pow_k = gFastLn.pow( dist * INV_FIRST_STEP + 1, INV_NO_STEPS );
+ F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
+
+ for (s = 0; s < NO_STEPS; ++s)
+ {
+ const F32 h = cur_point.magVec() - EARTH_RADIUS;
+ step *= e_pow_k;
+ opt_depth += calcSigExt(h) * step;
+ cur_point -= dir * step;
+ }
+ opt_depth *= 2;
+ cur_point = point + 2 * dist * dir;
+ }
+ else
+ {
+ cur_point = point;
+ }
+
+ dist = hitsAtmEdge(cur_point, dir);
+// const F32 K = log(dist * INV_FIRST_STEP + 1) * INV_NO_STEPS;
+// const F32 e_pow_k = LL_FAST_EXP(K);
+ const F32 e_pow_k = gFastLn.pow( dist * INV_FIRST_STEP + 1, INV_NO_STEPS );
+ F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
+
+ for (s = 0; s < NO_STEPS; ++s)
+ {
+ const F32 h = cur_point.magVec() - EARTH_RADIUS;
+ step *= e_pow_k;
+ opt_depth += calcSigExt(h) * step;
+ cur_point += dir * step;
+ }
+
+ opt_depth *= -4.0f*F_PI;
+ opt_depth.exp();
+ return opt_depth;
+}
+
+
+
+F32 LLTranspMap::hitsAtmEdge(const LLVector3& X, const LLVector3& dir) const
+{
+ const F32 tca = -dir * X;
+ const F32 R = EARTH_RADIUS + mAtmHeight;
+ const F32 thc2 = R * R - X.magVecSquared() + tca * tca;
+ return tca + sqrt ( thc2 );
+}
+
+
+
+
+
+void LLTranspMapSet::init(const S32 size, const F32 first_step, const F32 media_height, const LLHaze* const haze)
+{
+ const F32 angle_step = 0.005f;
+ mSize = size;
+ mMediaHeight = media_height;
+
+ delete[] mTransp;
+ mTransp = new LLTranspMap[mSize];
+
+ delete[] mHeights;
+ mHeights = new F32[mSize];
+
+ F32 h = 0;
+ mHeights[0] = h;
+ mTransp[0].init(h, angle_step, mMediaHeight, haze);
+ const F32 K = log(mMediaHeight / first_step + 1) / (mSize - 1);
+ const F32 e_pow_k = exp(K);
+ F32 step = first_step * (e_pow_k - 1);
+
+ for (S32 s = 1; s < mSize; ++s)
+ {
+ h += step;
+ mHeights[s] = h;
+ mTransp[s].init(h, angle_step, mMediaHeight, haze);
+ step *= e_pow_k;
+ }
+}
+
+LLTranspMapSet::~LLTranspMapSet()
+{
+ delete[] mTransp;
+ mTransp = NULL;
+ delete[] mHeights;
+ mHeights = NULL;
+}
+
+
+
+/***************************************
+ SkyTex
+***************************************/
+
+S32 LLSkyTex::sComponents = 4;
+S32 LLSkyTex::sResolution = 64;
+F32 LLSkyTex::sInterpVal = 0.f;
+S32 LLSkyTex::sCurrent = 0;
+
+
+LLSkyTex::LLSkyTex()
+{
+}
+
+void LLSkyTex::init()
+{
+ mSkyData = new LLColor3[sResolution * sResolution];
+ mSkyDirs = new LLVector3[sResolution * sResolution];
+
+ for (S32 i = 0; i < 2; ++i)
+ {
+ mImageGL[i] = new LLImageGL(FALSE);
+ mImageGL[i]->setClamp(TRUE, TRUE);
+ mImageRaw[i] = new LLImageRaw(sResolution, sResolution, sComponents);
+
+ initEmpty(i);
+ }
+}
+
+void LLSkyTex::cleanupGL()
+{
+ mImageGL[0] = NULL;
+ mImageGL[1] = NULL;
+}
+
+void LLSkyTex::restoreGL()
+{
+ for (S32 i = 0; i < 2; i++)
+ {
+ mImageGL[i] = new LLImageGL(FALSE);
+ mImageGL[i]->setClamp(TRUE, TRUE);
+ }
+}
+
+LLSkyTex::~LLSkyTex()
+{
+ delete[] mSkyData;
+ mSkyData = NULL;
+
+ delete[] mSkyDirs;
+ mSkyDirs = NULL;
+}
+
+
+void LLSkyTex::initEmpty(const S32 tex)
+{
+ U8* data = mImageRaw[tex]->getData();
+ for (S32 i = 0; i < sResolution; ++i)
+ {
+ for (S32 j = 0; j < sResolution; ++j)
+ {
+ const S32 basic_offset = (i * sResolution + j);
+ S32 offset = basic_offset * sComponents;
+ data[offset] = 0;
+ data[offset+1] = 0;
+ data[offset+2] = 0;
+ data[offset+3] = 255;
+
+ mSkyData[basic_offset].setToBlack();
+ }
+ }
+
+ createTexture(tex);
+}
+
+
+void LLSkyTex::create(const F32 brightness_scale, const LLColor3& multiscatt)
+{
+ U8* data = mImageRaw[sCurrent]->getData();
+ for (S32 i = 0; i < sResolution; ++i)
+ {
+ for (S32 j = 0; j < sResolution; ++j)
+ {
+ const S32 basic_offset = (i * sResolution + j);
+ S32 offset = basic_offset * sComponents;
+ LLColor3 col(mSkyData[basic_offset]);
+ if (getDir(i, j).mV[VZ] >= -0.02f) {
+ col += 0.1f * multiscatt;
+ col *= brightness_scale;
+ col.clamp();
+ color_gamma_correct(col);
+ }
+
+ U32* pix = (U32*)(data + offset);
+ LLColor4 temp = LLColor4(col);
+ LLColor4U temp1 = LLColor4U(temp);
+ *pix = temp1.mAll;
+ }
+ }
+ createTexture(sCurrent);
+}
+
+void LLSkyTex::createTexture(S32 which)
+{
+ mImageGL[which]->createGLTexture(0, mImageRaw[which]);
+ mImageGL[which]->setClamp(TRUE, TRUE);
+}
+
+void LLSkyTex::bindTexture(BOOL curr)
+{
+ mImageGL[getWhich(curr)]->bind();
+}
+
+/***************************************
+ Sky
+***************************************/
+
+F32 LLHeavenBody::sInterpVal = 0;
+
+F32 LLVOSky::sNighttimeBrightness = 1.5f;
+
+S32 LLVOSky::sResolution = LLSkyTex::getResolution();
+S32 LLVOSky::sTileResX = sResolution/NUM_TILES_X;
+S32 LLVOSky::sTileResY = sResolution/NUM_TILES_Y;
+
+LLVOSky::LLVOSky(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp),
+ mSun(SUN_DISK_RADIUS), mMoon(MOON_DISK_RADIUS),
+ mBrightnessScale(1.f),
+ mBrightnessScaleNew(0.f),
+ mBrightnessScaleGuess(1.f),
+ mWeatherChange(FALSE),
+ mCloudDensity(0.2f),
+ mWind(0.f),
+ mForceUpdate(FALSE),
+ mWorldScale(1.f)
+{
+ mInitialized = FALSE;
+ mbCanSelect = FALSE;
+ mUpdateTimer.reset();
+
+ for (S32 i = 0; i < 6; i++)
+ {
+ mSkyTex[i].init();
+ }
+ for (S32 i=0; i<FACE_COUNT; i++)
+ {
+ mFace[i] = NULL;
+ }
+
+ mCameraPosAgent = gAgent.getCameraPositionAgent();
+ mAtmHeight = ATM_HEIGHT;
+ mEarthCenter = LLVector3(mCameraPosAgent.mV[0], mCameraPosAgent.mV[1], -EARTH_RADIUS);
+ updateHaze();
+
+ mSunDefaultPosition = gSavedSettings.getVector3("SkySunDefaultPosition");
+ if (gSavedSettings.getBOOL("SkyOverrideSimSunPosition"))
+ {
+ initSunDirection(mSunDefaultPosition, LLVector3(0, 0, 0));
+ }
+ mAmbientScale = gSavedSettings.getF32("SkyAmbientScale");
+ mNightColorShift = gSavedSettings.getColor3("SkyNightColorShift");
+ mFogColor.mV[VRED] = mFogColor.mV[VGREEN] = mFogColor.mV[VBLUE] = 0.5f;
+ mFogColor.mV[VALPHA] = 0.0f;
+ mFogRatio = 1.2f;
+
+ mSun.setIntensity(SUN_INTENSITY);
+ mMoon.setIntensity(0.1f * SUN_INTENSITY);
+
+ mCubeMap = NULL;
+
+ mSunTexturep = gImageList.getImage(gSunTextureID, TRUE, TRUE);
+ mSunTexturep->setClamp(TRUE, TRUE);
+ mMoonTexturep = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
+ mMoonTexturep->setClamp(TRUE, TRUE);
+ mBloomTexturep = gImageList.getImage(IMG_BLOOM1);
+ mBloomTexturep->setClamp(TRUE, TRUE);
+}
+
+
+LLVOSky::~LLVOSky()
+{
+ // Don't delete images - it'll get deleted by gImageList on shutdown
+ // This needs to be done for each texture
+
+ delete mCubeMap;
+ mCubeMap = NULL;
+}
+
+void LLVOSky::initClass()
+{
+ LLHaze::initClass();
+}
+
+
+void LLVOSky::init()
+{
+ // index of refraction calculation.
+ mTransp.init(NO_STEPS+1+4, FIRST_STEP, mAtmHeight, &mHaze);
+
+ const F32 haze_int = color_intens(mHaze.calcSigSca(0));
+ mHazeConcentration = haze_int /
+ (color_intens(LLHaze::calcAirSca(0)) + haze_int);
+
+ mBrightnessScaleNew = 0;
+
+ // Initialize the cached normalized direction vectors
+ for (S32 side = 0; side < 6; ++side)
+ {
+ for (S32 tile = 0; tile < NUM_TILES; ++tile)
+ {
+ initSkyTextureDirs(side, tile);
+ createSkyTexture(side, tile);
+ }
+ }
+
+ calcBrightnessScaleAndColors();
+ initCubeMap();
+}
+
+void LLVOSky::initCubeMap()
+{
+ std::vector<LLPointer<LLImageRaw> > images;
+ for (S32 side = 0; side < 6; side++)
+ {
+ images.push_back(mSkyTex[side].getImageRaw());
+ }
+ if (mCubeMap != NULL)
+ {
+ mCubeMap->init(images);
+ }
+ else if (gSavedSettings.getBOOL("RenderWater") && gGLManager.mHasCubeMap)
+ {
+ mCubeMap = new LLCubeMap();
+ mCubeMap->init(images);
+ }
+}
+
+
+void LLVOSky::cleanupGL()
+{
+ S32 i;
+ for (i = 0; i < 6; i++)
+ {
+ mSkyTex[i].cleanupGL();
+ }
+ if (getCubeMap())
+ {
+ getCubeMap()->destroyGL();
+ }
+}
+
+void LLVOSky::restoreGL()
+{
+ S32 i;
+ for (i = 0; i < 6; i++)
+ {
+ mSkyTex[i].restoreGL();
+ }
+ mSunTexturep = gImageList.getImage(gSunTextureID, TRUE, TRUE);
+ mSunTexturep->setClamp(TRUE, TRUE);
+ mMoonTexturep = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
+ mMoonTexturep->setClamp(TRUE, TRUE);
+ mBloomTexturep = gImageList.getImage(IMG_BLOOM1);
+ mBloomTexturep->setClamp(TRUE, TRUE);
+
+ calcBrightnessScaleAndColors();
+
+ // Water is currently broken on Mac.
+ if (gSavedSettings.getBOOL("RenderWater") && gGLManager.mHasCubeMap)
+ {
+ LLCubeMap* cube_map = getCubeMap();
+
+ std::vector<LLPointer<LLImageRaw> > images;
+ for (S32 side = 0; side < 6; side++)
+ {
+ images.push_back(mSkyTex[side].getImageRaw());
+ }
+
+ if(cube_map)
+ {
+ cube_map->init(images);
+ mForceUpdate = TRUE;
+ }
+ }
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+}
+
+
+void LLVOSky::updateHaze()
+{
+ time_t timer;
+ time(&timer);
+ static LLRand WeatherRandomNumber(gmtime(&timer)->tm_mday);
+ if (gSavedSettings.getBOOL("FixedWeather"))
+ {
+ WeatherRandomNumber.seed(8008135);
+ }
+
+ const F32 fo_upper_bound = 5;
+ const F32 sca_upper_bound = 6;
+ const F32 fo = 1 + WeatherRandomNumber.llfrand(fo_upper_bound - 1);
+ const static F32 upper = 0.5f / gFastLn.ln(fo_upper_bound);
+ mHaze.setFalloff(fo);
+ mHaze.setG(WeatherRandomNumber.llfrand(0.0f + upper * gFastLn.ln(fo)));
+ LLColor3 sca;
+ const F32 cd = mCloudDensity * 3;
+ F32 min_r = cd - 1;
+ if (min_r < 0)
+ {
+ min_r = 0;
+ }
+ F32 max_r = cd + 1;
+ if (max_r > sca_upper_bound)
+ {
+ max_r = sca_upper_bound;
+ }
+
+ sca.mV[0] = min_r + WeatherRandomNumber.llfrand(max_r - min_r);//frand(6);
+
+ min_r = sca.mV[0] - 0.1f;
+ if (min_r < 0)
+ {
+ min_r = 0;
+ }
+ max_r = sca.mV[0] + 0.5f;
+ if (max_r > sca_upper_bound)
+ {
+ max_r = sca_upper_bound;
+ }
+
+ sca.mV[1] = min_r + WeatherRandomNumber.llfrand(max_r - min_r);
+
+ min_r = sca.mV[1];
+ if (min_r < 0)
+ {
+ min_r = 0;
+ }
+ max_r = sca.mV[1] + 1;
+ if (max_r > sca_upper_bound)
+ {
+ max_r = sca_upper_bound;
+ }
+
+ sca.mV[2] = min_r + WeatherRandomNumber.llfrand(max_r - min_r);
+
+ sca = AIR_SCA_AVG * sca;
+
+ mHaze.setSigSca(sca);
+}
+
+void LLVOSky::initSkyTextureDirs(const S32 side, const S32 tile)
+{
+ S32 tile_x = tile % NUM_TILES_X;
+ S32 tile_y = tile / NUM_TILES_X;
+
+ S32 tile_x_pos = tile_x * sTileResX;
+ S32 tile_y_pos = tile_y * sTileResY;
+
+ F32 coeff[3] = {0, 0, 0};
+ const S32 curr_coef = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
+ const S32 side_dir = (((side & 1) << 1) - 1); // even = -1, odd = 1
+ const S32 x_coef = (curr_coef + 1) % 3;
+ const S32 y_coef = (x_coef + 1) % 3;
+
+ coeff[curr_coef] = (F32)side_dir;
+
+ F32 inv_res = 1.f/sResolution;
+ S32 x, y;
+ for (y = tile_y_pos; y < (tile_y_pos + sTileResY); ++y)
+ {
+ for (x = tile_x_pos; x < (tile_x_pos + sTileResX); ++x)
+ {
+ coeff[x_coef] = F32((x<<1) + 1) * inv_res - 1.f;
+ coeff[y_coef] = F32((y<<1) + 1) * inv_res - 1.f;
+ LLVector3 dir(coeff[0], coeff[1], coeff[2]);
+ dir.normVec();
+ mSkyTex[side].setDir(dir, x, y);
+ }
+ }
+}
+
+void LLVOSky::createSkyTexture(const S32 side, const S32 tile)
+{
+ S32 tile_x = tile % NUM_TILES_X;
+ S32 tile_y = tile / NUM_TILES_X;
+
+ S32 tile_x_pos = tile_x * sTileResX;
+ S32 tile_y_pos = tile_y * sTileResY;
+
+ S32 x, y;
+ for (y = tile_y_pos; y < (tile_y_pos + sTileResY); ++y)
+ {
+ for (x = tile_x_pos; x < (tile_x_pos + sTileResX); ++x)
+ {
+ mSkyTex[side].setPixel(calcSkyColorInDir(mSkyTex[side].getDir(x, y)), x, y);
+ }
+ }
+}
+
+
+LLColor3 LLVOSky::calcSkyColorInDir(const LLVector3 &dir)
+{
+ LLColor3 col, transp;
+
+ if (dir.mV[VZ] < -0.02)
+ {
+ col = LLColor3(llmax(mFogColor[0],0.2f), llmax(mFogColor[1],0.2f), llmax(mFogColor[2],0.27f));
+ float x = 1.0f-fabsf(-0.1f-dir.mV[VZ]);
+ x *= x;
+ col.mV[0] *= x*x;
+ col.mV[1] *= powf(x, 2.5f);
+ col.mV[2] *= x*x*x;
+ return col;
+ }
+
+
+ calcSkyColorInDir(col, transp, dir);
+ F32 br = color_max(col);
+ if (br > mBrightnessScaleNew)
+ {
+ mBrightnessScaleNew = br;
+ mBrightestPointNew = col;
+ }
+ return col;
+}
+
+
+LLColor4 LLVOSky::calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exager = 1) const
+{
+ LLColor3 col, tr;
+ calcInScatter(col, tr, point, exager);
+ col *= mBrightnessScaleGuess;
+ transp = LLColor4(tr);
+ return LLColor4(col);
+}
+
+
+
+void LLVOSky::calcSkyColorInDir(LLColor3& res, LLColor3& transp, const LLVector3& dir) const
+{
+ const LLVector3& tosun = getToSunLast();
+ res.setToBlack();
+ LLColor3 haze_res(0.f, 0.f, 0.f);
+ transp.setToWhite();
+ LLVector3 step_v ;
+ LLVector3 cur_pos = mCameraPosAgent;
+ F32 h;
+
+ F32 dist = calcHitsAtmEdge(mCameraPosAgent, dir);
+// const F32 K = log(dist / FIRST_STEP + 1) / NO_STEPS;
+ const F32 K = gFastLn.ln(dist / FIRST_STEP + 1) / NO_STEPS;
+ const F32 e_pow_k = (F32)LL_FAST_EXP(K);
+ F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
+
+ for (S32 s = 0; s < NO_STEPS; ++s)
+ {
+ h = calcHeight(cur_pos);
+ step *= e_pow_k;
+ LLColor3 air_sca_opt_depth;
+ LLHaze::calcAirSca(h, air_sca_opt_depth);
+ air_sca_opt_depth *= step;
+
+ LLColor3 haze_sca_opt_depth;
+ mHaze.calcSigSca(h, haze_sca_opt_depth);
+ haze_sca_opt_depth *= step;
+
+ LLColor3 haze_ext_opt_depth = haze_sca_opt_depth;
+ haze_ext_opt_depth *= (1.f + mHaze.getAbsCoef());
+
+ if (calcHitsEarth(cur_pos, tosun) < 0) // calculates amount of in-scattered light from the sun
+ {
+ //visibility check is too expensive
+ LLColor3 air_transp;
+ mTransp.calcTransp(calcUpVec(cur_pos) * tosun, h, air_transp);
+ air_transp *= transp;
+ res += air_sca_opt_depth * air_transp;
+ haze_res += haze_sca_opt_depth * air_transp;
+ }
+ LLColor3 temp(-4.f * F_PI * (air_sca_opt_depth + haze_ext_opt_depth));
+ temp.exp();
+ transp *= temp;
+ step_v = dir * step;
+ cur_pos += step_v;
+ }
+ const F32 cos_dir = dir * tosun;
+ res *= calcAirPhaseFunc(cos_dir);
+ res += haze_res * mHaze.calcPhase(cos_dir);
+ res *= mSun.getIntensity();
+}
+
+
+
+
+void LLVOSky::calcInScatter(LLColor3& res, LLColor3& transp,
+ const LLVector3& P, const F32 exaggeration) const
+{
+ const LLVector3& tosun = getToSunLast();
+ res.setToBlack();
+ transp.setToWhite();
+
+ LLVector3 lower, upper;
+ LLVector3 dir = P - mCameraPosAgent;
+
+ F32 dist = exaggeration * dir.normVec();
+
+ const F32 cos_dir = dir * tosun;
+
+ if (dir.mV[VZ] > 0)
+ {
+ lower = mCameraPosAgent;
+ upper = P;
+ }
+ else
+ {
+ lower = P;
+ upper = mCameraPosAgent;
+ dir = -dir;
+ }
+
+ const F32 lower_h = calcHeight(lower);
+ const F32 upper_h = calcHeight(upper);
+ const LLVector3 up_upper = calcUpVec(upper);
+ const LLVector3 up_lower = calcUpVec(lower);
+
+ transp = color_div(mTransp.calcTransp(up_lower * dir, lower_h),
+ mTransp.calcTransp(up_upper * dir, upper_h));
+ color_pow(transp, exaggeration);
+
+ if (calcHitsEarth(upper, tosun) > 0)
+ {
+ const F32 avg = color_avg(transp);
+ //const F32 avg = llmin(1.f, 1.2f * color_avg(transp));
+ transp.setVec(avg, avg, avg);
+ return;
+ }
+
+ LLColor3 air_sca_opt_depth = LLHaze::calcAirSca(upper_h);
+ LLColor3 haze_sca_opt_depth = mHaze.calcSigSca(upper_h);
+ LLColor3 sun_transp;
+ mTransp.calcTransp(up_upper * tosun, upper_h, sun_transp);
+
+ if (calcHitsEarth(lower, tosun) < 0)
+ {
+ air_sca_opt_depth += LLHaze::calcAirSca(lower_h);
+ air_sca_opt_depth *= 0.5;
+ haze_sca_opt_depth += mHaze.calcSigSca(lower_h);
+ haze_sca_opt_depth *= 0.5;
+ sun_transp += mTransp.calcTransp(up_lower * tosun, lower_h);
+ sun_transp *= 0.5;
+ }
+
+ res = calcAirPhaseFunc(cos_dir) * air_sca_opt_depth;
+ res += mHaze.calcPhase(cos_dir) * haze_sca_opt_depth;
+ res = mSun.getIntensity() * dist * sun_transp * res;
+}
+
+
+
+
+
+
+F32 LLVOSky::calcHitsEarth(const LLVector3& orig, const LLVector3& dir) const
+{
+ const LLVector3 from_earth_center = mEarthCenter - orig;
+ const F32 tca = dir * from_earth_center;
+ if ( tca < 0 )
+ {
+ return -1;
+ }
+
+ const F32 thc2 = EARTH_RADIUS * EARTH_RADIUS -
+ from_earth_center.magVecSquared() + tca * tca;
+ if (thc2 < 0 )
+ {
+ return -1;
+ }
+
+ return tca - sqrt ( thc2 );
+}
+
+F32 LLVOSky::calcHitsAtmEdge(const LLVector3& orig, const LLVector3& dir) const
+{
+ const LLVector3 from_earth_center = mEarthCenter - orig;
+ const F32 tca = dir * from_earth_center;
+
+ const F32 thc2 = (EARTH_RADIUS + mAtmHeight) * (EARTH_RADIUS + mAtmHeight) -
+ from_earth_center.magVecSquared() + tca * tca;
+ return tca + sqrt(thc2);
+}
+
+
+void LLVOSky::updateBrightestDir()
+{
+ LLColor3 br_pt, transp;
+ const S32 test_no = 5;
+ const F32 step = F_PI_BY_TWO / (test_no + 1);
+ for (S32 i = 0; i < test_no; ++i)
+ {
+ F32 cos_dir = cos ((i + 1) * step);
+ calcSkyColorInDir(br_pt, transp, move_vec(getToSunLast(), cos_dir));
+ const F32 br = color_max(br_pt);
+ if (br > mBrightnessScaleGuess)
+ {
+ mBrightnessScaleGuess = br;
+ mBrightestPointGuess = br_pt;
+ }
+ }
+}
+
+
+void LLVOSky::calcBrightnessScaleAndColors()
+{
+ // new correct normalization.
+ if (mBrightnessScaleNew < 1e-7)
+ {
+ mBrightnessScale = 1;
+ mBrightestPoint.setToBlack();
+ }
+ else
+ {
+ mBrightnessScale = 1.f/mBrightnessScaleNew;
+ mBrightestPoint = mBrightestPointNew;
+ }
+
+ mBrightnessScaleNew = 0;
+ // and addition
+
+ // Calculate Sun and Moon color
+ const F32 h = llmax(0.0f, mCameraPosAgent.mV[2]);
+ const LLColor3 sun_color = mSun.getIntensity() * mTransp.calcTransp(getToSunLast().mV[2], h);
+ const LLColor3 moon_color = mNightColorShift *
+ mMoon.getIntensity() * mTransp.calcTransp(getToMoonLast().mV[2], h);
+
+ F32 intens = color_intens(sun_color);
+ F32 increase_sun_br = (intens > 0) ? 1.2f * color_intens(mBrightestPoint) / intens : 1;
+
+ intens = color_intens(moon_color);
+ F32 increase_moon_br = (intens > 0) ? 1.2f * llmax(1.0f, color_intens(mBrightestPoint) / intens) : 1;
+
+ mSun.setColor(mBrightnessScale * increase_sun_br * sun_color);
+ mMoon.setColor(mBrightnessScale * increase_moon_br * moon_color);
+
+ const LLColor3 haze_col = color_norm_abs(mHaze.getSigSca());
+ for (S32 i = 0; i < 6; ++i)
+ {
+ mSkyTex[i].create(mBrightnessScale, mHazeConcentration * mBrightestPoint * haze_col);
+ }
+
+ mBrightnessScaleGuess = mBrightnessScale;
+ mBrightestPointGuess = mBrightestPoint;
+
+// calculateColors(); // MSMSM Moving this down to before generateScatterMap(), per Milo Lindens suggestion, to fix orange flashing bug.
+
+ mSun.renewDirection();
+ mSun.renewColor();
+ mMoon.renewDirection();
+ mMoon.renewColor();
+
+ LLColor3 transp;
+
+ if (calcHitsEarth(mCameraPosAgent, getToSunLast()) < 0)
+ {
+ calcSkyColorInDir(mBrightestPointGuess, transp, getToSunLast());
+ mBrightnessScaleGuess = color_max(mBrightestPointGuess);
+ updateBrightestDir();
+ mBrightnessScaleGuess = 1.f / llmax(1.0f, mBrightnessScaleGuess);
+ }
+ else if (getToSunLast().mV[2] > -0.5)
+ {
+ const LLVector3 almost_to_sun = toHorizon(getToSunLast());
+ calcSkyColorInDir(mBrightestPointGuess, transp, almost_to_sun);
+ mBrightnessScaleGuess = color_max(mBrightestPointGuess);
+ updateBrightestDir();
+ mBrightnessScaleGuess = 1.f / llmax(1.0f, mBrightnessScaleGuess);
+ }
+ else
+ {
+ mBrightestPointGuess.setToBlack();
+ mBrightnessScaleGuess = 1;
+ }
+
+ calculateColors(); // MSMSM Moved this down here per Milo Lindens suggestion, to fix orange flashing bug at sunset.
+ generateScatterMap();
+}
+
+
+
+void LLVOSky::calculateColors()
+{
+ const F32 h = -0.1f;
+ const LLVector3& tosun = getToSunLast();
+
+ F32 full_on, full_off, on, on_cl;
+ F32 sun_factor = 1;
+
+ // Sun Diffuse
+ if (calcHitsEarth(mCameraPosAgent, tosun) < 0)
+ {
+ mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(tosun.mV[2], h);
+ }
+ else
+ {
+ mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(0, h);
+ }
+ mSunDiffuse = 1.0f * color_norm(mSunDiffuse);
+
+ // Sun Ambient
+ full_off = -0.3f;
+ full_on = -0.03f;
+ if (tosun.mV[2] < full_off)
+ {
+ mSunAmbient.setToBlack();
+ }
+ else
+ {
+ on = (tosun.mV[2] - full_off) / (full_on - full_off);
+ sun_factor = llmax(0.0f, llmin(on, 1.0f));
+
+ LLColor3 sun_amb = mAmbientScale * (0.8f * mSunDiffuse +
+ 0.2f * mBrightnessScaleGuess * mBrightestPointGuess);
+
+ color_norm_pow(sun_amb, 0.1f, TRUE);
+ sun_factor *= min_intens_factor(sun_amb, 1.9f);
+ mSunAmbient = LLColor4(sun_factor * sun_amb);
+ }
+
+
+ // Moon Diffuse
+ full_on = 0.3f;
+ full_off = 0.01f;
+ if (getToMoonLast().mV[2] < full_off)
+ {
+ mMoonDiffuse.setToBlack();
+ }
+ else
+ {
+ // Steve: Added moonlight diffuse factor scalar (was constant .3)
+ F32 diffuse_factor = .1f + sNighttimeBrightness * .2f; // [.1, .5] default = .3
+ on = (getToMoonLast().mV[2] - full_off) / (full_on - full_off);
+ on_cl = llmin(on, 1.0f);
+ mMoonDiffuse = on_cl * mNightColorShift * diffuse_factor;
+ }
+
+ // Moon Ambient
+
+ F32 moon_amb_factor = 1.f;
+
+ if (gAgent.inPrelude())
+ {
+ moon_amb_factor *= 2.0f;
+ }
+
+ full_on = 0.30f;
+ full_off = 0.01f;
+ if (getToMoonLast().mV[2] < full_off)
+ {
+ mMoonAmbient.setToBlack();
+ }
+ else
+ {
+ on = (getToMoonLast().mV[2] - full_off) / (full_on - full_off);
+ on_cl = llmax(0.0f, llmin(on, 1.0f));
+ mMoonAmbient = on_cl * moon_amb_factor * mMoonDiffuse;
+ }
+
+
+ // Sun Diffuse
+ full_off = -0.05f;
+ full_on = -0.00f;
+ if (tosun.mV[2] < full_off)
+ {
+ mSunDiffuse.setToBlack();
+ }
+ else
+ {
+ on = (getToSunLast().mV[2] - full_off) / (full_on - full_off);
+ sun_factor = llmax(0.0f, llmin(on, 1.0f));
+
+ color_norm_pow(mSunDiffuse, 0.12f, TRUE);
+ sun_factor *= min_intens_factor(mSunDiffuse, 2.1f);
+ mSunDiffuse *= sun_factor;
+ }
+
+
+ mTotalAmbient = mSunAmbient + mMoonAmbient;
+ mTotalAmbient.setAlpha(1);
+ //llinfos << "MoonDiffuse: " << mMoonDiffuse << llendl;
+ //llinfos << "TotalAmbient: " << mTotalAmbient << llendl;
+
+ mFadeColor = mTotalAmbient + (mSunDiffuse + mMoonDiffuse) * 0.5f;
+ mFadeColor.setAlpha(0);
+}
+
+
+BOOL LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ return TRUE;
+}
+
+BOOL LLVOSky::updateSky()
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)))
+ {
+ return TRUE;
+ }
+
+ if (mDead)
+ {
+ // It's dead. Don't update it.
+ return TRUE;
+ }
+ if (gGLManager.mIsDisabled)
+ {
+ return TRUE;
+ }
+
+ setPositionAgent(gAgent.getCameraPositionAgent());
+
+ static S32 next_frame = 0;
+ const S32 total_no_tiles = 6 * NUM_TILES;
+ const S32 cycle_frame_no = total_no_tiles + 1;
+
+// if (mUpdateTimer.getElapsedTimeF32() > 0.1f)
+ {
+ mUpdateTimer.reset();
+ const S32 frame = next_frame;
+
+ ++next_frame;
+ next_frame = next_frame % cycle_frame_no;
+
+ sInterpVal = (!mInitialized) ? 1 : (F32)next_frame / cycle_frame_no;
+ LLSkyTex::setInterpVal( sInterpVal );
+ LLHeavenBody::setInterpVal( sInterpVal );
+ calculateColors();
+ if (mForceUpdate || total_no_tiles == frame)
+ {
+ calcBrightnessScaleAndColors();
+ LLSkyTex::stepCurrent();
+
+ const static F32 LIGHT_DIRECTION_THRESHOLD = (F32) cos(DEG_TO_RAD * 1.f);
+ const static F32 COLOR_CHANGE_THRESHOLD = 0.01f;
+
+ LLVector3 direction = mSun.getDirection();
+ direction.normVec();
+ const F32 dot_lighting = direction * mLastLightingDirection;
+
+ LLColor3 delta_color;
+ delta_color.setVec(mLastTotalAmbient.mV[0] - mTotalAmbient.mV[0],
+ mLastTotalAmbient.mV[1] - mTotalAmbient.mV[1],
+ mLastTotalAmbient.mV[2] - mTotalAmbient.mV[2]);
+
+ if ( mForceUpdate
+ || ((dot_lighting < LIGHT_DIRECTION_THRESHOLD)
+ || (delta_color.magVec() > COLOR_CHANGE_THRESHOLD)
+ || !mInitialized)
+ && !direction.isExactlyZero())
+ {
+ mLastLightingDirection = direction;
+ mLastTotalAmbient = mTotalAmbient;
+ mInitialized = TRUE;
+
+ if (mCubeMap)
+ {
+ if (mForceUpdate)
+ {
+ updateFog(gCamera->getFar());
+ for (int side = 0; side < 6; side++)
+ {
+ for (int tile = 0; tile < NUM_TILES; tile++)
+ {
+ createSkyTexture(side, tile);
+ }
+ }
+
+ calcBrightnessScaleAndColors();
+
+ for (int side = 0; side < 6; side++)
+ {
+ LLImageRaw* raw1 = mSkyTex[side].getImageRaw(TRUE);
+ LLImageRaw* raw2 = mSkyTex[side].getImageRaw(FALSE);
+ raw2->copy(raw1);
+ mSkyTex[side].createTexture(mSkyTex[side].getWhich(FALSE));
+ }
+ next_frame = 0;
+ //llSkyTex::stepCurrent();
+ }
+
+ std::vector<LLPointer<LLImageRaw> > images;
+ for (S32 side = 0; side < 6; side++)
+ {
+ images.push_back(mSkyTex[side].getImageRaw(FALSE));
+ }
+ mCubeMap->init(images);
+ }
+ }
+
+ mForceUpdate = FALSE;
+ }
+ else
+ {
+ const S32 side = frame / NUM_TILES;
+ const S32 tile = frame % NUM_TILES;
+ createSkyTexture(side, tile);
+ }
+ }
+
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+
+void LLVOSky::updateTextures(LLAgent &agent)
+{
+ if (mSunTexturep)
+ {
+ mSunTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
+ mMoonTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
+ mBloomTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
+ }
+}
+
+LLDrawable *LLVOSky::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ LLDrawPoolSky *poolp = (LLDrawPoolSky*) gPipeline.getPool(LLDrawPool::POOL_SKY);
+ poolp->setSkyTex(mSkyTex);
+ poolp->setSun(&mSun);
+ poolp->setMoon(&mMoon);
+
+ for (S32 i = 0; i < 6; ++i)
+ {
+ mFace[FACE_SIDE0 + i] = mDrawable->addFace(poolp, NULL);
+ }
+
+ mFace[FACE_SUN] = mDrawable->addFace(poolp, mSunTexturep);
+ mFace[FACE_MOON] = mDrawable->addFace(poolp, mMoonTexturep);
+ mFace[FACE_BLOOM] = mDrawable->addFace(poolp, mBloomTexturep);
+
+ //mDrawable->addFace(poolp, LLViewerImage::sDefaultImagep);
+ gPipeline.markMaterialed(mDrawable);
+
+ return mDrawable;
+}
+
+BOOL LLVOSky::updateGeometry(LLDrawable *drawable)
+{
+ if (mFace[FACE_REFLECTION] == NULL)
+ {
+ mFace[FACE_REFLECTION] = drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_WATER), NULL);
+ }
+
+ mCameraPosAgent = drawable->getPositionAgent();
+ mEarthCenter.mV[0] = mCameraPosAgent.mV[0];
+ mEarthCenter.mV[1] = mCameraPosAgent.mV[1];
+
+
+ LLVector3 v_agent[8];
+ for (S32 i = 0; i < 8; ++i)
+ {
+ F32 x_sgn = (i&1) ? 1.f : -1.f;
+ F32 y_sgn = (i&2) ? 1.f : -1.f;
+ F32 z_sgn = (i&4) ? 1.f : -1.f;
+ v_agent[i] = mCameraPosAgent + HORIZON_DIST * LLVector3(x_sgn, y_sgn, z_sgn);
+ }
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face;
+
+ for (S32 side = 0; side < 6; ++side)
+ {
+ face = mFace[FACE_SIDE0 + side];
+ face->setPrimType(LLTriangles);
+ face->setSize(4, 6);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ S32 vtx = 0;
+ S32 curr_bit = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
+ S32 side_dir = side & 1; // even - 0, odd - 1
+ S32 i_bit = (curr_bit + 2) % 3;
+ S32 j_bit = (i_bit + 2) % 3;
+
+ LLVector3 axis;
+ axis.mV[curr_bit] = 1;
+ face->mCenterAgent = mCameraPosAgent + (F32)((side_dir << 1) - 1) * axis * HORIZON_DIST;
+
+ vtx = side_dir << curr_bit;
+ *(verticesp++) = v_agent[vtx];
+ *(verticesp++) = v_agent[vtx | 1 << j_bit];
+ *(verticesp++) = v_agent[vtx | 1 << i_bit];
+ *(verticesp++) = v_agent[vtx | 1 << i_bit | 1 << j_bit];
+
+ *(texCoordsp++) = TEX00;
+ *(texCoordsp++) = TEX01;
+ *(texCoordsp++) = TEX10;
+ *(texCoordsp++) = TEX11;
+
+ // Triangles for each side
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+ }
+
+ const LLVector3 &look_at = gCamera->getAtAxis();
+ LLVector3 right = look_at % LLVector3::z_axis;
+ LLVector3 up = right % look_at;
+ right.normVec();
+ up.normVec();
+
+ const static F32 elevation_factor = 0.0f/sResolution;
+ const F32 cos_max_angle = cosHorizon(elevation_factor);
+ mSun.setDraw(updateHeavenlyBodyGeometry(drawable, FACE_SUN, TRUE, mSun, cos_max_angle, up, right));
+ mMoon.setDraw(updateHeavenlyBodyGeometry(drawable, FACE_MOON, FALSE, mMoon, cos_max_angle, up, right));
+
+ const F32 water_height = gAgent.getRegion()->getWaterHeight() + 0.01f;
+ // gWorldPointer->getWaterHeight() + 0.01f;
+ const F32 camera_height = mCameraPosAgent.mV[2];
+ const F32 height_above_water = camera_height - water_height;
+
+ BOOL sun = FALSE;
+
+ if (mSun.isVisible())
+ {
+ if (mMoon.isVisible())
+ {
+ sun = look_at * mSun.getDirection() > 0;
+ }
+ else
+ {
+ sun = TRUE;
+ }
+ }
+
+ if (height_above_water > 0)
+ {
+#if 1 //1.9.1
+ BOOL render_ref = gPipeline.getPool(LLDrawPool::POOL_WATER)->getVertexShaderLevel() == 0;
+#else
+ BOOL render_ref = !(gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) >= LLDrawPoolWater::SHADER_LEVEL_RIPPLE);
+#endif
+ if (sun)
+ {
+ setDrawRefl(0);
+ if (render_ref)
+ {
+ updateReflectionGeometry(drawable, height_above_water, mSun);
+ }
+ }
+ else
+ {
+ setDrawRefl(1);
+ if (render_ref)
+ {
+ updateReflectionGeometry(drawable, height_above_water, mMoon);
+ }
+ }
+ }
+ else
+ {
+ setDrawRefl(-1);
+ }
+
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+
+BOOL LLVOSky::updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 f, const BOOL is_sun,
+ LLHeavenBody& hb, const F32 cos_max_angle,
+ const LLVector3 &up, const LLVector3 &right)
+{
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *facep;
+
+ LLVector3 to_dir = hb.getDirection();
+ LLVector3 draw_pos = to_dir * HEAVENLY_BODY_DIST;
+
+
+ LLVector3 hb_right = to_dir % LLVector3::z_axis;
+ LLVector3 hb_up = hb_right % to_dir;
+ hb_right.normVec();
+ hb_up.normVec();
+
+ //const static F32 cos_max_turn = sqrt(3.f) / 2; // 30 degrees
+ //const F32 cos_turn_right = 1. / (llmax(cos_max_turn, hb_right * right));
+ //const F32 cos_turn_up = 1. / llmax(cos_max_turn, hb_up * up);
+
+ const F32 enlargm_factor = ( 1 - to_dir.mV[2] );
+ F32 horiz_enlargement = 1 + enlargm_factor * 0.3f;
+ F32 vert_enlargement = 1 + enlargm_factor * 0.2f;
+
+ // Parameters for the water reflection
+ hb.setU(HEAVENLY_BODY_FACTOR * horiz_enlargement * hb.getDiskRadius() * hb_right);
+ hb.setV(HEAVENLY_BODY_FACTOR * vert_enlargement * hb.getDiskRadius() * hb_up);
+ // End of parameters for the water reflection
+
+ const LLVector3 scaled_right = HEAVENLY_BODY_DIST * hb.getU();
+ const LLVector3 scaled_up = HEAVENLY_BODY_DIST * hb.getV();
+
+ //const LLVector3 scaled_right = horiz_enlargement * HEAVENLY_BODY_SCALE * hb.getDiskRadius() * hb_right;//right;
+ //const LLVector3 scaled_up = vert_enlargement * HEAVENLY_BODY_SCALE * hb.getDiskRadius() * hb_up;//up;
+ LLVector3 v_clipped[4];
+
+ hb.corner(0) = draw_pos - scaled_right + scaled_up;
+ hb.corner(1) = draw_pos - scaled_right - scaled_up;
+ hb.corner(2) = draw_pos + scaled_right + scaled_up;
+ hb.corner(3) = draw_pos + scaled_right - scaled_up;
+
+
+ F32 t_left, t_right;
+ if (!clip_quad_to_horizon(t_left, t_right, v_clipped, hb.corners(), cos_max_angle))
+ {
+ hb.setVisible(FALSE);
+ return FALSE;
+ }
+ hb.setVisible(TRUE);
+
+ facep = mFace[f];
+ facep->setPrimType(LLTriangles);
+ facep->setSize(4, 6);
+ index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ hb.corner(vtx) = v_clipped[vtx];
+ *(verticesp++) = hb.corner(vtx) + mCameraPosAgent;
+ }
+
+ *(texCoordsp++) = TEX01;
+ *(texCoordsp++) = TEX00;
+ //*(texCoordsp++) = (t_left > 0) ? LLVector2(0, t_left) : TEX00;
+ *(texCoordsp++) = TEX11;
+ *(texCoordsp++) = TEX10;
+ //*(texCoordsp++) = (t_right > 0) ? LLVector2(1, t_right) : TEX10;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+
+ if (is_sun)
+ {
+ if ((t_left > 0) && (t_right > 0))
+ {
+ F32 t = (t_left + t_right) * 0.5f;
+ mSun.setHorizonVisibility(0.5f * (1 + cos(t * F_PI)));
+ }
+ else
+ {
+ mSun.setHorizonVisibility();
+ }
+ updateSunHaloGeometry(drawable);
+ }
+
+ return TRUE;
+}
+
+
+
+
+// Clips quads with top and bottom sides parallel to horizon.
+
+BOOL clip_quad_to_horizon(F32& t_left, F32& t_right, LLVector3 v_clipped[4],
+ const LLVector3 v_corner[4], const F32 cos_max_angle)
+{
+ t_left = clip_side_to_horizon(v_corner[1], v_corner[0], cos_max_angle);
+ t_right = clip_side_to_horizon(v_corner[3], v_corner[2], cos_max_angle);
+
+ if ((t_left >= 1) || (t_right >= 1))
+ {
+ return FALSE;
+ }
+
+ //const BOOL left_clip = (t_left > 0);
+ //const BOOL right_clip = (t_right > 0);
+
+ //if (!left_clip && !right_clip)
+ {
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ v_clipped[vtx] = v_corner[vtx];
+ }
+ }
+/* else
+ {
+ v_clipped[0] = v_corner[0];
+ v_clipped[1] = left_clip ? ((1 - t_left) * v_corner[1] + t_left * v_corner[0])
+ : v_corner[1];
+ v_clipped[2] = v_corner[2];
+ v_clipped[3] = right_clip ? ((1 - t_right) * v_corner[3] + t_right * v_corner[2])
+ : v_corner[3];
+ }*/
+
+ return TRUE;
+}
+
+
+F32 clip_side_to_horizon(const LLVector3& V0, const LLVector3& V1, const F32 cos_max_angle)
+{
+ const LLVector3 V = V1 - V0;
+ const F32 k2 = 1.f/(cos_max_angle * cos_max_angle) - 1;
+ const F32 A = V.mV[0] * V.mV[0] + V.mV[1] * V.mV[1] - k2 * V.mV[2] * V.mV[2];
+ const F32 B = V0.mV[0] * V.mV[0] + V0.mV[1] * V.mV[1] - k2 * V0.mV[2] * V.mV[2];
+ const F32 C = V0.mV[0] * V0.mV[0] + V0.mV[1] * V0.mV[1] - k2 * V0.mV[2] * V0.mV[2];
+
+ if (fabs(A) < 1e-7)
+ {
+ return -0.1f; // v0 is cone origin and v1 is on the surface of the cone.
+ }
+
+ const F32 det = sqrt(B*B - A*C);
+ const F32 t1 = (-B - det) / A;
+ const F32 t2 = (-B + det) / A;
+ const F32 z1 = V0.mV[2] + t1 * V.mV[2];
+ const F32 z2 = V0.mV[2] + t2 * V.mV[2];
+ if (z1 * cos_max_angle < 0)
+ {
+ return t2;
+ }
+ else if (z2 * cos_max_angle < 0)
+ {
+ return t1;
+ }
+ else if ((t1 < 0) || (t1 > 1))
+ {
+ return t2;
+ }
+ else
+ {
+ return t1;
+ }
+}
+
+
+void LLVOSky::updateSunHaloGeometry(LLDrawable *drawable )
+{
+ const LLVector3* v_corner = mSun.corners();
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face;
+
+ const LLVector3 right = 2 * (v_corner[2] - v_corner[0]);
+ LLVector3 up = 2 * (v_corner[2] - v_corner[3]);
+ up.normVec();
+ F32 size = right.magVec();
+ up = size * up;
+ const LLVector3 draw_pos = 0.25 * (v_corner[0] + v_corner[1] + v_corner[2] + v_corner[3]);
+
+ LLVector3 v_glow_corner[4];
+
+ v_glow_corner[0] = draw_pos - right + up;
+ v_glow_corner[1] = draw_pos - right - up;
+ v_glow_corner[2] = draw_pos + right + up;
+ v_glow_corner[3] = draw_pos + right - up;
+
+ face = mFace[FACE_BLOOM];
+ face->setPrimType(LLTriangles);
+ face->setSize(4, 6);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ *(verticesp++) = v_glow_corner[vtx] + mCameraPosAgent;
+ }
+
+ *(texCoordsp++) = TEX01;
+ *(texCoordsp++) = TEX00;
+ *(texCoordsp++) = TEX11;
+ *(texCoordsp++) = TEX10;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+}
+
+
+F32 dtReflection(const LLVector3& p, F32 cos_dir_from_top, F32 sin_dir_from_top, F32 diff_angl_dir)
+{
+ LLVector3 P = p;
+ P.normVec();
+
+ const F32 cos_dir_angle = -P.mV[VZ];
+ const F32 sin_dir_angle = sqrt(1 - cos_dir_angle * cos_dir_angle);
+
+ F32 cos_diff_angles = cos_dir_angle * cos_dir_from_top
+ + sin_dir_angle * sin_dir_from_top;
+
+ F32 diff_angles;
+ if (cos_diff_angles > (1 - 1e-7))
+ diff_angles = 0;
+ else
+ diff_angles = acos(cos_diff_angles);
+
+ const F32 rel_diff_angles = diff_angles / diff_angl_dir;
+ const F32 dt = 1 - rel_diff_angles;
+
+ return (dt < 0) ? 0 : dt;
+}
+
+
+F32 dtClip(const LLVector3& v0, const LLVector3& v1, F32 far_clip2)
+{
+ F32 dt_clip;
+ const LLVector3 otrezok = v1 - v0;
+ const F32 A = otrezok.magVecSquared();
+ const F32 B = v0 * otrezok;
+ const F32 C = v0.magVecSquared() - far_clip2;
+ const F32 det = sqrt(B*B - A*C);
+ dt_clip = (-B - det) / A;
+ if ((dt_clip < 0) || (dt_clip > 1))
+ dt_clip = (-B + det) / A;
+ return dt_clip;
+}
+
+
+void LLVOSky::updateReflectionGeometry(LLDrawable *drawable, F32 H,
+ const LLHeavenBody& HB)
+{
+ const LLVector3 &look_at = gCamera->getAtAxis();
+ // const F32 water_height = gAgent.getRegion()->getWaterHeight() + 0.001f;
+ // gWorldPointer->getWaterHeight() + 0.001f;
+
+ LLVector3 to_dir = HB.getDirection();
+ LLVector3 hb_pos = to_dir * (HORIZON_DIST - 10);
+ LLVector3 to_dir_proj = to_dir;
+ to_dir_proj.mV[VZ] = 0;
+ to_dir_proj.normVec();
+
+ LLVector3 Right = to_dir % LLVector3::z_axis;
+ LLVector3 Up = Right % to_dir;
+ Right.normVec();
+ Up.normVec();
+
+ // finding angle between look direction and sprite.
+ LLVector3 look_at_right = look_at % LLVector3::z_axis;
+ look_at_right.normVec();
+
+ const static F32 cos_horizon_angle = cosHorizon(0.0f/sResolution);
+ //const static F32 horizon_angle = acos(cos_horizon_angle);
+
+ const F32 enlargm_factor = ( 1 - to_dir.mV[2] );
+ F32 horiz_enlargement = 1 + enlargm_factor * 0.3f;
+ F32 vert_enlargement = 1 + enlargm_factor * 0.2f;
+
+ F32 vert_size = vert_enlargement * HEAVENLY_BODY_SCALE * HB.getDiskRadius();
+ Right *= /*cos_lookAt_toDir */ horiz_enlargement * HEAVENLY_BODY_SCALE * HB.getDiskRadius();
+ Up *= vert_size;
+
+ LLVector3 v_corner[2];
+ LLVector3 stretch_corner[2];
+
+ LLVector3 top_hb = v_corner[0] = stretch_corner[0] = hb_pos - Right + Up;
+ v_corner[1] = stretch_corner[1] = hb_pos - Right - Up;
+
+ F32 dt_hor, dt;
+ dt_hor = clip_side_to_horizon(v_corner[1], v_corner[0], cos_horizon_angle);
+
+ LLVector2 TEX0t = TEX00;
+ LLVector2 TEX1t = TEX10;
+ LLVector3 lower_corner = v_corner[1];
+
+ if ((dt_hor > 0) && (dt_hor < 1))
+ {
+ TEX0t = LLVector2(0, dt_hor);
+ TEX1t = LLVector2(1, dt_hor);
+ lower_corner = (1 - dt_hor) * v_corner[1] + dt_hor * v_corner[0];
+ }
+ else
+ dt_hor = llmax(0.0f, llmin(1.0f, dt_hor));
+
+ top_hb.normVec();
+ const F32 cos_angle_of_view = fabs(top_hb.mV[VZ]);
+ const F32 extension = llmin (5.0f, 1.0f / cos_angle_of_view);
+
+ const S32 cols = 1;
+ const S32 raws = lltrunc(16 * extension);
+ S32 quads = cols * raws;
+
+ stretch_corner[0] = lower_corner + extension * (stretch_corner[0] - lower_corner);
+ stretch_corner[1] = lower_corner + extension * (stretch_corner[1] - lower_corner);
+
+ dt = dt_hor;
+
+
+ F32 cos_dir_from_top[2];
+
+ LLVector3 dir = stretch_corner[0];
+ dir.normVec();
+ cos_dir_from_top[0] = dir.mV[VZ];
+
+ dir = stretch_corner[1];
+ dir.normVec();
+ cos_dir_from_top[1] = dir.mV[VZ];
+
+ const F32 sin_dir_from_top = sqrt(1 - cos_dir_from_top[0] * cos_dir_from_top[0]);
+ const F32 sin_dir_from_top2 = sqrt(1 - cos_dir_from_top[1] * cos_dir_from_top[1]);
+ const F32 cos_diff_dir = cos_dir_from_top[0] * cos_dir_from_top[1]
+ + sin_dir_from_top * sin_dir_from_top2;
+ const F32 diff_angl_dir = acos(cos_diff_dir);
+
+ v_corner[0] = stretch_corner[0];
+ v_corner[1] = lower_corner;
+
+
+ LLVector2 TEX0tt = TEX01;
+ LLVector2 TEX1tt = TEX11;
+
+ LLVector3 v_refl_corner[4];
+ LLVector3 v_sprite_corner[4];
+
+ S32 vtx;
+ for (vtx = 0; vtx < 2; ++vtx)
+ {
+ LLVector3 light_proj = v_corner[vtx];
+ light_proj.normVec();
+
+ const F32 z = light_proj.mV[VZ];
+ const F32 sin_angle = sqrt(1 - z * z);
+ light_proj *= 1.f / sin_angle;
+ light_proj.mV[VZ] = 0;
+ const F32 to_refl_point = H * sin_angle / fabs(z);
+
+ v_refl_corner[vtx] = to_refl_point * light_proj;
+ }
+
+
+ for (vtx = 2; vtx < 4; ++vtx)
+ {
+ const LLVector3 to_dir_vec = (to_dir_proj * v_refl_corner[vtx-2]) * to_dir_proj;
+ v_refl_corner[vtx] = v_refl_corner[vtx-2] + 2 * (to_dir_vec - v_refl_corner[vtx-2]);
+ }
+
+ for (vtx = 0; vtx < 4; ++vtx)
+ v_refl_corner[vtx].mV[VZ] -= H;
+
+ S32 side = 0;
+ LLVector3 refl_corn_norm[2];
+ refl_corn_norm[0] = v_refl_corner[1];
+ refl_corn_norm[0].normVec();
+ refl_corn_norm[1] = v_refl_corner[3];
+ refl_corn_norm[1].normVec();
+
+ F32 cos_refl_look_at[2];
+ cos_refl_look_at[0] = refl_corn_norm[0] * look_at;
+ cos_refl_look_at[1] = refl_corn_norm[1] * look_at;
+
+ if (cos_refl_look_at[1] > cos_refl_look_at[0])
+ {
+ side = 2;
+ }
+
+ //const F32 far_clip = (gCamera->getFar() - 0.01) / far_clip_factor;
+ const F32 far_clip = 512;
+ const F32 far_clip2 = far_clip*far_clip;
+
+ F32 dt_clip;
+ F32 vtx_near2, vtx_far2;
+
+ if ((vtx_far2 = v_refl_corner[side].magVecSquared()) > far_clip2)
+ {
+ // whole thing is sprite: reflection is beyond far clip plane.
+ dt_clip = 1.1f;
+ quads = 1;
+ }
+ else if ((vtx_near2 = v_refl_corner[side+1].magVecSquared()) > far_clip2)
+ {
+ // part is reflection, the rest is sprite.
+ dt_clip = dtClip(v_refl_corner[side + 1], v_refl_corner[side], far_clip2);
+ const LLVector3 P = (1 - dt_clip) * v_refl_corner[side + 1] + dt_clip * v_refl_corner[side];
+
+ F32 dt_tex = dtReflection(P, cos_dir_from_top[0], sin_dir_from_top, diff_angl_dir);
+
+ dt = dt_tex;
+ TEX0tt = LLVector2(0, dt);
+ TEX1tt = LLVector2(1, dt);
+ quads++;
+ }
+ else
+ {
+ // whole thing is correct reflection.
+ dt_clip = -0.1f;
+ }
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face = mFace[FACE_REFLECTION];
+
+ face->setPrimType(LLTriangles);
+ face->setSize(quads * 4, quads * 6);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ LLColor3 hb_col3 = HB.getInterpColor();
+ hb_col3.clamp();
+ const LLColor4 hb_col = LLColor4(hb_col3);
+
+ const F32 min_attenuation = 0.4f;
+ const F32 max_attenuation = 0.7f;
+ const F32 attenuation = min_attenuation
+ + cos_angle_of_view * (max_attenuation - min_attenuation);
+
+ LLColor4 hb_refl_col = (1-attenuation) * hb_col + attenuation * mFogColor;
+ face->setFaceColor(hb_refl_col);
+
+ LLVector3 v_far[2];
+ v_far[0] = v_refl_corner[1];
+ v_far[1] = v_refl_corner[3];
+
+ if(dt_clip > 0)
+ {
+ if (dt_clip >= 1)
+ {
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ F32 ratio = far_clip / v_refl_corner[vtx].magVec();
+ *(verticesp++) = v_refl_corner[vtx] = ratio * v_refl_corner[vtx] + mCameraPosAgent;
+ }
+ const LLVector3 draw_pos = 0.25 *
+ (v_refl_corner[0] + v_refl_corner[1] + v_refl_corner[2] + v_refl_corner[3]);
+ face->mCenterAgent = draw_pos;
+ }
+ else
+ {
+ F32 ratio = far_clip / v_refl_corner[1].magVec();
+ v_sprite_corner[1] = v_refl_corner[1] * ratio;
+
+ ratio = far_clip / v_refl_corner[3].magVec();
+ v_sprite_corner[3] = v_refl_corner[3] * ratio;
+
+ v_refl_corner[1] = (1 - dt_clip) * v_refl_corner[1] + dt_clip * v_refl_corner[0];
+ v_refl_corner[3] = (1 - dt_clip) * v_refl_corner[3] + dt_clip * v_refl_corner[2];
+ v_sprite_corner[0] = v_refl_corner[1];
+ v_sprite_corner[2] = v_refl_corner[3];
+
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ *(verticesp++) = v_sprite_corner[vtx] + mCameraPosAgent;
+ }
+
+ const LLVector3 draw_pos = 0.25 *
+ (v_refl_corner[0] + v_sprite_corner[1] + v_refl_corner[2] + v_sprite_corner[3]);
+ face->mCenterAgent = draw_pos;
+ }
+
+ *(texCoordsp++) = TEX0tt;
+ *(texCoordsp++) = TEX0t;
+ *(texCoordsp++) = TEX1tt;
+ *(texCoordsp++) = TEX1t;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+
+ index_offset += 4;
+ }
+
+ if (dt_clip < 1)
+ {
+ if (dt_clip <= 0)
+ {
+ const LLVector3 draw_pos = 0.25 *
+ (v_refl_corner[0] + v_refl_corner[1] + v_refl_corner[2] + v_refl_corner[3]);
+ face->mCenterAgent = draw_pos;
+ }
+
+ const F32 raws_inv = 1.f/raws;
+ const F32 cols_inv = 1.f/cols;
+ LLVector3 left = v_refl_corner[0] - v_refl_corner[1];
+ LLVector3 right = v_refl_corner[2] - v_refl_corner[3];
+ left *= raws_inv;
+ right *= raws_inv;
+
+ F32 dt_raw = dt;
+
+ for (S32 raw = 0; raw < raws; ++raw)
+ {
+ F32 dt_v0 = raw * raws_inv;
+ F32 dt_v1 = (raw + 1) * raws_inv;
+ const LLVector3 BL = v_refl_corner[1] + (F32)raw * left;
+ const LLVector3 BR = v_refl_corner[3] + (F32)raw * right;
+ const LLVector3 EL = BL + left;
+ const LLVector3 ER = BR + right;
+ dt_v0 = dt_raw;
+ dt_raw = dt_v1 = dtReflection(EL, cos_dir_from_top[0], sin_dir_from_top, diff_angl_dir);
+ for (S32 col = 0; col < cols; ++col)
+ {
+ F32 dt_h0 = col * cols_inv;
+ *(verticesp++) = (1 - dt_h0) * EL + dt_h0 * ER + mCameraPosAgent;
+ *(verticesp++) = (1 - dt_h0) * BL + dt_h0 * BR + mCameraPosAgent;
+ F32 dt_h1 = (col + 1) * cols_inv;
+ *(verticesp++) = (1 - dt_h1) * EL + dt_h1 * ER + mCameraPosAgent;
+ *(verticesp++) = (1 - dt_h1) * BL + dt_h1 * BR + mCameraPosAgent;
+
+ *(texCoordsp++) = LLVector2(dt_h0, dt_v1);
+ *(texCoordsp++) = LLVector2(dt_h0, dt_v0);
+ *(texCoordsp++) = LLVector2(dt_h1, dt_v1);
+ *(texCoordsp++) = LLVector2(dt_h1, dt_v0);
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+
+ index_offset += 4;
+ }
+ }
+ }
+}
+
+
+
+
+void LLVOSky::updateFog(const F32 distance)
+{
+ if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG))
+ {
+ /*gGLSFog.addCap(GL_FOG, FALSE);
+ gGLSPipeline.addCap(GL_FOG, FALSE);
+ gGLSPipelineAlpha.addCap(GL_FOG, FALSE);
+ gGLSPipelinePixieDust.addCap(GL_FOG, FALSE);
+ gGLSPipelineSelection.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatar.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaOnePass.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaPass1.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaPass2.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaPass3.addCap(GL_FOG, FALSE);*/
+ glFogf(GL_FOG_DENSITY, 0);
+ glFogfv(GL_FOG_COLOR, (F32 *) &LLColor4::white.mV);
+ glFogf(GL_FOG_END, 1000000.f);
+ return;
+ }
+ else
+ {
+ /*gGLSFog.addCap(GL_FOG, TRUE);
+ gGLSPipeline.addCap(GL_FOG, TRUE);
+ gGLSPipelineAlpha.addCap(GL_FOG, TRUE);
+ gGLSPipelinePixieDust.addCap(GL_FOG, TRUE);
+ gGLSPipelineSelection.addCap(GL_FOG, TRUE);
+ if (!gGLManager.mIsATI)
+ {
+ gGLSPipelineAvatar.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaOnePass.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaPass1.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaPass2.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaPass3.addCap(GL_FOG, TRUE);
+ }*/
+ }
+
+ const BOOL hide_clip_plane = TRUE;
+ LLColor4 target_fog(0.f, 0.2f, 0.5f, 0.f);
+
+ const F32 water_height = gAgent.getRegion()->getWaterHeight();
+ // gWorldPointer->getWaterHeight();
+ F32 camera_height = gAgent.getCameraPositionAgent().mV[2];
+
+ F32 near_clip_height = gCamera->getAtAxis().mV[VZ] * gCamera->getNear();
+ camera_height += near_clip_height;
+
+ F32 fog_distance = 0.f;
+ LLColor3 res_color[3];
+
+ LLColor3 sky_fog_color = LLColor3::white;
+ LLColor3 render_fog_color = LLColor3::white;
+
+ LLColor3 transp;
+ LLVector3 tosun = getToSunLast();
+ const F32 tosun_z = tosun.mV[VZ];
+ tosun.mV[VZ] = 0.f;
+ tosun.normVec();
+ LLVector3 perp_tosun;
+ perp_tosun.mV[VX] = -tosun.mV[VY];
+ perp_tosun.mV[VY] = tosun.mV[VX];
+ LLVector3 tosun_45 = tosun + perp_tosun;
+ tosun_45.normVec();
+
+ F32 delta = 0.06f;
+ tosun.mV[VZ] = delta;
+ perp_tosun.mV[VZ] = delta;
+ tosun_45.mV[VZ] = delta;
+ tosun.normVec();
+ perp_tosun.normVec();
+ tosun_45.normVec();
+
+ // Sky colors, just slightly above the horizon in the direction of the sun, perpendicular to the sun, and at a 45 degree angle to the sun.
+ calcSkyColorInDir(res_color[0],transp, tosun);
+ calcSkyColorInDir(res_color[1],transp, perp_tosun);
+ calcSkyColorInDir(res_color[2],transp, tosun_45);
+
+ sky_fog_color = color_norm(res_color[0] + res_color[1] + res_color[2]);
+
+ F32 full_off = -0.25f;
+ F32 full_on = 0.00f;
+ F32 on = (tosun_z - full_off) / (full_on - full_off);
+ on = llclamp(on, 0.01f, 1.f);
+ sky_fog_color *= 0.5f * on;
+
+
+ // We need to clamp these to non-zero, in order for the gamma correction to work. 0^y = ???
+ S32 i;
+ for (i = 0; i < 3; i++)
+ {
+ sky_fog_color.mV[i] = llmax(0.0001f, sky_fog_color.mV[i]);
+ }
+
+ color_gamma_correct(sky_fog_color);
+
+ if (!(gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) > LLDrawPool::SHADER_LEVEL_SCATTERING))
+ {
+ render_fog_color = sky_fog_color;
+ }
+
+ if (camera_height > water_height)
+ {
+ fog_distance = mFogRatio * distance;
+ LLColor4 fog(render_fog_color);
+ glFogfv(GL_FOG_COLOR, fog.mV);
+ }
+ else
+ {
+ // Interpolate between sky fog and water fog...
+ F32 depth = water_height - camera_height;
+ F32 depth_frac = 1.f/(1.f + 200.f*depth);
+ F32 color_frac = 1.f/(1.f + 0.5f* depth)* 0.2f;
+ fog_distance = (mFogRatio * distance) * depth_frac + 30.f * (1.f-depth_frac);
+ fog_distance = llmin(75.f, fog_distance);
+
+ F32 brightness = 1.f/(1.f + 0.05f*depth);
+ F32 sun_brightness = getSunDiffuseColor().magVec() * 0.3f;
+ brightness = llmin(1.f, brightness);
+ brightness = llmin(brightness, sun_brightness);
+ color_frac = llmin(0.7f, color_frac);
+
+ LLColor4 fogCol = brightness * (color_frac * render_fog_color + (1.f - color_frac) * LLColor4(0.f, 0.2f, 0.3f, 1.f));
+ fogCol.setAlpha(1);
+ glFogfv(GL_FOG_COLOR, (F32 *) &fogCol.mV);
+ }
+
+ mFogColor = sky_fog_color;
+ mFogColor.setAlpha(1);
+ LLGLSFog gls_fog;
+
+ F32 fog_density;
+ if (hide_clip_plane)
+ {
+ // For now, set the density to extend to the cull distance.
+ const F32 f_log = 2.14596602628934723963618357029f; // sqrt(fabs(log(0.01f)))
+ fog_density = f_log/fog_distance;
+ glFogi(GL_FOG_MODE, GL_EXP2);
+ }
+ else
+ {
+ const F32 f_log = 4.6051701859880913680359829093687f; // fabs(log(0.01f))
+ fog_density = (f_log)/fog_distance;
+ glFogi(GL_FOG_MODE, GL_EXP);
+ }
+
+ glFogf(GL_FOG_END, fog_distance*2.2f);
+
+ glFogf(GL_FOG_DENSITY, fog_density);
+
+ glHint(GL_FOG_HINT, GL_NICEST);
+ stop_glerror();
+}
+
+// static
+void LLHaze::initClass()
+{
+ sAirScaSeaLevel = LLHaze::calcAirScaSeaLevel();
+}
+
+
+
+// Functions used a lot.
+
+
+F32 color_norm_pow(LLColor3& col, F32 e, BOOL postmultiply)
+{
+ F32 mv = color_max(col);
+ if (0 == mv)
+ {
+ return 0;
+ }
+
+ col *= 1.f / mv;
+ color_pow(col, e);
+ if (postmultiply)
+ {
+ col *= mv;
+ }
+ return mv;
+}
+
+// Returns angle (RADIANs) between the horizontal projection of "v" and the x_axis.
+// Range of output is 0.0f to 2pi //359.99999...f
+// Returns 0.0f when "v" = +/- z_axis.
+F32 azimuth(const LLVector3 &v)
+{
+ F32 azimuth = 0.0f;
+ if (v.mV[VX] == 0.0f)
+ {
+ if (v.mV[VY] > 0.0f)
+ {
+ azimuth = F_PI * 0.5f;
+ }
+ else if (v.mV[VY] < 0.0f)
+ {
+ azimuth = F_PI * 1.5f;// 270.f;
+ }
+ }
+ else
+ {
+ azimuth = (F32) atan(v.mV[VY] / v.mV[VX]);
+ if (v.mV[VX] < 0.0f)
+ {
+ azimuth += F_PI;
+ }
+ else if (v.mV[VY] < 0.0f)
+ {
+ azimuth += F_PI * 2;
+ }
+ }
+ return azimuth;
+}
+
+
+#if 0
+// Not currently used
+LLColor3 LLVOSky::calcGroundFog(LLColor3& transp, const LLVector3 &view_dir, F32 obj_dist) const
+{
+ LLColor3 col;
+ calcGroundFog(col, transp, view_dir, obj_dist);
+ col *= mBrightnessScaleGuess;
+ return col;
+}
+#endif
+
+void LLVOSky::setSunDirection(const LLVector3 &sun_dir, const LLVector3 &sun_ang_velocity)
+{
+ LLVector3 sun_direction = (sun_dir.magVec() == 0) ? LLVector3::x_axis : sun_dir;
+ sun_direction.normVec();
+ F32 dp = mSun.getDirection() * sun_direction;
+ mSun.setDirection(sun_direction);
+ mSun.setAngularVelocity(sun_ang_velocity);
+ mMoon.setDirection(-sun_direction);
+ if (dp < 0.995f) { //the sun jumped a great deal, update immediately
+ updateHaze();
+ mWeatherChange = FALSE;
+ mForceUpdate = TRUE;
+ }
+ else if (mWeatherChange && (mSun.getDirection().mV[VZ] > -0.5) )
+ {
+ updateHaze();
+ init();
+ mWeatherChange = FALSE;
+ }
+ else if (mSun.getDirection().mV[VZ] < -0.5)
+ {
+ mWeatherChange = TRUE;
+ }
+}
+
+#define INV_WAVELENGTH_R_POW4 (1.f/0.2401f) // = 1/0.7^4
+#define INV_WAVELENGTH_G_POW4 (1.f/0.0789f) // = 1/0.53^4
+#define INV_WAVELENGTH_B_POW4 (1.f/0.03748f) // = 1/0.44^4
+
+// Dummy class for globals used below. Replace when KILLERSKY is merged in.
+class LLKillerSky
+{
+public:
+ static F32 sRaleighGroundDensity;
+ static F32 sMieFactor;
+ static F32 sNearFalloffFactor;
+ static F32 sSkyContrib;
+
+ static void getRaleighCoefficients(float eye_sun_dp, float density, float *coefficients)
+ {
+ float dp = eye_sun_dp;
+ float angle_dep = density*(1 + dp*dp);
+ coefficients[0] = angle_dep * INV_WAVELENGTH_R_POW4;
+ coefficients[1] = angle_dep * INV_WAVELENGTH_G_POW4;
+ coefficients[2] = angle_dep * INV_WAVELENGTH_B_POW4;
+ }
+
+ static void getMieCoefficients(float eye_sun_dp, float density, float *coefficient)
+ {
+ // TOTALLY ARBITRARY FUNCTION. Seems to work though
+ // If anyone can replace this with some *actual* mie function, that'd be great
+ float dp = eye_sun_dp;
+ float dp_highpower = dp*dp;
+ float angle_dep = density * (llclamp(dp_highpower*dp, 0.f, 1.f) + 0.4f);
+ *coefficient = angle_dep;
+ }
+};
+
+F32 LLKillerSky::sRaleighGroundDensity = 0.013f;
+F32 LLKillerSky::sMieFactor = 50;
+F32 LLKillerSky::sNearFalloffFactor = 1.5f;
+F32 LLKillerSky::sSkyContrib = 0.06f;
+
+void LLVOSky::generateScatterMap()
+{
+ float raleigh[3], mie;
+
+ mScatterMap = new LLImageGL(FALSE);
+ mScatterMapRaw = new LLImageRaw(256, 256, 4);
+ U8 *data = mScatterMapRaw->getData();
+
+ F32 light_brightness = gSky.getSunDirection().mV[VZ]+0.1f;
+ LLColor4 light_color;
+ LLColor4 sky_color;
+ if (light_brightness > 0)
+ {
+ F32 interp = sqrtf(light_brightness);
+ light_brightness = sqrt(sqrtf(interp));
+ light_color = lerp(gSky.getSunDiffuseColor(), LLColor4(1,1,1,1), interp) * light_brightness;
+ sky_color = lerp(LLColor4(0,0,0,0), LLColor4(0.4f, 0.6f, 1.f, 1.f), light_brightness)*LLKillerSky::sSkyContrib;
+ }
+ else
+ {
+ light_brightness = /*0.3f*/sqrt(-light_brightness);
+ light_color = gSky.getMoonDiffuseColor() * light_brightness;
+ sky_color = LLColor4(0,0,0,1);
+ }
+
+ // x = distance [0..1024m]
+ // y = dot product [-1,1]
+ for (int y=0;y<256;y++)
+ {
+ // Accumulate outward
+ float accum_r = 0, accum_g = 0, accum_b = 0;
+
+ float dp = (((float)y)/255.f)*1.95f - 0.975f;
+ U8 *scanline = &data[y*256*4];
+ for (int x=0;x<256;x++)
+ {
+ float dist = ((float)x+1)*4; // x -> 2048
+
+ float raleigh_density = LLKillerSky::sRaleighGroundDensity * 0.05f; // Arbitrary? Perhaps...
+ float mie_density = raleigh_density*LLKillerSky::sMieFactor;
+
+ float extinction_factor = dist/LLKillerSky::sNearFalloffFactor;
+
+ LLKillerSky::getRaleighCoefficients(dp, raleigh_density, raleigh);
+ LLKillerSky::getMieCoefficients(dp, mie_density, &mie);
+
+ float falloff_r = pow(llclamp(0.985f-raleigh[0],0.f,1.f), extinction_factor);
+ float falloff_g = pow(llclamp(0.985f-raleigh[1],0.f,1.f), extinction_factor);
+ float falloff_b = pow(llclamp(0.985f-raleigh[2],0.f,1.f), extinction_factor);
+
+ float light_r = light_color.mV[0] * (raleigh[0]+mie+sky_color.mV[0]) * falloff_r;
+ float light_g = light_color.mV[1] * (raleigh[1]+mie+sky_color.mV[1]) * falloff_g;
+ float light_b = light_color.mV[2] * (raleigh[2]+mie+sky_color.mV[2]) * falloff_b;
+
+ accum_r += light_r;
+ accum_g += light_g;
+ accum_b += light_b;
+
+ scanline[x*4] = (U8)llclamp(accum_r*255.f, 0.f, 255.f);
+ scanline[x*4+1] = (U8)llclamp(accum_g*255.f, 0.f, 255.f);
+ scanline[x*4+2] = (U8)llclamp(accum_b*255.f, 0.f, 255.f);
+ float alpha = ((falloff_r+falloff_g+falloff_b)*0.33f);
+ scanline[x*4+3] = (U8)llclamp(alpha*255.f, 0.f, 255.f); // Avg falloff
+
+ // Output color Co, Input color Ci, Map channels Mrgb, Ma:
+ // Co = (Ci * Ma) + Mrgb
+ }
+ }
+
+ mScatterMap->createGLTexture(0, mScatterMapRaw);
+ mScatterMap->bind(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+#if 0
+// Not currently used
+void LLVOSky::calcGroundFog(LLColor3& res, LLColor3& transp, const LLVector3 view_dir, F32 obj_dist) const
+{
+ const LLVector3& tosun = getToSunLast();//use_old_value ? sunDir() : toSunLast();
+ res.setToBlack();
+ transp.setToWhite();
+ const F32 dist = obj_dist * mWorldScale;
+
+ //LLVector3 view_dir = gCamera->getAtAxis();
+
+ const F32 cos_dir = view_dir * tosun;
+ LLVector3 dir = view_dir;
+ LLVector3 virtual_P = mCameraPosAgent + dist * dir;
+
+ if (dir.mV[VZ] < 0)
+ {
+ dir = -dir;
+ }
+
+ const F32 z_dir = dir.mV[2];
+
+ const F32 h = mCameraPosAgent.mV[2];
+
+ transp = color_div(mTransp.calcTransp(dir * calcUpVec(virtual_P), 0),
+ mTransp.calcTransp(z_dir, h));
+
+ if (calcHitsEarth(mCameraPosAgent, tosun) > 0)
+ {
+ const F32 avg = llmin(1.f, 1.2f * color_avg(transp));
+ transp = LLColor3(avg, avg, avg);
+ return;
+ }
+
+ LLColor3 haze_sca_opt_depth = mHaze.getSigSca();
+ LLColor3 sun_transp;
+ mTransp.calcTransp(tosun.mV[2], -0.1f, sun_transp);
+
+ res = calcAirPhaseFunc(cos_dir) * LLHaze::getAirScaSeaLevel();
+ res += mHaze.calcPhase(cos_dir) * mHaze.getSigSca();
+ res = mSun.getIntensity() * dist * sun_transp * res;
+}
+
+#endif
diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h
new file mode 100644
index 0000000000..1c819ebcd8
--- /dev/null
+++ b/indra/newview/llvosky.h
@@ -0,0 +1,907 @@
+/**
+ * @file llvosky.h
+ * @brief LLVOSky class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOSKY_H
+#define LL_LLVOSKY_H
+
+#include "stdtypes.h"
+#include "v3color.h"
+#include "v4coloru.h"
+#include "llviewerimage.h"
+#include "llviewerobject.h"
+#include "llframetimer.h"
+
+
+//////////////////////////////////
+//
+// Lots of constants
+//
+// Will clean these up at some point...
+//
+
+const F32 HORIZON_DIST = 1024.0f;
+const F32 HEAVENLY_BODY_DIST = HORIZON_DIST - 10.f;
+const F32 HEAVENLY_BODY_FACTOR = 0.1f;
+const F32 HEAVENLY_BODY_SCALE = HEAVENLY_BODY_DIST * HEAVENLY_BODY_FACTOR;
+const F32 EARTH_RADIUS = 6.4e6f; // exact radius = 6.37 x 10^6 m
+const F32 ATM_EXP_FALLOFF = 0.000126f;
+const F32 ATM_SEA_LEVEL_NDENS = 2.55e25f;
+// Somewhat arbitrary:
+const F32 ATM_HEIGHT = 100000.f;
+
+const F32 FIRST_STEP = 5000.f;
+const F32 INV_FIRST_STEP = 1.f/FIRST_STEP;
+const S32 NO_STEPS = 15;
+const F32 INV_NO_STEPS = 1.f/NO_STEPS;
+
+
+// constants used in calculation of scattering coeff of clear air
+const F32 sigma = 0.035f;
+const F32 fsigma = (6+3*sigma)/(6.f-7.f*sigma);
+const F64 Ndens = 2.55e25;
+const F64 Ndens2 = Ndens*Ndens;
+
+// !!! FIXME: This #define should be in llcommon somewhere...
+#ifdef __GNUC__
+#define __forceinline inline __attribute__((always_inline))
+#endif
+
+__forceinline LLColor3 color_div(const LLColor3 &col1, const LLColor3 &col2)
+{
+ return LLColor3(
+ col1.mV[0] / col2.mV[0],
+ col1.mV[1] / col2.mV[1],
+ col1.mV[2] / col2.mV[2] );
+}
+
+LLColor3 color_norm(const LLColor3 &col);
+LLVector3 move_vec (const LLVector3& v, F32 cos_max_angle);
+BOOL clip_quad_to_horizon(F32& t_left, F32& t_right, LLVector3 v_clipped[4],
+ const LLVector3 v_corner[4], const F32 cos_max_angle);
+F32 clip_side_to_horizon(const LLVector3& v0, const LLVector3& v1, const F32 cos_max_angle);
+
+inline F32 color_intens ( const LLColor3 &col )
+{
+ return col.mV[0] + col.mV[1] + col.mV[2];
+}
+
+inline F32 color_max(const LLColor3 &col)
+{
+ return llmax(col.mV[0], col.mV[1], col.mV[2]);
+}
+
+inline F32 color_max(const LLColor4 &col)
+{
+ return llmax(col.mV[0], col.mV[1], col.mV[2]);
+}
+
+
+inline F32 color_min(const LLColor3 &col)
+{
+ return llmin(col.mV[0], col.mV[1], col.mV[2]);
+}
+
+inline LLColor3 color_norm_abs(const LLColor3 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 1e-6)
+ {
+ return 1.f/m * col;
+ }
+ else return col;
+}
+
+
+
+class LLFace;
+class LLHaze;
+
+
+class LLSkyTex
+{
+ friend class LLVOSky;
+private:
+ static S32 sResolution;
+ static S32 sComponents;
+ LLPointer<LLImageGL> mImageGL[2];
+ LLPointer<LLImageRaw> mImageRaw[2];
+ LLColor3 *mSkyData;
+ LLVector3 *mSkyDirs; // Cache of sky direction vectors
+ static S32 sCurrent;
+ static F32 sInterpVal;
+
+public:
+ static F32 getInterpVal() { return sInterpVal; }
+ static void setInterpVal(const F32 v) { sInterpVal = v; }
+ static BOOL doInterpolate() { return sInterpVal > 0.001; }
+
+ void bindTexture(BOOL curr = TRUE);
+
+protected:
+ LLSkyTex();
+ void init();
+ void cleanupGL();
+ void restoreGL();
+
+ ~LLSkyTex();
+
+
+ static S32 getResolution() { return sResolution; }
+ static S32 getCurrent() { return sCurrent; }
+ static S32 stepCurrent() { return (sCurrent = ++sCurrent % 2); }
+ static S32 getNext() { return ((sCurrent+1) % 2); }
+ static S32 getWhich(const BOOL curr) { return curr ? sCurrent : getNext(); }
+
+ void initEmpty(const S32 tex);
+ void create(F32 brightness_scale, const LLColor3& multiscatt);
+
+ void setDir(const LLVector3 &dir, const S32 i, const S32 j)
+ {
+ S32 offset = i * sResolution + j;
+ mSkyDirs[offset] = dir;
+ }
+
+ const LLVector3 &getDir(const S32 i, const S32 j) const
+ {
+ S32 offset = i * sResolution + j;
+ return mSkyDirs[offset];
+ }
+
+ void setPixel(const LLColor3 &col, const S32 i, const S32 j)
+ {
+ S32 offset = i * sResolution + j;
+ mSkyData[offset] = col;
+ }
+
+ void setPixel(const LLColor4U &col, const S32 i, const S32 j)
+ {
+ S32 offset = (i * sResolution + j) * sComponents;
+ U32* pix = (U32*) &(mImageRaw[sCurrent]->getData()[offset]);
+ *pix = col.mAll;
+ }
+
+ LLColor4U getPixel(const S32 i, const S32 j)
+ {
+ LLColor4U col;
+ S32 offset = (i * sResolution + j) * sComponents;
+ U32* pix = (U32*) &(mImageRaw[sCurrent]->getData()[offset]);
+ col.mAll = *pix;
+ return col;
+ }
+
+ LLImageRaw* getImageRaw(BOOL curr=TRUE) { return mImageRaw[getWhich(curr)]; }
+ void createTexture(BOOL curr=TRUE);
+};
+
+
+class LLHeavenBody
+{
+protected:
+ LLVector3 mDirectionCached; // hack for events that shouldn't happen every frame
+
+ LLColor3 mColor;
+ LLColor3 mColorCached;
+ F32 mIntensity;
+ LLVector3 mDirection; // direction of the local heavenly body
+ LLVector3 mAngularVelocity; // velocity of the local heavenly body
+
+ F32 mDiskRadius;
+ BOOL mDraw; // FALSE - do not draw.
+ F32 mHorizonVisibility; // number [0, 1] due to how horizon
+ F32 mVisibility; // same but due to other objects being in frong.
+ BOOL mVisible;
+ static F32 sInterpVal;
+ LLVector3 mQuadCorner[4];
+ LLVector3 mU;
+ LLVector3 mV;
+ LLVector3 mO;
+
+public:
+ LLHeavenBody(const F32 rad) :
+ mDirectionCached(LLVector3(0,0,0)), mDirection(LLVector3(0,0,0)),
+ mDiskRadius(rad), mDraw(FALSE),
+ mHorizonVisibility(1), mVisibility(1)
+
+ {
+ mColor.setToBlack();
+ mColorCached.setToBlack();
+ }
+ ~LLHeavenBody() {}
+
+ const LLVector3& getDirection() const { return mDirection; }
+ void setDirection(const LLVector3 &direction) { mDirection = direction; }
+ void setAngularVelocity(const LLVector3 &ang_vel) { mAngularVelocity = ang_vel; }
+ const LLVector3& getAngularVelocity() const { return mAngularVelocity; }
+
+ const LLVector3& getDirectionCached() const { return mDirectionCached; }
+ void renewDirection() { mDirectionCached = mDirection; }
+
+ const LLColor3& getColorCached() const { return mColorCached; }
+ void setColorCached(const LLColor3& c) { mColorCached = c; }
+ const LLColor3& getColor() const { return mColor; }
+ void setColor(const LLColor3& c) { mColor = c; }
+
+ void renewColor() { mColorCached = mColor; }
+
+ static F32 interpVal() { return sInterpVal; }
+ static void setInterpVal(const F32 v) { sInterpVal = v; }
+
+ LLColor3 getInterpColor() const
+ {
+ return sInterpVal * mColor + (1 - sInterpVal) * mColorCached;
+ }
+
+// LLColor3 getDiffuseColor() const
+// {
+// LLColor3 dif = mColorCached;
+// dif.clamp();
+// return 2 * dif;
+// }
+
+// LLColor4 getAmbientColor(const LLColor3& scatt, F32 scale) const
+// {
+// const F32 min_val = 0.05f;
+// LLColor4 col = LLColor4(scale * (0.8f * color_norm_abs(getDiffuseColor()) + 0.2f * scatt));
+// //F32 left = max(0, 1 - col.mV[0]);
+// if (col.mV[0] >= 0.9)
+// {
+// col.mV[1] = llmax(col.mV[1], 2.f * min_val);
+// col.mV[2] = llmax(col.mV[2], min_val);
+// }
+// col.setAlpha(1.f);
+// return col;
+// }
+
+ const F32& getHorizonVisibility() const { return mHorizonVisibility; }
+ void setHorizonVisibility(const F32 c = 1) { mHorizonVisibility = c; }
+ const F32& getVisibility() const { return mVisibility; }
+ void setVisibility(const F32 c = 1) { mVisibility = c; }
+ const F32 getHaloBrighness() const
+ {
+ return llmax(0.f, llmin(0.9f, mHorizonVisibility)) * mVisibility;
+ }
+ BOOL isVisible() const { return mVisible; }
+ void setVisible(const BOOL v) { mVisible = v; }
+
+
+ const F32& getIntensity() const { return mIntensity; }
+ void setIntensity(const F32 c) { mIntensity = c; }
+
+ void setDiskRadius(const F32 radius) { mDiskRadius = radius; }
+ F32 getDiskRadius() const { return mDiskRadius; }
+
+ void setDraw(const BOOL draw) { mDraw = draw; }
+ BOOL getDraw() const { return mDraw; }
+
+ const LLVector3& corner(const S32 n) const { return mQuadCorner[n]; }
+ LLVector3& corner(const S32 n) { return mQuadCorner[n]; }
+ const LLVector3* corners() const { return mQuadCorner; }
+
+ const LLVector3& getU() const { return mU; }
+ const LLVector3& getV() const { return mV; }
+ void setU(const LLVector3& u) { mU = u; }
+ void setV(const LLVector3& v) { mV = v; }
+};
+
+
+__forceinline LLColor3 refr_ind_calc(const LLColor3 &wave_length)
+{
+ LLColor3 refr_ind;
+ for (S32 i = 0; i < 3; ++i)
+ {
+ const F32 wl2 = wave_length.mV[i] * wave_length.mV[i] * 1e-6f;
+ refr_ind.mV[i] = 6.43e3f + ( 2.95e6f / ( 146.0f - 1.f/wl2 ) ) + ( 2.55e4f / ( 41.0f - 1.f/wl2 ) );
+ refr_ind.mV[i] *= 1.0e-8f;
+ refr_ind.mV[i] += 1.f;
+ }
+ return refr_ind;
+}
+
+
+__forceinline LLColor3 calc_air_sca_sea_level()
+{
+ const static LLColor3 WAVE_LEN(675, 520, 445);
+ const static LLColor3 refr_ind = refr_ind_calc(WAVE_LEN);
+ const static LLColor3 n21 = refr_ind * refr_ind - LLColor3(1, 1, 1);
+ const static LLColor3 n4 = n21 * n21;
+ const static LLColor3 wl2 = WAVE_LEN * WAVE_LEN * 1e-6f;
+ const static LLColor3 wl4 = wl2 * wl2;
+ const static LLColor3 mult_const = fsigma * 2.0f/ 3.0f * 1e24f * (F_PI * F_PI) * n4;
+ const static F32 dens_div_N = F32( ATM_SEA_LEVEL_NDENS / Ndens2);
+ return dens_div_N * color_div ( mult_const, wl4 );
+}
+
+const LLColor3 gAirScaSeaLevel = calc_air_sca_sea_level();
+const F32 AIR_SCA_INTENS = color_intens(gAirScaSeaLevel);
+const F32 AIR_SCA_AVG = AIR_SCA_INTENS / 3;
+
+class LLHaze
+{
+public:
+ LLHaze() : mG(0), mFalloff(1) {mSigSca.setToBlack();}
+ LLHaze(const F32 g, const LLColor3& sca, const F32 fo = 2) :
+ mG(g), mSigSca(0.25f/F_PI * sca), mFalloff(fo), mAbsCoef(0)
+ {
+ mAbsCoef = color_intens(mSigSca) / AIR_SCA_INTENS;
+ }
+
+ LLHaze(const F32 g, const F32 sca, const F32 fo = 2) : mG(g),
+ mSigSca(0.25f/F_PI * LLColor3(sca, sca, sca)), mFalloff(fo)
+ {
+ mAbsCoef = 0.01f * sca / AIR_SCA_AVG;
+ }
+
+ static void initClass();
+
+
+ F32 getG() const { return mG; }
+
+ void setG(const F32 g)
+ {
+ mG = g;
+ }
+
+ const LLColor3& getSigSca() const // sea level
+ {
+ return mSigSca;
+ }
+
+ void setSigSca(const LLColor3& s)
+ {
+ mSigSca = s;
+ mAbsCoef = 0.01f * color_intens(mSigSca) / AIR_SCA_INTENS;
+ }
+
+ void setSigSca(const F32 s0, const F32 s1, const F32 s2)
+ {
+ mSigSca = AIR_SCA_AVG * LLColor3 (s0, s1, s2);
+ mAbsCoef = 0.01f * (s0 + s1 + s2) / 3;
+ }
+
+ F32 getFalloff() const
+ {
+ return mFalloff;
+ }
+
+ void setFalloff(const F32 fo)
+ {
+ mFalloff = fo;
+ }
+
+ F32 getAbsCoef() const
+ {
+ return mAbsCoef;
+ }
+
+ inline static F32 calcFalloff(const F32 h)
+ {
+ return (h <= 0) ? 1.0f : (F32)LL_FAST_EXP(-ATM_EXP_FALLOFF * h);
+ }
+
+ inline LLColor3 calcSigSca(const F32 h) const
+ {
+ return calcFalloff(h * mFalloff) * mSigSca;
+ }
+
+ inline void calcSigSca(const F32 h, LLColor3 &result) const
+ {
+ result = mSigSca;
+ result *= calcFalloff(h * mFalloff);
+ }
+
+ LLColor3 calcSigExt(const F32 h) const
+ {
+ return calcFalloff(h * mFalloff) * (1 + mAbsCoef) * mSigSca;
+ }
+
+ F32 calcPhase(const F32 cos_theta) const;
+
+ static inline LLColor3 calcAirSca(const F32 h);
+ static inline void calcAirSca(const F32 h, LLColor3 &result);
+ static LLColor3 calcAirScaSeaLevel() { return gAirScaSeaLevel; }
+ static const LLColor3 &getAirScaSeaLevel() { return sAirScaSeaLevel; }
+public:
+ static LLColor3 sAirScaSeaLevel;
+
+protected:
+ F32 mG;
+ LLColor3 mSigSca;
+ F32 mFalloff; // 1 - slow, >1 - faster
+ F32 mAbsCoef;
+};
+
+class LLTranspMap
+{
+public:
+ LLTranspMap() : mElevation(0), mMaxAngle(0), mStep(5), mHaze(NULL), mT(NULL) {}
+ ~LLTranspMap()
+ {
+ delete[] mT;
+ mT = NULL;
+ }
+
+ void init(const F32 elev, const F32 step, const F32 h, const LLHaze* const haze);
+
+ F32 calcHeight(const LLVector3& pos) const
+ {
+ return pos.magVec() - EARTH_RADIUS ;
+ }
+
+ BOOL hasHaze() const
+ {
+ return mHaze != NULL;
+ }
+
+ LLColor3 calcSigExt(const F32 h) const
+ {
+ return LLHaze::calcAirSca(h) + (hasHaze() ? mHaze->calcSigExt(h) : LLColor3(0, 0, 0));
+ }
+
+ inline void calcAirTransp(const F32 cos_angle, LLColor3 &result) const;
+ LLColor3 calcAirTranspDir(const F32 elevation, const LLVector3 &dir) const;
+ LLColor3 getHorizonAirTransp () const { return mT[mMapSize-1]; }
+ F32 hitsAtmEdge(const LLVector3& orig, const LLVector3& dir) const;
+
+protected:
+ F32 mAtmHeight;
+ F32 mElevation;
+ F32 mMaxAngle;
+ F32 mCosMaxAngle;
+ F32 mStep;
+ F32 mStepInv;
+ S32 mMapSize;
+ const LLHaze *mHaze;
+ LLColor3 *mT; // transparency values in all directions
+ //starting with mAngleBelowHorz at mElevation
+};
+
+class LLTranspMapSet
+{
+protected:
+ F32 *mHeights;
+ LLTranspMap *mTransp;
+ S32 mSize;
+ F32 mMediaHeight;
+ const LLHaze *mHaze;
+ S32 lerp(F32& dt, S32& indx, const F32 h) const;
+public:
+ LLTranspMapSet() : mHeights(NULL), mTransp(NULL), mHaze(NULL) {}
+ ~LLTranspMapSet();
+
+ void init (S32 size, F32 first_step, F32 media_height, const LLHaze* const haze);
+ S32 getSize() const { return mSize; }
+ F32 getMediaHeight() const { return mMediaHeight; }
+ const LLTranspMap& getLastTransp() const { return mTransp[mSize-1]; }
+ F32 getLastHeight() const { return mHeights[mSize-1]; }
+ const LLTranspMap& getMap(const S32 n) const { return mTransp[n]; }
+ F32 getHeight(const S32 n) const { return mHeights[n]; }
+ BOOL isReady() const { return mTransp != NULL; }
+
+ inline LLColor3 calcTransp(const F32 cos_angle, const F32 h) const;
+ inline void calcTransp(const F32 cos_angle, const F32 h, LLColor3 &result) const;
+};
+
+class LLCubeMap;
+
+
+class LLVOSky : public LLViewerObject
+{
+public:
+ enum
+ {
+ FACE_SIDE0,
+ FACE_SIDE1,
+ FACE_SIDE2,
+ FACE_SIDE3,
+ FACE_SIDE4,
+ FACE_SIDE5,
+ FACE_SUN, // was 6
+ FACE_MOON, // was 7
+ FACE_BLOOM, // was 8
+ FACE_REFLECTION, // was 10
+ FACE_COUNT
+ };
+
+ LLVOSky(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOSky();
+
+ // Initialize/delete data that's only inited once per class.
+ static void initClass();
+ void init();
+ void initCubeMap();
+ void initEmpty();
+ BOOL isReady() const { return mTransp.isReady(); }
+ const LLTranspMapSet& getTransp() const { return mTransp; }
+
+ void cleanupGL();
+ void restoreGL();
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ BOOL updateSky();
+
+ // Graphical stuff for objects - maybe broken out into render class
+ // later?
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void initSkyTextureDirs(const S32 side, const S32 tile);
+ void createSkyTexture(const S32 side, const S32 tile);
+
+ void updateBrightestDir();
+ void calcBrightnessScaleAndColors();
+
+ LLColor3 calcSkyColorInDir(const LLVector3& dir);
+ void calcSkyColorInDir(LLColor3& res, LLColor3& transp,
+ const LLVector3& dir) const;
+ LLColor4 calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exag) const;
+ void calcInScatter( LLColor3& res, LLColor3& transp,
+ const LLVector3& P, F32 exag) const;
+
+ // Not currently used.
+ //LLColor3 calcGroundFog(LLColor3& transp, const LLVector3 &view_dir, F32 obj_dist) const;
+ //void calcGroundFog(LLColor3& res, LLColor3& transp, const LLVector3 view_dir, F32 dist) const;
+
+ LLColor3 calcRadianceAtPoint(const LLVector3& pos) const
+ {
+ const F32 cos_angle = calcUpVec(pos) * getToSunLast();
+ LLColor3 tr;
+ mTransp.calcTransp(cos_angle, calcHeight(pos), tr);
+ return mBrightnessScaleGuess * mSun.getIntensity() * tr;
+ }
+
+ const LLHeavenBody& getSun() const { return mSun; }
+ const LLHeavenBody& getMoon() const { return mMoon; }
+
+ const LLVector3& getToSunLast() const { return mSun.getDirectionCached(); }
+ const LLVector3& getToSun() const { return mSun.getDirection(); }
+ const LLVector3& getToMoon() const { return mMoon.getDirection(); }
+ const LLVector3& getToMoonLast() const { return mMoon.getDirectionCached(); }
+ BOOL isSunUp() const { return mSun.getDirectionCached().mV[2] > -0.05f; }
+ void calculateColors();
+
+ LLColor3 getSunDiffuseColor() const { return mSunDiffuse; }
+ LLColor3 getMoonDiffuseColor() const { return mMoonDiffuse; }
+ LLColor4 getSunAmbientColor() const { return mSunAmbient; }
+ LLColor4 getMoonAmbientColor() const { return mMoonAmbient; }
+ const LLColor4& getTotalAmbientColor() const { return mTotalAmbient; }
+ LLColor4 getFogColor() const { return mFogColor; }
+
+ LLVector3 calcUpVec(const LLVector3 &pos) const
+ {
+ LLVector3 v = pos - mEarthCenter;
+ v.normVec();
+ return v;
+ }
+
+ F32 calcHeight(const LLVector3& pos) const
+ {
+ return dist_vec(pos, mEarthCenter) - EARTH_RADIUS;
+ }
+
+ // Phase function for atmospheric scattering.
+ // co = cos ( theta )
+ F32 calcAirPhaseFunc(const F32 co) const
+ {
+ return (0.75f * (1.f + co*co));
+ }
+
+
+ BOOL isSameFace(S32 idx, const LLFace* face) const { return mFace[idx] == face; }
+
+ void initSunDirection(const LLVector3 &sun_dir, const LLVector3 &sun_ang_velocity)
+ {
+ LLVector3 sun_direction = (sun_dir.magVec() == 0) ? LLVector3::x_axis : sun_dir;
+ sun_direction.normVec();
+ mSun.setDirection(sun_direction);
+ mSun.renewDirection();
+ mSun.setAngularVelocity(sun_ang_velocity);
+ mMoon.setDirection(-mSun.getDirection());
+ mMoon.renewDirection();
+ mLastLightingDirection = mSun.getDirection();
+
+ if ( !isReady() )
+ {
+ init();
+ LLSkyTex::stepCurrent();
+ }
+ }
+
+ void setSunDirection(const LLVector3 &sun_dir, const LLVector3 &sun_ang_velocity);
+
+ void updateHaze();
+
+ BOOL updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 side, const BOOL is_sun,
+ LLHeavenBody& hb, const F32 sin_max_angle,
+ const LLVector3 &up, const LLVector3 &right);
+
+ LLVector3 toHorizon(const LLVector3& dir, F32 delta = 0) const
+ {
+ return move_vec(dir, cosHorizon(delta));
+ }
+ F32 cosHorizon(const F32 delta = 0) const
+ {
+ const F32 sin_angle = EARTH_RADIUS/(EARTH_RADIUS + mCameraPosAgent.mV[2]);
+ return delta - (F32)sqrt(1.f - sin_angle * sin_angle);
+ }
+
+ void updateSunHaloGeometry(LLDrawable *drawable);
+ void updateReflectionGeometry(LLDrawable *drawable, F32 H, const LLHeavenBody& HB);
+
+
+ const LLHaze& getHaze() const { return mHaze; }
+ LLHaze& getHaze() { return mHaze; }
+ F32 getHazeConcentration() const { return mHazeConcentration; }
+ void setHaze(const LLHaze& h) { mHaze = h; }
+ F32 getWorldScale() const { return mWorldScale; }
+ void setWorldScale(const F32 s) { mWorldScale = s; }
+ void updateFog(const F32 distance);
+ void setFogRatio(const F32 fog_ratio) { mFogRatio = fog_ratio; }
+ LLColor4U getFadeColor() const { return mFadeColor; }
+ F32 getFogRatio() const { return mFogRatio; }
+ void setCloudDensity(F32 cloud_density) { mCloudDensity = cloud_density; }
+ void setWind ( const LLVector3& wind ) { mWind = wind.magVec(); }
+
+ const LLVector3 &getCameraPosAgent() const { return mCameraPosAgent; }
+ LLVector3 getEarthCenter() const { return mEarthCenter; }
+
+ LLCubeMap *getCubeMap() const { return mCubeMap; }
+ S32 getDrawRefl() const { return mDrawRefl; }
+ void setDrawRefl(const S32 r) { mDrawRefl = r; }
+ BOOL isReflFace(const LLFace* face) const { return face == mFace[FACE_REFLECTION]; }
+ LLFace* getReflFace() const { return mFace[FACE_REFLECTION]; }
+
+ F32 calcHitsEarth(const LLVector3& orig, const LLVector3& dir) const;
+ F32 calcHitsAtmEdge(const LLVector3& orig, const LLVector3& dir) const;
+ LLViewerImage* getSunTex() const { return mSunTexturep; }
+ LLViewerImage* getMoonTex() const { return mMoonTexturep; }
+ LLViewerImage* getBloomTex() const { return mBloomTexturep; }
+
+ void generateScatterMap();
+ LLImageGL* getScatterMap() { return mScatterMap; }
+
+public:
+ static F32 sNighttimeBrightness; // [0,2] default = 1.0
+
+protected:
+ LLPointer<LLViewerImage> mSunTexturep;
+ LLPointer<LLViewerImage> mMoonTexturep;
+ LLPointer<LLViewerImage> mBloomTexturep;
+
+ static S32 sResolution;
+ static S32 sTileResX;
+ static S32 sTileResY;
+ LLSkyTex mSkyTex[6];
+ LLHeavenBody mSun;
+ LLHeavenBody mMoon;
+ LLVector3 mSunDefaultPosition;
+ LLVector3 mSunAngVel;
+ F32 mAtmHeight;
+ LLVector3 mEarthCenter;
+ LLVector3 mCameraPosAgent;
+ F32 mBrightnessScale;
+ LLColor3 mBrightestPoint;
+ F32 mBrightnessScaleNew;
+ LLColor3 mBrightestPointNew;
+ F32 mBrightnessScaleGuess;
+ LLColor3 mBrightestPointGuess;
+ LLTranspMapSet mTransp;
+ LLHaze mHaze;
+ F32 mHazeConcentration;
+ BOOL mWeatherChange;
+ F32 mCloudDensity;
+ F32 mWind;
+ LLFace *mFace[FACE_COUNT];
+
+ BOOL mInitialized;
+ BOOL mForceUpdate; //flag to force instantaneous update of cubemap
+ LLVector3 mLastLightingDirection;
+ LLColor3 mLastTotalAmbient;
+ F32 mAmbientScale;
+ LLColor3 mNightColorShift;
+ F32 sInterpVal;
+
+ LLColor4 mFogColor;
+ F32 mFogRatio;
+ F32 mWorldScale;
+
+ LLColor4 mSunAmbient;
+ LLColor4 mMoonAmbient;
+ LLColor4 mTotalAmbient;
+ LLColor3 mSunDiffuse;
+ LLColor3 mMoonDiffuse;
+ LLColor4U mFadeColor; // Color to fade in from
+
+ LLCubeMap *mCubeMap; // Cube map for the sky
+ S32 mDrawRefl;
+
+ LLFrameTimer mUpdateTimer;
+
+ LLPointer<LLImageGL> mScatterMap;
+ LLPointer<LLImageRaw> mScatterMapRaw;
+};
+
+// Utility functions
+F32 azimuth(const LLVector3 &v);
+F32 color_norm_pow(LLColor3& col, F32 e, BOOL postmultiply = FALSE);
+
+
+/* Proportion of light that is scattered into 'path' from 'in' over distance dt. */
+/* assumes that vectors 'path' and 'in' are normalized. Scattering coef / 2pi */
+
+inline LLColor3 LLHaze::calcAirSca(const F32 h)
+{
+ static const LLColor3 air_sca_sea_level = calcAirScaSeaLevel();
+ return calcFalloff(h) * air_sca_sea_level;
+}
+
+inline void LLHaze::calcAirSca(const F32 h, LLColor3 &result)
+{
+ static const LLColor3 air_sca_sea_level = calcAirScaSeaLevel();
+ result = air_sca_sea_level;
+ result *= calcFalloff(h);
+}
+
+// Given cos of the angle between direction of interest and zenith,
+// compute transparency by interpolation of known values.
+inline void LLTranspMap::calcAirTransp(const F32 cos_angle, LLColor3 &result) const
+{
+ if (cos_angle > 1.f)
+ {
+ result = mT[0];
+ return;
+ }
+ if (cos_angle < mCosMaxAngle - 0.1f)
+ {
+ result.setVec(0.f, 0.f, 0.f);
+ return;
+ }
+ if (cos_angle < mCosMaxAngle)
+ {
+ result = mT[mMapSize-1];
+ return;
+ }
+
+
+ const F32 relative = (1 - cos_angle)*mStepInv;
+ const S32 index = llfloor(relative);
+ const F32 dt = relative - index;
+
+ if (index >= (mMapSize-1))
+ {
+ result = mT[0];
+ return;
+ }
+// result = mT[index];
+// LLColor3 res2(mT[index+1]);
+// result *= 1 - dt;
+// res2 *= dt;
+// result += res2;
+
+ const LLColor3& color1 = mT[index];
+ const LLColor3& color2 = mT[index + 1];
+
+ const F32 x1 = color1.mV[VX];
+ const F32 x2 = color2.mV[VX];
+ result.mV[VX] = x1 - dt * (x1 - x2);
+
+ const F32 y1 = color1.mV[VY];
+ const F32 y2 = color2.mV[VY];
+ result.mV[VY] = y1 - dt * (y1 - y2);
+
+ const F32 z1 = color1.mV[VZ];
+ const F32 z2 = color2.mV[VZ];
+ result.mV[VZ] = z1 - dt * (z1 - z2);
+}
+
+
+
+// Returns the translucency of the atmosphere along the ray in the sky.
+// dir is assumed to be normalized
+inline void LLTranspMapSet::calcTransp(const F32 cos_angle, const F32 h, LLColor3 &result) const
+{
+ S32 indx = 0;
+ F32 dt = 0.f;
+ const S32 status = lerp(dt, indx, h);
+
+ if (status < 0)
+ {
+ mTransp[0].calcAirTransp(cos_angle, result);
+ return;
+ }
+ if (status > 0)
+ {
+ mTransp[NO_STEPS].calcAirTransp(cos_angle, result);
+ return;
+ }
+
+ mTransp[indx].calcAirTransp(cos_angle, result);
+ result *= 1 - dt;
+
+ LLColor3 transp_above;
+
+ mTransp[indx + 1].calcAirTransp(cos_angle, transp_above);
+ transp_above *= dt;
+ result += transp_above;
+}
+
+
+inline LLColor3 LLTranspMapSet::calcTransp(const F32 cos_angle, const F32 h) const
+{
+ LLColor3 result;
+ S32 indx = 0;
+ F32 dt = 0;
+ const S32 status = lerp(dt, indx, h);
+
+ if (status < 0)
+ {
+ mTransp[0].calcAirTransp(cos_angle, result);
+ return result;
+ }
+ if (status > 0)
+ {
+ mTransp[NO_STEPS].calcAirTransp(cos_angle, result);
+ return result;
+ }
+
+ mTransp[indx].calcAirTransp(cos_angle, result);
+ result *= 1 - dt;
+
+ LLColor3 transp_above;
+
+ mTransp[indx + 1].calcAirTransp(cos_angle, transp_above);
+ transp_above *= dt;
+ result += transp_above;
+ return result;
+}
+
+
+// Returns -1 if height < 0; +1 if height > max height; 0 if within range
+inline S32 LLTranspMapSet::lerp(F32& dt, S32& indx, const F32 h) const
+{
+ static S32 last_indx = 0;
+
+ if (h < 0)
+ {
+ return -1;
+ }
+ if (h > getLastHeight())
+ {
+ return 1;
+ }
+
+ if (h < mHeights[last_indx])
+ {
+ indx = last_indx-1;
+ while (mHeights[indx] > h)
+ {
+ indx--;
+ }
+ last_indx = indx;
+ }
+ else if (h > mHeights[last_indx+1])
+ {
+ indx = last_indx+1;
+ while (mHeights[indx+1] < h)
+ {
+ indx++;
+ }
+ last_indx = indx;
+ }
+ else
+ {
+ indx = last_indx;
+ }
+
+ const F32 h_below = mHeights[indx];
+ const F32 h_above = mHeights[indx+1];
+ dt = (h - h_below) / (h_above - h_below);
+ return 0;
+}
+
+#endif
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
new file mode 100644
index 0000000000..ae7d46dfee
--- /dev/null
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -0,0 +1,924 @@
+/**
+ * @file llvosurfacepatch.cpp
+ * @brief Viewer-object derived "surface patch", which is a piece of terrain
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvosurfacepatch.h"
+
+#include "lldrawable.h"
+#include "llface.h"
+#include "llprimitive.h"
+#include "llsky.h"
+#include "llsurfacepatch.h"
+#include "llsurface.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "llvovolume.h"
+#include "pipeline.h"
+
+LLVOSurfacePatch::LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, LL_VO_SURFACE_PATCH, regionp)
+{
+ // Terrain must draw during selection passes so it can block objects behind it.
+ mbCanSelect = TRUE;
+
+ mBaseComp = 0;
+ setScale(LLVector3(16.f, 16.f, 16.f)); // Hack for setting scale for bounding boxes/visibility.
+ mPool = NULL;
+ mDirtiedPatch = FALSE;
+ mLastStride = 0;
+ mLastNorthStride = 0;
+ mLastEastStride = 0;
+ mLastLength = 0;
+
+ mDirtyTerrain = TRUE;
+}
+
+
+LLVOSurfacePatch::~LLVOSurfacePatch()
+{
+ mPatchp = NULL;
+}
+
+
+void LLVOSurfacePatch::markDead()
+{
+ if (mPatchp)
+ {
+ mPatchp->clearVObj();
+ mPatchp = NULL;
+ }
+ LLViewerObject::markDead();
+}
+
+
+BOOL LLVOSurfacePatch::isActive() const
+{
+ return FALSE;
+}
+
+
+void LLVOSurfacePatch::setPixelAreaAndAngle(LLAgent &agent)
+{
+ mAppAngle = 50;
+ mPixelArea = 500*500;
+}
+
+
+void LLVOSurfacePatch::updateTextures(LLAgent &agent)
+{
+}
+
+
+LLDrawPool *LLVOSurfacePatch::getPool()
+{
+ mPool = gPipeline.getPool(LLDrawPool::POOL_TERRAIN, mPatchp->getSurface()->getSTexture());
+
+ return mPool;
+}
+
+
+LLDrawable *LLVOSurfacePatch::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_TERRAIN);
+
+ mBaseComp = llfloor(mPatchp->getMinComposition());
+ S32 min_comp, max_comp, range;
+ min_comp = llfloor(mPatchp->getMinComposition());
+ max_comp = llceil(mPatchp->getMaxComposition());
+ range = (max_comp - min_comp);
+ range++;
+ if (range > 3)
+ {
+ if ((mPatchp->getMinComposition() - min_comp) > (max_comp - mPatchp->getMaxComposition()))
+ {
+ // The top side runs over more
+ mBaseComp++;
+ }
+ range = 3;
+ }
+
+ LLDrawPool *poolp = getPool();
+
+ mDrawable->addFace(poolp, NULL);
+ return mDrawable;
+}
+
+
+BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
+{
+ S32 min_comp, max_comp, range;
+ min_comp = lltrunc(mPatchp->getMinComposition());
+ max_comp = lltrunc(ceil(mPatchp->getMaxComposition()));
+ range = (max_comp - min_comp);
+ range++;
+ S32 new_base_comp = lltrunc(mPatchp->getMinComposition());
+ if (range > 3)
+ {
+ if ((mPatchp->getMinComposition() - min_comp) > (max_comp - mPatchp->getMaxComposition()))
+ {
+ // The top side runs over more
+ new_base_comp++;
+ }
+ range = 3;
+ }
+
+ // Pick the two closest detail textures for this patch...
+ // Then create the draw pool for it.
+ // Actually, should get the average composition instead of the center.
+ mBaseComp = new_base_comp;
+
+ //////////////////////////
+ //
+ // Figure out the strides
+ //
+ //
+
+ U32 patch_width, render_stride, north_stride, east_stride, length;
+ render_stride = mPatchp->getRenderStride();
+ patch_width = mPatchp->getSurface()->getGridsPerPatchEdge();
+
+ length = patch_width / render_stride;
+
+ if (mPatchp->getNeighborPatch(NORTH))
+ {
+ north_stride = mPatchp->getNeighborPatch(NORTH)->getRenderStride();
+ }
+ else
+ {
+ north_stride = render_stride;
+ }
+
+ if (mPatchp->getNeighborPatch(EAST))
+ {
+ east_stride = mPatchp->getNeighborPatch(EAST)->getRenderStride();
+ }
+ else
+ {
+ east_stride = render_stride;
+ }
+
+ S32 num_vertices = 0;
+ S32 num_indices = 0;
+ S32 new_north_offset = 0;
+ S32 new_east_offset = 0;
+
+ getGeomSizesMain(render_stride, num_vertices, num_indices);
+ new_north_offset = num_vertices;
+ getGeomSizesNorth(render_stride, north_stride, num_vertices, num_indices);
+ new_east_offset = num_vertices;
+ getGeomSizesEast(render_stride, east_stride, num_vertices, num_indices);
+ S32 new_num_vertices = num_vertices;
+ S32 new_num_indices = num_indices;
+
+ LLFace *facep = NULL;
+
+ // Update the allocated face
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoords0p;
+ LLStrider<LLVector2> texCoords1p;
+ LLStrider<LLColor4U> colorsp;
+ U32* indicesp = NULL;
+ S32 index_offset;
+
+ facep = mDrawable->getFace(0);
+
+ facep->setSize(new_num_vertices, new_num_indices);
+ facep->setPrimType(LLTriangles);
+
+ index_offset = facep->getGeometryTerrain(
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ mDrawable->updateLightSet();
+
+ updateMainGeometry(facep,
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+ updateNorthGeometry(facep,
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+ updateEastGeometry(facep,
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+
+ if (mLastLength != 0)
+ {
+ // lazy, should cache the geom sizes so we know the offsets.
+ num_vertices = 0;
+ num_indices = 0;
+
+ }
+
+ mLastLength = length;
+ mLastStride = render_stride;
+ mLastNorthStride = north_stride;
+ mLastEastStride = east_stride;
+
+ mDrawable->setState(LLDrawable::LIGHTING_BUILT);
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset)
+{
+ S32 i, j, x, y;
+
+ U32 patch_size, render_stride;
+ S32 num_vertices, num_indices;
+ U32 index;
+
+ render_stride = mPatchp->getRenderStride();
+ patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 vert_size = patch_size / render_stride;
+
+ ///////////////////////////
+ //
+ // Render the main patch
+ //
+ //
+
+ num_vertices = 0;
+ num_indices = 0;
+ // First, figure out how many vertices we need...
+ getGeomSizesMain(render_stride, num_vertices, num_indices);
+
+ if (num_vertices > 0)
+ {
+ facep->mCenterAgent = mPatchp->getPointAgent(8, 8);
+
+ // Generate patch points first
+ for (j = 0; j < vert_size; j++)
+ {
+ for (i = 0; i < vert_size; i++)
+ {
+ x = i * render_stride;
+ y = j * render_stride;
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+ }
+
+ for (j = 0; j < (vert_size - 1); j++)
+ {
+ if (j % 2)
+ {
+ for (i = (vert_size - 1); i > 0; i--)
+ {
+ index = (i - 1)+ j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i - 1) + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i - 1) + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+ }
+ }
+ else
+ {
+ for (i = 0; i < (vert_size - 1); i++)
+ {
+ index = i + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i + 1) + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i + 1) + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i + 1) + (j + 1)*vert_size;
+ *(indicesp++) = index_offset + index;
+ }
+ }
+ }
+ }
+ index_offset += num_vertices;
+}
+
+
+void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset)
+{
+ S32 vertex_count = 0;
+ S32 i, x, y;
+
+ S32 num_vertices, num_indices;
+
+ U32 render_stride = mPatchp->getRenderStride();
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / render_stride;
+ S32 half_length = length / 2;
+
+ U32 north_stride;
+ if (mPatchp->getNeighborPatch(NORTH))
+ {
+ north_stride = mPatchp->getNeighborPatch(NORTH)->getRenderStride();
+ }
+ else
+ {
+ north_stride = render_stride;
+ }
+
+ ///////////////////////////
+ //
+ // Render the north strip
+ //
+ //
+
+ // Stride lengths are the same
+ if (north_stride == render_stride)
+ {
+ num_vertices = 2 * length + 1;
+ num_indices = length * 6 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(8, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Main patch
+ for (i = 0; i < length; i++)
+ {
+ x = i * render_stride;
+ y = 16 - render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ // North patch
+ for (i = 0; i <= length; i++)
+ {
+ x = i * render_stride;
+ y = 16;
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+
+ for (i = 0; i < length; i++)
+ {
+ // Generate indices
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + i + 1;
+ *(indicesp++) = index_offset + length + i;
+
+ if (i != length - 1)
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + i + 1;
+ }
+ }
+ }
+ else if (north_stride > render_stride)
+ {
+ // North stride is longer (has less vertices)
+ num_vertices = length + length/2 + 1;
+ num_indices = half_length*9 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(7, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i++)
+ {
+ x = i * render_stride;
+ y = 16 - render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ // Iterate through the north patch's points
+ for (i = 0; i <= length; i+=2)
+ {
+ x = i * render_stride;
+ y = 16;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i % 2))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2);
+
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ *(indicesp++) = index_offset + length + (i/2);
+ }
+ else if (i < (length - 1))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ }
+ }
+ }
+ else
+ {
+ // North stride is shorter (more vertices)
+ length = patch_size / north_stride;
+ half_length = length / 2;
+ num_vertices = length + half_length + 1;
+ num_indices = 9*half_length - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(15, 7) + mPatchp->getPointAgent(16, 8))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i+=2)
+ {
+ x = i * north_stride;
+ y = 16 - render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ // Iterate through the north patch's points
+ for (i = 0; i <= length; i++)
+ {
+ x = i * north_stride;
+ y = 16;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i%2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ }
+ else if (i < (length - 2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2;
+ *(indicesp++) = index_offset + i/2 + 1;
+
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2 + 1;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ }
+ else
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ }
+ }
+ }
+ index_offset += num_vertices;
+}
+
+void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset)
+{
+ S32 i, x, y;
+
+ S32 num_vertices, num_indices;
+
+ U32 render_stride = mPatchp->getRenderStride();
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / render_stride;
+ S32 half_length = length / 2;
+
+ U32 east_stride;
+ if (mPatchp->getNeighborPatch(EAST))
+ {
+ east_stride = mPatchp->getNeighborPatch(EAST)->getRenderStride();
+ }
+ else
+ {
+ east_stride = render_stride;
+ }
+
+ // Stride lengths are the same
+ if (east_stride == render_stride)
+ {
+ num_vertices = 2 * length + 1;
+ num_indices = length * 6 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(8, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Main patch
+ for (i = 0; i < length; i++)
+ {
+ x = 16 - render_stride;
+ y = i * render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+ // East patch
+ for (i = 0; i <= length; i++)
+ {
+ x = 16;
+ y = i * render_stride;
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+
+ for (i = 0; i < length; i++)
+ {
+ // Generate indices
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + i;
+ *(indicesp++) = index_offset + length + i + 1;
+
+ if (i != length - 1)
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + i + 1;
+ *(indicesp++) = index_offset + i + 1;
+ }
+ }
+ }
+ else if (east_stride > render_stride)
+ {
+ // East stride is longer (has less vertices)
+ num_vertices = length + half_length + 1;
+ num_indices = half_length*9 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(7, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i++)
+ {
+ x = 16 - render_stride;
+ y = i * render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+ // Iterate through the east patch's points
+ for (i = 0; i <= length; i+=2)
+ {
+ x = 16;
+ y = i * render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i % 2))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + (i/2);
+ *(indicesp++) = index_offset + i + 1;
+
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2);
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ }
+ else if (i < (length - 1))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ *(indicesp++) = index_offset + i + 1;
+ }
+ }
+ }
+ else
+ {
+ // East stride is shorter (more vertices)
+ length = patch_size / east_stride;
+ half_length = length / 2;
+ num_vertices = length + length/2 + 1;
+ num_indices = 9*(length/2) - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(15, 7) + mPatchp->getPointAgent(16, 8))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i+=2)
+ {
+ x = 16 - render_stride;
+ y = i * east_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+ // Iterate through the east patch's points
+ for (i = 0; i <= length; i++)
+ {
+ x = 16;
+ y = i * east_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i%2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ *(indicesp++) = index_offset + i/2;
+ }
+ else if (i < (length - 2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2 + 1;
+ *(indicesp++) = index_offset + i/2;
+
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ *(indicesp++) = index_offset + i/2 + 1;
+ }
+ else
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ *(indicesp++) = index_offset + i/2;
+ }
+ }
+ }
+ index_offset += num_vertices;
+}
+
+void LLVOSurfacePatch::calcColor(const LLVector3* vertex, const LLVector3* normal, LLColor4U* colorp)
+{
+ LLColor4 color(0,0,0,0);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ for (LLDrawable::drawable_set_t::iterator iter = mDrawable->mLightSet.begin();
+ iter != mDrawable->mLightSet.end(); ++iter)
+ {
+ LLDrawable* light_drawable = *iter;
+ LLVOVolume* light = light_drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ LLColor4 light_color;
+ light->calcLightAtPoint(*vertex, *normal, light_color);
+ color += light_color;
+ }
+
+ color.mV[3] = 1.0f;
+ }
+ colorp->setVecScaleClamp(color);
+}
+
+BOOL LLVOSurfacePatch::updateShadows(BOOL use_shadow_factor)
+{
+ return FALSE; //terrain updates its shadows during standard relight
+}
+
+void LLVOSurfacePatch::setPatch(LLSurfacePatch *patchp)
+{
+ mPatchp = patchp;
+
+ dirtyPatch();
+};
+
+
+void LLVOSurfacePatch::dirtyPatch()
+{
+ if (mDrawable)
+ {
+ gPipeline.markMoved(mDrawable);
+ }
+ mDirtiedPatch = TRUE;
+ dirtyGeom();
+ mDirtyTerrain = TRUE;
+ LLVector3 center = mPatchp->getCenterRegion();
+ LLSurface *surfacep = mPatchp->getSurface();
+
+ setPositionRegion(center);
+
+ F32 scale_factor = surfacep->getGridsPerPatchEdge() * surfacep->getMetersPerGrid();
+ setScale(LLVector3(scale_factor, scale_factor, mPatchp->getMaxZ() - mPatchp->getMinZ()));
+}
+
+void LLVOSurfacePatch::dirtyGeom()
+{
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+}
+
+void LLVOSurfacePatch::getGeomSizesMain(const S32 stride, S32 &num_vertices, S32 &num_indices)
+{
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+
+ // First, figure out how many vertices we need...
+ S32 vert_size = patch_size / stride;
+ if (vert_size >= 2)
+ {
+ num_vertices += vert_size * vert_size;
+ num_indices += 6 * (vert_size - 1)*(vert_size - 1);
+ }
+}
+
+void LLVOSurfacePatch::getGeomSizesNorth(const S32 stride, const S32 north_stride,
+ S32 &num_vertices, S32 &num_indices)
+{
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / stride;
+ // Stride lengths are the same
+ if (north_stride == stride)
+ {
+ num_vertices += 2 * length + 1;
+ num_indices += length * 6 - 3;
+ }
+ else if (north_stride > stride)
+ {
+ // North stride is longer (has less vertices)
+ num_vertices += length + (length/2) + 1;
+ num_indices += (length/2)*9 - 3;
+ }
+ else
+ {
+ // North stride is shorter (more vertices)
+ length = patch_size / north_stride;
+ num_vertices += length + (length/2) + 1;
+ num_indices += 9*(length/2) - 3;
+ }
+}
+
+void LLVOSurfacePatch::getGeomSizesEast(const S32 stride, const S32 east_stride,
+ S32 &num_vertices, S32 &num_indices)
+{
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / stride;
+ // Stride lengths are the same
+ if (east_stride == stride)
+ {
+ num_vertices += 2 * length + 1;
+ num_indices += length * 6 - 3;
+ }
+ else if (east_stride > stride)
+ {
+ // East stride is longer (has less vertices)
+ num_vertices += length + (length/2) + 1;
+ num_indices += (length/2)*9 - 3;
+ }
+ else
+ {
+ // East stride is shorter (more vertices)
+ length = patch_size / east_stride;
+ num_vertices += length + (length/2) + 1;
+ num_indices += 9*(length/2) - 3;
+ }
+}
+
+void LLVOSurfacePatch::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+{
+ LLVector3 posAgent = getPositionAgent();
+ LLVector3 scale = getScale();
+ newMin = posAgent-scale;
+ newMax = posAgent+scale;
+ mDrawable->setPositionGroup((newMin+newMax)*0.5f);
+}
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
new file mode 100644
index 0000000000..f1c44aa598
--- /dev/null
+++ b/indra/newview/llvosurfacepatch.h
@@ -0,0 +1,93 @@
+/**
+ * @file llvosurfacepatch.h
+ * @brief Description of LLVOSurfacePatch class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VOSURFACEPATCH_H
+#define LL_VOSURFACEPATCH_H
+
+#include "llviewerobject.h"
+#include "llstrider.h"
+
+class LLSurfacePatch;
+class LLDrawPool;
+class LLVector2;
+
+class LLVOSurfacePatch : public LLViewerObject
+{
+public:
+ LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOSurfacePatch();
+
+ /*virtual*/ void markDead();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ /*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ void setPatch(LLSurfacePatch *patchp);
+ LLSurfacePatch *getPatch() const { return mPatchp; }
+
+ void dirtyPatch();
+ void dirtyGeom();
+
+ BOOL mDirtiedPatch;
+protected:
+ LLDrawPool *mPool;
+ LLDrawPool *getPool();
+ S32 mBaseComp;
+ LLSurfacePatch *mPatchp;
+ BOOL mDirtyTexture;
+ BOOL mDirtyTerrain;
+
+ S32 mLastNorthStride;
+ S32 mLastEastStride;
+ S32 mLastStride;
+ S32 mLastLength;
+
+ void calcColor(const LLVector3* vertex, const LLVector3* normal, LLColor4U* colorp);
+ BOOL updateShadows(BOOL use_shadow_factor = FALSE);
+ void getGeomSizesMain(const S32 stride, S32 &num_vertices, S32 &num_indices);
+ void getGeomSizesNorth(const S32 stride, const S32 north_stride,
+ S32 &num_vertices, S32 &num_indices);
+ void getGeomSizesEast(const S32 stride, const S32 east_stride,
+ S32 &num_vertices, S32 &num_indices);
+
+ void updateMainGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset);
+ void updateNorthGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset);
+ void updateEastGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset);
+};
+
+#endif // LL_VOSURFACEPATCH_H
diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp
new file mode 100644
index 0000000000..d9f3d83f11
--- /dev/null
+++ b/indra/newview/llvotextbubble.cpp
@@ -0,0 +1,204 @@
+/**
+ * @file llvotextbubble.cpp
+ * @brief Viewer-object text bubble.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvotextbubble.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+#include "llprimitive.h"
+#include "llsphere.h"
+
+#include "llagent.h"
+#include "llbox.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewerimagelist.h"
+#include "llvolume.h"
+#include "pipeline.h"
+
+LLVOTextBubble::LLVOTextBubble(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp)
+{
+ setScale(LLVector3(1.5f, 1.5f, 0.25f));
+ mbCanSelect = FALSE;
+ mLOD = MIN_LOD;
+ mVolumeChanged = TRUE;
+ setVelocity(LLVector3(0.f, 0.f, 0.75f));
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE);
+ volume_params.setBeginAndEndS(0.f, 1.f);
+ volume_params.setBeginAndEndT(0.f, 1.f);
+ volume_params.setRatio(0.25f, 0.25f);
+ volume_params.setShear(0.f, 0.f);
+ setVolume(volume_params);
+ mColor = LLColor4(1.0f, 0.0f, 0.0f, 1.f);
+ S32 i;
+ for (i = 0; i < getNumTEs(); i++)
+ {
+ setTEColor(i, mColor);
+ setTETexture(i, LLUUID(IMG_DEFAULT));
+ }
+}
+
+
+LLVOTextBubble::~LLVOTextBubble()
+{
+}
+
+
+BOOL LLVOTextBubble::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLVOTextBubble::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ F32 dt = mUpdateTimer.getElapsedTimeF32();
+ // Die after a few seconds.
+ if (dt > 1.5f)
+ {
+ return FALSE;
+ }
+
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ setScale(0.5f * (1.f+dt) * LLVector3(1.5f, 1.5f, 0.5f));
+
+ F32 alpha = 0.35f*dt;
+
+ LLColor4 color = mColor;
+ color.mV[VALPHA] -= alpha;
+ if (color.mV[VALPHA] <= 0.05f)
+ {
+ return FALSE;
+ }
+ S32 i;
+ for (i = 0; i < getNumTEs(); i++)
+ {
+ setTEColor(i, color);
+ }
+
+ return TRUE;
+}
+
+
+void LLVOTextBubble::updateTextures(LLAgent &agent)
+{
+ // Update the image levels of all textures...
+ // First we do some quick checks.
+ U32 i;
+
+ // This doesn't take into account whether the object is in front
+ // or behind...
+
+ LLVector3 position_local = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 dot_product = position_local * agent.getFrameAgent().getAtAxis();
+ F32 cos_angle = dot_product / position_local.magVec();
+
+ if (cos_angle > 1.f)
+ {
+ cos_angle = 1.f;
+ }
+
+ for (i = 0; i < getNumTEs(); i++)
+ {
+ const LLTextureEntry *te = getTE(i);
+ F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT);
+
+ LLViewerImage *imagep = getTEImage(i);
+ if (imagep)
+ {
+ imagep->addTextureStats(mPixelArea, texel_area_ratio, cos_angle);
+ }
+ }
+}
+
+
+LLDrawable *LLVOTextBubble::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+ LLDrawPool *poolp;
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ LLViewerImage *imagep;
+ const LLTextureEntry *texture_entry = getTE(i);
+ imagep = gImageList.getImage(texture_entry->getID());
+
+ poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ mDrawable->addFace(poolp, imagep);
+ }
+
+ return mDrawable;
+}
+
+
+BOOL LLVOTextBubble::setVolume(const LLVolumeParams &volume_params)
+{
+ if (LLPrimitive::setVolume(volume_params, mLOD))
+ {
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ mVolumeChanged = TRUE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLVOTextBubble::updateLOD()
+{
+ return FALSE;
+}
+
+BOOL LLVOTextBubble::updateGeometry(LLDrawable *drawable)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_VOLUME)))
+ return TRUE;
+
+ if (mVolumeChanged)
+ {
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params);
+
+ LLPipeline::sCompiles++;
+
+ drawable->setNumFaces(getVolume()->getNumFaces(), drawable->getFace(0)->getPool(), getTEImage(0));
+ }
+
+ LLMatrix4 identity4;
+ LLMatrix3 identity3;
+ for (S32 i = 0; i < drawable->getNumFaces(); i++)
+ {
+ LLFace *face = drawable->getFace(i);
+
+ if (i == 0)
+ {
+ face->setSize(0);
+ continue;
+ }
+ if (i == 2)
+ {
+ face->setSize(0);
+ continue;
+ }
+
+ // Need to figure out how to readd logic to determine face dirty vs. entire object dirty.
+ face->setTEOffset(i);
+ face->clearState(LLFace::GLOBAL);
+ face->genVolumeTriangles(*getVolume(), i, identity4, identity3);
+ }
+ mVolumeChanged = FALSE;
+
+ return TRUE;
+}
diff --git a/indra/newview/llvotextbubble.h b/indra/newview/llvotextbubble.h
new file mode 100644
index 0000000000..9fe6386a3b
--- /dev/null
+++ b/indra/newview/llvotextbubble.h
@@ -0,0 +1,37 @@
+/**
+ * @file llvotextbubble.h
+ * @brief Description of LLVORock class, which a derivation of LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOTEXTBUBBLE_H
+#define LL_LLVOTEXTBUBBLE_H
+
+#include "llviewerobject.h"
+#include "llframetimer.h"
+
+class LLVOTextBubble : public LLViewerObject
+{
+public:
+ LLVOTextBubble(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOTextBubble();
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ /*virtual*/ BOOL updateLOD();
+
+ LLColor4 mColor;
+ S32 mLOD;
+ BOOL mVolumeChanged;
+protected:
+ BOOL setVolume(const LLVolumeParams &volume_params);
+ LLFrameTimer mUpdateTimer;
+};
+
+#endif // LL_VO_TEXT_BUBBLE
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
new file mode 100644
index 0000000000..d2c7ed9b6a
--- /dev/null
+++ b/indra/newview/llvotree.cpp
@@ -0,0 +1,906 @@
+/**
+ * @file llvotree.cpp
+ * @brief LLVOTree class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvotree.h"
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llprimitive.h"
+#include "lltree_common.h"
+#include "llxmltree.h"
+#include "material_codes.h"
+#include "object_flags.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+#include "noise.h"
+#include "pipeline.h"
+#include "llviewerwindow.h"
+
+extern LLPipeline gPipeline;
+
+LLGLuint mLeafDList;
+
+S32 LLVOTree::sLODVertexOffset[4];
+S32 LLVOTree::sLODVertexCount[4];
+S32 LLVOTree::sLODIndexOffset[4];
+S32 LLVOTree::sLODIndexCount[4];
+S32 LLVOTree::sLODSlices[4] = {10, 5, 4, 3};
+F32 LLVOTree::sLODAngles[4] = {30.f, 20.f, 15.f, 0.f};
+
+F32 LLVOTree::sTreeFactor = 1.f;
+
+LLVOTree::SpeciesMap LLVOTree::sSpeciesTable;
+S32 LLVOTree::sMaxTreeSpecies = 0;
+
+// Tree variables and functions
+
+LLVOTree::LLVOTree(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp):
+ LLViewerObject(id, pcode, regionp)
+{
+ mSpecies = 0;
+ mFrameCount = 0;
+ mWind = mRegionp->mWind.getVelocity(getPositionRegion());
+}
+
+
+LLVOTree::~LLVOTree()
+{
+ if (mData)
+ {
+ delete[] mData;
+ mData = NULL;
+ }
+}
+
+// static
+void LLVOTree::initClass()
+{
+ std::string xml_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"trees.xml");
+
+ LLXmlTree tree_def_tree;
+
+ if (!tree_def_tree.parseFile(xml_filename))
+ {
+ llerrs << "Failed to parse tree file." << llendl;
+ }
+
+ LLXmlTreeNode* rootp = tree_def_tree.getRoot();
+
+ for (LLXmlTreeNode* tree_def = rootp->getFirstChild();
+ tree_def;
+ tree_def = rootp->getNextChild())
+ {
+ if (!tree_def->hasName("tree"))
+ {
+ llwarns << "Invalid tree definition node " << tree_def->getName() << llendl;
+ continue;
+ }
+ F32 F32_val;
+ LLUUID id;
+ S32 S32_val;
+
+ BOOL success = TRUE;
+
+
+
+ S32 species;
+ static LLStdStringHandle species_id_string = LLXmlTree::addAttributeString("species_id");
+ if (!tree_def->getFastAttributeS32(species_id_string, species))
+ {
+ llwarns << "No species id defined" << llendl;
+ continue;
+ }
+
+ if (species < 0)
+ {
+ llwarns << "Invalid species id " << species << llendl;
+ continue;
+ }
+
+ if (sSpeciesTable.count(species))
+ {
+ llwarns << "Tree species " << species << " already defined! Duplicate discarded." << llendl;
+ continue;
+ }
+
+ TreeSpeciesData* newTree = new TreeSpeciesData();
+
+ static LLStdStringHandle texture_id_string = LLXmlTree::addAttributeString("texture_id");
+ success &= tree_def->getFastAttributeUUID(texture_id_string, id);
+ newTree->mTextureID = id;
+
+ static LLStdStringHandle droop_string = LLXmlTree::addAttributeString("droop");
+ success &= tree_def->getFastAttributeF32(droop_string, F32_val);
+ newTree->mDroop = F32_val;
+
+ static LLStdStringHandle twist_string = LLXmlTree::addAttributeString("twist");
+ success &= tree_def->getFastAttributeF32(twist_string, F32_val);
+ newTree->mTwist = F32_val;
+
+ static LLStdStringHandle branches_string = LLXmlTree::addAttributeString("branches");
+ success &= tree_def->getFastAttributeF32(branches_string, F32_val);
+ newTree->mBranches = F32_val;
+
+ static LLStdStringHandle depth_string = LLXmlTree::addAttributeString("depth");
+ success &= tree_def->getFastAttributeS32(depth_string, S32_val);
+ newTree->mDepth = S32_val;
+
+ static LLStdStringHandle scale_step_string = LLXmlTree::addAttributeString("scale_step");
+ success &= tree_def->getFastAttributeF32(scale_step_string, F32_val);
+ newTree->mScaleStep = F32_val;
+
+ static LLStdStringHandle trunk_depth_string = LLXmlTree::addAttributeString("trunk_depth");
+ success &= tree_def->getFastAttributeS32(trunk_depth_string, S32_val);
+ newTree->mTrunkDepth = S32_val;
+
+ static LLStdStringHandle branch_length_string = LLXmlTree::addAttributeString("branch_length");
+ success &= tree_def->getFastAttributeF32(branch_length_string, F32_val);
+ newTree->mBranchLength = F32_val;
+
+ static LLStdStringHandle trunk_length_string = LLXmlTree::addAttributeString("trunk_length");
+ success &= tree_def->getFastAttributeF32(trunk_length_string, F32_val);
+ newTree->mTrunkLength = F32_val;
+
+ static LLStdStringHandle leaf_scale_string = LLXmlTree::addAttributeString("leaf_scale");
+ success &= tree_def->getFastAttributeF32(leaf_scale_string, F32_val);
+ newTree->mLeafScale = F32_val;
+
+ static LLStdStringHandle billboard_scale_string = LLXmlTree::addAttributeString("billboard_scale");
+ success &= tree_def->getFastAttributeF32(billboard_scale_string, F32_val);
+ newTree->mBillboardScale = F32_val;
+
+ static LLStdStringHandle billboard_ratio_string = LLXmlTree::addAttributeString("billboard_ratio");
+ success &= tree_def->getFastAttributeF32(billboard_ratio_string, F32_val);
+ newTree->mBillboardRatio = F32_val;
+
+ static LLStdStringHandle trunk_aspect_string = LLXmlTree::addAttributeString("trunk_aspect");
+ success &= tree_def->getFastAttributeF32(trunk_aspect_string, F32_val);
+ newTree->mTrunkAspect = F32_val;
+
+ static LLStdStringHandle branch_aspect_string = LLXmlTree::addAttributeString("branch_aspect");
+ success &= tree_def->getFastAttributeF32(branch_aspect_string, F32_val);
+ newTree->mBranchAspect = F32_val;
+
+ static LLStdStringHandle leaf_rotate_string = LLXmlTree::addAttributeString("leaf_rotate");
+ success &= tree_def->getFastAttributeF32(leaf_rotate_string, F32_val);
+ newTree->mRandomLeafRotate = F32_val;
+
+ static LLStdStringHandle noise_mag_string = LLXmlTree::addAttributeString("noise_mag");
+ success &= tree_def->getFastAttributeF32(noise_mag_string, F32_val);
+ newTree->mNoiseMag = F32_val;
+
+ static LLStdStringHandle noise_scale_string = LLXmlTree::addAttributeString("noise_scale");
+ success &= tree_def->getFastAttributeF32(noise_scale_string, F32_val);
+ newTree->mNoiseScale = F32_val;
+
+ static LLStdStringHandle taper_string = LLXmlTree::addAttributeString("taper");
+ success &= tree_def->getFastAttributeF32(taper_string, F32_val);
+ newTree->mTaper = F32_val;
+
+ static LLStdStringHandle repeat_z_string = LLXmlTree::addAttributeString("repeat_z");
+ success &= tree_def->getFastAttributeF32(repeat_z_string, F32_val);
+ newTree->mRepeatTrunkZ = F32_val;
+
+ sSpeciesTable[species] = newTree;
+
+ if (species >= sMaxTreeSpecies) sMaxTreeSpecies = species + 1;
+
+ if (!success)
+ {
+ LLString name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ tree_def->getFastAttributeString(name_string, name);
+ llwarns << "Incomplete definition of tree " << name << llendl;
+ }
+ }
+
+ BOOL have_all_trees = TRUE;
+ LLString err;
+ char buffer[10];
+
+ for (S32 i=0;i<sMaxTreeSpecies;++i)
+ {
+ if (!sSpeciesTable.count(i))
+ {
+ snprintf(buffer,10," %d",i);
+ err.append(buffer);
+ have_all_trees = FALSE;
+ }
+ }
+
+ if (!have_all_trees)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[SPECIES]"] = err;
+ gViewerWindow->alertXml("ErrorUndefinedTrees", args );
+ }
+};
+
+//static
+void LLVOTree::cleanupClass()
+{
+ std::for_each(sSpeciesTable.begin(), sSpeciesTable.end(), DeletePairedPointer());
+}
+
+U32 LLVOTree::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ if ( (getVelocity().magVecSquared() > 0.f)
+ ||(getAcceleration().magVecSquared() > 0.f)
+ ||(getAngularVelocity().magVecSquared() > 0.f))
+ {
+ llinfos << "ACK! Moving tree!" << llendl;
+ setVelocity(LLVector3::zero);
+ setAcceleration(LLVector3::zero);
+ setAngularVelocity(LLVector3::zero);
+ }
+
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ // Nothing else needs to be done for the terse message.
+ return retval;
+ }
+
+ //
+ // Load Instance-Specific data
+ //
+ if (mData)
+ {
+ mSpecies = ((U8 *)mData)[0];
+ }
+
+ if (!sSpeciesTable.count(mSpecies))
+ {
+ if (sSpeciesTable.size())
+ {
+ SpeciesMap::const_iterator it = sSpeciesTable.begin();
+ mSpecies = (*it).first;
+ }
+ }
+
+ //
+ // Load Species-Specific data
+ //
+ mTreeImagep = gImageList.getImage(sSpeciesTable[mSpecies]->mTextureID);
+ if (mTreeImagep)
+ {
+ mTreeImagep->bindTexture(0);
+ mTreeImagep->setClamp(TRUE, TRUE);
+ }
+ mBranchLength = sSpeciesTable[mSpecies]->mBranchLength;
+ mTrunkLength = sSpeciesTable[mSpecies]->mTrunkLength;
+ mLeafScale = sSpeciesTable[mSpecies]->mLeafScale;
+ mDroop = sSpeciesTable[mSpecies]->mDroop;
+ mTwist = sSpeciesTable[mSpecies]->mTwist;
+ mBranches = sSpeciesTable[mSpecies]->mBranches;
+ mDepth = sSpeciesTable[mSpecies]->mDepth;
+ mScaleStep = sSpeciesTable[mSpecies]->mScaleStep;
+ mTrunkDepth = sSpeciesTable[mSpecies]->mTrunkDepth;
+ mBillboardScale = sSpeciesTable[mSpecies]->mBillboardScale;
+ mBillboardRatio = sSpeciesTable[mSpecies]->mBillboardRatio;
+ mTrunkAspect = sSpeciesTable[mSpecies]->mTrunkAspect;
+ mBranchAspect = sSpeciesTable[mSpecies]->mBranchAspect;
+
+ return retval;
+}
+
+BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ const U16 FRAMES_PER_WIND_UPDATE = 20; // How many frames between wind update per tree
+ const F32 TREE_WIND_SENSITIVITY = 0.005f;
+ const F32 TREE_TRUNK_STIFFNESS = 0.1f;
+
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TREE)))
+ {
+ return TRUE;
+ }
+
+ F32 mass_inv;
+
+ // For all tree objects, update the trunk bending with the current wind
+ // Walk sprite list in order away from viewer
+ if (!(mFrameCount % FRAMES_PER_WIND_UPDATE))
+ {
+ // If needed, Get latest wind for this tree
+ mWind = mRegionp->mWind.getVelocity(getPositionRegion());
+ }
+ mFrameCount++;
+
+ mass_inv = 1.f/(5.f + mDepth*mBranches*0.2f);
+ mTrunkVel += (mWind * mass_inv * TREE_WIND_SENSITIVITY); // Pull in direction of wind
+ mTrunkVel -= (mTrunkBend * mass_inv * TREE_TRUNK_STIFFNESS); // Restoring force in direction of trunk
+ mTrunkBend += mTrunkVel;
+ mTrunkVel *= 0.99f; // Add damping
+
+ if (mTrunkBend.magVec() > 1.f)
+ {
+ mTrunkBend.normVec();
+ }
+
+ if (mTrunkVel.magVec() > 1.f)
+ {
+ mTrunkVel.normVec();
+ }
+
+ return TRUE;
+}
+
+
+const F32 TREE_BLEND_MIN = 1.f;
+const F32 TREE_BLEND_RANGE = 1.f;
+
+
+void LLVOTree::render(LLAgent &agent)
+{
+}
+
+
+void LLVOTree::setPixelAreaAndAngle(LLAgent &agent)
+{
+ // First calculate values as for any other object (for mAppAngle)
+ LLViewerObject::setPixelAreaAndAngle(agent);
+
+ // Re-calculate mPixelArea accurately
+
+ // This should be the camera's center, as soon as we move to all region-local.
+ LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 range = relative_position.magVec(); // ugh, square root
+
+ F32 max_scale = mBillboardScale * getMaxScale();
+ F32 area = max_scale * (max_scale*mBillboardRatio);
+
+ // Compute pixels per meter at the given range
+ F32 pixels_per_meter = gCamera->getViewHeightInPixels() /
+ (tan(gCamera->getView()) * range);
+
+ mPixelArea = (pixels_per_meter) * (pixels_per_meter) * area;
+#if 0
+ // mAppAngle is a bit of voodoo;
+ // use the one calculated LLViewerObject::setPixelAreaAndAngle above
+ // to avoid LOD miscalculations
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+#endif
+}
+
+void LLVOTree::updateTextures(LLAgent &agent)
+{
+ F32 texel_area_ratio = 1.f;
+ F32 cos_angle = 1.f;
+ if (mTreeImagep)
+ {
+ mTreeImagep->addTextureStats(mPixelArea, texel_area_ratio, cos_angle);
+ }
+
+}
+
+
+LLDrawable* LLVOTree::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_TREE);
+
+ LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_TREE, mTreeImagep);
+
+ // Just a placeholder for an actual object...
+ LLFace *facep = mDrawable->addFace(poolp, mTreeImagep, TRUE);
+ facep->setSize(1, 3);
+
+ gPipeline.markMaterialed(mDrawable);
+
+ updateRadius();
+
+ return mDrawable;
+}
+
+
+// Yes, I know this is bad. I'll clean this up soon. - djs 04/02/02
+const S32 LEAF_INDICES = 24;
+const S32 LEAF_VERTICES = 16;
+
+BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
+{
+ U32 i, j;
+ const S32 MAX_SLICES = 32;
+
+ const F32 LEAF_LEFT = 0.52f;
+ const F32 LEAF_RIGHT = 0.98f;
+ const F32 LEAF_TOP = 1.0f;
+ const F32 LEAF_BOTTOM = 0.52f;
+
+ const F32 LEAF_WIDTH = 1.f;
+
+
+ U32 slices = MAX_SLICES;
+
+ S32 max_indices = LEAF_INDICES;
+ S32 max_vertices = LEAF_VERTICES;
+ S32 lod;
+
+ LLFace *face = drawable->getFace(0);
+
+ face->mCenterAgent = getPositionAgent();
+ face->mCenterLocal = face->mCenterAgent;
+
+ LLDrawPool *poolp = face->getPool();
+
+ if (poolp->getVertexCount())
+ {
+ return TRUE;
+ }
+
+ if (!face->getDirty())
+ {
+ return FALSE;
+ }
+
+ poolp->setDirty();
+
+ for (lod = 0; lod < 4; lod++)
+ {
+ slices = sLODSlices[lod];
+ sLODVertexOffset[lod] = max_vertices;
+ sLODVertexCount[lod] = slices*slices;
+ sLODIndexOffset[lod] = max_indices;
+ sLODIndexCount[lod] = (slices-1)*(slices-1)*6;
+ max_indices += sLODIndexCount[lod];
+ max_vertices += sLODVertexCount[lod];
+ }
+
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector2> tex_coords;
+ U32 *indicesp;
+
+ face->setSize(max_vertices, max_indices);
+ face->getGeometry(vertices, normals, tex_coords, indicesp);
+
+ S32 vertex_count = 0;
+ S32 index_count = 0;
+
+ // First leaf
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+
+ *(indicesp++) = 0;
+ index_count++;
+ *(indicesp++) = 1;
+ index_count++;
+ *(indicesp++) = 2;
+ index_count++;
+
+ *(indicesp++) = 0;
+ index_count++;
+ *(indicesp++) = 3;
+ index_count++;
+ *(indicesp++) = 1;
+ index_count++;
+
+ // Same leaf, inverse winding/normals
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+ //*(tex_coords++) = LLVector2(1.f, 1.0f);
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ //*(tex_coords++) = LLVector2(0.52f, 1.0f);
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ //*(tex_coords++) = LLVector2(1.f, 0.52f);
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+ *(indicesp++) = 4;
+ index_count++;
+ *(indicesp++) = 6;
+ index_count++;
+ *(indicesp++) = 5;
+ index_count++;
+
+ *(indicesp++) = 4;
+ index_count++;
+ *(indicesp++) = 5;
+ index_count++;
+ *(indicesp++) = 7;
+ index_count++;
+
+
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+ *(indicesp++) = 8;
+ index_count++;
+ *(indicesp++) = 9;
+ index_count++;
+ *(indicesp++) = 10;
+ index_count++;
+
+ *(indicesp++) = 8;
+ index_count++;
+ *(indicesp++) = 11;
+ index_count++;
+ *(indicesp++) = 9;
+ index_count++;
+
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+
+ *(indicesp++) = 12;
+ index_count++;
+ *(indicesp++) = 14;
+ index_count++;
+ *(indicesp++) = 13;
+ index_count++;
+
+ *(indicesp++) = 12;
+ index_count++;
+ *(indicesp++) = 13;
+ index_count++;
+ *(indicesp++) = 15;
+ index_count++;
+
+ // Generate geometry for the cylinders
+
+ // Different LOD's
+
+ // Generate the vertices
+ // Generate the indices
+
+ for (lod = 0; lod < 4; lod++)
+ {
+ slices = sLODSlices[lod];
+ F32 base_radius = 0.65f;
+ F32 top_radius = base_radius * sSpeciesTable[mSpecies]->mTaper;
+ //llinfos << "Species " << ((U32) mSpecies) << ", taper = " << sSpeciesTable[mSpecies].mTaper << llendl;
+ //llinfos << "Droop " << mDroop << ", branchlength: " << mBranchLength << llendl;
+ F32 angle = 0;
+ F32 angle_inc = 360.f/(slices-1);
+ F32 z = 0.f;
+ F32 z_inc = 1.f;
+ if (slices > 3)
+ {
+ z_inc = 1.f/(slices - 3);
+ }
+ F32 radius = base_radius;
+
+ F32 x1,y1;
+ F32 noise_scale = sSpeciesTable[mSpecies]->mNoiseMag;
+ LLVector3 nvec;
+
+ const F32 cap_nudge = 0.1f; // Height to 'peak' the caps on top/bottom of branch
+
+ const S32 fractal_depth = 5;
+ F32 nvec_scale = 1.f * sSpeciesTable[mSpecies]->mNoiseScale;
+ F32 nvec_scalez = 4.f * sSpeciesTable[mSpecies]->mNoiseScale;
+
+ F32 tex_z_repeat = sSpeciesTable[mSpecies]->mRepeatTrunkZ;
+
+ F32 start_radius;
+ F32 nangle = 0;
+ F32 height = 1.f;
+ F32 r0;
+
+ for (i = 0; i < slices; i++)
+ {
+ if (i == 0)
+ {
+ z = - cap_nudge;
+ r0 = 0.0;
+ }
+ else if (i == (slices - 1))
+ {
+ z = 1.f + cap_nudge;//((i - 2) * z_inc) + cap_nudge;
+ r0 = 0.0;
+ }
+ else
+ {
+ z = (i - 1) * z_inc;
+ r0 = base_radius + (top_radius - base_radius)*z;
+ }
+
+ for (j = 0; j < slices; j++)
+ {
+ if (slices - 1 == j)
+ {
+ angle = 0.f;
+ }
+ else
+ {
+ angle = j*angle_inc;
+ }
+
+ nangle = angle;
+
+ x1 = cos(angle * DEG_TO_RAD);
+ y1 = sin(angle * DEG_TO_RAD);
+ LLVector2 tc;
+ // This isn't totally accurate. Should compute based on slope as well.
+ start_radius = r0 * (1.f + 1.2f*fabs(z - 0.66f*height)/height);
+ nvec.setVec( cos(nangle * DEG_TO_RAD)*start_radius*nvec_scale,
+ sin(nangle * DEG_TO_RAD)*start_radius*nvec_scale,
+ z*nvec_scalez);
+ // First and last slice at 0 radius (to bring in top/bottom of structure)
+ radius = start_radius + turbulence3((F32*)&nvec.mV, (F32)fractal_depth)*noise_scale;
+
+ if (slices - 1 == j)
+ {
+ // Not 0.5 for slight slop factor to avoid edges on leaves
+ tc = LLVector2(0.490f, (1.f - z/2.f)*tex_z_repeat);
+ }
+ else
+ {
+ tc = LLVector2((angle/360.f)*0.5f, (1.f - z/2.f)*tex_z_repeat);
+ }
+
+ *(vertices++) = LLVector3(x1*radius, y1*radius, z);
+ *(normals++) = LLVector3(x1, y1, 0.f);
+ *(tex_coords++) = tc;
+ vertex_count++;
+ }
+ }
+
+ for (i = 0; i < (slices - 1); i++)
+ {
+ for (j = 0; j < (slices - 1); j++)
+ {
+ S32 x1_offset = j+1;
+ if ((j+1) == slices)
+ {
+ x1_offset = 0;
+ }
+ // Generate the matching quads
+ *(indicesp) = j + (i*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = x1_offset + ((i+1)*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = j + ((i+1)*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+
+ *(indicesp) = j + (i*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = x1_offset + (i*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = x1_offset + ((i+1)*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ }
+ }
+ slices /= 2;
+ }
+
+ llassert(vertex_count == max_vertices);
+ llassert(index_count == max_indices);
+
+ return TRUE;
+}
+
+void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth, F32 scale, F32 twist, F32 droop, F32 branches, F32 alpha)
+{
+ //
+ // Draws a tree by recursing, drawing branches and then a 'leaf' texture.
+ // If stop_level = -1, simply draws the whole tree as a billboarded texture
+ //
+
+ if (!draw_pool->getIndexCount())
+ {
+ return; // safety net
+ }
+
+ static F32 constant_twist;
+ static F32 width = 0;
+
+ //F32 length = ((scale == 1.f)? mTrunkLength:mBranchLength);
+ //F32 aspect = ((scale == 1.f)? mTrunkAspect:mBranchAspect);
+ F32 length = ((trunk_depth || (scale == 1.f))? mTrunkLength:mBranchLength);
+ F32 aspect = ((trunk_depth || (scale == 1.f))? mTrunkAspect:mBranchAspect);
+
+ constant_twist = 360.f/branches;
+
+ if (stop_level >= 0)
+ {
+ //
+ // Draw the tree using recursion
+ //
+ if (depth > stop_level)
+ {
+ {
+ llassert(sLODIndexCount[trunk_LOD] > 0);
+ width = scale * length * aspect;
+ glPushMatrix();
+ glScalef(width,width,scale * length);
+ //glDrawElements(GL_TRIANGLES, sLODIndexCount[trunk_LOD], GL_UNSIGNED_INT, draw_pool.getRawIndices() + sLODIndexOffset[trunk_LOD]);
+ glDrawRangeElements(GL_TRIANGLES,
+ sLODVertexOffset[trunk_LOD],
+ sLODVertexOffset[trunk_LOD] + sLODVertexCount[trunk_LOD]-1,
+ sLODIndexCount[trunk_LOD],
+ GL_UNSIGNED_INT,
+ draw_pool->getRawIndices() + sLODIndexOffset[trunk_LOD]);
+ stop_glerror();
+ draw_pool->addIndicesDrawn(sLODIndexCount[trunk_LOD]);
+ glPopMatrix();
+ }
+
+ // Recurse to create more branches
+ for (S32 i=0; i < (S32)branches; i++)
+ {
+ glPushMatrix();
+ glTranslatef(0.f, 0.f, scale * length);
+ glRotatef((constant_twist + ((i%2==0)?twist:-twist))*i, 0.f, 0.f, 1.f);
+ glRotatef(droop, 0.f, 1.f, 0.f);
+ glRotatef(20.f, 0.f, 0.f, 1.f); // rotate 20deg about axis of new branch to add some random variation
+ drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth - 1, 0, scale*mScaleStep, twist, droop, branches, alpha);
+ glPopMatrix();
+ }
+ // Recurse to continue trunk
+ if (trunk_depth)
+ {
+ glPushMatrix();
+ glTranslatef(0.f, 0.f, scale * length);
+ glRotatef(70.5f, 0.f, 0.f, 1.f); // rotate a bit around Z when ascending
+ drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth, trunk_depth-1, scale*mScaleStep, twist, droop, branches, alpha);
+ glPopMatrix();
+ }
+ }
+ else
+ {
+ //
+ // Draw leaves as two 90 deg crossed quads with leaf textures
+ //
+ {
+ glPushMatrix();
+ //glRotatef(llFrand(50.0), llFrand(1.0), llFrand(1.0), llFrand(1.0);
+ //width = scale * (TREE_BRANCH_ASPECT + TREE_LEAF_ASPECT);
+ glScalef(scale*mLeafScale, scale*mLeafScale, scale*mLeafScale);
+ //glScalef(1.5f*width*mLeafScale,1,1.5f*scale*mLeafScale);
+// glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
+ glDrawRangeElements(GL_TRIANGLES,
+ 0,
+ LEAF_VERTICES-1,
+ LEAF_INDICES,
+ GL_UNSIGNED_INT,
+ draw_pool->getRawIndices());
+ stop_glerror();
+ draw_pool->addIndicesDrawn(LEAF_INDICES);
+ glPopMatrix();
+ }
+ }
+ }
+ else
+ {
+ //
+ // Draw the tree as a single billboard texture
+ //
+
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glTranslatef(0.0, -0.5, 0.0);
+ glMatrixMode(GL_MODELVIEW);
+ {
+ glPushMatrix();
+ glScalef(mBillboardScale*mBillboardRatio, mBillboardScale*mBillboardRatio, mBillboardScale);
+// glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
+ glDrawRangeElements(GL_TRIANGLES,
+ 0,
+ LEAF_VERTICES-1,
+ LEAF_INDICES,
+ GL_UNSIGNED_INT,
+ draw_pool->getRawIndices());
+ stop_glerror();
+ draw_pool->addIndicesDrawn(LEAF_INDICES);
+ glPopMatrix();
+ }
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ }
+}
+
+void LLVOTree::updateRadius()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ mDrawable->setRadius(32.0f);
+}
diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h
new file mode 100644
index 0000000000..f962516803
--- /dev/null
+++ b/indra/newview/llvotree.h
@@ -0,0 +1,118 @@
+/**
+ * @file llvotree.h
+ * @brief LLVOTree class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOTREE_H
+#define LL_LLVOTREE_H
+
+#include "llviewerobject.h"
+#include "lldarray.h"
+#include "xform.h"
+
+class LLFace;
+class LLDrawPool;
+
+
+class LLVOTree : public LLViewerObject
+{
+public:
+ LLVOTree(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOTree();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+ static void cleanupClass();
+
+ /*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ // Graphical stuff for objects - maybe broken out into render class later?
+ /*virtual*/ void render(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void updateRadius();
+
+ void drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth, F32 scale, F32 twist, F32 droop, F32 branches, F32 alpha);
+
+ void drawBranch(S32 stop_level, U16 depth, U16 trunk_depth, F32 scale, F32 twist, F32 droop, F32 branches, F32 alpha, BOOL draw_leaves);
+
+ static S32 sMaxTreeSpecies;
+
+ struct TreeSpeciesData
+ {
+ LLUUID mTextureID;
+
+ F32 mBranchLength; // Scale (length) of tree branches
+ F32 mDroop; // Droop from vertical (degrees) at each branch recursion
+ F32 mTwist; // Twist
+ F32 mBranches; // Number of branches emitted at each recursion level
+ U8 mDepth; // Number of recursions to tips of branches
+ F32 mScaleStep; // Multiplier for scale at each recursion level
+ U8 mTrunkDepth;
+
+ F32 mLeafScale; // Scales leaf texture when rendering
+ F32 mTrunkLength; // Scales branch diameters when rendering
+ F32 mBillboardScale; // Scales the billboard representation
+ F32 mBillboardRatio; // Height to width aspect ratio
+ F32 mTrunkAspect;
+ F32 mBranchAspect;
+ F32 mRandomLeafRotate;
+ F32 mNoiseScale; // Scaling of noise function in perlin space (norm = 1.0)
+ F32 mNoiseMag; // amount of perlin noise to deform by (0 = none)
+ F32 mTaper; // amount of perlin noise to deform by (0 = none)
+ F32 mRepeatTrunkZ; // Times to repeat the trunk texture vertically along trunk
+ };
+
+ static F32 sTreeFactor; // Tree level of detail factor
+
+ friend class LLDrawPoolTree;
+protected:
+ LLVector3 mTrunkBend; // Accumulated wind (used for blowing trees)
+ LLVector3 mTrunkVel; //
+ LLVector3 mWind;
+
+ LLPointer<LLViewerImage> mTreeImagep; // Pointer to proper tree image
+
+ U8 mSpecies; // Species of tree
+ F32 mBranchLength; // Scale (length) of tree branches
+ F32 mTrunkLength; // Trunk length (first recursion)
+ F32 mDroop; // Droop from vertical (degrees) at each branch recursion
+ F32 mTwist; // Twist
+ F32 mBranches; // Number of branches emitted at each recursion level
+ U8 mDepth; // Number of recursions to tips of branches
+ F32 mScaleStep; // Multiplier for scale at each recursion level
+ U8 mTrunkDepth;
+
+ F32 mLeafScale; // Scales leaf texture when rendering
+
+ F32 mBillboardScale; // How big to draw the billboard?
+ F32 mBillboardRatio; // Height to width ratio of billboard
+ F32 mTrunkAspect; // Ratio between width/length of trunk
+ F32 mBranchAspect; // Ratio between width/length of branch
+ F32 mRandomLeafRotate; // How much to randomly rotate leaves about arbitrary axis
+
+ U32 mFrameCount;
+
+ typedef std::map<U32, TreeSpeciesData*> SpeciesMap;
+ static SpeciesMap sSpeciesTable;
+
+ static S32 sLODIndexOffset[4];
+ static S32 sLODIndexCount[4];
+ static S32 sLODVertexOffset[4];
+ static S32 sLODVertexCount[4];
+ static S32 sLODSlices[4];
+ static F32 sLODAngles[4];
+};
+
+#endif
diff --git a/indra/newview/llvotreenew.h b/indra/newview/llvotreenew.h
new file mode 100644
index 0000000000..e027c3860a
--- /dev/null
+++ b/indra/newview/llvotreenew.h
@@ -0,0 +1,200 @@
+/**
+ * @file llvotreenew.h
+ * @brief LLVOTreeNew class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOTREENEW_H
+#define LL_LLVOTREENEW_H
+
+#include "llviewerobject.h"
+#include "lldarray.h"
+#include "xform.h"
+
+#include "lltreeparams.h"
+#include "llstrider.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "llviewerimage.h"
+
+class LLFace;
+class LLDrawPool;
+
+// number of static arrays created
+const U8 MAX_SPECIES = 16; // max species of trees
+const U8 MAX_PARTS = 15; // trunk, 2 or 3 branches per species?
+const U8 MAX_RES = 6; // max # cross sections for a branch curve
+const U8 MAX_FLARE = 6; // max # cross sections for flare of trunk
+const U8 MAX_LEVELS = 3;
+
+// initial vertex array allocations
+const U32 NUM_INIT_VERTS = 5000; // number of vertices/normals/texcoords
+const U32 NUM_INIT_INDICES = 15000; // number of indices to vert array (3 vertices per triangle, roughly 3x)
+const U32 NUM_TIMES_TO_DOUBLE = 2; // if we go over initial allocations, num times to double each step
+
+// for finding the closest parts...
+
+// the parts are searched based on:
+const F32 MAX_LOBES_DIFF = 2;
+const F32 MAX_LOBEDEPTH_DIFF = .3f;
+const F32 MAX_CURVEBACK_DIFF = 20.0f;
+const F32 MAX_CURVE_DIFF = 15.0f;
+const F32 MAX_CURVE_V_DIFF = 20.0f;
+
+const F32 CURVEV_DIVIDER = 10.0f; // curveV/CURVEV_DIVIDER = # branch variances...
+const U8 MAX_VARS = 3; // max number of variations of branches
+
+const U8 MAX_RAND_NUMS = 100; // max number of rand numbers to pregenerate and store
+
+// texture params
+const F32 WIDTH_OF_BARK = .48f;
+
+class LLVOTreeNew : public LLViewerObject
+{
+public:
+
+ // Some random number generators using the pre-generated random numbers
+ // return +- negPos
+ static S32 llrand_signed(S32 negPos)
+ {
+ return (gLindenLabRandomNumber.llrand((U32)negPos * 2) - negPos);
+ };
+
+ static S32 llrand_signed(S32 negPos, U32 index)
+ {
+ return lltrunc((sRandNums[index % MAX_RAND_NUMS] * (negPos * 2.0f) - negPos));
+ };
+
+ static S32 llrand_unsigned(S32 pos, U32 index)
+ {
+ return lltrunc((sRandNums[index % MAX_RAND_NUMS] * pos));
+ };
+
+ // return +- negPos
+ static F32 llfrand_signed(F32 negPos)
+ {
+ return (gLindenLabRandomNumber.llfrand(negPos * 2.0f) - negPos);
+ };
+
+ static F32 llfrand_signed(F32 negPos, U32 index)
+ {
+ return (sRandNums[index % MAX_RAND_NUMS] * negPos * 2.0f) - negPos;
+ };
+
+ static F32 llfrand_unsigned(F32 pos, U32 index)
+ {
+ return sRandNums[index % MAX_RAND_NUMS] * pos;
+ };
+
+ // return between 0-pos
+ static F32 llfrand_unsigned(F32 pos)
+ {
+ return gLindenLabRandomNumber.llfrand(pos);
+ };
+
+ static void cleanupTextures() {}; // not needed anymore
+
+ struct TreePart
+ {
+ F32 mRadius; // scale x/y
+ F32 mLength; // scale z
+ F32 mCurve;
+ F32 mCurveV;
+ F32 mCurveRes;
+ F32 mCurveBack;
+ U8 mLobes;
+ F32 mLobeDepth;
+ U8 mLevel;
+ U32 mNumTris;
+ U8 mVertsPerSection;
+ U8 mNumVariants;
+
+ // first index into the drawpool arrays for this particular branch
+ U32 mIndiceIndex[MAX_VARS];
+ U32 mOffsets[MAX_VARS][MAX_RES]; // offsets for the partial branch pieces
+ // local section frames for this branch
+ LLMatrix4 mFrames[MAX_VARS][(MAX_RES*(MAX_RES + 1))/2]; // (0...n) + (1...n) + ... + (n-1..n)
+ LLDynamicArray<LLVector3> mFaceNormals;
+
+ };
+
+ LLVOTreeNew(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOTreeNew();
+
+ /*virtual*/
+ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ void render(LLAgent &agent);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ F32 CalcZStep(TreePart *part, U8 section);
+
+ void createPart(U8 level, F32 length, F32 radius, LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &tex_coords, U32 *indices,
+ U32 &curVertexIndex, U32 &curTexCoordIndex,
+ U32 &curNormalIndex, U32 &curIndiceIndex);
+
+ S32 findSimilarPart(U8 level);
+
+ F32 CalculateSectionRadius(U8 level, F32 y, F32 stemLength, F32 stemRadius);
+ //F32 CalculateVerticalAttraction(U8 level, LLMatrix4 &sectionFrame);
+
+ void createSection(LLMatrix4 &frame, TreePart *part, F32 sectionRadius, F32 stemZ,
+ LLStrider<LLVector3> &vertices, LLStrider<LLVector2> &tex_coords, U32 *indices,
+ U32 &curVertexIndex, U32 &curTexCoordIndex, U32 &curIndiceIndex, U8 curSection, BOOL firstBranch);
+
+ void genIndicesAndFaceNormalsForLastSection(TreePart *part, U8 numVerts, LLStrider<LLVector3> &vertices, U32 curVertexIndex, U32 *indices, U32 &curIndiceIndex, BOOL firstBranch);
+
+ void genVertexNormals(TreePart *part, LLStrider<LLVector3> &normals, U8 numSections, U32 curNormalOffset);
+
+ void drawTree(LLDrawPool &draw_pool, const LLMatrix4 &frame, U8 level, F32 offsetChild, F32 curLength, F32 parentLength, F32 curRadius, F32 parentRadius, U8 part, U8 variant, U8 startSection);
+ void drawTree(LLDrawPool &draw_pool);
+
+
+ //LLTreeParams mParams;
+ U8 mSpecies;
+ LLPointer<LLViewerImage> mTreeImagep;
+ LLMatrix4 mTrunkFlareFrames[MAX_FLARE];
+ F32 mSegSplitsError[3];
+ U32 mRandOffset[MAX_LEVELS];
+
+ U32 mNumTrisDrawn;
+ U32 mTotalIndices;
+ U32 mTotalVerts;
+
+ static void initClass();
+
+ // tree params
+ static LLTreeParams sParameters;
+
+ // next indexes used to drawpool arrays
+ static U32 sNextVertexIndex[MAX_SPECIES];
+ static U32 sNextIndiceIndex[MAX_SPECIES];
+
+ // tree parts
+ static U32 sNextPartIndex[MAX_PARTS];
+ static TreePart sTreeParts[MAX_SPECIES][MAX_PARTS];
+
+ // species images
+ static LLUUID sTreeImageIDs[MAX_SPECIES];
+
+ // random numbers
+ static F32 sRandNums[MAX_RAND_NUMS];
+
+ // usage data
+ static U32 sTreePartsUsed[MAX_SPECIES][MAX_PARTS][MAX_VARS];
+
+
+};
+
+#endif
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
new file mode 100644
index 0000000000..03074d8a73
--- /dev/null
+++ b/indra/newview/llvovolume.cpp
@@ -0,0 +1,2027 @@
+/**
+ * @file llvovolume.cpp
+ * @brief LLVOVolume class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A "volume" is a box, cylinder, sphere, or other primitive shape.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvovolume.h"
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llflexibleobject.h"
+#include "llmaterialtable.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "llvolumemessage.h"
+#include "material_codes.h"
+#include "message.h"
+#include "object_flags.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolbump.h"
+#include "llface.h"
+
+// TEMP HACK ventrella
+#include "llhudmanager.h"
+#include "llflexibleobject.h"
+#include "llanimalcontrols.h"
+
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llviewertextureanim.h"
+#include "llworld.h"
+#include "llselectmgr.h"
+#include "pipeline.h"
+
+const S32 MIN_QUIET_FRAMES_COALESCE = 30;
+
+//#define LLDEBUG_DISPLAY_LODS 1
+
+BOOL gAnimateTextures = TRUE;
+
+F32 LLVOVolume::sLODFactor = 1.f;
+F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop
+F32 LLVOVolume::sLODComplexityDistanceBias = 0.0f;//Changing this to zero makes all prims LOD equally regardless of complexity
+F32 LLVOVolume::sDistanceFactor = 1.0f;
+S32 LLVOVolume::sNumLODChanges = 0;
+
+LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+ : LLViewerObject(id, pcode, regionp),
+ mVolumeImpl(NULL)
+{
+ mRelativeXform.identity();
+ mRelativeXformInvTrans.identity();
+
+ mLOD = MIN_LOD;
+ mInited = FALSE;
+ mAllTEsSame = FALSE;
+ mTextureAnimp = NULL;
+ mGlobalVolume = FALSE;
+
+ mTextureAnimp = NULL;
+ mAllTEsSame = FALSE;
+ mVObjRadius = LLVector3(1,1,0.5f).magVec();
+ mNumFaces = 0;
+}
+
+LLVOVolume::~LLVOVolume()
+{
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ delete mVolumeImpl;
+ mVolumeImpl = NULL;
+}
+
+
+// static
+void LLVOVolume::initClass()
+{
+}
+
+
+U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLColor4U color;
+
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ if (!dp)
+ {
+ if (update_type == OUT_FULL)
+ {
+ ////////////////////////////////
+ //
+ // Unpack texture animation data
+ //
+ //
+
+ if (mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureAnim))
+ {
+ if (!mTextureAnimp)
+ {
+ mTextureAnimp = new LLViewerTextureAnim();
+ }
+ else
+ {
+ if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
+ {
+ mTextureAnimp->reset();
+ }
+ }
+ mTextureAnimp->unpackTAMessage(mesgsys, block_num);
+ }
+ else
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ }
+
+ // Unpack volume data
+ LLVolumeParams volume_params;
+ LLVolumeMessage::unpackVolumeParams(&volume_params, mesgsys, _PREHASH_ObjectData, block_num);
+
+ if (setVolume(volume_params, 0))
+ {
+ markForUpdate(TRUE);
+ }
+ }
+
+ // Sigh, this needs to be done AFTER the volume is set as well, otherwise bad stuff happens...
+ ////////////////////////////
+ //
+ // Unpack texture entry data
+ //
+ if (unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+ }
+ else
+ {
+ // CORY TO DO: Figure out how to get the value here
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ LLVolumeParams volume_params;
+ BOOL res = LLVolumeMessage::unpackVolumeParams(&volume_params, *dp);
+ if (!res)
+ {
+ llwarns << "Bogus volume parameters in object " << getID() << llendl;
+ llwarns << getRegion()->getOriginGlobal() << llendl;
+ }
+
+ if (setVolume(volume_params, 0))
+ {
+ markForUpdate(TRUE);
+ }
+ S32 res2 = unpackTEMessage(*dp);
+ if (TEM_INVALID == res2)
+ {
+ // Well, crap, there's something bogus in the data that we're unpacking.
+ dp->dumpBufferToLog();
+ llwarns << "Flushing cache files" << llendl;
+ char mask[LL_MAX_PATH];
+ sprintf(mask, "%s*.slc", gDirUtilp->getDirDelimiter().c_str());
+ gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask);
+// llerrs << "Bogus TE data in " << getID() << ", crashing!" << llendl;
+ llwarns << "Bogus TE data in " << getID() << llendl;
+ }
+ else if (res2 & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+
+ U32 value = dp->getPassFlags();
+
+ if (value & 0x40)
+ {
+ if (!mTextureAnimp)
+ {
+ mTextureAnimp = new LLViewerTextureAnim();
+ }
+ else
+ {
+ if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
+ {
+ mTextureAnimp->reset();
+ }
+ }
+ mTextureAnimp->unpackTAMessage(*dp);
+ }
+ else
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ }
+ }
+ else
+ {
+ S32 texture_length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureEntry);
+ if (texture_length)
+ {
+ U8 tdpbuffer[1024];
+ LLDataPackerBinaryBuffer tdp(tdpbuffer, 1024);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureEntry, tdpbuffer, 0, block_num);
+ if ( unpackTEMessage(tdp) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ ///////////////////////
+ //
+ // Do texture animation stuff
+ //
+
+ if (mTextureAnimp && gAnimateTextures)
+ {
+ F32 off_s, off_t, scale_s, scale_t, rot;
+ S32 result;
+ if ((result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot)))
+ {
+ U8 has_bump = 0;
+ if (mTextureAnimp->mFace <= -1)
+ {
+ S32 face;
+ for (face = 0; face < getNumTEs(); face++)
+ {
+ if (result & LLViewerTextureAnim::TRANSLATE)
+ {
+ setTEOffset(face, off_s, off_t);
+ }
+ if (result & LLViewerTextureAnim::SCALE)
+ {
+ setTEScale(face, scale_s, scale_t);
+ }
+ if (result & LLViewerTextureAnim::ROTATE)
+ {
+ setTERotation(face, rot);
+ }
+ has_bump |= getTE(face)->getBumpmap();
+ }
+ }
+ else if (mTextureAnimp->mFace < getNumTEs())
+ {
+ if (result & LLViewerTextureAnim::TRANSLATE)
+ {
+ setTEOffset(mTextureAnimp->mFace, off_s, off_t);
+ }
+ if (result & LLViewerTextureAnim::SCALE)
+ {
+ setTEScale(mTextureAnimp->mFace, scale_s, scale_t);
+ }
+ if (result & LLViewerTextureAnim::ROTATE)
+ {
+ setTERotation(mTextureAnimp->mFace, rot);
+ }
+ has_bump |= getTE(mTextureAnimp->mFace)->getBumpmap();
+ }
+// mFaceMappingChanged = TRUE;
+ if (mDrawable->isVisible())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, TRUE);
+ }
+ }
+ }
+
+ // Dispatch to implementation
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->doIdleUpdate(agent, world, time);
+ }
+
+ return TRUE;
+}
+
+void LLVOVolume::updateTextures(LLAgent &agent)
+{
+
+}
+
+//static
+F32 LLVOVolume::getTextureVirtualSize(const LLFace* face)
+{
+ //LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0];
+ //F32 pixel_area = 1.f/llmin(llmax(tdim.mV[0] * tdim.mV[1], 1.f), 10.f);
+ LLVector3 cross_vec = (face->mExtents[1] - face->mExtents[0]);
+
+
+ LLVector3 lookAt = (face->getPositionAgent()-gCamera->getOrigin());
+ F32 dist = lookAt.normVec();
+
+ F32 face_area;
+
+ if (face->isState(LLFace::GLOBAL))
+ {
+ face_area = cross_vec.mV[0]*cross_vec.mV[1]*fabsf(lookAt.mV[2]) +
+ cross_vec.mV[1]*cross_vec.mV[2]*fabsf(lookAt.mV[0]) +
+ cross_vec.mV[0]*cross_vec.mV[2]*fabsf(lookAt.mV[1]);
+ }
+ else
+ {
+ face_area = cross_vec.mV[0]*cross_vec.mV[1] +
+ cross_vec.mV[1]*cross_vec.mV[2] +
+ cross_vec.mV[0]*cross_vec.mV[2];
+ }
+
+ if (face_area <= 0)
+ {
+ return 0.f;
+ }
+
+ F32 view = llmax(lookAt*gCamera->getAtAxis(), 0.5f);
+ F32 dist_ramp = dist * view/face_area;
+ //ramp down distance for things closer than 16 m * lookAt
+ dist /= dist_ramp;
+ dist *= dist;
+ dist *= dist_ramp;
+
+ F32 dist_ratio = face_area / llmax(dist, 0.1f);
+ F32 pixel_area = dist_ratio*gCamera->getScreenPixelArea();
+
+ return view*pixel_area;
+}
+
+void LLVOVolume::updateTextures(S32 lod)
+{
+ // Update the image levels of all textures...
+ // First we do some quick checks.
+
+ // This doesn't take into account whether the object is in front
+ // or behind...
+
+ if (LLViewerImage::sDontLoadVolumeTextures || mDrawable.isNull() || !mDrawable->isVisible())
+ {
+ return;
+ }
+
+ const S32 num_faces = mDrawable->getNumFaces();
+
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ const LLFace* face = mDrawable->getFace(i);
+ const LLTextureEntry *te = face->getTextureEntry();
+ LLViewerImage *imagep = face->getTexture();
+
+ if (!imagep || !te)
+ {
+ continue;
+ }
+
+ F32 vsize;
+
+ if (isHUDAttachment())
+ {
+ vsize = (F32) (imagep->getWidth(0) * imagep->getHeight(0));
+ imagep->setBoostLevel(LLViewerImage::BOOST_HUD);
+ }
+ else
+ {
+ vsize = getTextureVirtualSize(face);
+ }
+
+ imagep->addTextureStats(vsize);
+
+
+ U8 bump = te->getBumpmap();
+ if( te && bump)
+ {
+ gBumpImageList.addTextureStats( bump, imagep->getID(), vsize, 1, 1);
+ }
+ }
+}
+
+BOOL LLVOVolume::isActive() const
+{
+ return !mStatic || mTextureAnimp || isAttachment() || (mVolumeImpl && mVolumeImpl->isActive());
+}
+
+BOOL LLVOVolume::setMaterial(const U8 material)
+{
+ BOOL res = LLViewerObject::setMaterial(material);
+ if (res)
+ {
+ // for deprecated LL_MCODE_LIGHT
+ if (mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_LIGHTING, TRUE);
+ }
+ }
+ return res;
+}
+
+void LLVOVolume::setTexture(const S32 face)
+{
+ llassert(face < getNumTEs());
+ LLViewerImage::bindTexture(getTEImage(face));
+}
+
+void LLVOVolume::setScale(const LLVector3 &scale, BOOL damped)
+{
+ if (scale != getScale())
+ {
+ // store local radius
+ LLViewerObject::setScale(scale);
+
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onSetScale(scale, damped);
+ }
+
+ updateRadius();
+
+ //since drawable transforms do not include scale, changing volume scale
+ //requires an immediate rebuild of volume verts.
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+}
+
+LLFace* LLVOVolume::addFace(S32 f)
+{
+ const LLTextureEntry* te = getTE(f);
+ LLViewerImage* imagep = getTEImage(f);
+ LLDrawPool* poolp;
+ if (isHUDAttachment())
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
+ }
+ else
+ {
+ poolp = LLPipeline::getPoolFromTE(te, imagep);
+ }
+ return mDrawable->addFace(poolp, imagep);
+}
+
+LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+
+ S32 max_tes_to_set = calcAllTEsSame() ? 1 : getNumTEs();
+ for (S32 i = 0; i < max_tes_to_set; i++)
+ {
+ LLFace* face = addFace(i);
+ // JC - should there be a setViewerObject(this) call here?
+ face->setTEOffset(i);
+ }
+ mNumFaces = max_tes_to_set;
+
+ if (isAttachment())
+ {
+ mDrawable->makeActive();
+ }
+
+ if (getIsLight())
+ {
+ // Add it to the pipeline mLightSet
+ gPipeline.setLight(mDrawable, TRUE);
+ }
+
+ updateRadius();
+ mDrawable->updateDistance(*gCamera);
+
+ return mDrawable;
+}
+
+
+BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
+{
+ // Check if we need to change implementations
+ bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE);
+ if (is_flexible)
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, TRUE, false);
+ if (!mVolumeImpl)
+ {
+ LLFlexibleObjectData* data = (LLFlexibleObjectData*)getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
+ mVolumeImpl = new LLVolumeImplFlexible(this, data);
+ }
+ }
+ else
+ {
+ // Mark the parameter not in use
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, FALSE, false);
+ if (mVolumeImpl)
+ {
+ delete mVolumeImpl;
+ mVolumeImpl = NULL;
+ if (mDrawable.notNull())
+ {
+ // Undo the damage we did to this matrix
+ mDrawable->updateXform(FALSE);
+ }
+ }
+ }
+ mGlobalVolume = (mVolumeImpl && mVolumeImpl->isVolumeGlobal());
+
+ //MSMSM Recompute LOD here in case the object was just created,
+ // its LOD might be incorrectly set to minumum detail...
+ calcLOD();
+
+ if (LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique())))
+ {
+ mFaceMappingChanged = TRUE;
+
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onSetVolume(volume_params, detail);
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+F32 LLVOVolume::computeLODProfilePathComplexityBias(){
+ //compute a complexity cost from 0 to 1.0 where the 'simplest' prim has a cost of 0.0
+ // and the 'heaviest' prim has a cost of 1.0
+// LLVolume* volume = getVolume();
+ F32 complexity = 0.0f;
+// const LLVolumeParams& params = volume->getParams();
+// U8 type = volume->getPathType();
+// U8 pcode = this->getPCode();
+// U8 proftype = volume->getProfileType();
+
+ //if(params.getHollow()>0.0f){// || (proftype == 1) || (proftype == 0)){
+ //If it is hollow, or a cube/pyramid(subdivided), the complexity is roughly doubled
+ // complexity+=0.5f;
+ //}
+
+ if(this->getVolume()->getProfile().mParams.getCurveType()==LL_PCODE_PROFILE_SQUARE &&
+ this->getVolume()->getPath().mParams.getCurveType()==LL_PCODE_PATH_LINE)
+ {
+ //Object is a cube so bias it heavily since cubes are subdivided alot.
+// this->setDebugText("CUBE");
+ complexity += 1.0f;
+ }
+
+// if(params.getTwist() != params.getTwistBegin()){
+ //if there is twist.. the complexity is bumped
+// complexity+=0.25f;
+// }
+// if(type != LL_PCODE_PATH_LINE)//If the path is not a line it is more complex
+// complexity+=0.2f;
+ return complexity * sLODComplexityDistanceBias;
+}
+
+S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius)
+{
+ S32 cur_detail;
+ // We've got LOD in the profile, and in the twist. Use radius.
+ F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
+ cur_detail = LLVolumeLODGroup::getDetailFromTan(tan_angle);
+ return cur_detail;
+}
+
+BOOL LLVOVolume::calcLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+ S32 cur_detail = 0;
+ /*if (isHUDAttachment())
+ {
+ cur_detail = LLVolumeLODGroup::NUM_LODS-1; // max detail
+ }
+ else*/
+ {
+ F32 radius = (mVolumep->mLODScaleBias.scaledVec(getScale())).magVec();
+ F32 distance = mDrawable->mDistanceWRTCamera;
+ distance *= sDistanceFactor;
+
+ F32 rampDist = LLVOVolume::sLODFactor * 2;
+
+ if (distance < rampDist)
+ {
+ // Boost LOD when you're REALLY close
+ distance *= 1.0f/rampDist;
+ distance *= distance;
+ distance *= rampDist;
+ }
+ else
+ {
+ //Now adjust the computed distance by some factor based on the geometric complexity of the primitive
+ distance += computeLODProfilePathComplexityBias();
+ }
+ // Compensate for field of view changing on FOV zoom.
+ distance *= gCamera->getView();
+
+ cur_detail = computeLODDetail(distance, radius);
+
+ //update textures with what the real LOD is
+ updateTextures(cur_detail);
+
+ if(cur_detail != mLOD)
+ {
+ // Here we test whether the LOD is increasing or decreasing to introduce a slop factor
+ if(cur_detail < mLOD)
+ {
+ // Viewer is moving away from the object
+ // so bias our LOD by adding a fixed amount to the distance.
+ // This will reduce the problem of LOD twitching when the
+ // user makes slight movements near the LOD transition threshhold.
+ F32 test_distance = distance - (distance*sLODSlopDistanceFactor/(1.0f+sLODFactor));
+ if(test_distance < 0.0f) test_distance = 0.0f;
+ S32 potential_detail = computeLODDetail( test_distance, radius );
+ if(potential_detail >= mLOD )
+ { //The LOD has truly not changed
+ cur_detail = mLOD;
+ }
+ }
+ }
+ }
+
+ if (cur_detail != mLOD)
+ {
+ mAppAngle = (F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG;
+ mLOD = cur_detail;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLVOVolume::updateLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ BOOL lod_changed = calcLOD();
+
+#if LLDEBUG_DISPLAY_LODS
+ //MS Enable this to display LOD numbers on objects
+ std::ostringstream msg;
+ msg << cur_detail;//((cur_detail<mLOD)?"-":cur_detail==mLOD?"=":"+") << (int)cur_detail << " , " << mDrawable->mDistanceWRTCamera << " , " << ((LLVOVolume::sLODFactor*mVObjRadius)/mDrawable->mDistanceWRTCamera);
+ this->setDebugText(msg.str());
+#endif
+
+ if (lod_changed)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, FALSE);
+ mLODChanged = TRUE;
+ }
+
+ return lod_changed;
+}
+
+BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp)
+{
+ if (!LLViewerObject::setDrawableParent(parentp))
+ {
+ // no change in drawable parent
+ return FALSE;
+ }
+
+ if (!mDrawable->isRoot())
+ {
+ // parent is dynamic, so I'll need to share its drawable, must rebuild to share drawables
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+
+ if (mDrawable->isActive() && !parentp->isActive())
+ {
+ parentp->makeActive();
+ }
+ else if (mDrawable->isStatic() && parentp->isActive())
+ {
+ mDrawable->makeActive();
+ }
+ }
+
+ return TRUE;
+}
+
+void LLVOVolume::updateFaceFlags()
+{
+ for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ BOOL fullbright = getTE(i)->getFullbright();
+ face->clearState(LLFace::FULLBRIGHT | LLFace::HUD_RENDER | LLFace::LIGHT);
+
+ if (fullbright || (mMaterial == LL_MCODE_LIGHT))
+ {
+ face->setState(LLFace::FULLBRIGHT);
+ }
+ if (mDrawable->isLight())
+ {
+ face->setState(LLFace::LIGHT);
+ }
+ if (isHUDAttachment())
+ {
+ face->setState(LLFace::HUD_RENDER);
+ }
+ if (getAllTEsSame())
+ {
+ break; // only 1 face
+ }
+ }
+}
+
+// NOTE: regenFaces() MUST be followed by genTriangles()!
+void LLVOVolume::regenFaces()
+{
+ // remove existing faces
+ // use mDrawable->getVOVolume() in case of shared drawables
+ mDrawable->getVOVolume()->deleteFaces(this);
+ mFaceIndexOffset = mDrawable->getNumFaces();
+ // add new faces
+ mNumFaces = getAllTEsSame() ? 1 : getNumTEs();
+ for (S32 i = 0; i < mNumFaces; i++)
+ {
+ LLFace* facep = addFace(i);
+ facep->setViewerObject(this);
+ facep->setTEOffset(i);
+ }
+ // Need to do this as texture entries may not correspond to faces any more!
+ mDrawable->updateTexture();
+ gPipeline.markMaterialed(mDrawable);
+}
+
+BOOL LLVOVolume::genTriangles(BOOL force_global)
+{
+ BOOL res = TRUE;
+
+ LLVector3 min,max;
+
+ if (getAllTEsSame())
+ {
+ setupSingleFace(mFaceIndexOffset);
+ LLFace *face = mDrawable->getFace(mFaceIndexOffset);
+ S32 num_faces = getVolume()->getNumFaces();
+ res = face->genVolumeTriangles(*getVolume(), 0, num_faces-1,
+ mRelativeXform, mRelativeXformInvTrans,
+ mGlobalVolume | force_global);
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ min = face->mExtents[0];
+ max = face->mExtents[1];
+ }
+ mWereAllTEsSame = TRUE;
+ }
+ else
+ {
+ for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ res &= face->genVolumeTriangles(*getVolume(), i,
+ mRelativeXform, mRelativeXformInvTrans,
+ mGlobalVolume | force_global);
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ if (i == 0)
+ {
+ min = face->mExtents[0];
+ max = face->mExtents[1];
+ }
+ else
+ {
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (face->mExtents[0].mV[i] < min.mV[i])
+ {
+ min.mV[i] = face->mExtents[0].mV[i];
+ }
+ if (face->mExtents[1].mV[i] > max.mV[i])
+ {
+ max.mV[i] = face->mExtents[1].mV[i];
+ }
+ }
+ }
+ }
+ }
+ mWereAllTEsSame = FALSE;
+ }
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ mDrawable->setSpatialExtents(min,max);
+ if (!isVolumeGlobal())
+ {
+ mDrawable->setPositionGroup((min+max)*0.5f);
+ }
+ else
+ {
+ mDrawable->setPositionGroup(getPosition());
+ }
+
+ updateRadius();
+ mDrawable->updateBinRadius();
+ mDrawable->movePartition();
+ }
+
+ return res;
+}
+
+void LLVOVolume::updateRelativeXform(BOOL global_volume)
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->updateRelativeXform(global_volume);
+ return;
+ }
+
+ LLDrawable* drawable = mDrawable;
+
+ if (drawable->isActive())
+ {
+ // setup relative transforms
+ LLQuaternion delta_rot;
+ LLVector3 delta_pos, delta_scale;
+
+ //matrix from local space to parent relative/global space
+ delta_rot = drawable->isSpatialRoot() ? LLQuaternion() : mDrawable->getRotation();
+ delta_pos = drawable->isSpatialRoot() ? LLVector3(0,0,0) : mDrawable->getPosition();
+ delta_scale = mDrawable->getScale();
+
+ // Vertex transform (4x4)
+ LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
+ LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
+
+ mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(delta_pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ }
+ else
+ {
+ LLVector3 pos = getPosition();
+ LLVector3 scale = getScale();
+ LLQuaternion rot = getRotation();
+
+ if (mParent)
+ {
+ pos *= mParent->getRotation();
+ pos += mParent->getPosition();
+ rot *= mParent->getRotation();
+ }
+
+ LLViewerRegion* region = getRegion();
+ pos += region->getOriginAgent();
+
+ LLVector3 x_axis = LLVector3(scale.mV[VX], 0.f, 0.f) * rot;
+ LLVector3 y_axis = LLVector3(0.f, scale.mV[VY], 0.f) * rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, scale.mV[VZ]) * rot;
+
+ mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ }
+}
+
+BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
+{
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_PRIMITIVES);
+
+ if (mVolumeImpl != NULL)
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_FLEX);
+ BOOL res = mVolumeImpl->doUpdateGeometry(drawable);
+ updateFaceFlags();
+ if (res)
+ {
+ drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+ }
+
+ return res;
+ }
+
+ BOOL compiled = FALSE;
+ BOOL change_shared = FALSE;
+
+ updateRelativeXform();
+
+ if (mDrawable.isNull()) // Not sure why this is happening, but it is...
+ {
+ return TRUE; // No update to complete
+ }
+
+ calcAllTEsSame();
+
+ if (mVolumeChanged || mFaceMappingChanged || change_shared)
+ {
+ compiled = TRUE;
+ mInited = TRUE;
+
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ }
+ drawable->setState(LLDrawable::REBUILD_GEOMETRY);
+ if (mVolumeChanged || change_shared)
+ {
+ drawable->setState(LLDrawable::REBUILD_LIGHTING);
+ }
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ regenFaces();
+ genTriangles(FALSE);
+ }
+ }
+ else if (mLODChanged)
+ {
+ LLPointer<LLVolume> old_volumep, new_volumep;
+ F32 old_lod, new_lod;
+
+ old_volumep = getVolume();
+ old_lod = old_volumep->getDetail();
+
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ }
+ new_volumep = getVolume();
+ new_lod = new_volumep->getDetail();
+
+ if (new_lod != old_lod)
+ {
+ compiled = TRUE;
+ sNumLODChanges += (getAllTEsSame() ? 1 : getVolume()->getNumFaces());
+
+ drawable->setState(LLDrawable::REBUILD_ALL); // for face->genVolumeTriangles()
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ if (new_volumep->getNumFaces() != old_volumep->getNumFaces())
+ {
+ regenFaces();
+ }
+ genTriangles(FALSE);
+ }
+ }
+ }
+ // it has its own drawable (it's moved) or it has changed UVs or it has changed xforms from global<->local
+ else
+ {
+ compiled = TRUE;
+ // All it did was move or we changed the texture coordinate offset
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ genTriangles(FALSE);
+ }
+
+ // Update face flags
+ updateFaceFlags();
+
+ if(compiled)
+ {
+ LLPipeline::sCompiles++;
+ }
+
+ mVolumeChanged = FALSE;
+ mLODChanged = FALSE;
+ mFaceMappingChanged = FALSE;
+
+ drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+
+ return TRUE;
+}
+
+BOOL LLVOVolume::isRootEdit() const
+{
+ if (mParent && !((LLViewerObject*)mParent)->isAvatar())
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void LLVOVolume::setTEImage(const U8 te, LLViewerImage *imagep)
+{
+// llinfos << "SetTEImage:" << llendl;
+ BOOL changed = (mTEImages[te] != imagep);
+ LLViewerObject::setTEImage(te, imagep);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ mFaceMappingChanged = TRUE;
+ }
+ }
+}
+
+S32 LLVOVolume::setTETexture(const U8 te, const LLUUID &uuid)
+{
+ BOOL changed = (uuid != getTE(te)->getID() || (uuid == LLUUID::null));
+
+ S32 res = LLViewerObject::setTETexture(te, uuid);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEColor(const U8 te, const LLColor4 &color)
+{
+ BOOL changed = (color != getTE(te)->getColor());
+ S32 res = LLViewerObject::setTEColor(te, color);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+// mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEBumpmap(const U8 te, const U8 bumpmap)
+{
+ BOOL changed = (bumpmap != getTE(te)->getBumpmap());
+ S32 res = LLViewerObject::setTEBumpmap(te, bumpmap);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTETexGen(const U8 te, const U8 texgen)
+{
+ BOOL changed = (texgen != getTE(te)->getTexGen());
+ S32 res = LLViewerObject::setTETexGen(te, texgen);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEShiny(const U8 te, const U8 shiny)
+{
+ BOOL changed = (shiny != getTE(te)->getShiny());
+ S32 res = LLViewerObject::setTEShiny(te, shiny);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ BOOL changed = (fullbright != getTE(te)->getFullbright());
+ S32 res = LLViewerObject::setTEFullbright(te, fullbright);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ if (!mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ updateFaceFlags();
+ }
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ bool changed = (media_flags != getTE(te)->getMediaFlags());
+ S32 res = LLViewerObject::setTEMediaFlags(te, media_flags);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (s != olds || t != oldt);
+ S32 res = LLViewerObject::setTEScale(te, s, t);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleS(const U8 te, const F32 s)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (s != olds);
+ S32 res = LLViewerObject::setTEScaleS(te, s);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleT(const U8 te, const F32 t)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (t != oldt);
+ S32 res = LLViewerObject::setTEScaleT(te, t);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+void LLVOVolume::updateTEData()
+{
+ if (mDrawable.notNull())
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+}
+
+BOOL LLVOVolume::calcAllTEsSame()
+{
+ BOOL is_alpha = FALSE;
+ BOOL was_same = mAllTEsSame;
+ BOOL all_same = TRUE;
+ S32 num_tes = getNumTEs();
+
+ LLViewerImage *first_texturep = getTEImage(0);
+ if (!first_texturep)
+ {
+ return FALSE;
+ }
+
+ const LLTextureEntry *tep = getTE(0);
+ if (!tep)
+ {
+ llwarns << "Volume with zero textures!" << llendl;
+ return FALSE;
+ }
+
+ if (tep->getColor().mV[3] != 1.f)
+ {
+ is_alpha = TRUE;
+ }
+ const LLColor4 first_color = tep->getColor();
+ const U8 first_bump = tep->getBumpShinyFullbright();
+ const U8 first_media_flags = tep->getMediaTexGen();
+
+ if (first_texturep->getComponents() == 4)
+ {
+ is_alpha = TRUE;
+ }
+
+ F32 s_scale, t_scale;
+ tep->getScale(&s_scale, &t_scale);
+
+ for (S32 f = 1; f < num_tes; f++)
+ {
+ LLViewerImage *texturep = getTEImage(f);
+ if (texturep != first_texturep)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ tep = getTE(f);
+
+ if( tep->getBumpShinyFullbright() != first_bump )
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (first_bump)
+ {
+ F32 cur_s, cur_t;
+ tep->getScale(&cur_s, &cur_t);
+ if ((cur_s != s_scale) || (cur_t != t_scale))
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+
+ if ((texturep->getComponents() == 4) || (tep->getColor().mV[3] != 1.f))
+ {
+ if (!is_alpha)
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+ else if (is_alpha)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (tep->getColor() != first_color)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (tep->getMediaTexGen() != first_media_flags)
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+
+ mAllTEsSame = all_same;
+ if (was_same != all_same)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); // rebuild NOW
+ mFaceMappingChanged = TRUE;
+ }
+ return mAllTEsSame;
+}
+
+void LLVOVolume::setupSingleFace(S32 face_offset)
+{
+ S32 num_indices = 0;
+ S32 num_vertices = 0;
+
+ if (mDrawable.isNull())
+ {
+ llerrs << "setupSingleFace called with NULL mDrawable" << llendl;
+ }
+ if (face_offset >= mDrawable->getNumFaces())
+ {
+ llerrs << "setupSingleFace called with invalid face_offset" << llendl;
+ }
+
+ const S32 num_faces = getVolume()->getNumFaces();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ const LLVolumeFace &vf = getVolume()->getVolumeFace(i);
+ num_vertices += vf.mVertices.size();
+ num_indices += vf.mIndices.size();
+ }
+ LLFace *facep = mDrawable->getFace(face_offset);
+ facep->setSize(num_vertices, num_indices);
+}
+
+//----------------------------------------------------------------------------
+
+void LLVOVolume::setIsLight(BOOL is_light)
+{
+ if (is_light != getIsLight())
+ {
+ if (is_light)
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT, TRUE, true);
+ }
+ else
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT, FALSE, true);
+ }
+
+ if (is_light)
+ {
+ // Add it to the pipeline mLightSet
+ gPipeline.setLight(mDrawable, TRUE);
+ }
+ else
+ {
+ // Not a light. Remove it from the pipeline's light set.
+ gPipeline.setLight(mDrawable, FALSE);
+
+ // Remove this object from any object which has it as a light
+ if (mDrawable)
+ {
+ mDrawable->clearLightSet();
+ }
+ }
+ }
+}
+
+void LLVOVolume::setLightColor(const LLColor3& color)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getColor() != color)
+ {
+ param_block->setColor(LLColor4(color, param_block->getColor().mV[3]));
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightIntensity(F32 intensity)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getColor().mV[3] != intensity)
+ {
+ param_block->setColor(LLColor4(LLColor3(param_block->getColor()), intensity));
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightRadius(F32 radius)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getRadius() != radius)
+ {
+ param_block->setRadius(radius);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightFalloff(F32 falloff)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getFalloff() != falloff)
+ {
+ param_block->setFalloff(falloff);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightCutoff(F32 cutoff)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getCutoff() != cutoff)
+ {
+ param_block->setCutoff(cutoff);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLVOVolume::getIsLight() const
+{
+ return getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT);
+}
+
+LLColor3 LLVOVolume::getLightBaseColor() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return LLColor3(param_block->getColor());
+ }
+ else
+ {
+ return LLColor3(1,1,1);
+ }
+}
+
+LLColor3 LLVOVolume::getLightColor() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return LLColor3(param_block->getColor()) * param_block->getColor().mV[3];
+ }
+ else
+ {
+ return LLColor3(1,1,1);
+ }
+}
+
+F32 LLVOVolume::getLightIntensity() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getColor().mV[3];
+ }
+ else
+ {
+ return 1.f;
+ }
+}
+
+F32 LLVOVolume::getLightRadius() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getRadius();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLVOVolume::getLightFalloff() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getFalloff();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLVOVolume::getLightCutoff() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getCutoff();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// returns < 0 if inside radius
+F32 LLVOVolume::getLightDistance(const LLVector3& pos) const
+{
+ LLVector3 dpos = getRenderPosition() - pos;
+ F32 dist = dpos.magVec() - getLightRadius();
+ return dist;
+}
+
+// returns intensity, modifies color in result
+F32 LLVOVolume::calcLightAtPoint(const LLVector3& pos, const LLVector3& norm, LLColor4& result)
+{
+ if (!getIsLight())
+ {
+ return 0.0f;
+ }
+ F32 light_radius = getLightRadius();
+ LLVector3 light_pos = getRenderPosition();
+ LLVector3 light_dir = light_pos - pos;
+ F32 dist = light_dir.normVec();
+ F32 dp = norm * light_dir;
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS))
+ {
+ if (dp <= 0)
+ {
+ result *= 0;
+ return 0;
+ }
+
+ if (dist >= light_radius)
+ {
+ result *= 0;
+ return 0;
+ }
+
+ F32 mag = 1.0f-(dist/light_radius);
+ mag = powf(mag, 0.75f);
+ mag *= dp;
+ result = getLightColor() * mag;
+ return mag;
+ }
+ else
+ {
+ F32 light_radius = getLightRadius();
+ LLVector3 light_pos = getRenderPosition();
+ LLVector3 light_dir = light_pos - pos;
+ F32 dist = light_dir.normVec();
+ F32 dp = norm * light_dir;
+ F32 atten = (1.f/.2f) / (light_radius); // 20% of brightness at radius
+ F32 falloff = 1.f / (dist * atten);
+ F32 mag = falloff * dp;
+ mag = llmax(mag, 0.0f);
+ result = getLightColor() * mag;
+ return mag;
+ }
+}
+
+BOOL LLVOVolume::updateLighting(BOOL do_lighting)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mDrawable->isStatic())
+ {
+ do_lighting = FALSE;
+ }
+
+ const LLMatrix4& mat_vert = mDrawable->getWorldMatrix();
+ const LLMatrix3& mat_normal = LLMatrix3(mDrawable->getWorldRotation());
+
+ LLVolume* volume = getVolume();
+ if (getAllTEsSame())
+ {
+ LLFace *face = mDrawable->getFace(mFaceIndexOffset);
+ S32 num_faces = volume->getNumFaces();
+ if (face && face->getGeomCount())
+ {
+ face->genLighting(volume, mDrawable, 0, num_faces-1, mat_vert, mat_normal, do_lighting);
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < volume->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ if (face && face->getGeomCount())
+ {
+ face->genLighting(volume, mDrawable, i, i, mat_vert, mat_normal, do_lighting);
+ }
+ }
+ }
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLVOVolume::isFlexible() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE))
+ {
+ if (getVolume()->getParams().getPathParams().getCurveType() != LL_PCODE_PATH_FLEXIBLE)
+ {
+ llwarns << "wtf" << llendl;
+ LLVolumeParams volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_FLEXIBLE);
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLVOVolume::isVolumeGlobal() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE;
+ }
+ return FALSE;
+}
+
+BOOL LLVOVolume::canBeFlexible() const
+{
+ U8 path = getVolume()->getParams().getPathParams().getCurveType();
+ return (path == LL_PCODE_PATH_FLEXIBLE || path == LL_PCODE_PATH_LINE);
+}
+
+BOOL LLVOVolume::setIsFlexible(BOOL is_flexible)
+{
+ BOOL res = FALSE;
+ BOOL was_flexible = isFlexible();
+ LLVolumeParams volume_params;
+ if (is_flexible)
+ {
+ if (!was_flexible)
+ {
+ volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_FLEXIBLE);
+ res = TRUE;
+ setFlags(FLAGS_USE_PHYSICS, FALSE);
+ setFlags(FLAGS_PHANTOM, TRUE);
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, TRUE, true);
+ if (mDrawable)
+ {
+ mDrawable->makeActive();
+ }
+ }
+ }
+ else
+ {
+ if (was_flexible)
+ {
+ volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_LINE);
+ res = TRUE;
+ setFlags(FLAGS_PHANTOM, FALSE);
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, FALSE, true);
+ }
+ }
+ if (res)
+ {
+ res = setVolume(volume_params, 1);
+ if (res)
+ {
+ markForUpdate(TRUE);
+ }
+ }
+ return res;
+}
+
+//----------------------------------------------------------------------------
+
+void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point)
+{
+ LLVolume *volume = getVolume();
+
+ if (volume)
+ {
+ LLVector3 view_vector;
+ view_vector = view_point;
+
+ if (!isVolumeGlobal())
+ { //transform view vector into volume space
+ view_vector -= getRenderPosition();
+ LLQuaternion worldRot = getRenderRotation();
+ view_vector = view_vector * ~worldRot;
+ LLVector3 objScale = getScale();
+ LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+ view_vector.scaleVec(invObjScale);
+ }
+
+ updateRelativeXform();
+ volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, mRelativeXform, mRelativeXformInvTrans);
+
+ nodep->mSilhouetteExists = TRUE;
+ }
+}
+
+void LLVOVolume::deleteFaces(LLVOVolume* childp)
+{
+ S32 face_count = childp->mNumFaces;
+ S32 start_index = childp->mFaceIndexOffset;
+ if (mDrawable.notNull())
+ {
+ mDrawable->deleteFaces(start_index, face_count);
+ }
+ if (mFaceIndexOffset > start_index)
+ {
+ mFaceIndexOffset -= face_count;
+ }
+
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ LLViewerObject* siblingp = mChildList[i];
+ if (siblingp != childp)
+ {
+ if (siblingp->getPCode() == LL_PCODE_VOLUME &&
+ ((LLVOVolume*)siblingp)->mFaceIndexOffset > start_index)
+ {
+ ((LLVOVolume*)siblingp)->mFaceIndexOffset -= face_count;
+ }
+ }
+ }
+ childp->mFaceIndexOffset = 0;
+ childp->mNumFaces = 0;
+}
+
+void LLVOVolume::updateRadius()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ mVObjRadius = getScale().magVec();
+ mDrawable->setRadius(mVObjRadius);
+}
+
+
+BOOL LLVOVolume::isAttachment() const
+{
+ if (mState == 0)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+BOOL LLVOVolume::isHUDAttachment() const
+{
+ //HACK assume hud attachment points are in defined range
+ // since this range is constant for backwards compatibility reasons
+ // this is probably a reasonable assumption to make
+ S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mState);
+ return ( attachment_id >= 31 && attachment_id <= 38 );
+}
+
+
+const LLMatrix4 LLVOVolume::getRenderMatrix() const
+{
+ if (mDrawable->isActive() && !mDrawable->isRoot())
+ {
+ return mDrawable->getParent()->getWorldMatrix();
+ }
+ return mDrawable->getWorldMatrix();
+}
+
+void LLVOVolume::writeCAL3D(apr_file_t* fp, std::string& path, std::string& file_base, S32 joint_num, LLVector3& pos, LLQuaternion& rot, S32& material_index, S32& texture_index, std::multimap<LLUUID, LLMaterialExportInfo*>& material_map)
+{
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLVector3 final_pos = getPosition();
+ final_pos *= 100.f;
+
+ final_pos = final_pos * rot;
+ final_pos += pos;
+ LLQuaternion final_rot;
+ final_rot = getRotation() * rot;
+ LLMatrix4 transform;
+ transform.initAll(getScale(), final_rot, final_pos);
+
+ LLMatrix4 int_transpose_transform;
+ int_transpose_transform.initAll(LLVector3(1.f / getScale().mV[VX], 1.f / getScale().mV[VY], 1.f / getScale().mV[VZ]), final_rot, LLVector3::zero);
+
+ for (S32 i = 0; i < mDrawable->getNumFaces(); i++)
+ {
+ S32 vert_num = 0;
+ LLFace* facep = mDrawable->getFace(i);
+ LLDrawPool* poolp = facep->getPool();
+
+ const LLTextureEntry* tep = facep->getTextureEntry();
+ if (!tep)
+ {
+ continue;
+ }
+
+ S32 my_material = -1;
+ S32 my_texture = -1;
+ LLColor4 face_color = tep->getColor();
+
+ typedef std::multimap<LLUUID, LLMaterialExportInfo*>::iterator material_it_t;
+ std::pair<material_it_t, material_it_t> found_range = material_map.equal_range(tep->getID());
+ material_it_t material_it = found_range.first;
+
+ LLMaterialExportInfo* material_info = NULL;
+
+ while(material_it != material_map.end() && material_it != found_range.second)
+ {
+ // we've at least found a matching texture, so reuse it
+ my_texture = material_it->second->mTextureIndex;
+ if (material_it->second->mColor == face_color)
+ {
+ // we've found a matching material
+ material_info = material_it->second;
+ }
+ ++material_it;
+ }
+
+ if (material_info)
+ {
+ // material already exported, just reuse it
+ my_material = material_info->mMaterialIndex;
+ my_texture = material_info->mTextureIndex;
+ }
+ else
+ {
+ // reserve new material number
+ my_material = material_index++;
+
+ // if we didn't already find a matching texture...
+ if (my_texture == -1)
+ {
+ //...use the next available slot...
+ my_texture = texture_index++;
+
+ //...and export texture as image file
+ char filename[MAX_PATH];
+ sprintf(filename, "%s\\%s_material_tex_%d.tga", path.c_str(), file_base.c_str(), my_texture);
+
+ LLViewerImage* imagep = facep->getTexture();
+ if (imagep->getTexName() == 0)
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ continue;
+ }
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ material_info = new LLMaterialExportInfo(my_material, my_texture, face_color);
+ material_map.insert(std::make_pair<LLUUID, LLMaterialExportInfo*>(tep->getID(), material_info));
+ }
+
+ apr_file_printf(fp, "\t<SUBMESH NUMVERTICES=\"%d\" NUMFACES=\"%d\" MATERIAL=\"%d\" NUMLODSTEPS=\"0\" NUMSPRINGS=\"0\" NUMTEXCOORDS=\"1\">\n",
+ facep->getGeomCount(), facep->getIndicesCount() / 3, my_material);
+
+ for (S32 vert_index = 0; vert_index < facep->getGeomCount(); vert_index++)
+ {
+ LLVector3 vert_pos = poolp->getVertex(facep->getGeomStart() + vert_index);
+ vert_pos *= 100.f;
+ vert_pos = vert_pos * transform;
+ LLVector3 vert_norm = poolp->getNormal(facep->getGeomStart() + vert_index);
+ vert_norm = vert_norm * int_transpose_transform;
+ LLVector2 vert_tc = poolp->getTexCoord(facep->getGeomStart() + vert_index, 0);
+ apr_file_printf(fp, " <VERTEX ID=\"%d\" NUMINFLUENCES=\"1\">\n", vert_num++);
+ apr_file_printf(fp, " <POS>%.4f %.4f %.4f</POS>\n", vert_pos.mV[VX], vert_pos.mV[VY], vert_pos.mV[VZ]);
+ apr_file_printf(fp, " <NORM>%.6f %.6f %.6f</NORM>\n", vert_norm.mV[VX], vert_norm.mV[VY], vert_norm.mV[VZ]);
+ apr_file_printf(fp, " <TEXCOORD>%.6f %.6f</TEXCOORD>\n", vert_tc.mV[VX], 1.f - vert_tc.mV[VY]);
+ apr_file_printf(fp, " <INFLUENCE ID=\"%d\">1.0</INFLUENCE>\n", joint_num + 1);
+ apr_file_printf(fp, " </VERTEX>\n");
+ }
+
+ for (U32 index_i = 0; index_i < facep->getIndicesCount(); index_i += 3)
+ {
+ U32 index_a = poolp->getIndex(facep->getIndicesStart() + index_i) - facep->getGeomStart();
+ U32 index_b = poolp->getIndex(facep->getIndicesStart() + index_i + 1) - facep->getGeomStart();
+ U32 index_c = poolp->getIndex(facep->getIndicesStart() + index_i + 2) - facep->getGeomStart();
+ apr_file_printf(fp, " <FACE VERTEXID=\"%d %d %d\" />\n", index_a, index_b, index_c);
+ }
+
+ apr_file_printf(fp, " </SUBMESH>\n");
+ }
+
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ ((LLVOVolume*)(LLViewerObject*)mChildList[i])->writeCAL3D(fp, path, file_base, joint_num, final_pos, final_rot, material_index, texture_index, material_map);
+ }
+}
+
+//static
+void LLVOVolume::preUpdateGeom()
+{
+ sNumLODChanges = 0;
+}
+
+void LLVOVolume::parameterChanged(U16 param_type, bool local_origin)
+{
+ LLViewerObject::parameterChanged(param_type, local_origin);
+}
+
+void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin)
+{
+ LLViewerObject::parameterChanged(param_type, data, in_use, local_origin);
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onParameterChanged(param_type, data, in_use, local_origin);
+ }
+ if (mDrawable.notNull())
+ {
+ BOOL is_light = getIsLight();
+ if (is_light != mDrawable->isState(LLDrawable::LIGHT))
+ {
+ gPipeline.setLight(mDrawable, is_light);
+ }
+ }
+}
+
+void LLVOVolume::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+{
+}
+
+const LLVector3 LLVOVolume::getPivotPositionAgent() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getPivotPosition();
+ }
+ return LLViewerObject::getPivotPositionAgent();
+}
+
+void LLVOVolume::onShift(const LLVector3 &shift_vector)
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onShift(shift_vector);
+ }
+}
+
+const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getWorldMatrix(xform);
+ }
+ return xform->getWorldMatrix();
+}
+
+LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
+{
+ if (isVolumeGlobal())
+ {
+ return pos;
+ }
+
+ LLVector3 ret = pos - getRenderPosition();
+ ret = ret * ~getRenderRotation();
+ LLVector3 objScale = getScale();
+ LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+ ret.scaleVec(invObjScale);
+
+ return ret;
+}
+
+LLVector3 LLVOVolume::agentDirectionToVolume(const LLVector3& dir) const
+{
+ return isVolumeGlobal() ? dir : (dir * ~getRenderRotation());
+}
+
+LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
+{
+ LLVector3 ret = dir;
+ ret.scaleVec(getScale());
+ ret = ret * getRenderRotation();
+ ret += getRenderPosition();
+
+ return ret;
+}
+
+BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ LLVolume* volume = getVolume();
+ BOOL ret = FALSE;
+ if (volume)
+ {
+ LLVector3 v_start, v_end, v_dir;
+
+ v_start = agentPositionToVolume(start);
+ v_end = agentPositionToVolume(end);
+
+ if (LLLineSegmentAABB(v_start, v_end, volume->mBounds[0], volume->mBounds[1]))
+ {
+ if (volume->lineSegmentIntersect(v_start, v_end) >= 0)
+ {
+ end = volumePositionToAgent(v_end);
+ ret = TRUE;
+ }
+ }
+ }
+ return ret;
+}
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
new file mode 100644
index 0000000000..26764d62c9
--- /dev/null
+++ b/indra/newview/llvovolume.h
@@ -0,0 +1,213 @@
+/**
+ * @file llvovolume.h
+ * @brief LLVOVolume class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOVOLUME_H
+#define LL_LLVOVOLUME_H
+
+#include "llviewerobject.h"
+#include "llviewerimage.h"
+#include "llframetimer.h"
+#include "llapr.h"
+#include <map>
+
+class LLViewerTextureAnim;
+class LLDrawPool;
+class LLSelectNode;
+
+enum LLVolumeInterfaceType
+{
+ INTERFACE_FLEXIBLE = 1,
+};
+
+// Base class for implementations of the volume - Primitive, Flexible Object, etc.
+class LLVolumeInterface
+{
+public:
+ virtual ~LLVolumeInterface() { }
+ virtual LLVolumeInterfaceType getInterfaceType() const = 0;
+ virtual BOOL doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) = 0;
+ virtual BOOL doUpdateGeometry(LLDrawable *drawable) = 0;
+ virtual LLVector3 getPivotPosition() const = 0;
+ virtual void onSetVolume(const LLVolumeParams &volume_params, const S32 detail) = 0;
+ virtual void onSetScale(const LLVector3 &scale, BOOL damped) = 0;
+ virtual void onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin) = 0;
+ virtual void onShift(const LLVector3 &shift_vector) = 0;
+ virtual bool isVolumeUnique() const = 0; // Do we need a unique LLVolume instance?
+ virtual bool isVolumeGlobal() const = 0; // Are we in global space?
+ virtual bool isActive() const = 0; // Is this object currently active?
+ virtual const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const = 0;
+ virtual void updateRelativeXform(BOOL global_volume = FALSE) = 0;
+};
+
+// Class which embodies all Volume objects (with pcode LL_PCODE_VOLUME)
+class LLVOVolume : public LLViewerObject
+{
+public:
+ static void initClass();
+ static void preUpdateGeom();
+ static F32 getTextureVirtualSize(const LLFace* face);
+
+ BOOL mWereAllTEsSame;
+
+public:
+ LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOVolume();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+
+ void deleteFaces(LLVOVolume* childp);
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ BOOL isActive() const;
+ /*virtual*/ BOOL isAttachment() const;
+ /*virtual*/ BOOL isRootEdit() const; // overridden for sake of attachments treating themselves as a root object
+ /*virtual*/ BOOL isHUDAttachment() const;
+
+ void generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point);
+
+ BOOL getAllTEsSame() const { return mAllTEsSame; }
+ F32 getIndividualRadius() { return mRadius; }
+ S32 getLOD() const { return mLOD; }
+ const LLVector3 getPivotPositionAgent() const;
+ const LLMatrix4& getRelativeXform() const { return mRelativeXform; }
+ const LLMatrix3& getRelativeXformInvTrans() const { return mRelativeXformInvTrans; }
+ /*virtual*/ const LLMatrix4 getRenderMatrix() const;
+
+ /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+ LLVector3 agentPositionToVolume(const LLVector3& pos) const;
+ LLVector3 agentDirectionToVolume(const LLVector3& dir) const;
+ LLVector3 volumePositionToAgent(const LLVector3& dir) const;
+
+
+ BOOL getVolumeChanged() const { return mVolumeChanged; }
+ /*virtual*/ F32 getRadius() const { return mVObjRadius; };
+ const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
+
+ void markForUpdate(BOOL priority) { LLViewerObject::markForUpdate(priority); mVolumeChanged = TRUE; }
+
+ /*virtual*/ void onShift(const LLVector3 &shift_vector); // Called when the drawable shifts
+
+ /*virtual*/ void parameterChanged(U16 param_type, bool local_origin);
+ /*virtual*/ void parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin);
+
+ /*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+
+ /*virtual*/ BOOL setDrawableParent(LLDrawable* parentp);
+
+ /*virtual*/ void setScale(const LLVector3 &scale, BOOL damped);
+
+ /*virtual*/ void setTEImage(const U8 te, LLViewerImage *imagep);
+ /*virtual*/ S32 setTETexture(const U8 te, const LLUUID &uuid);
+ /*virtual*/ S32 setTEColor(const U8 te, const LLColor4 &color);
+ /*virtual*/ S32 setTEBumpmap(const U8 te, const U8 bump);
+ /*virtual*/ S32 setTEShiny(const U8 te, const U8 shiny);
+ /*virtual*/ S32 setTEFullbright(const U8 te, const U8 fullbright);
+ /*virtual*/ S32 setTEMediaFlags(const U8 te, const U8 media_flags);
+ /*virtual*/ S32 setTEScale(const U8 te, const F32 s, const F32 t);
+ /*virtual*/ S32 setTEScaleS(const U8 te, const F32 s);
+ /*virtual*/ S32 setTEScaleT(const U8 te, const F32 t);
+ /*virtual*/ S32 setTETexGen(const U8 te, const U8 texgen);
+ /*virtual*/ BOOL setMaterial(const U8 material);
+
+ void setTexture(const S32 face);
+
+ /*virtual*/ BOOL setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false);
+ void updateRelativeXform(BOOL global_volume = FALSE);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ /*virtual*/ BOOL updateLOD();
+ void updateRadius();
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ void updateTextures(S32 lod);
+
+ void updateFaceFlags();
+ void regenFaces();
+ BOOL genTriangles(BOOL force_global);
+ virtual void updateSpatialExtents(LLVector3& min, LLVector3& max);
+ virtual void writeCAL3D(apr_file_t* fp,
+ std::string& path,
+ std::string& file_base,
+ S32 joint_num,
+ LLVector3& pos,
+ LLQuaternion& rot,
+ S32& material_index,
+ S32& texture_index,
+ std::multimap<LLUUID, LLMaterialExportInfo*>& material_map);
+
+ // For Lights
+ void setIsLight(BOOL is_light);
+ void setLightColor(const LLColor3& color);
+ void setLightIntensity(F32 intensity);
+ void setLightRadius(F32 radius);
+ void setLightFalloff(F32 falloff);
+ void setLightCutoff(F32 cutoff);
+ BOOL getIsLight() const;
+ LLColor3 getLightBaseColor() const; // not scaled by intensity
+ LLColor3 getLightColor() const; // scaled by intensity
+ F32 getLightIntensity() const;
+ F32 getLightRadius() const;
+ F32 getLightFalloff() const;
+ F32 getLightCutoff() const;
+ F32 getLightDistance(const LLVector3& pos) const; // returns < 0 if inside radius
+
+ // Flexible Objects
+ virtual BOOL isFlexible() const;
+ BOOL isVolumeGlobal() const;
+ BOOL canBeFlexible() const;
+ BOOL setIsFlexible(BOOL is_flexible);
+
+ // Lighting
+ F32 calcLightAtPoint(const LLVector3& pos, const LLVector3& norm, LLColor4& result);
+ BOOL updateLighting(BOOL do_lighting);
+
+protected:
+ F32 computeLODProfilePathComplexityBias();
+ S32 computeLODDetail(F32 distance, F32 radius);
+ BOOL calcLOD();
+ void setupSingleFace(S32 face_offset); // Set up the face for combined volumes.
+ LLFace* addFace(S32 face_index);
+ void updateTEData();
+ BOOL calcAllTEsSame();
+
+public:
+ LLViewerTextureAnim *mTextureAnimp;
+
+protected:
+ friend class LLDrawable;
+
+ BOOL mAllTEsSame; // All TE's have the same pool/texture
+ BOOL mFaceMappingChanged;
+ BOOL mGlobalVolume;
+ BOOL mInited;
+ S32 mLOD;
+ BOOL mLODChanged;
+ F32 mRadius;
+ LLMatrix4 mRelativeXform;
+ LLMatrix3 mRelativeXformInvTrans;
+ BOOL mVolumeChanged;
+ F32 mVObjRadius;
+ LLVolumeInterface *mVolumeImpl;
+
+ // statics
+public:
+ static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop
+ static F32 sLODComplexityDistanceBias; // Changing this to zero makes all prims LOD at the same distance,
+ // regardless of complexity
+ static F32 sLODFactor; // LOD scale factor
+ static F32 sDistanceFactor; // LOD distance factor
+
+protected:
+ static S32 sNumLODChanges;
+
+ friend class LLVolumeImplFlexible;
+};
+
+#endif // LL_LLVOVOLUME_H
diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp
new file mode 100644
index 0000000000..135873b5b8
--- /dev/null
+++ b/indra/newview/llvowater.cpp
@@ -0,0 +1,1028 @@
+/**
+ * @file llvowater.cpp
+ * @brief LLVOWater class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvowater.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolwater.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llsurface.h"
+#include "llvosky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+#include "pipeline.h"
+
+const BOOL gUseRoam = FALSE;
+
+
+///////////////////////////////////
+
+#include "randgauss.h"
+
+template<class T> inline T LERP(T a, T b, F32 factor)
+{
+ return a + (b - a) * factor;
+}
+
+const U32 N_RES_HALF = (N_RES >> 1);
+
+const U32 WIDTH = (N_RES * WAVE_STEP); //128.f //64 // width of wave tile, in meters
+const F32 WAVE_STEP_INV = (1. / WAVE_STEP);
+
+const F32 g = 9.81f; // gravitational constant (m/s^2)
+
+///////////////////////////////////
+
+LLWaterSurface::LLWaterSurface() :
+ mInitialized(FALSE),
+ mWind(9, 0, 0),
+ mA(0.2f),
+ mVisc(0.001f),
+ mShininess(8.0f)
+{}
+
+
+LLWaterGrid *LLVOWater::sGrid = 0;
+
+
+LLVOWater::LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, LL_VO_WATER, regionp)
+{
+ // Terrain must draw during selection passes so it can block objects behind it.
+ mbCanSelect = FALSE;
+ setScale(LLVector3(256.f, 256.f, 0.f)); // Hack for setting scale for bounding boxes/visibility.
+
+ mUseTexture = TRUE;
+}
+
+
+void LLVOWater::markDead()
+{
+ LLViewerObject::markDead();
+}
+
+
+BOOL LLVOWater::isActive() const
+{
+ return FALSE;
+}
+
+
+void LLVOWater::setPixelAreaAndAngle(LLAgent &agent)
+{
+ mAppAngle = 50;
+ mPixelArea = 500*500;
+}
+
+
+// virtual
+void LLVOWater::updateTextures(LLAgent &agent)
+{
+}
+
+// virtual
+void LLVOWater::updateDrawable(BOOL force_damped)
+{
+ // Force an immediate rebuild on any update
+ if (mDrawable.notNull())
+ {
+ gPipeline.updateMoveNormalAsync(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ clearChanged(SHIFTED);
+}
+
+// Never gets called
+BOOL LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER)))
+ {
+ return TRUE;
+ }
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_WATER);
+
+ LLDrawPoolWater *pool = (LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER);
+
+ if (mUseTexture)
+ {
+ mDrawable->setNumFaces(1, pool, mRegionp->getLand().getWaterTexture());
+ }
+ else
+ {
+ mDrawable->setNumFaces(1, pool, gWorldp->getDefaultWaterTexture());
+ }
+
+ return mDrawable;
+}
+
+BOOL LLVOWater::updateGeometry(LLDrawable *drawable)
+{
+ return updateGeometryFlat(drawable);
+}
+
+
+BOOL LLVOWater::updateGeometryFlat(LLDrawable *drawable)
+{
+ LLFace *face;
+
+ if (drawable->getNumFaces() < 1)
+ {
+ drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_WATER), NULL);
+ }
+ face = drawable->getFace(0);
+
+ LLVector2 uvs[4];
+ LLVector3 vtx[4];
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ S32 size = 16;
+
+ S32 num_quads = size*size;
+
+ face->setPrimType(LLTriangles);
+ face->setSize(4*num_quads, 6*num_quads);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ LLVector3 position_agent;
+ position_agent = getPositionAgent();
+ face->mCenterAgent = position_agent;
+
+ S32 x, y;
+ F32 step_x = getScale().mV[0] / size;
+ F32 step_y = getScale().mV[1] / size;
+
+ const LLVector3 up(0.f, step_y * 0.5f, 0.f);
+ const LLVector3 right(step_x * 0.5f, 0.f, 0.f);
+ const LLVector3 normal(0.f, 0.f, 1.f);
+
+ F32 size_inv = 1.f / size;
+
+ for (y = 0; y < size; y++)
+ {
+ for (x = 0; x < size; x++)
+ {
+ S32 toffset = index_offset + 4*(y*size + x);
+ position_agent = getPositionAgent() - getScale() * 0.5f;
+ position_agent.mV[VX] += (x + 0.5f) * step_x;
+ position_agent.mV[VY] += (y + 0.5f) * step_y;
+
+ vtx[0] = position_agent - right + up;
+ vtx[1] = position_agent - right - up;
+ vtx[2] = position_agent + right + up;
+ vtx[3] = position_agent + right - up;
+
+ *(verticesp++) = vtx[0];
+ *(verticesp++) = vtx[1];
+ *(verticesp++) = vtx[2];
+ *(verticesp++) = vtx[3];
+
+ uvs[0].setVec(x*size_inv, (y+1)*size_inv);
+ uvs[1].setVec(x*size_inv, y*size_inv);
+ uvs[2].setVec((x+1)*size_inv, (y+1)*size_inv);
+ uvs[3].setVec((x+1)*size_inv, y*size_inv);
+
+ *(texCoordsp) = uvs[0];
+ texCoordsp++;
+ *(texCoordsp) = uvs[1];
+ texCoordsp++;
+ *(texCoordsp) = uvs[2];
+ texCoordsp++;
+ *(texCoordsp) = uvs[3];
+ texCoordsp++;
+
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+
+ *indicesp++ = toffset + 0;
+ *indicesp++ = toffset + 1;
+ *indicesp++ = toffset + 2;
+
+ *indicesp++ = toffset + 1;
+ *indicesp++ = toffset + 3;
+ *indicesp++ = toffset + 2;
+ }
+ }
+
+
+ mDrawable->movePartition();
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+
+BOOL LLVOWater::updateGeometryHeightFieldRoam(LLDrawable *drawable)
+{
+ LLVector3 position_agent = getPositionAgent();
+ const LLVector3 region_size = getScale();
+ const LLVector3 region_origin = position_agent - region_size * 0.5f;
+
+ S32 patch_origx = llround(region_origin.mV[VX] - sGrid->mRegionOrigin.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_origy = llround(region_origin.mV[VY] - sGrid->mRegionOrigin.mV[VY]) / sGrid->mRegionWidth;
+ S32 patch_dimx = llround(region_size.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_dimy = llround(region_size.mV[VY]) / sGrid->mRegionWidth;
+
+ static S32 res = (S32)sGrid->mPatchRes;
+ if (patch_origx < 0)
+ {
+ patch_dimx -= - patch_origx;
+ if (patch_dimx < 1)
+ {
+ return TRUE;
+ }
+ patch_origx = 0;
+ }
+ if (patch_origy < 0)
+ {
+ patch_dimy -= - patch_origy;
+ if (patch_dimy < 1)
+ {
+ return TRUE;
+ }
+ patch_origy = 0;
+ }
+ if (patch_origx >= res)
+ {
+ return TRUE;
+ }
+ if (patch_origy >= res)
+ {
+ return TRUE;
+ }
+
+ patch_dimx = llmin<U32>(patch_dimx, res - patch_origx);
+ patch_dimy = llmin<U32>(patch_dimy, res - patch_origy);
+
+ U32 num_of_tris = 0;
+ S32 px, py;
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ const U32 ind = py * sGrid->mPatchRes + px;
+ if (sGrid->mPatches[ind].visible() && sGrid->mTab[px][py] == 0)
+ {
+ num_of_tris += sGrid->mPatches[ind].numTris();
+ sGrid->mTab[px][py] = this;
+ }
+ }
+ }
+
+ if (num_of_tris == 0)
+ {
+ return TRUE;
+ }
+
+ if (drawable->getNumFaces() < 1)
+ {
+ drawable->addFace((LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER),
+ gWorldp->getDefaultWaterTexture());
+ }
+
+ LLFace *face;
+
+ face = drawable->getFace(0);
+ face->mCenterAgent = position_agent;
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ const F32 water_height = getRegion()->getWaterHeight();
+
+
+ face->setPrimType(LLTriangles);
+ face->setSize(3 * num_of_tris, 3 * num_of_tris);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ U32 num_of_vtx = 0;
+
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ for (U8 h = 0; h < 2; h++)
+ {
+ const U32 ind = py * sGrid->mPatchRes + px;
+ if (!sGrid->mPatches[ind].visible() || sGrid->mTab[px][py] != this)
+ continue;
+ LLWaterTri* half = (LLWaterTri*) sGrid->mPatches[ind].half(h);
+ for (const LLWaterTri* tri = (LLWaterTri*) half->getFirstLeaf();
+ tri != NULL;
+ tri = (LLWaterTri*) tri->getNextLeaf())
+ {
+ /////// check for coordinates
+ *(verticesp++) = sGrid->vtx(tri->Lvtx(), water_height);
+ *(verticesp++) = sGrid->vtx(tri->Rvtx(), water_height);
+ *(verticesp++) = sGrid->vtx(tri->Tvtx(), water_height);
+
+ *(normalsp++) = sGrid->norm(tri->Lvtx());
+ *(normalsp++) = sGrid->norm(tri->Rvtx());
+ *(normalsp++) = sGrid->norm(tri->Tvtx());
+
+ *(indicesp++) = index_offset + num_of_vtx + 0;
+ *(indicesp++) = index_offset + num_of_vtx + 1;
+ *(indicesp++) = index_offset + num_of_vtx + 2;
+ num_of_vtx += 3;
+ }
+ }
+ }
+ }
+
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+
+
+BOOL LLVOWater::updateGeometryHeightFieldSimple(LLDrawable *drawable)
+{
+ LLVector3 position_agent = getPositionAgent();
+ const LLVector3 region_size = getScale();
+ const LLVector3 region_origin = position_agent - region_size * 0.5f;
+
+ S32 patch_origx = llround(region_origin.mV[VX] - sGrid->mRegionOrigin.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_origy = llround(region_origin.mV[VY] - sGrid->mRegionOrigin.mV[VY]) / sGrid->mRegionWidth;
+ S32 patch_dimx = llround(region_size.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_dimy = llround(region_size.mV[VY]) / sGrid->mRegionWidth;
+
+ static S32 res = sGrid->mPatchRes;
+ if (patch_origx < 0)
+ {
+ patch_dimx -= - patch_origx;
+ if (patch_dimx < 1)
+ {
+ return TRUE;
+ }
+ patch_origx = 0;
+ }
+ if (patch_origy < 0)
+ {
+ patch_dimy -= - patch_origy;
+ if (patch_dimy < 1)
+ {
+ return TRUE;
+ }
+ patch_origy = 0;
+ }
+ if (patch_origx >= res)
+ {
+ return TRUE;
+ }
+ if (patch_origy >= res)
+ {
+ return TRUE;
+ }
+
+ patch_dimx = llmin<U32>(patch_dimx, res - patch_origx);
+ patch_dimy = llmin<U32>(patch_dimy, res - patch_origy);
+
+
+ U32 num_of_regions = 0;
+ S32 px, py;
+
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ // if (sGrid->mTab[px][py] != 0)
+ // bool stop = true;
+ if (sGrid->mPatches[py * sGrid->mPatchRes + px].visible() && sGrid->mTab[px][py] == 0)
+ {
+ num_of_regions++;
+ sGrid->mTab[px][py] = this;
+ }
+ }
+ }
+
+ if (num_of_regions == 0)
+ {
+ return TRUE;
+ }
+
+ if (drawable->getNumFaces() < 1)
+ {
+ drawable->addFace((LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER),
+ gWorldp->getDefaultWaterTexture());
+ }
+
+ LLFace *face;
+
+ face = drawable->getFace(0);
+ face->mCenterAgent = position_agent;
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ const F32 water_height = getRegion()->getWaterHeight();
+
+ const U32 steps_in_region = sGrid->mStepsInRegion / sGrid->mResDecrease;
+ const U32 num_quads = steps_in_region * steps_in_region * num_of_regions;
+
+ face->setPrimType(LLTriangles);
+ face->setSize(4*num_quads, 6*num_quads);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ U32 num_of_vtx = 0;
+
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ if (!sGrid->mPatches[py * sGrid->mPatchRes + px].visible() || sGrid->mTab[px][py] != this)
+ {
+ continue;
+ }
+
+ U32 orig_indx = px * sGrid->mStepsInRegion;
+ U32 orig_indy = py * sGrid->mStepsInRegion;
+
+ for (U32 qy = 0; qy < steps_in_region; qy++)
+ {
+ for (U32 qx = 0; qx < steps_in_region; qx++)
+ {
+ const S32 x0 = orig_indx + qx * sGrid->mResDecrease;
+ const S32 y0 = orig_indy + qy * sGrid->mResDecrease;
+ const S32 x1 = x0 + sGrid->mResDecrease;
+ const S32 y1 = y0 + sGrid->mResDecrease;
+
+ sGrid->setVertex(x0, y1, water_height, *(verticesp));
+ verticesp++;
+ sGrid->setVertex(x0, y0, water_height, *(verticesp));
+ verticesp++;
+ sGrid->setVertex(x1, y1, water_height, *(verticesp));
+ verticesp++;
+ sGrid->setVertex(x1, y0, water_height, *(verticesp));
+ verticesp++;
+ /*
+ *(verticesp++) = sGrid->vtx(x0, y1, water_height);
+ *(verticesp++) = sGrid->vtx(x0, y0, water_height);
+ *(verticesp++) = sGrid->vtx(x1, y1, water_height);
+ *(verticesp++) = sGrid->vtx(x1, y0, water_height);
+ */
+ *(normalsp++) = sGrid->norm(x0, y1);
+ *(normalsp++) = sGrid->norm(x0, y0);
+ *(normalsp++) = sGrid->norm(x1, y1);
+ *(normalsp++) = sGrid->norm(x1, y0);
+
+ const S32 curr_index_offset = index_offset + num_of_vtx;
+
+ *indicesp++ = curr_index_offset + 0;
+ *indicesp++ = curr_index_offset + 1;
+ *indicesp++ = curr_index_offset + 2;
+
+ *indicesp++ = curr_index_offset + 1;
+ *indicesp++ = curr_index_offset + 3;
+ *indicesp++ = curr_index_offset + 2;
+ num_of_vtx += 4;
+ }
+ }
+ }
+ }
+
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+void LLVOWater::initClass()
+{
+ sGrid = new LLWaterGrid;
+}
+
+void LLVOWater::cleanupClass()
+{
+ if (sGrid)
+ {
+ sGrid->cleanup();
+ delete sGrid;
+ sGrid = 0;
+ }
+}
+
+
+LLWaterGrid::LLWaterGrid() : mResIncrease(1)//0.5)
+{
+ init();
+}
+
+
+void LLWaterGrid::init()
+{
+ //mRegionOrigin = LLVector3(-2 * mRegionWidth, -2 * mRegionWidth, 0);
+ mRegionWidth = 256;
+ mPatchRes = 5;
+ mMaxGridSize = mPatchRes * mRegionWidth;
+ mMinStep = (U32)(WAVE_STEP * mResIncrease);
+
+ LLWaterTri::sMinStep = mMinStep;
+ LLWaterTri::sQueues = &mRoam;
+
+ setGridDim(mMaxGridSize / mMinStep);
+
+ mVtx = new LLVector3[mGridDim1 * mGridDim1];
+ mNorms = new LLVector3[mGridDim1 * mGridDim1];
+
+ mPatches = new LLWaterPatch[mPatchRes * mPatchRes];
+
+ mStepsInRegion = mRegionWidth / mMinStep;
+ const U32 max_div_level = 2 * (U32)(log((F32)mStepsInRegion) / log(2.0f));
+
+ for (U32 y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ LLVector3 patch_center(mRegionWidth * (x + 0.5f), mRegionWidth * (y + 0.5f), 0);
+
+ mPatches[y * mPatchRes + x].set(x * mStepsInRegion, y * mStepsInRegion,
+ mStepsInRegion, mRegionWidth, patch_center, max_div_level);
+ if (x > 0)
+ {
+ mPatches[y * mPatchRes + x].left()->setRight(mPatches[y * mPatchRes + x - 1].right());
+ mPatches[y * mPatchRes + x - 1].right()->setRight(mPatches[y * mPatchRes + x].left());
+ }
+ if (y > 0)
+ {
+ mPatches[y * mPatchRes + x].left()->setLeft(mPatches[(y - 1) * mPatchRes + x].right());
+ mPatches[(y - 1) * mPatchRes + x].right()->setLeft(mPatches[y * mPatchRes + x].left());
+ }
+ }
+ }
+}
+
+void LLWaterGrid::cleanup()
+{
+ delete[] mVtx;
+ mVtx = NULL;
+
+ delete[] mNorms;
+ mNorms = NULL;
+
+ delete[] mPatches;
+ mPatches = NULL;
+}
+
+
+void setVecZ(LLVector3& v)
+{
+ v.mV[VX] = 0;
+ v.mV[VY] = 0;
+ v.mV[VZ] = 1;
+}
+
+void LLWaterGrid::update()
+{
+ static LLViewerRegion* prev_region = gAgent.getRegion();
+ LLViewerRegion* region = gAgent.getRegion();
+
+ mRegionOrigin = region->getOriginAgent();
+ mRegionOrigin.mV[VX] -= 2 * mRegionWidth;
+ mRegionOrigin.mV[VY] -= 2 * mRegionWidth;
+ mRegionOrigin.mV[VZ] = 0;
+
+ const F32 clip_far = gCamera->getFar() - 31;
+ const F32 clip_far2 = clip_far * clip_far;
+
+ const LLVector3 camera_pos = gAgent.getCameraPositionAgent();
+ const LLVector3 look_at = gCamera->getAtAxis();
+
+
+ if (camera_pos.mV[VZ] > 200)
+ {
+ mResDecrease = 4;
+ }
+ else if (camera_pos.mV[VZ] > 100)
+ {
+ mResDecrease = 2;
+ }
+ else
+ {
+ mResDecrease = 1;
+ }
+
+
+ //U32 mResDecrease = res_decrease;
+ U32 res_decrease = 1;
+
+ const F32 res_change = mResIncrease;// * res_decrease ;
+
+ F32 height;
+
+ // Set the grid
+
+ //U32 fractions = 1;
+ U32 fractions_res = res_decrease;
+ if (res_change < 1)
+ {
+ //fractions = llround(1. / res_change);
+ fractions_res = llround(1.f / mResIncrease);
+ }
+
+
+ //const U32 fractions_res = fractions * res_decrease;
+
+ LLVector3 cur_pos;
+ U32 x, y;
+ U32 ind = 0;
+ for (y = 0; y < mGridDim1; y += fractions_res)
+ {
+ const F32 dispy = (F32)(y * mMinStep);//step;
+ for (x = 0; x < mGridDim1; x += fractions_res)
+ {
+ const F32 dispx = (F32)(x * mMinStep);//step;
+ cur_pos = mRegionOrigin;
+ cur_pos.mV[VX] += dispx;
+ cur_pos.mV[VY] += dispy;
+
+ const F32 x_dist = cur_pos.mV[VX] - camera_pos.mV[VX];
+ const F32 y_dist = cur_pos.mV[VY] - camera_pos.mV[VY];
+
+ if (x_dist * look_at.mV[VX] + y_dist * look_at.mV[VY] < 0)
+ {
+ mVtx[ind] = cur_pos;
+ setVecZ(mNorms[ind]);
+ ind++;
+ continue;
+ }
+
+ const F32 dist_to_vtx2 = x_dist * x_dist + y_dist * y_dist;
+ if (dist_to_vtx2 > .81 * clip_far2)
+ {
+ mVtx[ind] = cur_pos;
+ setVecZ(mNorms[ind]);
+ ind++;
+ continue;
+ }
+
+ mWater.getIntegerHeightAndNormal(llround(WAVE_STEP_INV * dispx),
+ llround(WAVE_STEP_INV * dispy), height, mNorms[ind]);
+
+ cur_pos.mV[VZ] += height;
+ mVtx[ind] = cur_pos;
+ ind++;
+ }
+ }
+
+ if (res_change < 1)
+ {
+ U32 fractions = llround(1.f / mResIncrease);
+ for (y = 0; y < mGridDim1; y += fractions_res)
+ {
+ for (x = 0; x < mGridDim; x += fractions_res)
+ {
+ const U32 ind00 = index(x, y);
+ const U32 ind01 = ind00 + fractions_res;
+ for (U32 frx = 1; frx < fractions; frx += res_decrease)
+ {
+ const U32 ind = ind00 + frx;
+ mNorms[ind] = LERP(mNorms[ind00], mNorms[ind01], frx * res_change);
+ mVtx[ind] = LERP( mVtx[ind00], mVtx[ind01], frx * res_change);
+ }
+ }
+ }
+ for (x = 0; x < mGridDim1; x += res_decrease)
+ {
+ for (y = 0; y < mGridDim; y += fractions_res)
+ {
+ const U32 ind00 = index(x, y);
+ const U32 ind10 = ind00 + fractions_res * mGridDim1;//(y + fractions) * quad_resx1 + x;
+ for (U32 fry = 1; fry < fractions; fry += res_decrease)
+ {
+ const U32 ind = ind00 + fry * mGridDim1;//(y + fry) * quad_resx1 + x;
+ mNorms[ind] = LERP(mNorms[ind00], mNorms[ind10], fry * res_change);
+ mVtx[ind] = LERP( mVtx[ind00], mVtx[ind10], fry * res_change);
+ }
+ }
+ }
+ }
+
+ if (gUseRoam)
+ {
+ updateTree(camera_pos, look_at, clip_far, prev_region != region);
+ }
+ else
+ {
+ updateVisibility(camera_pos, look_at, clip_far);
+ }
+
+ prev_region = region;
+
+
+ //mTab[0][0] = 0;
+ for (y = 0; y < mPatchRes; y++)
+ {
+ for (x = 0; x < mPatchRes; x++)
+ mTab[x][y] = 0;
+ }
+
+}
+
+void LLWaterGrid::updateTree(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far,
+ BOOL restart = FALSE)
+{
+ static S8 recalculate_frame = 0;
+
+ if (restart)
+ {
+ recalculate_frame = 0;
+ }
+
+ if (recalculate_frame == 0)
+ {
+ LLWaterTri::nextRound();
+ setCamPosition(LLWaterTri::sCam, camera_pos);
+ LLWaterTri::sClipFar = clip_far;
+
+
+ const U32 step = (U32)(WAVE_STEP * mResIncrease * mResDecrease);
+ const U32 steps_in_region = mRegionWidth / step;
+ LLWaterTri::sMaxDivLevel = 2 * llround(log((F32)steps_in_region) / log(2.0f));
+
+ for (U32 y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ U32 patch_ind = y * mPatchRes + x;
+ mPatches[patch_ind].updateTree(camera_pos, look_at, mRegionOrigin);
+ }
+ }
+
+ mRoam.process();
+
+ // debug
+ /*
+ for (y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ //mPatches[y * mPatchRes + x].checkUpToDate();
+ //mPatches[y * mPatchRes + x].checkConsistensy();
+ mPatches[y * mPatchRes + x].checkCount();
+ }
+ }
+ */
+ }
+ ++recalculate_frame;
+ recalculate_frame = recalculate_frame % 2;
+}
+
+void LLWaterGrid::updateVisibility(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far)
+{
+ for (U32 y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ mPatches[y * mPatchRes + x].updateVisibility(camera_pos, look_at, mRegionOrigin);
+ }
+ }
+}
+
+
+void LLVOWater::setUseTexture(const BOOL use_texture)
+{
+ mUseTexture = use_texture;
+}
+
+F32 LLWaterSurface::agentDepth() const
+{
+ const LLViewerRegion* region = gAgent.getRegion();
+ LLVector3 position_agent = region->getOriginAgent();// getPositionAgent();
+ const LLVector3 region_origin = position_agent;
+ const LLVector3 camera_pos = gAgent.getCameraPositionAgent();
+
+ F32 height;
+ LLVector3 normal;
+
+ getHeightAndNormal(WAVE_STEP_INV * camera_pos.mV[VX],
+ WAVE_STEP_INV * camera_pos.mV[VY], height, normal);
+ F32 agent_water_height = gAgent.getRegion()->getWaterHeight();
+ return camera_pos.mV[VZ] - (agent_water_height + height);
+}
+
+////////////////////////////////////////////////
+
+
+void LLWaterSurface::getHeightAndNormal(F32 i, F32 j, F32& wave_height, LLVector3& normal) const
+{
+ S32 i_ind = llfloor(i);
+ S32 j_ind = llfloor(j);
+ F32 i_fr = i - i_ind;
+ F32 j_fr = j - j_ind;
+
+ i_ind = i_ind % N_RES;
+ j_ind = j_ind % N_RES;
+
+ S32 i_ind_next = i_ind + 1;
+ S32 j_ind_next = j_ind + 1;
+ if (i_ind_next == (S32)N_RES) i_ind_next = 0;
+ if (j_ind_next == (S32)N_RES) j_ind_next = 0;
+
+ const F32 i_fr1 = 1 - i_fr;
+ const F32 j_fr1 = 1 - j_fr;
+
+ const F32 hi0 = i_fr1 * height(i_ind, j_ind) + i_fr * height(i_ind_next, j_ind);
+ const F32 hi1 = i_fr1 * height(i_ind, j_ind_next) + i_fr * height(i_ind_next, j_ind_next);
+ wave_height = j_fr1 * hi0 + j_fr * hi1;
+
+ normal = i_fr1 * mNorms[i_ind][j_ind];
+ normal += i_fr * mNorms[i_ind_next][j_ind];
+ LLVector3 vi1 = i_fr1 * mNorms[i_ind][j_ind_next];
+ vi1 += i_fr * mNorms[i_ind_next][j_ind_next];
+ normal *= j_fr1;
+ normal += j_fr * vi1;
+
+ //normal.normVec();
+}
+
+void LLWaterSurface::getIntegerHeightAndNormal(S32 i, S32 j, F32& wave_height, LLVector3& normal) const
+{
+ S32 i_ind = i % N_RES;
+ S32 j_ind = j % N_RES;
+
+ wave_height = height(i_ind, j_ind);
+ normal = mNorms[i_ind][j_ind];
+}
+
+F32 LLWaterSurface::phillips(const LLVector2& k, const LLVector2& wind_n, F32 L, F32 L_small)
+{
+ F32 k2 = k * k;
+ F32 k_dot_wind = k * wind_n;
+ F32 spectrum = mA * (F32) exp(-1 / (L * L * k2)) / (k2 * k2) * (k_dot_wind * k_dot_wind / k2);
+
+ if (k_dot_wind < 0) spectrum *= .25f; // scale down waves that move opposite to the wind
+
+ F32 damp = (F32) exp(- k2 * L_small * L_small);
+
+ return (spectrum * damp);
+}
+
+
+
+void LLWaterSurface::initAmplitudes()
+{
+ U16 i, j;
+ LLVector2 k;
+ F32 sqrtPhillips;
+
+ const LLVector2 wind(mWind.mV);
+
+ LLVector2 wind_n = wind;
+ const F32 wind_vel = wind_n.normVec();
+
+ const F32 L = wind_vel * wind_vel / g; // largest wave arising from constant wind of speed wind_vel
+
+ const F32 L_small = L / 70; // eliminate waves with very small length (L_small << L)
+
+
+ for (i = 0; i <= N_RES; i++)
+ {
+ k.mV[VX] = (- (S32)N_RES_HALF + i) * (F_TWO_PI / WIDTH);
+ for (j = 0; j <= N_RES; j++)
+ {
+ k.mV[VY] = (- (S32)N_RES_HALF + j) * (F_TWO_PI / WIDTH);
+
+ const F32 k_mag = k.magVec();
+ mOmega[i][j] = (F32) sqrt(g * k_mag);
+
+ if (k_mag < F_APPROXIMATELY_ZERO)
+ sqrtPhillips = 0;
+ else
+ sqrtPhillips = (F32) sqrt(phillips(k, wind_n, L, L_small));
+
+ //const F32 r1 = rand() / (F32) RAND_MAX;
+ //const F32 r2 = rand() / (F32) RAND_MAX;
+ const F32 r1 = randGauss(0, 1);
+ const F32 r2 = randGauss(0, 1);
+
+ mHtilda0[i][j].re = sqrtPhillips * r1 * OO_SQRT2;
+ mHtilda0[i][j].im = sqrtPhillips * r2 * OO_SQRT2;
+
+ }
+ }
+
+ mPlan.init(N_RES, N_RES);
+ mInitialized = 1; // initialization complete
+}
+
+void LLWaterSurface::generateWaterHeightField(F64 t)
+{
+ S32 i, j;
+ S32 mi, mj; // -K indices
+ COMPLEX plus, minus;
+
+ if (!mInitialized) initAmplitudes();
+
+ for (i = 0; i < (S32)N_RES_HALF; i++)
+ {
+ mi = N_RES - i;
+ for (j = 0; j < (S32)N_RES ; j++)
+ {
+ mj = N_RES - j;
+
+ const F32 cos_wt = cosf(mOmega[i][j] * t); // = cos(-mOmega[i][j] * t)
+ const F32 sin_wt = sinf(mOmega[i][j] * t); // = -sin(-mOmega[i][j] * t)
+ plus.re = mHtilda0[i][j].re * cos_wt - mHtilda0[i][j].im * sin_wt;
+ plus.im = mHtilda0[i][j].re * sin_wt + mHtilda0[i][j].im * cos_wt;
+ minus.re = mHtilda0[mi][mj].re * cos_wt - mHtilda0[mi][mj].im * sin_wt;
+ minus.im = -mHtilda0[mi][mj].re * sin_wt - mHtilda0[mi][mj].im * cos_wt;
+
+ // now sum the plus and minus waves to get the total wave amplitude h
+ mHtilda[i * N_RES + j].re = plus.re + minus.re;
+ mHtilda[i * N_RES + j].im = plus.im + minus.im;
+ if (mi < (S32)N_RES && mj < (S32)N_RES)
+ {
+ mHtilda[mi * N_RES + mj].re = plus.re + minus.re;
+ mHtilda[mi * N_RES + mj].im = -plus.im - minus.im;
+ }
+ }
+ }
+
+ inverse_fft(mPlan, mHtilda, N_RES, N_RES);
+
+ calcNormals();
+}
+
+
+/*
+ * Computer normals by taking finite differences.
+ */
+
+void LLWaterSurface::calcNormals()
+{
+ LLVector3 n;
+
+ for (U32 i = 0; i < N_RES; i++)
+ {
+ for (U32 j = 0; j < N_RES; j++)
+ {
+ F32 px = heightWrapped(i + 1, j);
+ F32 mx = heightWrapped(i - 1, j);
+ F32 py = heightWrapped(i, j + 1);
+ F32 my = heightWrapped(i, j - 1);
+ F32 pxpy = heightWrapped(i + 1, j + 1);
+ F32 pxmy = heightWrapped(i + 1, j - 1);
+ F32 mxpy = heightWrapped(i - 1, j + 1);
+ F32 mxmy = heightWrapped(i - 1, j - 1);
+
+ n.mV[VX] = -((2 * px + pxpy + pxmy) - (2 * mx + mxpy + mxmy));
+ n.mV[VY] = -((2 * py + pxpy + mxpy) - (2 * my + pxmy + mxmy));
+ n.mV[VZ] = 8 * WIDTH / (F32) N_RES;
+ n.normVec();
+
+ mNorms[i][j] = n;
+ }
+ }
+}
+
+void LLVOWater::generateNewWaves(F64 time)
+{
+ getWaterSurface()->generateWaterHeightField(time);
+ sGrid->update();
+}
+
diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h
new file mode 100644
index 0000000000..2b846dba20
--- /dev/null
+++ b/indra/newview/llvowater.h
@@ -0,0 +1,240 @@
+/**
+ * @file llvowater.h
+ * @brief Description of LLVOWater class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VOWATER_H
+#define LL_VOWATER_H
+
+#include "llviewerobject.h"
+#include "llviewerimage.h"
+#include "v2math.h"
+#include "llfft.h"
+
+#include "llwaterpatch.h"
+
+
+const U32 N_RES = 16; //32 // number of subdivisions of wave tile
+const U8 WAVE_STEP = 8;
+
+/*
+#define N_DET 32 // number of subdivisions of wave tile for details
+
+class LLWaterDetail
+{
+protected:
+ S32 mResolution;
+ LLViewerImage *mTex;
+ U8 *mTexData;
+
+public:
+ LLWaterDetail() : mResolution(N_DET), mTex(0), mTexData(0) {init();}
+ void init();
+
+ ~LLWaterDetail()
+ {
+ delete[] mTexData;
+ mTexData = NULL;
+ }
+
+
+ //void initEmpty();
+
+ void setPixel(const LLVector3 &norm, const S32 i, const S32 j)
+ {
+ S32 offset = (i * mResolution + j) * 3;
+ mTexData[offset] = llround(norm.mV[VX] * 255);
+ mTexData[offset+1] = llround(norm.mV[VX] * 255);
+ mTexData[offset+2] = llround(norm.mV[VX] * 255);
+ }
+ void setPixel(F32 x, F32 y, F32 z, const S32 i, const S32 j)
+ {
+ S32 offset = (i * mResolution + j) * 3;
+ mTexData[offset] = llround(x * 255);
+ mTexData[offset+1] = llround(y * 255);
+ mTexData[offset+2] = llround(z * 255);
+ }
+
+ S32 getResolution() { return mResolution; }
+
+ void createDetailBumpmap(F32* u, F32* v);
+ void createTexture() const { mTex->createTexture(); }
+ void bindTexture() const { mTex->bindTexture(); }
+ LLViewerImage* getTexture() const { return mTex; }
+};
+*/
+
+class LLWaterSurface
+{
+protected:
+ BOOL mInitialized;
+ LLVector3 mWind;
+ F32 mA;
+ F32 mVisc; // viscosity of the fluid
+ F32 mShininess;
+
+ //LLWaterDetail* mDetail;
+
+ LLFFTPlan mPlan;
+ F32 mOmega[N_RES+1][N_RES+1]; // wave frequency
+ COMPLEX mHtilda0[N_RES+1][N_RES+1]; // wave amplitudes and phases at time 0.
+ LLVector3 mNorms[N_RES][N_RES];
+ COMPLEX mHtilda[N_RES * N_RES];
+
+public:
+ LLWaterSurface();
+ ~LLWaterSurface() {}
+
+ //void initSpecularLookup();
+
+ F32 phillips(const LLVector2& k, const LLVector2& wind_n, F32 L, F32 L_small);
+
+ void initAmplitudes();
+
+ F32 height(S32 i, S32 j) const { return mHtilda[i * N_RES + j].re; }
+ F32 heightWrapped(S32 i, S32 j) const
+ {
+ return height((i + N_RES) % N_RES, (j + N_RES) % N_RES);
+ }
+
+ void generateWaterHeightField(F64 time);
+ void calcNormals();
+
+ void getHeightAndNormal(F32 i, F32 j, F32& height, LLVector3& normal) const;
+ void getIntegerHeightAndNormal(S32 i, S32 j, F32& height, LLVector3& normal) const;
+ F32 agentDepth() const;
+
+// const LLWaterHeightField* hField() const { return &mHeightField; }
+
+ //void generateDetail(F64 t);
+ //void fluidSolver(F32* u, F32* v, F32* u0, F32* v0, F64 dt);
+
+ const LLVector3& getWind() const { return mWind; }
+ void setWind(const LLVector3& w) { mWind = w; } // initialized = 0?
+ F32 A() const { return mA; }
+ void setA(F32 a) { mA = a; }
+ F32 getShininess() const { return mShininess; }
+ //LLViewerImage* getSpecularLookup() const { return mSpecularLookup; }
+ //LLViewerImage* getDetail() const { return mDetail->getTexture(); }
+};
+
+class LLVOWater;
+
+class LLWaterGrid
+{
+public:
+ LLWaterGrid();
+
+ void init();
+ void cleanup();
+
+ LLWaterSurface* getWaterSurface() { return &mWater; }
+
+ void update();
+ void updateTree(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far,
+ BOOL restart);
+ void updateVisibility(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far);
+
+ LLVector3 mRegionOrigin;
+
+ LLVector3* mVtx;
+ LLVector3* mNorms;
+ U32 mRegionWidth;
+ U32 mMaxGridSize;
+ U32 mPatchRes;
+ U32 mMinStep;
+ U32 mStepsInRegion;
+ LLWaterPatch* mPatches;
+ LLRoam mRoam;
+ F32 mResIncrease;
+ U32 mResDecrease;
+
+ LLVOWater* mTab[5][5];
+
+ U32 gridDim() const { return mGridDim; }
+ U32 rowSize() const { return mGridDim1; }
+ void setGridDim(U32 gd) { mGridDim = gd; mGridDim1 = mGridDim + 1; }
+ U32 index(const LL2Coord& c) const { return c.y() * mGridDim1 + c.x(); }
+ U32 index(U32 x, U32 y) const { return y * mGridDim1 + x; }
+
+ LLVector3 vtx(const LL2Coord& c, F32 raised) const
+ {
+ LLVector3 v = mVtx[index(c)];
+ v.mV[VZ] += raised;
+ return v;
+ }
+
+ LLVector3 vtx(U32 x, U32 y, F32 raised) const
+ {
+ LLVector3 v = mVtx[index(x, y)];
+ v.mV[VZ] += raised;
+ return v;
+ }
+
+ void setVertex(const U32 x, const U32 y, const F32 raised, LLVector3 &vertex) const
+ {
+ vertex = mVtx[index(x, y)];
+ vertex.mV[VZ] += raised;
+ }
+
+ void setCamPosition(LL2Coord& cam, const LLVector3& cam_pos)
+ {
+ cam.x() = llround((cam_pos.mV[VX] - mRegionOrigin.mV[VX]) / mMinStep);
+ cam.y() = llround((cam_pos.mV[VY] - mRegionOrigin.mV[VY]) / mMinStep);
+ }
+
+ LLVector3 vtx(const LL2Coord& c) const { return mVtx[index(c)]; }
+ LLVector3 norm(const LL2Coord& c) const { return mNorms[index(c)]; }
+ LLVector3 vtx(U32 x, U32 y) const { return mVtx[index(x, y)]; }
+ LLVector3 norm(U32 x, U32 y) const { return mNorms[index(x, y)]; }
+
+protected:
+ LLWaterSurface mWater;
+ U32 mGridDim;
+ U32 mGridDim1;
+};
+
+class LLSurface;
+class LLHeavenBody;
+class LLVOSky;
+class LLFace;
+
+class LLVOWater : public LLViewerObject
+{
+public:
+ LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOWater() {}
+
+ /*virtual*/ void markDead();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+ static void cleanupClass();
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ BOOL updateGeometryFlat(LLDrawable *drawable);
+ BOOL updateGeometryHeightFieldSimple(LLDrawable *drawable);
+ BOOL updateGeometryHeightFieldRoam(LLDrawable *drawable);
+
+ /*virtual*/ void updateDrawable(BOOL force_damped);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ void setUseTexture(const BOOL use_texture);
+
+ static void generateNewWaves(F64 time);
+ static LLWaterSurface* getWaterSurface() { return sGrid->getWaterSurface(); }//return &mWater; }
+ static const LLWaterGrid* getGrid() { return sGrid; }
+protected:
+ BOOL mUseTexture;
+ static LLWaterGrid *sGrid;
+};
+
+#endif // LL_VOSURFACEPATCH_H
diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp
new file mode 100644
index 0000000000..ebe3c69c5c
--- /dev/null
+++ b/indra/newview/llwearable.cpp
@@ -0,0 +1,952 @@
+/**
+ * @file llwearable.cpp
+ * @brief LLWearable class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "imageids.h"
+#include "llassetstorage.h"
+#include "lldbstrings.h"
+#include "lldir.h"
+#include "llquantize.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llfloatercustomize.h"
+#include "llinventorymodel.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llvoavatar.h"
+#include "llwearable.h"
+
+//#include "viewer.h"
+//#include "llvfs.h"
+
+// static
+S32 LLWearable::sCurrentDefinitionVersion = 1;
+
+// static
+const char* LLWearable::sTypeName[ WT_COUNT ] =
+{
+ "shape",
+ "skin",
+ "hair",
+ "eyes",
+ "shirt",
+ "pants",
+ "shoes",
+ "socks",
+ "jacket",
+ "gloves",
+ "undershirt",
+ "underpants",
+ "skirt"
+};
+
+// static
+const char* LLWearable::sTypeLabel[ WT_COUNT ] =
+{
+ "Shape",
+ "Skin",
+ "Hair",
+ "Eyes",
+ "Shirt",
+ "Pants",
+ "Shoes",
+ "Socks",
+ "Jacket",
+ "Gloves",
+ "Undershirt",
+ "Underpants",
+ "Skirt"
+};
+
+
+// static
+LLAssetType::EType LLWearable::typeToAssetType(EWearableType wearable_type)
+{
+ switch( wearable_type )
+ {
+ case WT_SHAPE:
+ case WT_SKIN:
+ case WT_HAIR:
+ case WT_EYES:
+ return LLAssetType::AT_BODYPART;
+ case WT_SHIRT:
+ case WT_PANTS:
+ case WT_SHOES:
+ case WT_SOCKS:
+ case WT_JACKET:
+ case WT_GLOVES:
+ case WT_UNDERSHIRT:
+ case WT_UNDERPANTS:
+ case WT_SKIRT:
+ return LLAssetType::AT_CLOTHING;
+ default:
+ return LLAssetType::AT_NONE;
+ }
+}
+
+
+LLWearable::LLWearable(const LLTransactionID& transaction_id) :
+ mDefinitionVersion(LLWearable::sCurrentDefinitionVersion),
+ mType(WT_SHAPE)
+{
+ mTransactionID = transaction_id;
+ mAssetID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+}
+
+LLWearable::LLWearable(const LLAssetID& asset_id) :
+ mDefinitionVersion( LLWearable::sCurrentDefinitionVersion ),
+ mType(WT_SHAPE)
+{
+ mAssetID = asset_id;
+ mTransactionID.setNull();
+}
+
+LLWearable::~LLWearable()
+{
+ mVisualParamMap.deleteAllData();
+ mTEMap.deleteAllData();
+}
+
+
+// static
+EWearableType LLWearable::typeNameToType( const LLString& type_name )
+{
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ if( type_name == LLWearable::sTypeName[ i ] )
+ {
+ return (EWearableType)i;
+ }
+ }
+ return WT_INVALID;
+}
+
+
+const char* terse_F32_to_string( F32 f, char s[MAX_STRING] )
+{
+ char* r = s;
+ S32 len = sprintf( s, "%.2f", f );
+
+ // "1.20" -> "1.2"
+ // "24.00" -> "24."
+ while( '0' == r[len - 1] )
+ {
+ len--;
+ r[len] = '\0';
+ }
+
+ if( '.' == r[len - 1] )
+ {
+ // "24." -> "24"
+ len--;
+ r[len] = '\0';
+ }
+ else
+ if( ('-' == r[0]) && ('0' == r[1]) )
+ {
+ // "-0.59" -> "-.59"
+ r++;
+ r[0] = '-';
+ }
+ else
+ if( '0' == r[0] )
+ {
+ // "0.59" -> ".59"
+ r++;
+ }
+
+ return r;
+}
+
+BOOL LLWearable::exportFile( FILE* file )
+{
+ // header and version
+ if( fprintf( file, "LLWearable version %d\n", mDefinitionVersion ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // name
+ if( fprintf( file, "%s\n", mName.c_str() ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // description
+ if( fprintf( file, "%s\n", mDescription.c_str() ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // permissions
+ if( !mPermissions.exportFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // sale info
+ if( !mSaleInfo.exportFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // wearable type
+ S32 type = (S32)mType;
+ if( fprintf( file, "type %d\n", type ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // parameters
+ S32 num_parameters = mVisualParamMap.getLength();
+ if( fprintf( file, "parameters %d\n", num_parameters ) < 0 )
+ {
+ return FALSE;
+ }
+
+ char s[ MAX_STRING ];
+ for( F32* param_weightp = mVisualParamMap.getFirstData(); param_weightp; param_weightp = mVisualParamMap.getNextData() )
+ {
+ S32 param_id = mVisualParamMap.getCurrentKeyWithoutIncrement();
+ if( fprintf( file, "%d %s\n", param_id, terse_F32_to_string( *param_weightp, s ) ) < 0 )
+ {
+ return FALSE;
+ }
+ }
+
+ // texture entries
+ S32 num_textures = mTEMap.getLength();
+ if( fprintf( file, "textures %d\n", num_textures ) < 0 )
+ {
+ return FALSE;
+ }
+
+ for( LLUUID* image_id = mTEMap.getFirstData(); image_id; image_id = mTEMap.getNextData() )
+ {
+ S32 te = mTEMap.getCurrentKeyWithoutIncrement();
+ char image_id_string[UUID_STR_LENGTH];
+ image_id->toString( image_id_string );
+ if( fprintf( file, "%d %s\n", te, image_id_string) < 0 )
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+BOOL LLWearable::importFile( FILE* file )
+{
+ char text_buffer[2048];
+ S32 fields_read = 0;
+
+ // read header and version
+ fields_read = fscanf( file, "LLWearable version %d\n", &mDefinitionVersion );
+ if( fields_read != 1 )
+ {
+ // Shouldn't really log the asset id for security reasons, but
+ // we need it in this case.
+ llwarns << "Bad Wearable asset header: " << mAssetID << llendl;
+ //gVFS->dumpMap();
+ return FALSE;
+ }
+
+ if( mDefinitionVersion > LLWearable::sCurrentDefinitionVersion )
+ {
+ llwarns << "Wearable asset has newer version (" << mDefinitionVersion << ") than XML (" << LLWearable::sCurrentDefinitionVersion << ")" << llendl;
+ return FALSE;
+ }
+
+ // name
+ char next_char = fgetc( file );
+ if( '\n' == next_char )
+ {
+ // no name
+ mName = "";
+ }
+ else
+ {
+ ungetc( next_char, file );
+ fields_read = fscanf( file, "%[^\n]", text_buffer );
+ if( (1 != fields_read) || (fgetc( file ) != '\n') )
+ {
+ llwarns << "Bad Wearable asset: early end of file" << llendl;
+ return FALSE;
+ }
+ mName = text_buffer;
+ LLString::truncate(mName, DB_INV_ITEM_NAME_STR_LEN );
+ }
+
+ // description
+ next_char = fgetc( file );
+ if( '\n' == next_char )
+ {
+ // no description
+ mDescription = "";
+ }
+ else
+ {
+ ungetc( next_char, file );
+ fields_read = fscanf( file, "%[^\n]", text_buffer );
+ if( (1 != fields_read) || (fgetc( file ) != '\n') )
+ {
+ llwarns << "Bad Wearable asset: early end of file" << llendl;
+ return FALSE;
+ }
+ mDescription = text_buffer;
+ LLString::truncate(mDescription, DB_INV_ITEM_DESC_STR_LEN );
+ }
+
+ // permissions
+ S32 perm_version;
+ fields_read = fscanf( file, " permissions %d\n", &perm_version );
+ if( (fields_read != 1) || (perm_version != 0) )
+ {
+ llwarns << "Bad Wearable asset: missing permissions" << llendl;
+ return FALSE;
+ }
+ if( !mPermissions.importFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // sale info
+ S32 sale_info_version;
+ fields_read = fscanf( file, " sale_info %d\n", &sale_info_version );
+ if( (fields_read != 1) || (sale_info_version != 0) )
+ {
+ llwarns << "Bad Wearable asset: missing sale_info" << llendl;
+ return FALSE;
+ }
+ // Sale info used to contain next owner perm. It is now in the
+ // permissions. Thus, we read that out, and fix legacy
+ // objects. It's possible this op would fail, but it should pick
+ // up the vast majority of the tasks.
+ BOOL has_perm_mask = FALSE;
+ U32 perm_mask = 0;
+ if( !mSaleInfo.importFile(file, has_perm_mask, perm_mask) )
+ {
+ return FALSE;
+ }
+ if(has_perm_mask)
+ {
+ // fair use fix.
+ if(!(perm_mask & PERM_COPY))
+ {
+ perm_mask |= PERM_TRANSFER;
+ }
+ mPermissions.setMaskNext(perm_mask);
+ }
+
+ // wearable type
+ S32 type = -1;
+ fields_read = fscanf( file, "type %d\n", &type );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: bad type" << llendl;
+ return FALSE;
+ }
+ if( 0 <= type && type < WT_COUNT )
+ {
+ mType = (EWearableType)type;
+ }
+ else
+ {
+ llwarns << "Bad Wearable asset: bad type #" << type << llendl;
+ return FALSE;
+ }
+
+
+ // parameters header
+ S32 num_parameters = 0;
+ fields_read = fscanf( file, "parameters %d\n", &num_parameters );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: missing parameters block" << llendl;
+ return FALSE;
+ }
+
+ // parameters
+ S32 i;
+ for( i = 0; i < num_parameters; i++ )
+ {
+ S32 param_id = 0;
+ F32 param_weight = 0.f;
+ fields_read = fscanf( file, "%d %f\n", &param_id, &param_weight );
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad Wearable asset: bad parameter, #" << i << llendl;
+ return FALSE;
+ }
+ mVisualParamMap.addData( param_id, new F32(param_weight) );
+ }
+
+ // textures header
+ S32 num_textures = 0;
+ fields_read = fscanf( file, "textures %d\n", &num_textures);
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: missing textures block" << llendl;
+ return FALSE;
+ }
+
+ // textures
+ for( i = 0; i < num_textures; i++ )
+ {
+ S32 te = 0;
+ fields_read = fscanf( file, "%d %s\n", &te, text_buffer);
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad Wearable asset: bad texture, #" << i << llendl;
+ return FALSE;
+ }
+
+ if( !LLUUID::validate( text_buffer ) )
+ {
+ llwarns << "Bad Wearable asset: bad texture uuid: " << text_buffer << llendl;
+ return FALSE;
+ }
+
+ mTEMap.addData( te, new LLUUID( text_buffer ) );
+ }
+
+ return TRUE;
+}
+
+
+// Avatar parameter and texture definitions can change over time.
+// This function returns true if parameters or textures have been added or removed
+// since this wearable was created.
+BOOL LLWearable::isOldVersion()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+ if( LLWearable::sCurrentDefinitionVersion < mDefinitionVersion )
+ {
+ llwarns << "Wearable asset has newer version (" << mDefinitionVersion << ") than XML (" << LLWearable::sCurrentDefinitionVersion << ")" << llendl;
+ llassert(0);
+ }
+
+ if( LLWearable::sCurrentDefinitionVersion != mDefinitionVersion )
+ {
+ return TRUE;
+ }
+
+ S32 param_count = 0;
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ param_count++;
+ if( !mVisualParamMap.checkKey( param->getID() ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+ if( param_count != mVisualParamMap.getLength() )
+ {
+ return TRUE;
+ }
+
+
+ S32 te_count = 0;
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ te_count++;
+ if( !mTEMap.checkKey( te ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+ if( te_count != mTEMap.getLength() )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// Avatar parameter and texture definitions can change over time.
+// * If parameters or textures have been REMOVED since the wearable was created,
+// they're just ignored, so we consider the wearable clean even though isOldVersion()
+// will return true.
+// * If parameters or textures have been ADDED since the wearable was created,
+// they are taken to have default values, so we consider the wearable clean
+// only if those values are the same as the defaults.
+BOOL LLWearable::isDirty()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ F32* weightp = mVisualParamMap.getIfThere( param->getID() );
+ F32 weight;
+ if( weightp )
+ {
+ weight = llclamp( *weightp, param->getMinWeight(), param->getMaxWeight() );
+ }
+ else
+ {
+ weight = param->getDefaultWeight();
+ }
+
+ U8 a = F32_to_U8( param->getWeight(), param->getMinWeight(), param->getMaxWeight() );
+ U8 b = F32_to_U8( weight, param->getMinWeight(), param->getMaxWeight() );
+ if( a != b )
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLViewerImage* avatar_image = avatar->getTEImage( te );
+ if( !avatar_image )
+ {
+ llassert( 0 );
+ continue;
+ }
+ LLUUID* mapped_image_id = mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ if( avatar_image->getID() != image_id )
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ //if( gFloaterCustomize )
+ //{
+ // if( mDescription != gFloaterCustomize->getWearableDescription( mType ) )
+ // {
+ // return TRUE;
+ // }
+ //}
+
+ return FALSE;
+}
+
+
+void LLWearable::setParamsToDefaults()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mVisualParamMap.deleteAllData();
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType ) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ mVisualParamMap.addData( param->getID(), new F32( param->getDefaultWeight() ) );
+ }
+ }
+}
+
+void LLWearable::setTexturesToDefaults()
+{
+ mTEMap.deleteAllData();
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ mTEMap.addData( te, new LLUUID( LLVOAvatar::getDefaultTEImageID( te ) ) );
+ }
+ }
+}
+
+// Updates the user's avatar's appearance
+void LLWearable::writeToAvatar( BOOL set_by_user )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ ESex old_sex = avatar->getSex();
+
+ // Pull params
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 param_id = param->getID();
+ F32* weight = mVisualParamMap.getIfThere( param_id );
+ if( weight )
+ {
+ // only animate with user-originated changes
+ if (set_by_user)
+ {
+ param->setAnimationTarget(*weight, set_by_user);
+ }
+ else
+ {
+ avatar->setVisualParamWeight( param_id, *weight, set_by_user );
+ }
+ }
+ else
+ {
+ // only animate with user-originated changes
+ if (set_by_user)
+ {
+ param->setAnimationTarget(param->getDefaultWeight(), set_by_user);
+ }
+ else
+ {
+ avatar->setVisualParamWeight( param_id, param->getDefaultWeight(), set_by_user );
+ }
+ }
+ }
+ }
+
+ // only interpolate with user-originated changes
+ if (set_by_user)
+ {
+ avatar->startAppearanceAnimation(TRUE, TRUE);
+ }
+
+ // Pull texture entries
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLUUID* mapped_image_id = mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ LLViewerImage* image = gImageList.getImage( image_id );
+ avatar->setLocTexTE( te, image, set_by_user );
+ }
+ }
+
+ avatar->updateVisualParams();
+
+ if( gFloaterCustomize )
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(gAgent.getWearableItem(mType));
+ U32 perm_mask = PERM_NONE;
+ BOOL is_complete = FALSE;
+ if(item)
+ {
+ perm_mask = item->getPermissions().getMaskOwner();
+ is_complete = item->isComplete();
+ if(!is_complete)
+ {
+ item->fetchFromServer();
+ }
+ }
+ gFloaterCustomize->setWearable(mType, this, perm_mask, is_complete);
+ LLFloaterCustomize::setCurrentWearableType( mType );
+ }
+
+ ESex new_sex = avatar->getSex();
+ if( old_sex != new_sex )
+ {
+ avatar->updateSexDependentLayerSets( set_by_user );
+ }
+
+ avatar->updateMeshTextures();
+
+// if( set_by_user )
+// {
+// gAgent.sendAgentSetAppearance();
+// }
+}
+
+// Updates the user's avatar's appearance, replacing this wearables' parameters and textures with default values.
+// static
+void LLWearable::removeFromAvatar( EWearableType type, BOOL set_by_user )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ // You can't just remove body parts.
+ if( (type == WT_SHAPE) ||
+ (type == WT_SKIN) ||
+ (type == WT_HAIR) ||
+ (type == WT_EYES) )
+ {
+ return;
+ }
+
+ // Pull params
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == type) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 param_id = param->getID();
+ avatar->setVisualParamWeight( param_id, param->getDefaultWeight(), set_by_user );
+ }
+ }
+
+ // Pull textures
+ LLViewerImage* image = gImageList.getImage( IMG_DEFAULT_AVATAR );
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == type )
+ {
+ avatar->setLocTexTE( te, image, set_by_user );
+ }
+ }
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->setWearable(type, NULL, PERM_ALL, TRUE);
+ }
+
+ avatar->updateVisualParams();
+ avatar->updateMeshTextures();
+
+// if( set_by_user )
+// {
+// gAgent.sendAgentSetAppearance();
+// }
+}
+
+
+
+// Updates asset from the user's avatar
+void LLWearable::readFromAvatar()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mDefinitionVersion = LLWearable::sCurrentDefinitionVersion;
+
+ mVisualParamMap.deleteAllData();
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ mVisualParamMap.addData( param->getID(), new F32( param->getWeight() ) );
+ }
+ }
+
+ mTEMap.deleteAllData();
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLViewerImage* image = avatar->getTEImage( te );
+ if( image )
+ {
+ mTEMap.addData( te, new LLUUID( image->getID() ) );
+ }
+ }
+ }
+
+ //if( gFloaterCustomize )
+ //{
+ // mDescription = gFloaterCustomize->getWearableDescription( mType );
+ //}
+}
+
+// Does not copy mAssetID.
+// Definition version is current: removes obsolete enties and creates default values for new ones.
+void LLWearable::copyDataFrom( LLWearable* src )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mDefinitionVersion = LLWearable::sCurrentDefinitionVersion;
+
+ mName = src->mName;
+ mDescription = src->mDescription;
+ mPermissions = src->mPermissions;
+ mSaleInfo = src->mSaleInfo;
+ mType = src->mType;
+
+ // Deep copy of mVisualParamMap (copies only those params that are current, filling in defaults where needed)
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 id = param->getID();
+ F32* weightp = src->mVisualParamMap.getIfThere( id );
+ F32 weight = weightp ? *weightp : param->getDefaultWeight();
+ mVisualParamMap.addData( id, new F32( weight ) );
+ }
+ }
+
+ // Deep copy of mTEMap (copies only those tes that are current, filling in defaults where needed)
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLUUID* mapped_image_id = src->mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ mTEMap.addData( te, new LLUUID( image_id ) );
+ }
+ }
+}
+
+struct LLWearableSaveData
+{
+ EWearableType mType;
+};
+
+void LLWearable::saveNewAsset()
+{
+// llinfos << "LLWearable::saveNewAsset() type: " << getTypeName() << llendl;
+ //dump();
+
+ char new_asset_id_string[UUID_STR_LENGTH];
+ mAssetID.toString(new_asset_id_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.wbl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,new_asset_id_string).c_str());
+ FILE* fp = LLFile::fopen(filename, "wb");
+ BOOL successful_save = FALSE;
+ if(fp && exportFile(fp))
+ {
+ successful_save = TRUE;
+ }
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+ if(!successful_save)
+ {
+ char buffer[2*MAX_STRING];
+ sprintf(buffer,
+ "Unable to save '%s' to wearable file.",
+ mName.c_str());
+ llwarns << buffer << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = mName;
+ gViewerWindow->alertXml("CannotSaveWearableOutOfSpace", args);
+ return;
+ }
+
+ // save it out to database
+ if( gAssetStorage )
+ {
+ LLWearableSaveData* data = new LLWearableSaveData;
+ data->mType = mType;
+ gAssetStorage->storeAssetData(filename, mTransactionID, getAssetType(),
+ &LLWearable::onSaveNewAssetComplete,
+ (void*)data);
+ }
+}
+
+// static
+void LLWearable::onSaveNewAssetComplete(const LLUUID& new_asset_id, void* userdata, S32 status) // StoreAssetData callback (fixed)
+{
+ LLWearableSaveData* data = (LLWearableSaveData*)userdata;
+ const char* type_name = LLWearable::typeToTypeName(data->mType);
+ if(0 == status)
+ {
+ // Success
+ llinfos << "Saved wearable " << type_name << llendl;
+ }
+ else
+ {
+ char buffer[2*MAX_STRING];
+ sprintf(buffer,
+ "Unable to save %s to central asset store.",
+ type_name);
+ llwarns << buffer << " Status: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = type_name;
+ gViewerWindow->alertXml("CannotSaveToAssetStore", args);
+ }
+
+ // Delete temp file
+ char new_asset_id_string[UUID_STR_LENGTH];
+ new_asset_id.toString(new_asset_id_string);
+ char src_filename[LL_MAX_PATH];
+ sprintf(src_filename, "%s.wbl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,new_asset_id_string).c_str());
+ LLFile::remove(src_filename);
+
+ // delete the context data
+ delete data;
+}
+
+BOOL LLWearable::isMatchedToInventoryItem( LLViewerInventoryItem* item )
+{
+ return
+ ( mName == item->getName() ) &&
+ ( mDescription == item->getDescription() ) &&
+ ( mPermissions == item->getPermissions() ) &&
+ ( mSaleInfo == item->getSaleInfo() );
+}
+
+void LLWearable::dump()
+{
+ llinfos << "wearable " << LLWearable::typeToTypeName( mType ) << llendl;
+ llinfos << " Name: " << mName << llendl;
+ llinfos << " Desc: " << mDescription << llendl;
+ //mPermissions
+ //mSaleInfo
+
+ llinfos << " Params:" << llendl;
+ for( F32* param_weightp = mVisualParamMap.getFirstData();
+ param_weightp;
+ param_weightp = mVisualParamMap.getNextData() )
+ {
+ S32 param_id = mVisualParamMap.getCurrentKeyWithoutIncrement();
+ llinfos << " " << param_id << " " << *param_weightp << llendl;
+ }
+
+ llinfos << " Textures:" << llendl;
+ for( LLUUID* image_id = mTEMap.getFirstData();
+ image_id;
+ image_id = mTEMap.getNextData() )
+ {
+ S32 te = mTEMap.getCurrentKeyWithoutIncrement();
+ llinfos << " " << te << " " << *image_id << llendl;
+ }
+}
+
diff --git a/indra/newview/llwearable.h b/indra/newview/llwearable.h
new file mode 100644
index 0000000000..d2dcd764d5
--- /dev/null
+++ b/indra/newview/llwearable.h
@@ -0,0 +1,119 @@
+/**
+ * @file llwearable.h
+ * @brief LLWearable class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWEARABLE_H
+#define LL_LLWEARABLE_H
+
+#include "lluuid.h"
+#include "llptrskipmap.h"
+#include "llstring.h"
+#include "llpermissions.h"
+#include "llsaleinfo.h"
+#include "llassetstorage.h"
+
+class LLViewerInventoryItem;
+
+enum EWearableType // If you change this, update LLWearable::getTypeName(), getTypeLabel(), and LLVOAvatar::getTEWearableType()
+{
+ WT_SHAPE = 0,
+ WT_SKIN = 1,
+ WT_HAIR = 2,
+ WT_EYES = 3,
+ WT_SHIRT = 4,
+ WT_PANTS = 5,
+ WT_SHOES = 6,
+ WT_SOCKS = 7,
+ WT_JACKET = 8,
+ WT_GLOVES = 9,
+ WT_UNDERSHIRT = 10,
+ WT_UNDERPANTS = 11,
+ WT_SKIRT = 12,
+ WT_COUNT = 13,
+ WT_INVALID = 255
+};
+
+class LLWearable
+{
+public:
+ LLWearable(const LLTransactionID& transactionID);
+ LLWearable(const LLAssetID& assetID);
+ ~LLWearable();
+
+ const LLAssetID& getID() { return mAssetID; }
+ const LLTransactionID& getTransactionID() { return mTransactionID; }
+
+ BOOL readData( const char *buffer );
+ BOOL isDirty();
+ BOOL isOldVersion();
+
+ void writeToAvatar( BOOL set_by_user );
+ void readFromAvatar();
+ void removeFromAvatar( BOOL set_by_user ) { LLWearable::removeFromAvatar( mType, set_by_user ); }
+ static void removeFromAvatar( EWearableType type, BOOL set_by_user );
+
+ BOOL exportFile(FILE* file);
+ BOOL importFile(FILE* file);
+
+ EWearableType getType() const { return mType; }
+ void setType( EWearableType type ) { mType = type; }
+
+ void setName( const std::string& name ) { mName = name; }
+ const std::string& getName() { return mName; }
+
+ void setDescription( const std::string& desc ) { mDescription = desc; }
+ const std::string& getDescription() { return mDescription; }
+
+ void setPermissions( const LLPermissions& p ) { mPermissions = p; }
+ const LLPermissions& getPermissions() { return mPermissions; }
+
+ void setSaleInfo( const LLSaleInfo& info ) { mSaleInfo = info; }
+ const LLSaleInfo& getSaleInfo() { return mSaleInfo; }
+
+ const char* getTypeLabel() const { return LLWearable::sTypeLabel[ mType ]; }
+ const char* getTypeName() const { return LLWearable::sTypeName[ mType ]; }
+
+ void setParamsToDefaults();
+ void setTexturesToDefaults();
+
+ LLAssetType::EType getAssetType() const { return LLWearable::typeToAssetType( mType ); }
+
+ static EWearableType typeNameToType( const LLString& type_name );
+ static const char* typeToTypeName( EWearableType type ) { return (type<WT_COUNT) ? LLWearable::sTypeName[type] : "invalid"; }
+ static const char* typeToTypeLabel( EWearableType type ) { return (type<WT_COUNT) ? LLWearable::sTypeLabel[type] : "invalid"; }
+ static LLAssetType::EType typeToAssetType( EWearableType wearable_type );
+
+ void saveNewAsset();
+ static void onSaveNewAssetComplete( const LLUUID& asset_uuid, void* user_data, S32 status );
+
+ BOOL isMatchedToInventoryItem( LLViewerInventoryItem* item );
+
+ void copyDataFrom( LLWearable* src );
+
+ static void setCurrentDefinitionVersion( S32 version ) { LLWearable::sCurrentDefinitionVersion = version; }
+
+ void dump();
+
+private:
+ static S32 sCurrentDefinitionVersion; // Depends on the current state of the avatar_lad.xml.
+ S32 mDefinitionVersion; // Depends on the state of the avatar_lad.xml when this asset was created.
+ LLString mName;
+ LLString mDescription;
+ LLPermissions mPermissions;
+ LLSaleInfo mSaleInfo;
+ LLAssetID mAssetID;
+ LLTransactionID mTransactionID;
+ EWearableType mType;
+
+ LLPtrSkipMap<S32, F32*> mVisualParamMap; // maps visual param id to weight
+ LLPtrSkipMap<S32, LLUUID*> mTEMap; // maps TE to Image ID
+
+ static const char* LLWearable::sTypeName[ WT_COUNT ];
+ static const char* LLWearable::sTypeLabel[ WT_COUNT ];
+};
+
+#endif // LL_LLWEARABLE_H
diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp
new file mode 100644
index 0000000000..297ced71fc
--- /dev/null
+++ b/indra/newview/llwearablelist.cpp
@@ -0,0 +1,325 @@
+/**
+ * @file llwearablelist.cpp
+ * @brief LLWearableList class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llwearablelist.h"
+
+#include "message.h"
+#include "llassetstorage.h"
+#include "llagent.h"
+#include "llvoavatar.h"
+#include "llviewerinventory.h"
+//#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "llnotify.h"
+
+// Globals
+LLWearableList gWearableList;
+
+
+struct LLWearableArrivedData
+{
+ LLWearableArrivedData(
+ LLAssetType::EType asset_type,
+ const LLString& wearable_name,
+ void(*asset_arrived_callback)(LLWearable*, void* userdata),
+ void* userdata )
+ :
+ mAssetType( asset_type ),
+ mCallback( asset_arrived_callback ),
+ mUserdata( userdata ),
+ mName( wearable_name )
+ {}
+
+ LLAssetType::EType mAssetType;
+ void (*mCallback)(LLWearable*, void* userdata);
+ void* mUserdata;
+ LLString mName;
+};
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// LLWearableList
+
+LLWearableList::~LLWearableList()
+{
+ mList.deleteAllData();
+}
+
+void LLWearableList::getAsset( const LLAssetID& assetID, const LLString& wearable_name, LLAssetType::EType asset_type, void(*asset_arrived_callback)(LLWearable*, void* userdata), void* userdata )
+{
+ llassert( (asset_type == LLAssetType::AT_CLOTHING) || (asset_type == LLAssetType::AT_BODYPART) );
+ LLWearable* instance = mList.getIfThere( assetID );
+ if( instance )
+ {
+ asset_arrived_callback( instance, userdata );
+ }
+ else
+ {
+ gAssetStorage->getAssetData(
+ assetID,
+ asset_type,
+ LLWearableList::processGetAssetReply,
+ (void*)new LLWearableArrivedData( asset_type, wearable_name, asset_arrived_callback, userdata ),
+ TRUE);
+ }
+}
+
+// static
+void LLWearableList::processGetAssetReply( const char* filename, const LLAssetID& uuid, void* userdata, S32 status )
+{
+ BOOL success = FALSE;
+ LLWearableArrivedData* data = (LLWearableArrivedData*) userdata;
+
+ if( !filename )
+ {
+ llinfos << "Bad Wearable Asset: missing file." << llendl;
+ }
+ else
+ if( status >= 0 )
+ {
+ // read the file
+ FILE* fp = LLFile::fopen(filename, "rb");
+ if( !fp )
+ {
+ llinfos << "Bad Wearable Asset: unable to open file: '" << filename << "'" << llendl;
+ }
+ else
+ {
+ LLWearable *wearable = new LLWearable(uuid);
+ if( wearable->importFile( fp ) )
+ {
+// llinfos << "processGetAssetReply()" << llendl;
+// wearable->dump();
+
+ gWearableList.mList[ uuid ] = wearable;
+ if( data->mCallback )
+ {
+ data->mCallback( wearable, data->mUserdata );
+ }
+ success = TRUE;
+ }
+ else
+ {
+ LLString::format_map_t args;
+ // XUI:translate
+ args["[TYPE]"] = LLAssetType::lookupHumanReadable(data->mAssetType);
+ if (data->mName.empty())
+ {
+ LLNotifyBox::showXml("FailedToLoadWearableUnnamed", args);
+ }
+ else
+ {
+ args["[DESC]"] = data->mName;
+ LLNotifyBox::showXml("FailedToLoadWearable", args);
+ }
+ delete wearable;
+ }
+
+ fclose( fp );
+ if(filename)
+ {
+ LLFile::remove(filename);
+ }
+ }
+ }
+ else
+ {
+ if(filename)
+ {
+ LLFile::remove(filename);
+ }
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ llwarns << "Wearable download failed: " << LLAssetStorage::getErrorString( status ) << " " << uuid << llendl;
+ switch( status )
+ {
+ case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
+ {
+ LLString::format_map_t args;
+ // XUI:translate
+ args["[TYPE]"] = LLAssetType::lookupHumanReadable(data->mAssetType);
+ if (data->mName.empty())
+ {
+ LLNotifyBox::showXml("FailedToFindWearableUnnamed", args);
+ }
+ else
+ {
+ args["[DESC]"] = data->mName;
+ LLNotifyBox::showXml("FailedToFindWearable", args);
+ }
+
+ // Asset does not exist in the database.
+ // Can't load asset, so return NULL
+ if( data->mCallback )
+ {
+ data->mCallback( NULL, data->mUserdata );
+ }
+ break;
+ }
+ default:
+ {
+ // Try again
+ gAssetStorage->getAssetData(uuid,
+ data->mAssetType,
+ LLWearableList::processGetAssetReply,
+ userdata); // re-use instead of deleting.
+ return;
+ }
+ }
+ }
+
+ delete data;
+}
+
+
+LLWearable* LLWearableList::createLegacyWearableFromAvatar( EWearableType type )
+{
+ llinfos << "LLWearableList::createLegacyWearableFromAvatar" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->setType( type );
+ wearable->setName( wearable->getTypeLabel() );
+ wearable->setDescription( "Recovered from lost asset." );
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLPermissions perm;
+ perm.init( avatar->getID(), avatar->getID(), LLUUID::null, LLUUID::null );
+ perm.initMasks(PERM_TRANSFER, PERM_TRANSFER, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
+ wearable->setPermissions( perm );
+
+ // Save info is the default.
+
+ wearable->readFromAvatar();
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+
+// Creates a new wearable just like the old_wearable but with data copied over from item
+LLWearable* LLWearableList::createWearableMatchedToInventoryItem( LLWearable* old_wearable, LLViewerInventoryItem* item )
+{
+ lldebugs << "LLWearableList::createWearableMatchedToInventoryItem()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->copyDataFrom( old_wearable );
+
+ wearable->setName( item->getName() );
+ wearable->setDescription( item->getDescription() );
+ wearable->setPermissions( item->getPermissions() );
+ wearable->setSaleInfo( item->getSaleInfo() );
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+LLWearable* LLWearableList::createCopyFromAvatar( LLWearable* old_wearable, const std::string& new_name )
+{
+ lldebugs << "LLWearableList::createCopyFromAvatar()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->copyDataFrom( old_wearable );
+ LLPermissions perm(old_wearable->getPermissions());
+ perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true);
+ wearable->setPermissions(perm);
+ wearable->readFromAvatar(); // update from the avatar
+
+ if (!new_name.empty()) wearable->setName(new_name);
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+
+LLWearable* LLWearableList::createCopy( LLWearable* old_wearable )
+{
+ lldebugs << "LLWearableList::createCopy()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->copyDataFrom( old_wearable );
+ LLPermissions perm(old_wearable->getPermissions());
+ perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true);
+ wearable->setPermissions(perm);
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+LLWearable* LLWearableList::createNewWearable( EWearableType type )
+{
+ lldebugs << "LLWearableList::createNewWearable()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->setType( type );
+
+ LLString name = "New ";
+ name.append( wearable->getTypeLabel() );
+ wearable->setName( name );
+
+ LLPermissions perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
+ perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
+ wearable->setPermissions(perm);
+
+ // Description and sale info have default values.
+
+ wearable->setParamsToDefaults();
+ wearable->setTexturesToDefaults();
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
diff --git a/indra/newview/llwearablelist.h b/indra/newview/llwearablelist.h
new file mode 100644
index 0000000000..587ea9e507
--- /dev/null
+++ b/indra/newview/llwearablelist.h
@@ -0,0 +1,50 @@
+/**
+ * @file llwearablelist.h
+ * @brief LLWearableList class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWEARABLELIST_H
+#define LL_LLWEARABLELIST_H
+
+#include "llwearable.h"
+#include "llskiplist.h"
+#include "lluuid.h"
+#include "llassetstorage.h"
+
+class LLWearableList
+{
+public:
+ LLWearableList() {}
+ ~LLWearableList();
+
+ S32 getLength() { return mList.getLength(); }
+ const LLWearable* getFirst() { return mList.getFirstData(); }
+ const LLWearable* getNext() { return mList.getNextData(); }
+
+ void getAsset(
+ const LLAssetID& assetID,
+ const LLString& wearable_name,
+ LLAssetType::EType asset_type,
+ void(*asset_arrived_callback)(LLWearable*, void* userdata),
+ void* userdata );
+
+ LLWearable* createLegacyWearableFromAvatar( EWearableType type );
+
+ LLWearable* createWearableMatchedToInventoryItem( LLWearable* old_wearable, LLViewerInventoryItem* item );
+ LLWearable* createCopyFromAvatar( LLWearable* old_wearable, const std::string& new_name = "" );
+ LLWearable* createCopy( LLWearable* old_wearable );
+ LLWearable* createNewWearable( EWearableType type );
+
+ // Pseudo-private
+ static void processGetAssetReply(const char* filename, const LLAssetID& assetID, void* user_data, S32 status);
+
+protected:
+ LLPtrSkipMap< const LLUUID, LLWearable* > mList;
+};
+
+extern LLWearableList gWearableList;
+
+#endif // LL_LLWEARABLELIST_H
diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp
new file mode 100644
index 0000000000..d57bb7da82
--- /dev/null
+++ b/indra/newview/llweb.cpp
@@ -0,0 +1,69 @@
+/**
+ * @file llweb.cpp
+ * @brief Functions dealing with web browsers
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llweb.h"
+
+#include "llwindow.h"
+
+#include "llfloaterhtml.h"
+#include "llviewercontrol.h"
+
+// static
+void LLWeb::loadURL(std::string url)
+{
+#if LL_MOZILLA_ENABLED
+ if (gSavedSettings.getBOOL("UseExternalBrowser"))
+ {
+ loadURLExternal(url);
+ }
+ else
+ {
+ LLFloaterHTML::show((void*)url.c_str());
+ }
+#else
+ loadURLExternal(url);
+#endif
+}
+
+
+// static
+void LLWeb::loadURLExternal(std::string url)
+{
+ std::string escaped_url = escapeURL(url);
+ spawn_web_browser(escaped_url.c_str());
+}
+
+
+// static
+std::string LLWeb::escapeURL(std::string url)
+{
+ // The CURL curl_escape() function escapes colons, slashes,
+ // and all characters but A-Z and 0-9. Do a cheesy mini-escape.
+ std::string escaped_url;
+ S32 len = url.length();
+ for (S32 i = 0; i < len; i++)
+ {
+ char c = url[i];
+ if (c == ' ')
+ {
+ escaped_url += "%20";
+ }
+ else if (c == '\\')
+ {
+ escaped_url += "%5C";
+ }
+ else
+ {
+ escaped_url += c;
+ }
+ }
+ return escaped_url;
+}
diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h
new file mode 100644
index 0000000000..69bfd4f3ae
--- /dev/null
+++ b/indra/newview/llweb.h
@@ -0,0 +1,31 @@
+/**
+ * @file llweb.h
+ * @brief Functions dealing with web browsers
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWEB_H
+#define LL_LLWEB_H
+
+#include <string>
+
+class LLWeb
+{
+public:
+ // Loads unescaped url in either internal web browser or external
+ // browser, depending on user settings.
+ static void loadURL(std::string url);
+
+ static void loadURL(const char* url) { loadURL( std::string(url) ); }
+
+ // Loads unescaped url in external browser.
+ static void loadURLExternal(std::string url);
+
+ // Returns escaped (eg, " " to "%20") url
+ static std::string escapeURL(std::string url);
+};
+
+#endif
diff --git a/indra/newview/llwind.cpp b/indra/newview/llwind.cpp
new file mode 100644
index 0000000000..15b6304136
--- /dev/null
+++ b/indra/newview/llwind.cpp
@@ -0,0 +1,340 @@
+/**
+ * @file llwind.cpp
+ * @brief LLWind class implementation
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Wind is a lattice. It is computed on the simulator, and transmitted to the viewer.
+// It drives special effects like smoke blowing, trees bending, and grass wiggling.
+//
+// Currently wind lattice does not interpolate correctly to neighbors. This will need
+// work.
+
+#include "llviewerprecompiledheaders.h"
+#include "indra_constants.h"
+
+#include "llwind.h"
+
+// linden libraries
+#include "llgl.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+
+// viewer
+#include "noise.h"
+#include "v4color.h"
+#include "viewer.h"
+#include "llagent.h"
+#include "llworld.h"
+
+
+const F32 CLOUD_DIVERGENCE_COEF = 0.5f;
+
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+LLWind::LLWind()
+: mSize(16),
+ mCloudDensityp(NULL)
+{
+ init();
+}
+
+
+LLWind::~LLWind()
+{
+ delete [] mVelX;
+ delete [] mVelY;
+ delete [] mCloudVelX;
+ delete [] mCloudVelY;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Public Methods
+//////////////////////////////////////////////////////////////////////
+
+
+void LLWind::init()
+{
+ // Initialize vector data
+ mVelX = new F32[mSize*mSize];
+ mVelY = new F32[mSize*mSize];
+
+ mCloudVelX = new F32[mSize*mSize];
+ mCloudVelY = new F32[mSize*mSize];
+
+ S32 i;
+ for (i = 0; i < mSize*mSize; i++)
+ {
+ mVelX[i] = 0.5f;
+ mVelY[i] = 0.5f;
+ mCloudVelX[i] = 0.0f;
+ mCloudVelY[i] = 0.0f;
+ }
+}
+
+
+void LLWind::decompress(LLBitPack &bitpack, LLGroupHeader *group_headerp)
+{
+ if (!mCloudDensityp)
+ {
+ return;
+ }
+
+ LLPatchHeader patch_header;
+ S32 buffer[16*16];
+
+ init_patch_decompressor(group_headerp->patch_size);
+
+ // Don't use the packed group_header stride because the strides used on
+ // simulator and viewer are not equal.
+ group_headerp->stride = group_headerp->patch_size;
+ set_group_of_patch_header(group_headerp);
+
+ // X component
+ decode_patch_header(bitpack, &patch_header);
+ decode_patch(bitpack, buffer);
+ decompress_patch(mVelX, buffer, &patch_header);
+
+ // Y component
+ decode_patch_header(bitpack, &patch_header);
+ decode_patch(bitpack, buffer);
+ decompress_patch(mVelY, buffer, &patch_header);
+
+
+
+ S32 i, j, k;
+ // HACK -- mCloudVelXY is the same as mVelXY, except we add a divergence
+ // that is proportional to the gradient of the cloud density
+ // ==> this helps to clump clouds together
+ // NOTE ASSUMPTION: cloud density has the same dimensions as the wind field
+ // This needs to be fixed... causes discrepency at region boundaries
+
+ for (j=1; j<mSize-1; j++)
+ {
+ for (i=1; i<mSize-1; i++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 1) - *(mCloudDensityp + k - 1));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + mSize) - *(mCloudDensityp + k - mSize));
+ }
+ }
+
+ i = mSize - 1;
+ for (j=1; j<mSize-1; j++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k) - *(mCloudDensityp + k - 2));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + mSize) - *(mCloudDensityp + k - mSize));
+ }
+ i = 0;
+ for (j=1; j<mSize-1; j++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 2) - *(mCloudDensityp + k));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + mSize) - *(mCloudDensityp + k + mSize));
+ }
+ j = mSize - 1;
+ for (i=1; i<mSize-1; i++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 1) - *(mCloudDensityp + k - 1));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k) - *(mCloudDensityp + k - 2*mSize));
+ }
+ j = 0;
+ for (i=1; i<mSize-1; i++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 1) - *(mCloudDensityp + k -1));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 2*mSize) - *(mCloudDensityp + k));
+ }
+}
+
+
+LLVector3 LLWind::getAverage()
+{
+ // Returns in average_wind the average wind velocity
+ LLVector3 average(0.0f, 0.0f, 0.0f);
+ S32 i, grid_count;
+ grid_count = mSize * mSize;
+ for (i = 0; i < grid_count; i++)
+ {
+ average.mV[VX] += mVelX[i];
+ average.mV[VY] += mVelY[i];
+ }
+
+ average *= 1.f/((F32)(grid_count)) * WIND_SCALE_HACK;
+ return average;
+}
+
+
+LLVector3 LLWind::getVelocityNoisy(const LLVector3 &pos_region, const F32 dim)
+{
+ // Resolve a value, using fractal summing to perturb the returned value
+ LLVector3 r_val(0,0,0);
+ F32 norm = 1.0f;
+ if (dim == 8)
+ {
+ norm = 1.875;
+ }
+ else if (dim == 4)
+ {
+ norm = 1.75;
+ }
+ else if (dim == 2)
+ {
+ norm = 1.5;
+ }
+
+ F32 temp_dim = dim;
+ while (temp_dim >= 1.0)
+ {
+ LLVector3 pos_region_scaled(pos_region * temp_dim);
+ r_val += getVelocity(pos_region_scaled) * (1.0f/temp_dim);
+ temp_dim /= 2.0;
+ }
+
+ return r_val * (1.0f/norm) * WIND_SCALE_HACK;
+}
+
+
+LLVector3 LLWind::getVelocity(const LLVector3 &pos_region)
+{
+ llassert(mSize == 16);
+ // Resolves value of wind at a location relative to SW corner of region
+ //
+ // Returns wind magnitude in X,Y components of vector3
+ LLVector3 r_val;
+ F32 dx,dy;
+ S32 k;
+
+ LLVector3 pos_clamped_region(pos_region);
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+
+ if (pos_clamped_region.mV[VX] < 0.f)
+ {
+ pos_clamped_region.mV[VX] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VX] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VX] = (F32) fmod(pos_clamped_region.mV[VX], region_width_meters);
+ }
+
+ if (pos_clamped_region.mV[VY] < 0.f)
+ {
+ pos_clamped_region.mV[VY] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VY] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VY] = (F32) fmod(pos_clamped_region.mV[VY], region_width_meters);
+ }
+
+
+ S32 i = llfloor(pos_clamped_region.mV[VX] * mSize / region_width_meters);
+ S32 j = llfloor(pos_clamped_region.mV[VY] * mSize / region_width_meters);
+ k = i + j*mSize;
+ dx = ((pos_clamped_region.mV[VX] * mSize / region_width_meters) - (F32) i);
+ dy = ((pos_clamped_region.mV[VY] * mSize / region_width_meters) - (F32) j);
+
+ if ((i < mSize-1) && (j < mSize-1))
+ {
+ // Interior points, no edges
+ r_val.mV[VX] = mVelX[k]*(1.0f - dx)*(1.0f - dy) +
+ mVelX[k + 1]*dx*(1.0f - dy) +
+ mVelX[k + mSize]*dy*(1.0f - dx) +
+ mVelX[k + mSize + 1]*dx*dy;
+ r_val.mV[VY] = mVelY[k]*(1.0f - dx)*(1.0f - dy) +
+ mVelY[k + 1]*dx*(1.0f - dy) +
+ mVelY[k + mSize]*dy*(1.0f - dx) +
+ mVelY[k + mSize + 1]*dx*dy;
+ }
+ else
+ {
+ r_val.mV[VX] = mVelX[k];
+ r_val.mV[VY] = mVelY[k];
+ }
+
+ r_val.mV[VZ] = 0.f;
+ return r_val * WIND_SCALE_HACK;
+}
+
+
+LLVector3 LLWind::getCloudVelocity(const LLVector3 &pos_region)
+{
+ llassert(mSize == 16);
+ // Resolves value of wind at a location relative to SW corner of region
+ //
+ // Returns wind magnitude in X,Y components of vector3
+ LLVector3 r_val;
+ F32 dx,dy;
+ S32 k;
+
+ LLVector3 pos_clamped_region(pos_region);
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+
+ if (pos_clamped_region.mV[VX] < 0.f)
+ {
+ pos_clamped_region.mV[VX] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VX] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VX] = (F32) fmod(pos_clamped_region.mV[VX], region_width_meters);
+ }
+
+ if (pos_clamped_region.mV[VY] < 0.f)
+ {
+ pos_clamped_region.mV[VY] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VY] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VY] = (F32) fmod(pos_clamped_region.mV[VY], region_width_meters);
+ }
+
+
+ S32 i = llfloor(pos_clamped_region.mV[VX] * mSize / region_width_meters);
+ S32 j = llfloor(pos_clamped_region.mV[VY] * mSize / region_width_meters);
+ k = i + j*mSize;
+ dx = ((pos_clamped_region.mV[VX] * mSize / region_width_meters) - (F32) i);
+ dy = ((pos_clamped_region.mV[VY] * mSize / region_width_meters) - (F32) j);
+
+ if ((i < mSize-1) && (j < mSize-1))
+ {
+ // Interior points, no edges
+ r_val.mV[VX] = mCloudVelX[k]*(1.0f - dx)*(1.0f - dy) +
+ mCloudVelX[k + 1]*dx*(1.0f - dy) +
+ mCloudVelX[k + mSize]*dy*(1.0f - dx) +
+ mCloudVelX[k + mSize + 1]*dx*dy;
+ r_val.mV[VY] = mCloudVelY[k]*(1.0f - dx)*(1.0f - dy) +
+ mCloudVelY[k + 1]*dx*(1.0f - dy) +
+ mCloudVelY[k + mSize]*dy*(1.0f - dx) +
+ mCloudVelY[k + mSize + 1]*dx*dy;
+ }
+ else
+ {
+ r_val.mV[VX] = mCloudVelX[k];
+ r_val.mV[VY] = mCloudVelY[k];
+ }
+
+ r_val.mV[VZ] = 0.f;
+ return r_val * WIND_SCALE_HACK;
+}
+
+
+void LLWind::setCloudDensityPointer(F32 *densityp)
+{
+ mCloudDensityp = densityp;
+}
+
+void LLWind::setOriginGlobal(const LLVector3d &origin_global)
+{
+ mOriginGlobal = origin_global;
+}
+
+
diff --git a/indra/newview/llwind.h b/indra/newview/llwind.h
new file mode 100644
index 0000000000..0a9e343945
--- /dev/null
+++ b/indra/newview/llwind.h
@@ -0,0 +1,50 @@
+/**
+ * @file llwind.h
+ * @brief LLWind class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWIND_H
+#define LL_LLWIND_H
+
+//#include "vmath.h"
+#include "llmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+
+class LLVector3;
+class LLBitPack;
+class LLGroupHeader;
+
+
+class LLWind
+{
+public:
+ LLWind();
+ ~LLWind();
+ void renderVectors();
+ LLVector3 getVelocity(const LLVector3 &location); // "location" is region-local
+ LLVector3 getCloudVelocity(const LLVector3 &location); // "location" is region-local
+ LLVector3 getVelocityNoisy(const LLVector3 &location, const F32 dim); // "location" is region-local
+
+ void decompress(LLBitPack &bitpack, LLGroupHeader *group_headerp);
+ LLVector3 getAverage();
+ void setCloudDensityPointer(F32 *densityp);
+
+ void setOriginGlobal(const LLVector3d &origin_global);
+private:
+ S32 mSize;
+ F32 * mVelX;
+ F32 * mVelY;
+ F32 * mCloudVelX;
+ F32 * mCloudVelY;
+ F32 * mCloudDensityp;
+
+ LLVector3d mOriginGlobal;
+ void init();
+
+};
+
+#endif
diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp
new file mode 100644
index 0000000000..428df0b45a
--- /dev/null
+++ b/indra/newview/llwindebug.cpp
@@ -0,0 +1,254 @@
+/**
+ * @file llwindebug.cpp
+ * @brief Windows debugging functions
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#ifdef LL_WINDOWS
+
+#include "llwindebug.h"
+#include "llviewercontrol.h"
+#include "lldir.h"
+
+// From viewer.h
+extern BOOL gInProductionGrid;
+
+extern void (*gCrashCallback)(void);
+extern void write_debug(const char *str);
+extern void write_debug(const std::string &str);
+
+// based on dbghelp.h
+typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
+ );
+
+MINIDUMPWRITEDUMP f_mdwp = NULL;
+
+
+
+class LLMemoryReserve {
+public:
+ LLMemoryReserve();
+ ~LLMemoryReserve();
+ void reserve();
+ void release();
+protected:
+ unsigned char *mReserve;
+ static const size_t MEMORY_RESERVATION_SIZE;
+};
+
+LLMemoryReserve::LLMemoryReserve() :
+ mReserve(NULL)
+{
+};
+
+LLMemoryReserve::~LLMemoryReserve()
+{
+ release();
+}
+
+// I dunno - this just seemed like a pretty good value.
+const size_t LLMemoryReserve::MEMORY_RESERVATION_SIZE = 5 * 1024 * 1024;
+
+void LLMemoryReserve::reserve()
+{
+ if(NULL == mReserve)
+ mReserve = new unsigned char[MEMORY_RESERVATION_SIZE];
+};
+
+void LLMemoryReserve::release()
+{
+ delete [] mReserve;
+ mReserve = NULL;
+};
+
+static LLMemoryReserve gEmergencyMemoryReserve;
+
+// static
+BOOL LLWinDebug::setupExceptionHandler()
+{
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+
+ static BOOL s_first_run = TRUE;
+ // Load the dbghelp dll now, instead of waiting for the crash.
+ // Less potential for stack mangling
+
+ BOOL ok = TRUE;
+ if (s_first_run)
+ {
+ // First, try loading from the directory that the app resides in.
+ std::string local_dll_name = gDirUtilp->findFile("dbghelp.dll", gDirUtilp->getWorkingDir(), gDirUtilp->getExecutableDir());
+
+ HMODULE hDll = NULL;
+ hDll = LoadLibraryA(local_dll_name.c_str());
+ if (!hDll)
+ {
+ hDll = LoadLibrary(L"dbghelp.dll");
+ }
+
+ if (!hDll)
+ {
+ llwarns << "Couldn't find dbghelp.dll!" << llendl;
+
+ std::string msg = "Couldn't find dbghelp.dll at ";
+ msg += local_dll_name;
+ msg += "!\n";
+
+ write_debug(msg.c_str());
+
+ ok = FALSE;
+ }
+ else
+ {
+ f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");
+
+ if (!f_mdwp)
+ {
+ write_debug("No MiniDumpWriteDump!\n");
+ ok = FALSE;
+ }
+ }
+
+ gEmergencyMemoryReserve.reserve();
+ }
+
+ LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
+ prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException);
+
+ if (s_first_run)
+ {
+ // We're fine, this is the first run.
+ s_first_run = FALSE;
+ return ok;
+ }
+ if (!prev_filter)
+ {
+ llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl;
+ ok = FALSE;
+ }
+ if (prev_filter != LLWinDebug::handleException)
+ {
+ llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl;
+ ok = FALSE;
+ }
+ return ok;
+#else
+ // Internal builds don't mess with exception handling.
+ return TRUE;
+#endif
+}
+
+void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename)
+{
+ if(f_mdwp == NULL)
+ {
+ write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n");
+ }
+ else if(gDirUtilp == NULL)
+ {
+ write_debug("No way to generate a minidump, no gDirUtilp!\n");
+ }
+ else
+ {
+ std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+ filename);
+
+ HANDLE hFile = CreateFileA(dump_path.c_str(),
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ // Write the dump, ignoring the return value
+ f_mdwp(GetCurrentProcess(),
+ GetCurrentProcessId(),
+ hFile,
+ type,
+ ExInfop,
+ NULL,
+ NULL);
+
+ CloseHandle(hFile);
+ }
+
+ }
+}
+
+// static
+LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
+{
+
+ //
+ // Let go of a bunch of reserved memory to give library calls etc
+ // a chance to execute normally in the case that we ran out of
+ // memory.
+ //
+ gEmergencyMemoryReserve.release();
+
+ BOOL userWantsMaxiDump =
+ (stricmp(gSavedSettings.getString("LastName").c_str(), "linden") == 0)
+ || (stricmp(gSavedSettings.getString("LastName").c_str(), "tester") == 0);
+
+ BOOL alsoSaveMaxiDump = userWantsMaxiDump && !gInProductionGrid;
+
+ /* Calculate alsoSaveMaxiDump here */
+
+ if (exception_infop)
+ {
+ _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
+
+ ExInfo.ThreadId = ::GetCurrentThreadId();
+ ExInfo.ExceptionPointers = exception_infop;
+ ExInfo.ClientPointers = NULL;
+
+ writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
+
+ if(alsoSaveMaxiDump)
+ writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
+ }
+ else
+ {
+ writeDumpToFile(MiniDumpNormal, NULL, "SecondLife.dmp");
+
+ if(alsoSaveMaxiDump)
+ writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), NULL, "SecondLifePlus.dmp");
+ }
+
+ if (!exception_infop)
+ {
+ // We're calling this due to a network error, not due to an actual exception.
+ // It doesn't realy matter what we return.
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ //
+ // Call the newview crash callback, which will spawn the crash
+ // reporter. It may or may not spawn a dialog.
+ //
+ if (gCrashCallback)
+ {
+ gCrashCallback();
+ }
+
+ //
+ // At this point, we always want to exit the app. There's no graceful
+ // recovery for an unhandled exception.
+ //
+ // Just kill the process.
+ LONG retval = EXCEPTION_EXECUTE_HANDLER;
+
+ return retval;
+}
+
+#endif
+
diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h
new file mode 100644
index 0000000000..e328ffe58d
--- /dev/null
+++ b/indra/newview/llwindebug.h
@@ -0,0 +1,24 @@
+/**
+ * @file llwindebug.h
+ * @brief LLWinDebug class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDEBUG_H
+#define LL_LLWINDEBUG_H
+
+#include "stdtypes.h"
+#include <dbghelp.h>
+
+class LLWinDebug
+{
+public:
+ static BOOL setupExceptionHandler();
+
+ static LONG WINAPI handleException(struct _EXCEPTION_POINTERS *pExceptionInfo);
+ static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename);
+};
+
+#endif // LL_LLWINDEBUG_H
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
new file mode 100644
index 0000000000..813eb0c7ea
--- /dev/null
+++ b/indra/newview/llworld.cpp
@@ -0,0 +1,1141 @@
+/**
+ * @file llworld.cpp
+ * @brief Initial test structure to organize viewer regions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworld.h"
+
+#include "indra_constants.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawpool.h"
+#include "llglheaders.h"
+#include "llregionhandle.h"
+#include "llsurface.h"
+#include "llviewercamera.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvlcomposition.h"
+#include "llvoavatar.h"
+#include "llvowater.h"
+#include "message.h"
+#include "pipeline.h"
+#include "viewer.h" // for do_disconnect()
+
+//
+// Globals
+//
+LLWorld* gWorldp = NULL;
+U32 gAgentPauseSerialNum = 0;
+
+//
+// Constants
+//
+const S32 MAX_NUMBER_OF_CLOUDS = 750;
+const F32 MIN_IDLE_UPDATE_TIME = 0.025f;
+const S32 WORLD_PATCH_SIZE = 16;
+
+extern LLColor4U MAX_WATER_COLOR;
+
+//
+// Functions
+//
+
+// allocate the stack
+LLWorld::LLWorld(const U32 grids_per_region, const F32 meters_per_grid)
+: mWidth(grids_per_region),
+ mScale(meters_per_grid),
+ mWidthInMeters( grids_per_region * meters_per_grid )
+{
+ mSpaceTimeUSec = 0;
+ mLastPacketsIn = 0;
+ mLastPacketsOut = 0;
+ mLastPacketsLost = 0;
+ mLandFarClip = DEFAULT_FAR_PLANE;
+ mIdleUpdateTime = MIN_IDLE_UPDATE_TIME;
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ for (S32 i = 0; i < 8; i++)
+ {
+ mEdgeWaterObjects[i] = NULL;
+ }
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,4);
+ U8 *default_texture = raw->getData();
+ *(default_texture++) = MAX_WATER_COLOR.mV[0];
+ *(default_texture++) = MAX_WATER_COLOR.mV[1];
+ *(default_texture++) = MAX_WATER_COLOR.mV[2];
+ *(default_texture++) = MAX_WATER_COLOR.mV[3];
+
+ mDefaultWaterTexturep = new LLViewerImage(raw, FALSE);
+ mDefaultWaterTexturep->bind();
+ mDefaultWaterTexturep->setClamp(TRUE, TRUE);
+
+}
+
+
+LLWorld::~LLWorld()
+{
+ gObjectList.killAllObjects();
+
+ mRegionList.deleteAllData();
+}
+
+
+LLViewerRegion* LLWorld::addRegion(const U64 &region_handle, const LLHost &host)
+{
+ LLMemType mt(LLMemType::MTYPE_REGIONS);
+
+ LLViewerRegion *regionp = getRegionFromHandle(region_handle);
+ if (regionp)
+ {
+ LLHost old_host = regionp->getHost();
+ // region already exists!
+ if (host == old_host && regionp->mAlive)
+ {
+ // This is a duplicate for the same host and it's alive, don't bother.
+ return regionp;
+ }
+
+ if (host != old_host)
+ {
+ llwarns << "LLWorld::addRegion exists, but old host " << old_host
+ << " does not match new host " << host << llendl;
+ }
+ if (!regionp->mAlive)
+ {
+ llwarns << "LLWorld::addRegion exists, but isn't alive" << llendl;
+ }
+
+ // Kill the old host, and then we can continue on and add the new host. We have to kill even if the host
+ // matches, because all the agent state for the new camera is completely different.
+ removeRegion(old_host);
+ }
+
+ U32 iindex = 0;
+ U32 jindex = 0;
+ from_region_handle(region_handle, &iindex, &jindex);
+ S32 x = (S32)(iindex/mWidth);
+ S32 y = (S32)(jindex/mWidth);
+ llinfos << "Adding new region (" << x << ":" << y << ")" << llendl;
+ llinfos << "Host: " << host << llendl;
+
+ LLVector3d origin_global;
+
+ origin_global = from_region_handle(region_handle);
+
+ regionp = new LLViewerRegion(region_handle,
+ host,
+ mWidth,
+ WORLD_PATCH_SIZE,
+ getRegionWidthInMeters() );
+ if (!regionp)
+ {
+ llerrs << "Unable to create new region!" << llendl;
+ }
+
+ regionp->mCloudLayer.create(regionp);
+ regionp->mCloudLayer.setWidth((F32)mWidth);
+ regionp->mCloudLayer.setWindPointer(&regionp->mWind);
+
+ mRegionList.addData(regionp);
+ mActiveRegionList.addData(regionp);
+ mCulledRegionList.addData(regionp);
+
+
+ // Find all the adjacent regions, and attach them.
+ // Generate handles for all of the adjacent regions, and attach them in the correct way.
+ // connect the edges
+ F32 adj_x = 0.f;
+ F32 adj_y = 0.f;
+ F32 region_x = 0.f;
+ F32 region_y = 0.f;
+ U64 adj_handle = 0;
+
+ F32 width = getRegionWidthInMeters();
+
+ LLViewerRegion *neighborp;
+ from_region_handle(region_handle, &region_x, &region_y);
+
+ // Iterate through all directions, and connect neighbors if there.
+ S32 dir;
+ for (dir = 0; dir < 8; dir++)
+ {
+ adj_x = region_x + width * gDirAxes[dir][0];
+ adj_y = region_y + width * gDirAxes[dir][1];
+ to_region_handle(adj_x, adj_y, &adj_handle);
+
+ neighborp = getRegionFromHandle(adj_handle);
+ if (neighborp)
+ {
+ //llinfos << "Connecting " << region_x << ":" << region_y << " -> " << adj_x << ":" << adj_y << llendl;
+ regionp->connectNeighbor(neighborp, dir);
+ }
+ }
+
+ updateWaterObjects();
+
+ return regionp;
+}
+
+
+void LLWorld::removeRegion(const LLHost &host)
+{
+ F32 x, y;
+
+ LLViewerRegion *regionp = getRegion(host);
+ if (!regionp)
+ {
+ llwarns << "Trying to remove region that doesn't exist!" << llendl;
+ return;
+ }
+
+ if (regionp == gAgent.getRegion())
+ {
+ LLViewerRegion *reg;
+ for (reg = mRegionList.getFirstData(); reg; reg = mRegionList.getNextData())
+ {
+ llwarns << "RegionDump: " << reg->getName()
+ << " " << reg->getHost()
+ << " " << reg->getOriginGlobal()
+ << llendl;
+ }
+
+ llwarns << "Agent position global " << gAgent.getPositionGlobal()
+ << " agent " << gAgent.getPositionAgent()
+ << llendl;
+
+ llwarns << "Regions visited " << gAgent.getRegionsVisited() << llendl;
+
+ llwarns << "gFrameTimeSeconds " << gFrameTimeSeconds << llendl;
+
+ llwarns << "Disabling region " << regionp->getName() << " that agent is in!" << llendl;
+ do_disconnect("You have been disconnected from the region you were in.");
+ return;
+ }
+
+ from_region_handle(regionp->getHandle(), &x, &y);
+ llinfos << "Removing region " << x << ":" << y << llendl;
+
+ // This code can probably be blitzed now...
+ if (!mRegionList.removeData(regionp))
+ {
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ llwarns << "RegionDump: " << regionp->getName()
+ << " " << regionp->getHost()
+ << " " << regionp->getOriginGlobal()
+ << llendl;
+ }
+
+ llerrs << "Region list is broken" << llendl;
+ }
+
+ if (!mActiveRegionList.removeData(regionp))
+ {
+ llwarns << "LLWorld.mActiveRegionList is broken." << llendl;
+ }
+ if (!mCulledRegionList.removeData(regionp))
+ {
+ if (!mVisibleRegionList.removeData(regionp))
+ {
+ llwarns << "LLWorld.mCulled/mVisibleRegionList are broken" << llendl;;
+ }
+ }
+ delete regionp;
+
+ updateWaterObjects();
+}
+
+
+LLViewerRegion *LLWorld::getRegion(const LLHost &host)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->getHost() == host)
+ {
+ return regionp;
+ }
+ }
+ return NULL;
+}
+
+LLViewerRegion *LLWorld::getRegionFromPosAgent(const LLVector3 &pos)
+{
+ return getRegionFromPosGlobal(gAgent.getPosGlobalFromAgent(pos));
+}
+
+LLViewerRegion *LLWorld::getRegionFromPosGlobal(const LLVector3d &pos)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->pointInRegionGlobal(pos))
+ {
+ return regionp;
+ }
+ }
+ return NULL;
+}
+
+
+LLVector3d LLWorld::clipToVisibleRegions(const LLVector3d &start_pos, const LLVector3d &end_pos)
+{
+ if (positionRegionValidGlobal(end_pos))
+ {
+ return end_pos;
+ }
+
+ LLViewerRegion* regionp = getRegionFromPosGlobal(start_pos);
+ if (!regionp)
+ {
+ return start_pos;
+ }
+
+ LLVector3d delta_pos = end_pos - start_pos;
+ LLVector3d delta_pos_abs;
+ delta_pos_abs.setVec(delta_pos);
+ delta_pos_abs.abs();
+
+ LLVector3 region_coord = regionp->getPosRegionFromGlobal(end_pos);
+ F64 clip_factor = 1.0;
+ F32 region_width = regionp->getWidth();
+ if (region_coord.mV[VX] < 0.f)
+ {
+ if (region_coord.mV[VY] < region_coord.mV[VX])
+ {
+ // clip along y -
+ clip_factor = -(region_coord.mV[VY] / delta_pos_abs.mdV[VY]);
+ }
+ else
+ {
+ // clip along x -
+ clip_factor = -(region_coord.mV[VX] / delta_pos_abs.mdV[VX]);
+ }
+ }
+ else if (region_coord.mV[VX] > region_width)
+ {
+ if (region_coord.mV[VY] > region_coord.mV[VX])
+ {
+ // clip along y +
+ clip_factor = (region_coord.mV[VY] - region_width) / delta_pos_abs.mdV[VY];
+ }
+ else
+ {
+ //clip along x +
+ clip_factor = (region_coord.mV[VX] - region_width) / delta_pos_abs.mdV[VX];
+ }
+ }
+ else if (region_coord.mV[VY] < 0.f)
+ {
+ // clip along y -
+ clip_factor = -(region_coord.mV[VY] / delta_pos_abs.mdV[VY]);
+ }
+ else if (region_coord.mV[VY] > region_width)
+ {
+ // clip along y +
+ clip_factor = (region_coord.mV[VY] - region_width) / delta_pos_abs.mdV[VY];
+ }
+
+ // clamp to < 256 to stay in sim
+ LLVector3d final_region_pos = LLVector3d(region_coord) - (delta_pos * clip_factor);
+ final_region_pos.clamp(0.0, 255.999);
+ return regionp->getPosGlobalFromRegion(LLVector3(final_region_pos));
+}
+
+LLViewerRegion *LLWorld::getRegionFromHandle(const U64 &handle)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->getHandle() == handle)
+ {
+ return regionp;
+ }
+ }
+ return NULL;
+}
+
+
+void LLWorld::updateAgentOffset(const LLVector3d &offset_global)
+{
+#if 0
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ regionp->setAgentOffset(offset_global);
+ }
+#endif
+}
+
+
+BOOL LLWorld::positionRegionValidGlobal(const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->pointInRegionGlobal(pos_global))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+// Allow objects to go up to their radius underground.
+F32 LLWorld::getMinAllowedZ(LLViewerObject* object)
+{
+ F32 land_height = resolveLandHeightGlobal(object->getPositionGlobal());
+ F32 radius = 0.5f * object->getScale().magVec();
+ return land_height - radius;
+}
+
+
+LLViewerRegion* LLWorld::resolveRegionGlobal(LLVector3 &pos_region, const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+
+ if (regionp)
+ {
+ pos_region = regionp->getPosRegionFromGlobal(pos_global);
+ return regionp;
+ }
+
+ return NULL;
+}
+
+
+LLViewerRegion* LLWorld::resolveRegionAgent(LLVector3 &pos_region, const LLVector3 &pos_agent)
+{
+ LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent);
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+
+ if (regionp)
+ {
+ pos_region = regionp->getPosRegionFromGlobal(pos_global);
+ return regionp;
+ }
+
+ return NULL;
+}
+
+
+F32 LLWorld::resolveLandHeightAgent(const LLVector3 &pos_agent)
+{
+ LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent);
+ return resolveLandHeightGlobal(pos_global);
+}
+
+
+F32 LLWorld::resolveLandHeightGlobal(const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+ if (regionp)
+ {
+ return regionp->getLand().resolveHeightGlobal(pos_global);
+ }
+ return 0.0f;
+}
+
+
+// Takes a line defined by "point_a" and "point_b" and determines the closest (to point_a)
+// point where the the line intersects an object or the land surface. Stores the results
+// in "intersection" and "intersection_normal" and returns a scalar value that represents
+// the normalized distance along the line from "point_a" to "intersection".
+//
+// Currently assumes point_a and point_b only differ in z-direction,
+// but it may eventually become more general.
+F32 LLWorld::resolveStepHeightGlobal(const LLVOAvatar* avatarp, const LLVector3d &point_a, const LLVector3d &point_b,
+ LLVector3d &intersection, LLVector3 &intersection_normal,
+ LLViewerObject **viewerObjectPtr)
+{
+ // initialize return value to null
+ if (viewerObjectPtr)
+ {
+ *viewerObjectPtr = NULL;
+ }
+
+ LLViewerRegion *regionp = getRegionFromPosGlobal(point_a);
+ if (!regionp)
+ {
+ // We're outside the world
+ intersection = 0.5f * (point_a + point_b);
+ intersection_normal.setVec(0.0f, 0.0f, 1.0f);
+ return 0.5f;
+ }
+
+ // calculate the length of the segment
+ F32 segment_length = (F32)((point_a - point_b).magVec());
+ if (0.0f == segment_length)
+ {
+ intersection = point_a;
+ intersection_normal.setVec(0.0f, 0.0f, 1.0f);
+ return segment_length;
+ }
+
+ // get land height
+ // Note: we assume that the line is parallel to z-axis here
+ LLVector3d land_intersection = point_a;
+ F32 normalized_land_distance;
+
+ land_intersection.mdV[VZ] = regionp->getLand().resolveHeightGlobal(point_a);
+ normalized_land_distance = (F32)(point_a.mdV[VZ] - land_intersection.mdV[VZ]) / segment_length;
+
+ if (avatarp && !avatarp->mFootPlane.isExactlyClear())
+ {
+ LLVector3 foot_plane_normal(avatarp->mFootPlane.mV);
+ LLVector3 start_pt = avatarp->getRegion()->getPosRegionFromGlobal(point_a);
+ // added 0.05 meters to compensate for error in foot plane reported by Havok
+ F32 norm_dist_from_plane = ((start_pt * foot_plane_normal) - avatarp->mFootPlane.mV[VW]) + 0.05f;
+ norm_dist_from_plane = llclamp(norm_dist_from_plane / segment_length, 0.f, 1.f);
+ if (norm_dist_from_plane < normalized_land_distance)
+ {
+ normalized_land_distance = norm_dist_from_plane;
+ intersection = point_a;
+ intersection.mdV[VZ] -= norm_dist_from_plane * segment_length;
+ intersection_normal = foot_plane_normal;
+ }
+ }
+ else
+ {
+ intersection = land_intersection;
+ intersection_normal = resolveLandNormalGlobal(land_intersection);
+ }
+
+ return normalized_land_distance;
+}
+
+
+LLSurfacePatch * LLWorld::resolveLandPatchGlobal(const LLVector3d &pos_global)
+{
+ // returns a pointer to the patch at this location
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+ if (!regionp)
+ {
+ return NULL;
+ }
+
+ return regionp->getLand().resolvePatchGlobal(pos_global);
+}
+
+
+LLVector3 LLWorld::resolveLandNormalGlobal(const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+ if (!regionp)
+ {
+ return LLVector3::z_axis;
+ }
+
+ return regionp->getLand().resolveNormalGlobal(pos_global);
+}
+
+
+void LLWorld::updateVisibilities()
+{
+ F32 cur_far_clip = gCamera->getFar();
+
+ gCamera->setFar(mLandFarClip);
+
+ LLViewerRegion *regionp;
+
+ F32 diagonal_squared = F_SQRT2 * F_SQRT2 * mWidth * mWidth;
+ // Go through the culled list and check for visible regions
+ for (regionp = mCulledRegionList.getFirstData();
+ regionp;
+ regionp = mCulledRegionList.getNextData())
+ {
+ F32 height = regionp->getLand().getMaxZ() - regionp->getLand().getMinZ();
+ F32 radius = 0.5f*fsqrtf(height * height + diagonal_squared);
+ if (!regionp->getLand().hasZData()
+ || gCamera->sphereInFrustum(regionp->getCenterAgent(), radius))
+ {
+ mCulledRegionList.removeCurrentData();
+ mVisibleRegionList.addDataAtEnd(regionp);
+ }
+ }
+
+ F32 last_dist_squared = 0.0f;
+ F32 dist_squared;
+
+ // Update all of the visible regions and make single bubble-sort pass
+ for (regionp = mVisibleRegionList.getFirstData();
+ regionp;
+ regionp = mVisibleRegionList.getNextData())
+ {
+ if (!regionp->getLand().hasZData())
+ {
+ continue;
+ }
+
+ F32 height = regionp->getLand().getMaxZ() - regionp->getLand().getMinZ();
+ F32 radius = 0.5f*fsqrtf(height * height + diagonal_squared);
+ if (gCamera->sphereInFrustum(regionp->getCenterAgent(), radius))
+ {
+ if (!gNoRender)
+ {
+ regionp->getLand().updatePatchVisibilities(gAgent);
+ }
+
+ // sort by distance... closer regions to the front
+ // Note: regions use absolute frame so we use the agent's center
+ dist_squared = (F32)(gAgent.getCameraPositionGlobal() - regionp->getCenterGlobal()).magVecSquared();
+ if (dist_squared < last_dist_squared)
+ {
+ mVisibleRegionList.swapCurrentWithPrevious();
+ }
+ else
+ {
+ last_dist_squared = dist_squared;
+ }
+ }
+ else
+ {
+ mVisibleRegionList.removeCurrentData();
+ mCulledRegionList.addData(regionp);
+ }
+ }
+
+ gCamera->setFar(cur_far_clip);
+}
+
+
+void LLWorld::updateRegions()
+{
+ LLViewerRegion *regionp;
+ LLTimer update_timer;
+
+ // Perform idle time updates for the regions (and associated surfaces)
+ for (regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ update_timer.reset();
+ if (!regionp->idleUpdate(update_timer, mIdleUpdateTime))
+ {
+ // Didn't finish all the updates. Slightly increase the idle update time.
+ mIdleUpdateTime *= 1.05f;
+ }
+ else
+ {
+ mIdleUpdateTime *= 0.9f;
+ if (mIdleUpdateTime < MIN_IDLE_UPDATE_TIME)
+ {
+ mIdleUpdateTime = MIN_IDLE_UPDATE_TIME;
+ }
+ }
+ }
+}
+
+void LLWorld::updateParticles()
+{
+ mPartSim.updateSimulation();
+}
+
+void LLWorld::updateClouds(const F32 dt)
+{
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ // don't move clouds in snapshot mode
+ return;
+ }
+ LLViewerRegion *regionp;
+ if (mActiveRegionList.getLength())
+ {
+ // Update all the cloud puff positions, and timer based stuff
+ // such as death decay
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ regionp->mCloudLayer.updatePuffs(dt);
+ }
+
+ // Reshuffle who owns which puffs
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ regionp->mCloudLayer.updatePuffOwnership();
+ }
+
+ // Add new puffs
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ regionp->mCloudLayer.updatePuffCount();
+ }
+ }
+}
+
+LLCloudGroup *LLWorld::findCloudGroup(const LLCloudPuff &puff)
+{
+ LLViewerRegion *regionp;
+ if (mActiveRegionList.getLength())
+ {
+ // Update all the cloud puff positions, and timer based stuff
+ // such as death decay
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ LLCloudGroup *groupp = regionp->mCloudLayer.findCloudGroup(puff);
+ if (groupp)
+ {
+ return groupp;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+void LLWorld::renderPropertyLines()
+{
+ S32 region_count = 0;
+ S32 vertex_count = 0;
+
+ LLViewerRegion* region;
+ for (region = mVisibleRegionList.getFirstData(); region; region = mVisibleRegionList.getNextData() )
+ {
+ region_count++;
+ vertex_count += region->renderPropertyLines();
+ }
+}
+
+
+void LLWorld::updateNetStats()
+{
+ F32 bits = 0.f;
+ U32 packets = 0;
+ LLViewerRegion *regionp;
+
+ for (regionp = mActiveRegionList.getFirstData(); regionp; regionp = mActiveRegionList.getNextData())
+ {
+ regionp->updateNetStats();
+ bits += regionp->mBitStat.getCurrent();
+ packets += llfloor( regionp->mPacketsStat.getCurrent() );
+ }
+
+ S32 packets_in = gMessageSystem->mPacketsIn - mLastPacketsIn;
+ S32 packets_out = gMessageSystem->mPacketsOut - mLastPacketsOut;
+ S32 packets_lost = gMessageSystem->mDroppedPackets - mLastPacketsLost;
+
+ S32 actual_in_bits = gMessageSystem->mPacketRing.getAndResetActualInBits();
+ S32 actual_out_bits = gMessageSystem->mPacketRing.getAndResetActualOutBits();
+ gViewerStats->mActualInKBitStat.addValue(actual_in_bits/1024.f);
+ gViewerStats->mActualOutKBitStat.addValue(actual_out_bits/1024.f);
+ gViewerStats->mKBitStat.addValue(bits/1024.f);
+ gViewerStats->mPacketsInStat.addValue(packets_in);
+ gViewerStats->mPacketsOutStat.addValue(packets_out);
+ gViewerStats->mPacketsLostStat.addValue(gMessageSystem->mDroppedPackets);
+ if (packets_in)
+ {
+ gViewerStats->mPacketsLostPercentStat.addValue(100.f*((F32)packets_lost/(F32)packets_in));
+ }
+ else
+ {
+ gViewerStats->mPacketsLostPercentStat.addValue(0.f);
+ }
+
+ mLastPacketsIn = gMessageSystem->mPacketsIn;
+ mLastPacketsOut = gMessageSystem->mPacketsOut;
+ mLastPacketsLost = gMessageSystem->mDroppedPackets;
+}
+
+
+void LLWorld::printPacketsLost()
+{
+ LLViewerRegion *regionp;
+
+ llinfos << "Simulators:" << llendl;
+ llinfos << "----------" << llendl;
+
+ LLCircuitData *cdp = NULL;
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ cdp = gMessageSystem->mCircuitInfo.findCircuit(regionp->getHost());
+ if (cdp)
+ {
+ LLVector3d range = regionp->getCenterGlobal() - gAgent.getPositionGlobal();
+
+ llinfos << regionp->getHost() << ", range: " << range.magVec() <<
+ " packets lost: " <<
+ cdp->getPacketsLost() << llendl;
+ }
+ }
+
+ llinfos << "UserServer:" << llendl;
+ llinfos << "-----------" << llendl;
+
+ cdp = gMessageSystem->mCircuitInfo.findCircuit(gUserServer);
+ if (cdp)
+ {
+ llinfos << gUserServer << " packets lost: " << cdp->getPacketsLost() << llendl;
+ }
+}
+
+void LLWorld::processCoarseUpdate(LLMessageSystem* msg, void** user_data)
+{
+ LLViewerRegion* region = NULL;
+ region = gWorldp->getRegion(msg->getSender());
+ if( region ) region->updateCoarseLocations(msg);
+}
+
+F32 LLWorld::getLandFarClip() const
+{
+ return mLandFarClip;
+}
+
+void LLWorld::setLandFarClip(const F32 far_clip)
+{
+ mLandFarClip = far_clip;
+}
+
+
+void LLWorld::updateWaterObjects()
+{
+ //llinfos << "Start water update" << llendl;
+ if (!gAgent.getRegion())
+ {
+ return;
+ }
+ S32 min_x, min_y, max_x, max_y;
+ U32 region_x, region_y;
+
+ S32 rwidth = llfloor(getRegionWidthInMeters());
+
+ // First, determine the min and max "box" of water objects
+ LLViewerRegion *regionp;
+ regionp = mRegionList.getFirstData();
+
+ if (!regionp)
+ {
+ llwarns << "No regions!" << llendl;
+ return;
+ }
+
+ from_region_handle(regionp->getHandle(), &region_x, &region_y);
+ min_x = max_x = region_x;
+ min_y = max_y = region_y;
+
+ LLVOWater *waterp;
+
+ for (; regionp; regionp = mRegionList.getNextData())
+ {
+ from_region_handle(regionp->getHandle(), &region_x, &region_y);
+ min_x = llmin(min_x, (S32)region_x);
+ min_y = llmin(min_y, (S32)region_y);
+ max_x = llmax(max_x, (S32)region_x);
+ max_y = llmax(max_y, (S32)region_y);
+ waterp = regionp->getLand().getWaterObj();
+ if (waterp)
+ {
+ gObjectList.updateActive(waterp);
+ }
+ }
+
+ for (waterp = mHoleWaterObjects.getFirstData(); waterp; waterp = mHoleWaterObjects.getNextData())
+ {
+ gObjectList.killObject(waterp);
+ }
+ mHoleWaterObjects.removeAllNodes();
+
+ // We only want to fill in holes for stuff that's near us, say, within 512m
+ regionp = gAgent.getRegion();
+ from_region_handle(regionp->getHandle(), &region_x, &region_y);
+
+ min_x = llmax((S32)region_x - 512, min_x);
+ min_y = llmax((S32)region_y - 512, min_y);
+ max_x = llmin((S32)region_x + 512, max_x);
+ max_y = llmin((S32)region_y + 512, max_y);
+
+ // Now, get a list of the holes
+ S32 x, y;
+ for (x = min_x; x <= max_x; x += rwidth)
+ {
+ for (y = min_y; y <= max_y; y += rwidth)
+ {
+ U64 region_handle = to_region_handle(x, y);
+ if (!getRegionFromHandle(region_handle))
+ {
+ waterp = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER, gAgent.getRegion());
+ waterp->setUseTexture(FALSE);
+ gPipeline.addObject(waterp);
+ waterp->setPositionGlobal(LLVector3d(x + rwidth/2,
+ y + rwidth/2,
+ DEFAULT_WATER_HEIGHT));
+ waterp->setScale(LLVector3((F32)rwidth, (F32)rwidth, 0.f));
+ mHoleWaterObjects.addData(waterp);
+ }
+ }
+ }
+
+ // Update edge water objects
+ S32 wx, wy;
+ S32 center_x, center_y;
+ wx = (max_x - min_x) + rwidth;
+ wy = (max_y - min_y) + rwidth;
+ center_x = min_x + (wx >> 1);
+ center_y = min_y + (wy >> 1);
+
+
+
+ S32 add_boundary[4] = {
+ 512 - (max_x - region_x),
+ 512 - (max_y - region_y),
+ 512 - (region_x - min_x),
+ 512 - (region_y - min_y) };
+
+
+ S32 dir;
+ for (dir = 0; dir < 8; dir++)
+ {
+ S32 dim[2] = { 0 };
+ switch (gDirAxes[dir][0])
+ {
+ case -1: dim[0] = add_boundary[2]; break;
+ case 0: dim[0] = wx; break;
+ default: dim[0] = add_boundary[0]; break;
+ }
+ switch (gDirAxes[dir][1])
+ {
+ case -1: dim[1] = add_boundary[3]; break;
+ case 0: dim[1] = wy; break;
+ default: dim[1] = add_boundary[1]; break;
+ }
+
+ if (dim[0] == 0 || dim[1] == 0)
+ {
+ continue;
+ }
+
+ // Resize and reshape the water objects
+ const S32 water_center_x = center_x + llround((wx + dim[0]) * 0.5f * gDirAxes[dir][0]);
+ const S32 water_center_y = center_y + llround((wy + dim[1]) * 0.5f * gDirAxes[dir][1]);
+
+
+ waterp = mEdgeWaterObjects[dir];
+ if (!waterp || waterp->isDead())
+ {
+ // The edge water objects can be dead because they're attached to the region that the
+ // agent was in when they were originally created.
+ mEdgeWaterObjects[dir] = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER,
+ gAgent.getRegion());
+ waterp = mEdgeWaterObjects[dir];
+ waterp->setUseTexture(FALSE);
+ gPipeline.addObject(waterp);
+ }
+
+ waterp->setRegion(gAgent.getRegion());
+ LLVector3d water_pos(water_center_x, water_center_y,
+ DEFAULT_WATER_HEIGHT);
+ waterp->setPositionGlobal(water_pos);
+ waterp->setScale(LLVector3((F32)dim[0], (F32)dim[1], 0.f));
+ gObjectList.updateActive(waterp);
+ /*if (!gNoRender)
+ {
+ gPipeline.markMoved(waterp->mDrawable);
+ }*/
+ }
+
+
+ //llinfos << "End water update" << llendl;
+}
+
+LLViewerImage *LLWorld::getDefaultWaterTexture()
+{
+ return mDefaultWaterTexturep;
+}
+
+void LLWorld::setSpaceTimeUSec(const U64 space_time_usec)
+{
+ mSpaceTimeUSec = space_time_usec;
+}
+
+U64 LLWorld::getSpaceTimeUSec() const
+{
+ return mSpaceTimeUSec;
+}
+
+void LLWorld::requestCacheMisses()
+{
+ for(LLViewerRegion* regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ regionp->requestCacheMisses();
+ }
+}
+
+LLString LLWorld::getInfoString()
+{
+ LLString info_string("World Info:\n");
+ for (LLViewerRegion* regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ info_string += regionp->getInfoString();
+ }
+ return info_string;
+}
+
+void LLWorld::disconnectRegions()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ for(LLViewerRegion* regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ if (regionp == gAgent.getRegion())
+ {
+ // Skip the main agent
+ continue;
+ }
+
+ llinfos << "Sending AgentQuitCopy to: " << regionp->getHost() << llendl;
+ msg->newMessageFast(_PREHASH_AgentQuitCopy);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FuseBlock);
+ msg->addU32Fast(_PREHASH_ViewerCircuitCode, gMessageSystem->mOurCircuitCode);
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+
+void process_enable_simulator(LLMessageSystem *msg, void **user_data)
+{
+ // enable the appropriate circuit for this simulator and
+ // add its values into the gSimulator structure
+ U64 handle;
+ U32 ip_u32;
+ U16 port;
+
+ msg->getU64Fast(_PREHASH_SimulatorInfo, _PREHASH_Handle, handle);
+ msg->getIPAddrFast(_PREHASH_SimulatorInfo, _PREHASH_IP, ip_u32);
+ msg->getIPPortFast(_PREHASH_SimulatorInfo, _PREHASH_Port, port);
+
+ // which simulator should we modify?
+ LLHost sim(ip_u32, port);
+
+ // Viewer trusts the simulator.
+ msg->enableCircuit(sim, TRUE);
+ gWorldp->addRegion(handle, sim);
+
+ // give the simulator a message it can use to get ip and port
+ llinfos << "simulator_enable() Enabling " << sim << " with code " << msg->getOurCircuitCode() << llendl;
+ msg->newMessageFast(_PREHASH_UseCircuitCode);
+ msg->nextBlockFast(_PREHASH_CircuitCode);
+ msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_ID, gAgent.getID());
+ msg->sendReliable(sim);
+}
+
+
+// disable the circuit to this simulator
+// Called in response to "DisableSimulator" message.
+void process_disable_simulator(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLHost host = mesgsys->getSender();
+
+ //llinfos << "Disabling simulator with message from " << host << llendl;
+ gWorldp->removeRegion(host);
+
+ mesgsys->disableCircuit(host);
+}
+
+
+void process_region_handshake(LLMessageSystem* msg, void** user_data)
+{
+ LLHost host = msg->getSender();
+ LLViewerRegion* regionp = gWorldp->getRegion(host);
+ if (!regionp)
+ {
+ llwarns << "Got region handshake for unknown region "
+ << host << llendl;
+ return;
+ }
+
+ regionp->unpackRegionHandshake();
+}
+
+
+void send_agent_pause()
+{
+ // world not initialized yet
+ if (!gWorldp) return;
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentPause);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgentID);
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
+
+ gAgentPauseSerialNum++;
+ gMessageSystem->addU32Fast(_PREHASH_SerialNum, gAgentPauseSerialNum);
+
+ LLViewerRegion *regionp;
+ for (regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ gMessageSystem->sendReliable(regionp->getHost());
+ }
+
+ gMessageSystem->sendReliable(gUserServer);
+
+ gObjectList.mWasPaused = TRUE;
+}
+
+
+void send_agent_resume()
+{
+ // world not initialized yet
+ if (!gWorldp) return;
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentResume);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgentID);
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
+
+ gAgentPauseSerialNum++;
+ gMessageSystem->addU32Fast(_PREHASH_SerialNum, gAgentPauseSerialNum);
+
+
+ LLViewerRegion *regionp;
+ for (regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ gMessageSystem->sendReliable(regionp->getHost());
+ }
+
+ gMessageSystem->sendReliable(gUserServer);
+
+ // Reset the FPS counter to avoid an invalid fps
+ gViewerStats->mFPSStat.start();
+}
+
+
diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h
new file mode 100644
index 0000000000..71d7a02322
--- /dev/null
+++ b/indra/newview/llworld.h
@@ -0,0 +1,178 @@
+/**
+ * @file llworld.h
+ * @brief Initial test structure to organize viewer regions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWORLD_H
+#define LL_LLWORLD_H
+
+#include "llpatchvertexarray.h"
+#include "doublelinkedlist.h"
+#include "linked_lists.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "llviewerpartsim.h"
+#include "llviewerimage.h"
+
+class LLViewerRegion;
+class LLVector3d;
+class LLMessageSystem;
+class LLNetMap;
+class LLHost;
+
+class LLViewerObject;
+class LLVOWater;
+class LLSurfacePatch;
+
+class LLCloudPuff;
+class LLCloudGroup;
+class LLVOAvatar;
+
+// LLWorld maintains a stack of unused viewer_regions and an array of pointers to viewer regions
+// as simulators are connected to, viewer_regions are popped off the stack and connected as required
+// as simulators are removed, they are pushed back onto the stack
+
+class LLWorld
+{
+public:
+ LLWorld(const U32 grids_per_region, const F32 meters_per_grid);
+ ~LLWorld();
+
+ LLViewerRegion* addRegion(const U64 &region_handle, const LLHost &host);
+ // safe to call if already present, does the "right thing" if
+ // hosts are same, or if hosts are different, etc...
+ void removeRegion(const LLHost &host);
+
+ void disconnectRegions(); // Send quit messages to all child regions
+
+ LLViewerRegion* getRegion(const LLHost &host);
+ LLViewerRegion* getRegionFromPosGlobal(const LLVector3d &pos);
+ LLViewerRegion* getRegionFromPosAgent(const LLVector3 &pos);
+ LLViewerRegion* getRegionFromHandle(const U64 &handle);
+ BOOL positionRegionValidGlobal(const LLVector3d& pos); // true if position is in valid region
+ LLVector3d clipToVisibleRegions(const LLVector3d &start_pos, const LLVector3d &end_pos);
+
+ void updateAgentOffset(const LLVector3d &offset);
+
+ // All of these should be in the agent coordinate frame
+ LLViewerRegion* resolveRegionGlobal(LLVector3 &localpos, const LLVector3d &position);
+ LLViewerRegion* resolveRegionAgent(LLVector3 &localpos, const LLVector3 &position);
+ F32 resolveLandHeightGlobal(const LLVector3d &position);
+ F32 resolveLandHeightAgent(const LLVector3 &position);
+
+ // Return the lowest allowed Z point to prevent objects from being moved
+ // underground.
+ F32 getMinAllowedZ(LLViewerObject* object);
+
+ // takes a line segment defined by point_a and point_b, then
+ // determines the closest (to point_a) point of intersection that is
+ // on the land surface or on an object of the world.
+ // Stores results in "intersection" and "intersection_normal" and
+ // returns a scalar value that is the normalized (by length of line segment)
+ // distance along the line from "point_a" to "intersection".
+ //
+ // Currently assumes point_a and point_b only differ in z-direction,
+ // but it may eventually become more general.
+ F32 resolveStepHeightGlobal(const LLVOAvatar* avatarp, const LLVector3d &point_a, const LLVector3d &point_b,
+ LLVector3d &intersection, LLVector3 &intersection_normal,
+ LLViewerObject** viewerObjectPtr=NULL);
+
+ LLSurfacePatch * resolveLandPatchGlobal(const LLVector3d &position);
+ LLVector3 resolveLandNormalGlobal(const LLVector3d &position); // absolute frame
+
+ U32 getRegionWidthInPoints() const { return mWidth; }
+ F32 getRegionScale() const { return mScale; }
+
+ // region X and Y size in meters
+ F32 getRegionWidthInMeters() const { return mWidthInMeters; }
+ F32 getRegionMinHeight() const { return -mWidthInMeters; }
+ F32 getRegionMaxHeight() const { return 3.f*mWidthInMeters; }
+
+ void updateRegions();
+ void updateVisibilities();
+ void updateParticles();
+ void updateClouds(const F32 dt);
+ LLCloudGroup * findCloudGroup(const LLCloudPuff &puff);
+
+ void renderPropertyLines();
+
+ void resetStats();
+ void updateNetStats(); // Update network statistics for all the regions...
+
+ void printPacketsLost();
+ void requestCacheMisses();
+
+ // deal with map object updates in the world.
+ static void processCoarseUpdate(LLMessageSystem* msg, void** user_data);
+
+ F32 getLandFarClip() const;
+ void setLandFarClip(const F32 far_clip);
+
+ LLViewerImage *getDefaultWaterTexture();
+ void updateWaterObjects();
+
+ void setSpaceTimeUSec(const U64 space_time_usec);
+ U64 getSpaceTimeUSec() const;
+
+ LLString getInfoString();
+public:
+ LLDoubleLinkedList<LLViewerRegion> mActiveRegionList;
+ LLViewerPartSim mPartSim;
+
+private:
+ LLLinkedList<LLViewerRegion> mRegionList;
+ LLDoubleLinkedList<LLViewerRegion> mVisibleRegionList;
+ LLDoubleLinkedList<LLViewerRegion> mCulledRegionList;
+
+ // Number of points on edge
+ const U32 mWidth;
+
+ // meters/point, therefore mWidth * mScale = meters per edge
+ const F32 mScale;
+
+ const F32 mWidthInMeters;
+
+ F32 mLandFarClip; // Far clip distance for land.
+ F32 mIdleUpdateTime;
+ LLPatchVertexArray mLandPatch;
+ S32 mLastPacketsIn;
+ S32 mLastPacketsOut;
+ S32 mLastPacketsLost;
+
+ ////////////////////////////
+ //
+ // Data for "Fake" objects
+ //
+
+ // Used to define the "Square" which we need to fill in
+ U32 mMinRegionX;
+ U32 mMaxRegionX;
+ U32 mMinRegionY;
+ U32 mMaxRegionY;
+
+ LLLinkedList<LLVOWater> mHoleWaterObjects;
+ LLPointer<LLVOWater> mEdgeWaterObjects[8];
+
+ LLPointer<LLViewerImage> mDefaultWaterTexturep;
+ U64 mSpaceTimeUSec;
+};
+
+extern LLWorld *gWorldp;
+#define gWorldPointer gWorldp
+
+void process_enable_simulator(LLMessageSystem *mesgsys, void **user_data);
+void process_disable_simulator(LLMessageSystem *mesgsys, void **user_data);
+
+void process_region_handshake(LLMessageSystem* msg, void** user_data);
+
+void send_agent_pause();
+void send_agent_resume();
+
+#endif
diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp
new file mode 100644
index 0000000000..42d8b9fd20
--- /dev/null
+++ b/indra/newview/llworldmap.cpp
@@ -0,0 +1,915 @@
+/**
+ * @file llworldmap.cpp
+ * @brief Underlying data representation for map of the world
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworldmap.h"
+
+#include "llregionhandle.h"
+#include "message.h"
+
+#include "viewer.h" // for gPacificDaylightTime
+#include "llagent.h"
+#include "llmapresponders.h"
+#include "llviewercontrol.h"
+#include "llfloaterworldmap.h"
+#include "lltracker.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llregionflags.h"
+
+LLWorldMap* gWorldMap = NULL;
+
+const F32 REQUEST_ITEMS_TIMER = 10.f * 60.f; // 10 minutes
+
+LLItemInfo::LLItemInfo(F32 global_x, F32 global_y,
+ const std::string& name,
+ LLUUID id,
+ S32 extra, S32 extra2)
+: mName(name),
+ mToolTip(""),
+ mPosGlobal(global_x, global_y, 40.0),
+ mID(id),
+ mSelected(FALSE),
+ mExtra(extra),
+ mExtra2(extra2)
+{
+ mRegionHandle = to_region_handle(mPosGlobal);
+}
+
+LLSimInfo::LLSimInfo()
+: mHandle(0),
+ mName(),
+ mAgentsUpdateTime(0),
+ mAccess(0x0),
+ mRegionFlags(0x0),
+ mWaterHeight(0.f),
+ mAlpha(-1.f)
+{
+}
+
+
+LLVector3d LLSimInfo::getGlobalPos(LLVector3 local_pos) const
+{
+ LLVector3d pos = from_region_handle(mHandle);
+ pos.mdV[VX] += local_pos.mV[VX];
+ pos.mdV[VY] += local_pos.mV[VY];
+ pos.mdV[VZ] += local_pos.mV[VZ];
+ return pos;
+}
+
+
+//---------------------------------------------------------------------------
+// World Map
+//---------------------------------------------------------------------------
+
+LLWorldMap::LLWorldMap() :
+ mIsTrackingUnknownLocation( FALSE ),
+ mInvalidLocation( FALSE ),
+ mIsTrackingDoubleClick( FALSE ),
+ mIsTrackingCommit( FALSE ),
+ mUnknownLocation( 0, 0, 0 ),
+ mRequestLandForSale(true),
+ mCurrentMap(0),
+ mMinX(U32_MAX),
+ mMaxX(U32_MIN),
+ mMinY(U32_MAX),
+ mMaxY(U32_MIN),
+ mNeighborMap(NULL),
+ mTelehubCoverageMap(NULL),
+ mNeighborMapWidth(0),
+ mNeighborMapHeight(0)
+{
+ for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ {
+ mMapLoaded[map] = FALSE;
+ mMapBlockLoaded[map] = new BOOL[MAP_BLOCK_RES*MAP_BLOCK_RES];
+ for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx)
+ {
+ mMapBlockLoaded[map][idx] = FALSE;
+ }
+ }
+}
+
+
+LLWorldMap::~LLWorldMap()
+{
+ reset();
+ for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ {
+ delete[] mMapBlockLoaded[map];
+ }
+}
+
+
+void LLWorldMap::reset()
+{
+ for_each(mSimInfoMap.begin(), mSimInfoMap.end(), DeletePairedPointer());
+ mSimInfoMap.clear();
+
+ for (S32 m=0; m<MAP_SIM_IMAGE_TYPES; ++m)
+ {
+ mMapLoaded[m] = FALSE;
+ }
+
+ clearSimFlags();
+
+ eraseItems();
+
+ mMinX = U32_MAX;
+ mMaxX = U32_MIN;
+
+ mMinY = U32_MAX;
+ mMaxY = U32_MIN;
+
+ delete [] mNeighborMap;
+ mNeighborMap = NULL;
+ delete [] mTelehubCoverageMap;
+ mTelehubCoverageMap = NULL;
+
+ mNeighborMapWidth = 0;
+ mNeighborMapHeight = 0;
+}
+
+void LLWorldMap::eraseItems()
+{
+ if (mRequestTimer.getElapsedTimeF32() > REQUEST_ITEMS_TIMER)
+ {
+ mRequestTimer.reset();
+
+ mTelehubs.clear();
+ mInfohubs.clear();
+ mPGEvents.clear();
+ mMatureEvents.clear();
+ mPopular.clear();
+ mLandForSale.clear();
+ mClassifieds.clear();
+ }
+// mAgentLocationsMap.clear(); // persists
+// mNumAgents.clear(); // persists
+}
+
+
+void LLWorldMap::clearImageRefs()
+{
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ LLSimInfo* info = (*it).second;
+ if (info->mCurrentImage)
+ {
+ info->mCurrentImage->setBoostLevel(0);
+ info->mCurrentImage = NULL;
+ }
+ if (info->mOverlayImage)
+ {
+ info->mOverlayImage->setBoostLevel(0);
+ info->mOverlayImage = NULL;
+ }
+ }
+}
+
+// Doesn't clear the already-loaded sim infos, just re-requests them
+void LLWorldMap::clearSimFlags()
+{
+ for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ {
+ for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx)
+ {
+ mMapBlockLoaded[map][idx] = FALSE;
+ }
+ }
+}
+
+LLSimInfo* LLWorldMap::simInfoFromPosGlobal(const LLVector3d& pos_global)
+{
+ U64 handle = to_region_handle(pos_global);
+ return simInfoFromHandle(handle);
+}
+
+LLSimInfo* LLWorldMap::simInfoFromHandle(const U64 handle)
+{
+ sim_info_map_t::iterator it = mSimInfoMap.find(handle);
+ if (it != mSimInfoMap.end())
+ {
+ LLSimInfo* sim_info = (*it).second;
+ if (sim_info)
+ {
+ return sim_info;
+ }
+ }
+ return NULL;
+}
+
+
+LLSimInfo* LLWorldMap::simInfoFromName(const LLString& sim_name)
+{
+ LLSimInfo* sim_info = NULL;
+ if (!sim_name.empty())
+ {
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ sim_info = (*it).second;
+ if (sim_info
+ && (0 == LLString::compareInsensitive(sim_name.c_str(), sim_info->mName.c_str())) )
+ {
+ break;
+ }
+ sim_info = NULL;
+ }
+ }
+ return sim_info;
+}
+
+LLString LLWorldMap::simNameFromPosGlobal(const LLVector3d& pos_global)
+{
+ U64 handle = to_region_handle(pos_global);
+
+ sim_info_map_t::iterator it = mSimInfoMap.find(handle);
+ if (it != mSimInfoMap.end())
+ {
+ LLSimInfo* info = (*it).second;
+ return info->mName.c_str();
+ }
+ else
+ {
+ return "(unknown region)";
+ }
+}
+
+void LLWorldMap::setCurrentLayer(S32 layer, bool request_layer)
+{
+ mCurrentMap = layer;
+ if (!mMapLoaded[layer] || request_layer)
+ {
+ sendMapLayerRequest();
+ sendItemRequest(MAP_ITEM_AGENT_COUNT);
+ }
+
+ if (mTelehubs.size() == 0 ||
+ mInfohubs.size() == 0)
+ {
+ // Request for telehubs
+ sendItemRequest(MAP_ITEM_TELEHUB);
+ }
+
+ if (mPGEvents.size() == 0)
+ {
+ // Request for events
+ sendItemRequest(MAP_ITEM_PG_EVENT);
+ }
+
+ if (mMatureEvents.size() == 0)
+ {
+ // Request for events (mature)
+ sendItemRequest(MAP_ITEM_MATURE_EVENT);
+ }
+
+ if (mPopular.size() == 0)
+ {
+ // Request for popular
+ sendItemRequest(MAP_ITEM_POPULAR);
+ }
+
+ if (mLandForSale.size() == 0)
+ {
+ // Request for Land For Sale
+ sendItemRequest(MAP_ITEM_LAND_FOR_SALE);
+ }
+
+ if (mClassifieds.size() == 0)
+ {
+ sendItemRequest(MAP_ITEM_CLASSIFIED);
+ }
+
+ clearImageRefs();
+ clearSimFlags();
+}
+
+void LLWorldMap::sendItemRequest(U32 type, U64 handle)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ S32 layer = mCurrentMap;
+
+ msg->newMessageFast(_PREHASH_MapItemRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, layer);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+
+ msg->nextBlockFast(_PREHASH_RequestData);
+ msg->addU32Fast(_PREHASH_ItemType, type);
+ msg->addU64Fast(_PREHASH_RegionHandle, handle); // If zero, filled in on sim
+
+ gAgent.sendReliableMessage();
+}
+
+// public
+void LLWorldMap::sendMapLayerRequest()
+{
+ LLSD body;
+ body["Flags"] = mCurrentMap;
+ std::string url = gAgent.getRegion()->getCapability(
+ gAgent.isGodlike() ? "MapLayerGod" : "MapLayer");
+
+ if (!url.empty())
+ {
+ llinfos << "LLWorldMap::sendMapLayerRequest via capability" << llendl;
+ LLHTTPClient::post(url, body, new LLMapLayerResponder());
+ }
+ else
+ {
+ llinfos << "LLWorldMap::sendMapLayerRequest via message system" << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ S32 layer = mCurrentMap;
+
+ // Request for layer
+ msg->newMessageFast(_PREHASH_MapLayerRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, layer);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ gAgent.sendReliableMessage();
+
+ if (mRequestLandForSale)
+ {
+ msg->newMessageFast(_PREHASH_MapLayerRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, 2);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ gAgent.sendReliableMessage();
+ }
+ }
+}
+
+// public
+void LLWorldMap::sendNamedRegionRequest(std::string region_name)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ S32 layer = mCurrentMap;
+
+ // Request for layer
+ msg->newMessageFast(_PREHASH_MapNameRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, layer);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_NameData);
+ msg->addStringFast(_PREHASH_Name, region_name);
+ gAgent.sendReliableMessage();
+}
+
+// public
+void LLWorldMap::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent)
+{
+ S32 layer = mCurrentMap;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MapBlockRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ U32 flags = layer;
+ flags |= (return_nonexistent ? 0x10000 : 0);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_PositionData);
+ msg->addU16Fast(_PREHASH_MinX, min_x);
+ msg->addU16Fast(_PREHASH_MinY, min_y);
+ msg->addU16Fast(_PREHASH_MaxX, max_x);
+ msg->addU16Fast(_PREHASH_MaxY, max_y);
+ gAgent.sendReliableMessage();
+
+ if (mRequestLandForSale)
+ {
+ msg->newMessageFast(_PREHASH_MapBlockRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, 2);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_PositionData);
+ msg->addU16Fast(_PREHASH_MinX, min_x);
+ msg->addU16Fast(_PREHASH_MinY, min_y);
+ msg->addU16Fast(_PREHASH_MaxX, max_x);
+ msg->addU16Fast(_PREHASH_MaxY, max_y);
+ gAgent.sendReliableMessage();
+ }
+}
+
+// public static
+void LLWorldMap::processMapLayerReply(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLWorldMap::processMapLayerReply from message system" << llendl;
+
+ U32 agent_flags;
+ msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags);
+
+ if (agent_flags != (U32)gWorldMap->mCurrentMap)
+ {
+ llwarns << "Invalid or out of date map image type returned!" << llendl;
+ return;
+ }
+
+ LLUUID image_id;
+ //U32 left, right, top, bottom;
+
+ S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_LayerData);
+
+ gWorldMap->mMapLayers[agent_flags].clear();
+
+ BOOL adjust = FALSE;
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ LLWorldMapLayer new_layer;
+ new_layer.LayerDefined = TRUE;
+ msg->getUUIDFast(_PREHASH_LayerData, _PREHASH_ImageID, new_layer.LayerImageID, block);
+ new_layer.LayerImage = gImageList.getImage(new_layer.LayerImageID, MIPMAP_TRUE, FALSE);
+ new_layer.LayerImage->bindTexture(0);
+ new_layer.LayerImage->setClamp(TRUE, TRUE);
+
+ U32 left, right, top, bottom;
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Left, left, block);
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Right, right, block);
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Top, top, block);
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Bottom, bottom, block);
+
+ new_layer.LayerExtents.mLeft = left;
+ new_layer.LayerExtents.mRight = right;
+ new_layer.LayerExtents.mBottom = bottom;
+ new_layer.LayerExtents.mTop = top;
+
+ F32 x_meters = F32(left*REGION_WIDTH_UNITS);
+ F32 y_meters = F32(bottom*REGION_WIDTH_UNITS);
+ adjust = gWorldMap->extendAABB(U32(x_meters), U32(y_meters),
+ U32(x_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getWidth()),
+ U32(y_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getHeight())) || adjust;
+
+ gWorldMap->mMapLayers[agent_flags].push_back(new_layer);
+ }
+
+ gWorldMap->mMapLoaded[agent_flags] = TRUE;
+ if(adjust) gFloaterWorldMap->adjustZoomSliderBounds();
+}
+
+// public static
+void LLWorldMap::processMapBlockReply(LLMessageSystem* msg, void**)
+{
+ U32 agent_flags;
+ msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags);
+
+ if (agent_flags < 0 || agent_flags >= MAP_SIM_IMAGE_TYPES)
+ {
+ llwarns << "Invalid map image type returned! " << agent_flags << llendl;
+ return;
+ }
+
+ S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_Data);
+
+ bool found_null_sim = false;
+
+ BOOL adjust = FALSE;
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ U16 x_regions;
+ U16 y_regions;
+ char name[MAX_STRING];
+ U8 access;
+ U32 region_flags;
+ U8 water_height;
+ U8 agents;
+ LLUUID image_id;
+ msg->getU16Fast(_PREHASH_Data, _PREHASH_X, x_regions, block);
+ msg->getU16Fast(_PREHASH_Data, _PREHASH_Y, y_regions, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, MAX_STRING, name, block);
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_Access, access, block);
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_RegionFlags, region_flags, block);
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_WaterHeight, water_height, block);
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_Agents, agents, block);
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_MapImageID, image_id, block);
+
+ U32 x_meters = x_regions * REGION_WIDTH_UNITS;
+ U32 y_meters = y_regions * REGION_WIDTH_UNITS;
+
+ if (access == 255)
+ {
+ // This region doesn't exist
+ if (gWorldMap->mIsTrackingUnknownLocation &&
+ gWorldMap->mUnknownLocation.mdV[0] >= x_meters &&
+ gWorldMap->mUnknownLocation.mdV[0] < x_meters + 256 &&
+ gWorldMap->mUnknownLocation.mdV[1] >= y_meters &&
+ gWorldMap->mUnknownLocation.mdV[1] < y_meters + 256)
+ {
+ // We were tracking this location, but it doesn't exist
+ gWorldMap->mInvalidLocation = TRUE;
+ }
+
+ found_null_sim = true;
+ }
+ else
+ {
+ adjust = gWorldMap->extendAABB(x_meters,
+ y_meters,
+ x_meters+REGION_WIDTH_UNITS,
+ y_meters+REGION_WIDTH_UNITS) || adjust;
+ U64 handle = to_region_handle(x_meters, y_meters);
+
+// llinfos << "Map sim " << name << " image layer " << agent_flags << " ID " << image_id.getString() << llendl;
+
+ LLSimInfo* siminfo = new LLSimInfo();
+ sim_info_map_t::iterator iter = gWorldMap->mSimInfoMap.find(handle);
+ if (iter != gWorldMap->mSimInfoMap.end())
+ {
+ LLSimInfo* oldinfo = iter->second;
+ for (S32 image=0; image<MAP_SIM_IMAGE_TYPES; ++image)
+ {
+ siminfo->mMapImageID[image] = oldinfo->mMapImageID[image];
+ }
+ delete oldinfo;
+ }
+ gWorldMap->mSimInfoMap[handle] = siminfo;
+
+ siminfo->mHandle = handle;
+ siminfo->mName.assign( name );
+ siminfo->mAccess = access;
+ siminfo->mRegionFlags = region_flags;
+ siminfo->mWaterHeight = (F32) water_height;
+ siminfo->mMapImageID[agent_flags] = image_id;
+ siminfo->mCurrentImage = gImageList.getImage(siminfo->mMapImageID[gWorldMap->mCurrentMap], MIPMAP_TRUE, FALSE);
+ siminfo->mCurrentImage->bindTexture(0);
+ siminfo->mCurrentImage->setClamp(TRUE, TRUE);
+
+ if (siminfo->mMapImageID[2].notNull())
+ {
+ siminfo->mOverlayImage = gImageList.getImage(siminfo->mMapImageID[2], MIPMAP_TRUE, FALSE);
+ }
+ else
+ {
+ siminfo->mOverlayImage = NULL;
+ }
+
+ if (gWorldMap->mIsTrackingUnknownLocation &&
+ gWorldMap->mUnknownLocation.mdV[0] >= x_meters &&
+ gWorldMap->mUnknownLocation.mdV[0] < x_meters + 256 &&
+ gWorldMap->mUnknownLocation.mdV[1] >= y_meters &&
+ gWorldMap->mUnknownLocation.mdV[1] < y_meters + 256)
+ {
+ if (siminfo->mAccess == SIM_ACCESS_DOWN)
+ {
+ // We were tracking this location, but it doesn't exist
+ gWorldMap->mInvalidLocation = true;
+ }
+ else
+ {
+ // We were tracking this location, and it does exist
+ bool is_tracking_dbl = gWorldMap->mIsTrackingDoubleClick == TRUE;
+ gFloaterWorldMap->trackLocation(gWorldMap->mUnknownLocation);
+ if (is_tracking_dbl)
+ {
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ gAgent.teleportViaLocation( pos_global );
+ }
+ }
+ }
+ }
+ }
+ if(adjust) gFloaterWorldMap->adjustZoomSliderBounds();
+ gFloaterWorldMap->updateSims(found_null_sim);
+}
+
+// public static
+void LLWorldMap::processMapItemReply(LLMessageSystem* msg, void**)
+{
+ U32 type;
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ItemType, type);
+
+ S32 num_blocks = msg->getNumberOfBlocks("Data");
+
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ U32 X, Y;
+ char name[MAX_STRING];
+ S32 extra, extra2;
+ LLUUID uuid;
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_X, X, block);
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_Y, Y, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, MAX_STRING, name, block);
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ID, uuid, block);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra, extra, block);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra2, extra2, block);
+
+ F32 world_x = (F32)X;
+ X /= REGION_WIDTH_UNITS;
+ F32 world_y = (F32)Y;
+ Y /= REGION_WIDTH_UNITS;
+
+ LLItemInfo new_item(world_x, world_y, name, uuid, extra, extra2);
+ LLSimInfo* siminfo = gWorldMap->simInfoFromHandle(new_item.mRegionHandle);
+
+ switch (type)
+ {
+ case MAP_ITEM_TELEHUB: // telehubs
+ {
+ // Telehub color, store in extra as 4 U8's
+ U8 *color = (U8 *)&new_item.mExtra;
+
+ F32 red = fmod((F32)X * 0.11f, 1.f) * 0.8f;
+ F32 green = fmod((F32)Y * 0.11f, 1.f) * 0.8f;
+ F32 blue = fmod(1.5f * (F32)(X + Y) * 0.11f, 1.f) * 0.8f;
+ F32 add_amt = (X % 2) ? 0.15f : -0.15f;
+ add_amt += (Y % 2) ? -0.15f : 0.15f;
+ color[0] = U8((red + add_amt) * 255);
+ color[1] = U8((green + add_amt) * 255);
+ color[2] = U8((blue + add_amt) * 255);
+ color[3] = 255;
+
+ // extra2 specifies whether this is an infohub or a telehub.
+ if (extra2)
+ {
+ gWorldMap->mInfohubs.push_back(new_item);
+ }
+ else
+ {
+ gWorldMap->mTelehubs.push_back(new_item);
+ }
+
+ break;
+ }
+ case MAP_ITEM_PG_EVENT: // events
+ case MAP_ITEM_MATURE_EVENT:
+ {
+ char buffer[32];
+ struct tm* timep;
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(extra, gPacificDaylightTime);
+
+ S32 display_hour = timep->tm_hour % 12;
+ if (display_hour == 0) display_hour = 12;
+
+ sprintf(buffer, "%d:%02d %s",
+ display_hour,
+ timep->tm_min,
+ (timep->tm_hour < 12 ? "AM" : "PM") );
+ new_item.mToolTip.assign( buffer );
+
+ // HACK: store Z in extra2
+ new_item.mPosGlobal.mdV[VZ] = (F64)extra2;
+ if (type == MAP_ITEM_PG_EVENT)
+ {
+ gWorldMap->mPGEvents.push_back(new_item);
+ }
+ else
+ {
+ gWorldMap->mMatureEvents.push_back(new_item);
+ }
+ break;
+ }
+ case MAP_ITEM_POPULAR: // popular
+ {
+ new_item.mPosGlobal.mdV[VZ] = (F64)extra2;
+ gWorldMap->mPopular.push_back(new_item);
+ break;
+ }
+ case MAP_ITEM_LAND_FOR_SALE: // land for sale
+ {
+ new_item.mToolTip = llformat("%d sq. m. L$%d", new_item.mExtra, new_item.mExtra2);
+ gWorldMap->mLandForSale.push_back(new_item);
+ break;
+ }
+ case MAP_ITEM_CLASSIFIED: // classifieds
+ {
+ // HACK: Z-height is in Extra2 field.
+ new_item.mPosGlobal.mdV[VZ] = (F64)extra2;
+ gWorldMap->mClassifieds.push_back(new_item);
+ break;
+ }
+ case MAP_ITEM_AGENT_COUNT: // agent counts
+ {
+ // We only ever receive one per region, i.e. this update superceeds any others
+ gWorldMap->mNumAgents[new_item.mRegionHandle] = new_item.mExtra;
+ break;
+ }
+ case MAP_ITEM_AGENT_LOCATIONS: // agent locations
+ {
+ if (!siminfo)
+ {
+ llinfos << "siminfo missing for " << new_item.mPosGlobal.mdV[0] << ", " << new_item.mPosGlobal.mdV[1] << llendl;
+ break;
+ }
+// llinfos << "New Location " << new_item.mName << llendl;
+
+ item_info_list_t& agentcounts = gWorldMap->mAgentLocationsMap[new_item.mRegionHandle];
+
+ // Find the last item in the list with a different name and erase them
+ item_info_list_t::iterator lastiter;
+ for (lastiter = agentcounts.begin(); lastiter!=agentcounts.end(); ++lastiter)
+ {
+ const LLItemInfo& info = *lastiter;
+ if (info.mName == new_item.mName)
+ {
+ break;
+ }
+ }
+ if (lastiter != agentcounts.begin())
+ {
+ agentcounts.erase(agentcounts.begin(), lastiter);
+ }
+ // Now append the new location
+ if (new_item.mExtra > 0)
+ {
+ agentcounts.push_back(new_item);
+ }
+ break;
+ }
+ default:
+ break;
+ };
+ }
+}
+
+void LLWorldMap::dump()
+{
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+
+ llinfos << x_pos << "," << y_pos
+ << " " << info->mName.c_str()
+ << " " << (S32)info->mAccess
+ << " " << std::hex << info->mRegionFlags << std::dec
+ << " " << info->mWaterHeight
+ //<< " " << info->mTelehubName
+ //<< " " << info->mTelehubPosition
+ << llendl;
+
+ if (info->mCurrentImage)
+ {
+ llinfos << "image discard " << (S32)info->mCurrentImage->getDiscardLevel()
+ << " fullwidth " << info->mCurrentImage->getWidth(0)
+ << " fullheight " << info->mCurrentImage->getHeight(0)
+ << " maxvirt " << info->mCurrentImage->mMaxVirtualSize
+ << " maxdisc " << (S32)info->mCurrentImage->getMaxDiscardLevel()
+ << llendl;
+ }
+ }
+}
+
+
+BOOL LLWorldMap::extendAABB(U32 min_x, U32 min_y, U32 max_x, U32 max_y)
+{
+ BOOL rv = FALSE;
+ if (min_x < mMinX)
+ {
+ rv = TRUE;
+ mMinX = min_x;
+ }
+ if (min_y < mMinY)
+ {
+ rv = TRUE;
+ mMinY = min_y;
+ }
+ if (max_x > mMaxX)
+ {
+ rv = TRUE;
+ mMaxX = max_x;
+ }
+ if (max_y > mMaxY)
+ {
+ rv = TRUE;
+ mMaxY = max_y;
+ }
+ lldebugs << "World map aabb: (" << mMinX << ", " << mMinY << "), ("
+ << mMaxX << ", " << mMaxY << ")" << llendl;
+ return rv;
+}
+
+
+U32 LLWorldMap::getWorldWidth() const
+{
+ return mMaxX - mMinX;
+}
+
+
+U32 LLWorldMap::getWorldHeight() const
+{
+ return mMaxY - mMinY;
+}
+
+BOOL LLWorldMap::coveredByTelehub(LLSimInfo* infop)
+{
+ /*if (!mTelehubCoverageMap)
+ {
+ return FALSE;
+ }
+ U32 x_pos, y_pos;
+ from_region_handle(infop->mHandle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+
+ S32 index = x_pos - (mMinX / REGION_WIDTH_UNITS - 1) + (mNeighborMapWidth * (y_pos - (mMinY / REGION_WIDTH_UNITS - 1)));
+ return mTelehubCoverageMap[index] != 0; */
+ return FALSE;
+}
+
+void LLWorldMap::updateTelehubCoverage()
+{
+ /*S32 neighbor_width = getWorldWidth() / REGION_WIDTH_UNITS + 2;
+ S32 neighbor_height = getWorldHeight() / REGION_WIDTH_UNITS + 2;
+ if (neighbor_width > mNeighborMapWidth || neighbor_height > mNeighborMapHeight)
+ {
+ mNeighborMapWidth = neighbor_width;
+ mNeighborMapHeight = neighbor_height;
+ delete mNeighborMap;
+ delete mTelehubCoverageMap;
+
+ mNeighborMap = new U8[mNeighborMapWidth * mNeighborMapHeight];
+ mTelehubCoverageMap = new U8[mNeighborMapWidth * mNeighborMapHeight];
+ }
+
+ memset(mNeighborMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8));
+ memset(mTelehubCoverageMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8));
+
+ // leave 1 sim border
+ S32 min_x = (mMinX / REGION_WIDTH_UNITS) - 1;
+ S32 min_y = (mMinY / REGION_WIDTH_UNITS) - 1;
+
+ std::map<U64, LLSimInfo*>::const_iterator it;
+ for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ //LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+ x_pos -= min_x;
+ y_pos -= min_y;
+
+ S32 index = x_pos + (mNeighborMapWidth * y_pos);
+ mNeighborMap[index - 1]++;
+ mNeighborMap[index + 1]++;
+ mNeighborMap[index - mNeighborMapWidth]++;
+ mNeighborMap[index + mNeighborMapWidth]++;
+ }
+
+ for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+ x_pos -= min_x;
+ y_pos -= min_y;
+
+ S32 index = x_pos + (mNeighborMapWidth * y_pos);
+
+ if (!info->mTelehubName.empty() && mNeighborMap[index])
+ {
+ S32 x_start = llmax(0, S32(x_pos - 5));
+ S32 x_span = llmin(mNeighborMapWidth - 1, (S32)(x_pos + 5)) - x_start + 1;
+ S32 y_start = llmax(0, (S32)y_pos - 5);
+ S32 y_end = llmin(mNeighborMapHeight - 1, (S32)(y_pos + 5));
+ for (S32 y_index = y_start; y_index <= y_end; y_index++)
+ {
+ memset(&mTelehubCoverageMap[x_start + y_index * mNeighborMapWidth], 0xff, sizeof(U8) * x_span);
+ }
+ }
+ }
+
+ for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ //LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+
+ S32 index = x_pos - min_x + (mNeighborMapWidth * (y_pos - min_y));
+ mTelehubCoverageMap[index] *= mNeighborMap[index];
+ }*/
+}
diff --git a/indra/newview/llworldmap.h b/indra/newview/llworldmap.h
new file mode 100644
index 0000000000..76ab88de1e
--- /dev/null
+++ b/indra/newview/llworldmap.h
@@ -0,0 +1,192 @@
+/**
+ * @file llworldmap.h
+ * @brief Underlying data storage for the map of the entire world.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWORLDMAP_H
+#define LL_LLWORLDMAP_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "v3math.h"
+#include "v3dmath.h"
+#include "llframetimer.h"
+#include "llmapimagetype.h"
+#include "lluuid.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+#include "lleventinfo.h"
+#include "v3color.h"
+
+class LLMessageSystem;
+
+
+class LLItemInfo
+{
+public:
+ LLItemInfo(F32 global_x, F32 global_y, const std::string& name, LLUUID id, S32 extra = 0, S32 extra2 = 0);
+
+ std::string mName;
+ std::string mToolTip;
+ LLVector3d mPosGlobal;
+ LLUUID mID;
+ BOOL mSelected;
+ S32 mExtra;
+ S32 mExtra2;
+ U64 mRegionHandle;
+};
+
+#define MAP_SIM_IMAGE_TYPES 3
+// 0 - Prim
+// 1 - Terrain Only
+// 2 - Overlay: Land For Sale
+
+class LLSimInfo
+{
+public:
+ LLSimInfo();
+
+ LLVector3d getGlobalPos(LLVector3 local_pos) const;
+
+public:
+ U64 mHandle;
+ std::string mName;
+
+ F64 mAgentsUpdateTime;
+ BOOL mShowAgentLocations; // are agents visible?
+
+ U8 mAccess;
+ U32 mRegionFlags;
+ F32 mWaterHeight;
+
+ F32 mAlpha;
+
+ // Image ID for the current overlay mode.
+ LLUUID mMapImageID[MAP_SIM_IMAGE_TYPES];
+
+ // Hold a reference to the currently displayed image.
+ LLPointer<LLViewerImage> mCurrentImage;
+ LLPointer<LLViewerImage> mOverlayImage;
+};
+
+#define MAP_BLOCK_RES 256
+
+struct LLWorldMapLayer
+{
+ BOOL LayerDefined;
+ LLPointer<LLViewerImage> LayerImage;
+ LLUUID LayerImageID;
+ LLRect LayerExtents;
+
+ LLWorldMapLayer() : LayerDefined(FALSE) { }
+};
+
+
+class LLWorldMap
+{
+public:
+ LLWorldMap();
+ ~LLWorldMap();
+
+ // clears the list
+ void reset();
+
+ // clear the visible items
+ void eraseItems();
+
+ // Removes references to cached images
+ void clearImageRefs();
+
+ // Clears the flags indicating that we've received sim infos
+ // Causes a re-request of the sim info without erasing extisting info
+ void clearSimFlags();
+
+ // Returns simulator information, or NULL if out of range
+ LLSimInfo* simInfoFromHandle(const U64 handle);
+
+ // Returns simulator information, or NULL if out of range
+ LLSimInfo* simInfoFromPosGlobal(const LLVector3d& pos_global);
+
+ // Returns simulator information for named sim, or NULL if non-existent
+ LLSimInfo* simInfoFromName(const LLString& sim_name);
+
+ // Returns simulator name
+ LLString simNameFromPosGlobal(const LLVector3d& pos_global);
+
+ // Sets the current layer
+ void setCurrentLayer(S32 layer, bool request_layer = false);
+
+ void sendMapLayerRequest();
+ void sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent = false);
+ void sendNamedRegionRequest(std::string region_name);
+ void sendItemRequest(U32 type, U64 handle = 0);
+
+ static void processMapLayerReply(LLMessageSystem*, void**);
+ static void processMapBlockReply(LLMessageSystem*, void**);
+ static void processMapItemReply(LLMessageSystem*, void**);
+
+ void dump();
+
+ // Extend the bounding box of the list of simulators. Returns true
+ // if the extents changed.
+ BOOL extendAABB(U32 x_min, U32 y_min, U32 x_max, U32 y_max);
+
+ // build coverage maps for telehub region visualization
+ void updateTelehubCoverage();
+ BOOL coveredByTelehub(LLSimInfo* infop);
+
+ // Bounds of the world, in meters
+ U32 getWorldWidth() const;
+ U32 getWorldHeight() const;
+public:
+ // Map from region-handle to simulator info
+ typedef std::map<U64, LLSimInfo*> sim_info_map_t;
+ sim_info_map_t mSimInfoMap;
+
+ BOOL mIsTrackingUnknownLocation, mInvalidLocation, mIsTrackingDoubleClick, mIsTrackingCommit;
+ LLVector3d mUnknownLocation;
+
+ bool mRequestLandForSale;
+
+ typedef std::vector<LLItemInfo> item_info_list_t;
+ item_info_list_t mTelehubs;
+ item_info_list_t mInfohubs;
+ item_info_list_t mPGEvents;
+ item_info_list_t mMatureEvents;
+ item_info_list_t mPopular;
+ item_info_list_t mLandForSale;
+ item_info_list_t mClassifieds;
+
+ std::map<U64,S32> mNumAgents;
+
+ typedef std::map<U64, item_info_list_t> agent_list_map_t;
+ agent_list_map_t mAgentLocationsMap;
+
+ std::vector<LLWorldMapLayer> mMapLayers[MAP_SIM_IMAGE_TYPES];
+ BOOL mMapLoaded[MAP_SIM_IMAGE_TYPES];
+ BOOL * mMapBlockLoaded[MAP_SIM_IMAGE_TYPES];
+ S32 mCurrentMap;
+
+ // AABB of the list of simulators
+ U32 mMinX;
+ U32 mMaxX;
+ U32 mMinY;
+ U32 mMaxY;
+
+ U8* mNeighborMap;
+ U8* mTelehubCoverageMap;
+ S32 mNeighborMapWidth;
+ S32 mNeighborMapHeight;
+
+private:
+ LLTimer mRequestTimer;
+};
+
+extern LLWorldMap* gWorldMap;
+
+#endif
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
new file mode 100644
index 0000000000..56e496e303
--- /dev/null
+++ b/indra/newview/llworldmapview.cpp
@@ -0,0 +1,1940 @@
+/**
+ * @file llworldmapview.cpp
+ * @brief LLWorldMapView class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworldmapview.h"
+
+#include "indra_constants.h"
+#include "llui.h"
+#include "linked_lists.h"
+#include "llmath.h" // clampf()
+#include "llregionhandle.h"
+#include "lleventflags.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "llcylinder.h"
+#include "llfloaterdirectory.h"
+#include "llfloatermap.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "lltextbox.h"
+#include "lltextureview.h"
+#include "lltracker.h"
+#include "llviewercamera.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "viewer.h" // Only for constants!
+
+#include "llglheaders.h"
+
+const F32 GODLY_TELEPORT_HEIGHT = 200.f;
+const S32 SCROLL_HINT_WIDTH = 65;
+const F32 BIG_DOT_RADIUS = 5.f;
+BOOL LLWorldMapView::sHandledLastClick = FALSE;
+
+LLPointer<LLViewerImage> LLWorldMapView::sAvatarYouSmallImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sAvatarSmallImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sAvatarLargeImage = NULL;
+
+LLPointer<LLViewerImage> LLWorldMapView::sTelehubImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sInfohubImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sHomeImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sEventImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sEventMatureImage = NULL;
+
+LLPointer<LLViewerImage> LLWorldMapView::sTrackCircleImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sTrackArrowImage = NULL;
+
+LLPointer<LLViewerImage> LLWorldMapView::sClassifiedsImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sPopularImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sForSaleImage = NULL;
+
+F32 LLWorldMapView::sThresholdA = 48.f;
+F32 LLWorldMapView::sThresholdB = 96.f;
+F32 LLWorldMapView::sPanX = 0.f;
+F32 LLWorldMapView::sPanY = 0.f;
+F32 LLWorldMapView::sTargetPanX = 0.f;
+F32 LLWorldMapView::sTargetPanY = 0.f;
+S32 LLWorldMapView::sTrackingArrowX = 0;
+S32 LLWorldMapView::sTrackingArrowY = 0;
+F32 LLWorldMapView::sPixelsPerMeter = 1.f;
+F32 CONE_SIZE = 0.6f;
+
+void LLWorldMapView::initClass()
+{
+ LLUUID image_id;
+
+ image_id.set( gViewerArt.getString("map_avatar_you_8.tga") );
+ sAvatarYouSmallImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_avatar_8.tga") );
+ sAvatarSmallImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_avatar_16.tga") );
+ sAvatarLargeImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_home.tga") );
+ sHomeImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_telehub.tga") );
+ sTelehubImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_infohub.tga") );
+ sInfohubImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_event.tga") );
+ sEventImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_event_mature.tga") );
+ sEventMatureImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_track_16.tga") );
+ sTrackCircleImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("direction_arrow.tga") );
+ sTrackArrowImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+ // Make sure tracker arrow doesn't wrap
+ sTrackArrowImage->bindTexture(0);
+ sTrackArrowImage->setClamp(TRUE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_top_pick.tga") );
+ sClassifiedsImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_popular.tga") );
+ sPopularImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_for_sale.tga") );
+ sForSaleImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+}
+
+// static
+void LLWorldMapView::cleanupClass()
+{
+ sAvatarYouSmallImage = NULL;
+ sAvatarSmallImage = NULL;
+ sAvatarLargeImage = NULL;
+ sTelehubImage = NULL;
+ sInfohubImage = NULL;
+ sHomeImage = NULL;
+ sEventImage = NULL;
+ sEventMatureImage = NULL;
+ sTrackCircleImage = NULL;
+ sTrackArrowImage = NULL;
+ sClassifiedsImage = NULL;
+ sPopularImage = NULL;
+ sForSaleImage = NULL;
+}
+
+LLWorldMapView::LLWorldMapView(const std::string& name, const LLRect& rect )
+: LLPanel(name, rect, BORDER_NO),
+ mBackgroundColor( LLColor4( 4.f/255.f, 4.f/255.f, 75.f/255.f, 1.f ) ),
+ mItemPicked(FALSE),
+ mPanning( FALSE ),
+ mMouseDownPanX( 0 ),
+ mMouseDownPanY( 0 ),
+ mMouseDownX( 0 ),
+ mMouseDownY( 0 ),
+ mSelectIDStart(0),
+ mAgentCountsUpdateTime(0)
+{
+ sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS;
+ clearLastClick();
+
+ const S32 DIR_WIDTH = 10;
+ const S32 DIR_HEIGHT = 10;
+ LLRect major_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH, 0 );
+
+ mTextBoxNorth = new LLTextBox( "N", major_dir_rect );
+ mTextBoxNorth->setDropshadowVisible( TRUE );
+ addChild( mTextBoxNorth );
+
+ LLColor4 minor_color( 1.f, 1.f, 1.f, .7f );
+
+ mTextBoxEast = new LLTextBox( "E", major_dir_rect );
+ mTextBoxEast->setColor( minor_color );
+ addChild( mTextBoxEast );
+
+ mTextBoxWest = new LLTextBox( "W", major_dir_rect );
+ mTextBoxWest->setColor( minor_color );
+ addChild( mTextBoxWest );
+
+ mTextBoxSouth = new LLTextBox( "S", major_dir_rect );
+ mTextBoxSouth->setColor( minor_color );
+ addChild( mTextBoxSouth );
+
+ LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 );
+
+ mTextBoxSouthEast = new LLTextBox( "SE", minor_dir_rect );
+ mTextBoxSouthEast->setColor( minor_color );
+ addChild( mTextBoxSouthEast );
+
+ mTextBoxNorthEast = new LLTextBox( "NE", minor_dir_rect );
+ mTextBoxNorthEast->setColor( minor_color );
+ addChild( mTextBoxNorthEast );
+
+ mTextBoxSouthWest = new LLTextBox( "SW", minor_dir_rect );
+ mTextBoxSouthWest->setColor( minor_color );
+ addChild( mTextBoxSouthWest );
+
+ mTextBoxNorthWest = new LLTextBox( "NW", minor_dir_rect );
+ mTextBoxNorthWest->setColor( minor_color );
+ addChild( mTextBoxNorthWest );
+}
+
+
+LLWorldMapView::~LLWorldMapView()
+{
+ cleanupTextures();
+}
+
+
+// static
+void LLWorldMapView::cleanupTextures()
+{
+}
+
+
+// static
+void LLWorldMapView::setScale( F32 scale )
+{
+ if (scale != gMapScale)
+ {
+ F32 old_scale = gMapScale;
+
+ gMapScale = scale;
+ if (gMapScale == 0.f)
+ {
+ gMapScale = 0.1f;
+ }
+
+ F32 ratio = (scale / old_scale);
+ sPanX *= ratio;
+ sPanY *= ratio;
+ sTargetPanX = sPanX;
+ sTargetPanY = sPanY;
+
+ sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS;
+ }
+}
+
+
+// static
+void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y )
+{
+ sPanX += delta_x;
+ sPanY += delta_y;
+ sTargetPanX = sPanX;
+ sTargetPanY = sPanY;
+}
+
+
+// static
+void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap )
+{
+ sTargetPanX = (F32)x;
+ sTargetPanY = (F32)y;
+ if (snap)
+ {
+ sPanX = sTargetPanX;
+ sPanY = sTargetPanY;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////
+
+// dumb helper function
+BOOL is_agent_in_region(LLViewerRegion* region, LLSimInfo* info)
+{
+ if((region && info)
+ && (info->mName == region->getName()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLWorldMapView::draw()
+{
+ if (!getVisible() || !gWorldPointer)
+ {
+ return;
+ }
+
+ LLTextureView::clearDebugImages();
+
+ F64 current_time = LLTimer::getElapsedSeconds();
+
+ if (current_time - mAgentCountsUpdateTime > AGENT_COUNTS_UPDATE_TIME)
+ {
+ gWorldMap->sendItemRequest(MAP_ITEM_AGENT_COUNT);
+ mAgentCountsUpdateTime = current_time;
+ }
+
+ mVisibleRegions.clear();
+
+ // animate pan if necessary
+ sPanX = lerp(sPanX, sTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
+ sPanY = lerp(sPanY, sTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
+
+ LLVector3d pos_global;
+ LLVector3 pos_map;
+
+ const S32 width = mRect.getWidth();
+ const S32 height = mRect.getHeight();
+ const S32 half_width = width / 2;
+ const S32 half_height = height / 2;
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+
+ LLGLEnable scissor_test(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(LLRect(0, height, width, 0));
+ {
+ LLGLSNoTexture no_texture;
+
+
+ glMatrixMode(GL_MODELVIEW);
+
+ // Clear the background alpha to 0
+ glColorMask(FALSE, FALSE, FALSE, TRUE);
+ glAlphaFunc(GL_GEQUAL, 0.00f);
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
+ gl_rect_2d(0, height, width, 0);
+ }
+
+ glAlphaFunc(GL_GEQUAL, 0.01f);
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ F32 layer_alpha = 1.f;
+
+ // Draw one image per layer
+ for (U32 layer_idx=0; layer_idx<gWorldMap->mMapLayers[gWorldMap->mCurrentMap].size(); ++layer_idx)
+ {
+ if (!gWorldMap->mMapLayers[gWorldMap->mCurrentMap][layer_idx].LayerDefined)
+ {
+ continue;
+ }
+ LLWorldMapLayer *layer = &gWorldMap->mMapLayers[gWorldMap->mCurrentMap][layer_idx];
+ LLViewerImage *current_image = layer->LayerImage;
+#if 1 || LL_RELEASE_FOR_DOWNLOAD
+ if (current_image->isMissingAsset())
+ {
+ continue; // better to draw nothing than the missing asset image
+ }
+#endif
+
+ LLVector3d origin_global((F64)layer->LayerExtents.mLeft * REGION_WIDTH_METERS, (F64)layer->LayerExtents.mBottom * REGION_WIDTH_METERS, 0.f);
+
+ // Find x and y position relative to camera's center.
+ LLVector3d rel_region_pos = origin_global - camera_global;
+ S32 relative_x = lltrunc((rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale);
+ S32 relative_y = lltrunc((rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale);
+
+ F32 pix_width = gMapScale*(layer->LayerExtents.getWidth() + 1);
+ F32 pix_height = gMapScale*(layer->LayerExtents.getHeight() + 1);
+
+ // When the view isn't panned, 0,0 = center of rectangle
+ F32 bottom = sPanY + half_height + relative_y;
+ F32 left = sPanX + half_width + relative_x;
+ F32 top = bottom + pix_height;
+ F32 right = left + pix_width;
+ F32 pixel_area = pix_width*pix_height;
+ // discard layers that are outside the rectangle
+ // and discard small layers
+ if (top < 0.f ||
+ bottom > height ||
+ right < 0.f ||
+ left > width ||
+ (pixel_area < 4*4))
+ {
+ current_image->setBoostLevel(0);
+ continue;
+ }
+
+ current_image->setBoostLevel(LLViewerImage::BOOST_MAP_LAYER);
+ current_image->setKnownDrawSize(llround(pix_width), llround(pix_height));
+
+#if 1 || LL_RELEASE_FOR_DOWNLOAD
+ if (!current_image->getHasGLTexture())
+ {
+ continue; // better to draw nothing than the default image
+ }
+#endif
+
+ LLTextureView::addDebugImage(current_image);
+
+ // Draw using the texture. If we don't clamp we get artifact at
+ // the edge.
+ LLViewerImage::bindTexture(current_image);
+
+ // Draw map image into RGB
+ //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColorMask(TRUE, TRUE, TRUE, FALSE);
+ glColor4f(1.f, 1.f, 1.f, layer_alpha);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0f, 1.0f);
+ glVertex3f(left, top, -1.0f);
+ glTexCoord2f(0.0f, 0.0f);
+ glVertex3f(left, bottom, -1.0f);
+ glTexCoord2f(1.0f, 0.0f);
+ glVertex3f(right, bottom, -1.0f);
+ glTexCoord2f(1.0f, 1.0f);
+ glVertex3f(right, top, -1.0f);
+ glEnd();
+
+ // draw an alpha of 1 where the sims are visible
+ glColorMask(FALSE, FALSE, FALSE, TRUE);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0f, 1.0f);
+ glVertex2f(left, top);
+ glTexCoord2f(0.0f, 0.0f);
+ glVertex2f(left, bottom);
+ glTexCoord2f(1.0f, 0.0f);
+ glVertex2f(right, bottom);
+ glTexCoord2f(1.0f, 1.0f);
+ glVertex2f(right, top);
+ glEnd();
+ }
+
+ glAlphaFunc(GL_GEQUAL, 0.01f);
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+
+#if 1
+ F32 sim_alpha = 1.f;
+
+ // Draw one image per region, centered on the camera position.
+ for (LLWorldMap::sim_info_map_t::iterator it = gWorldMap->mSimInfoMap.begin();
+ it != gWorldMap->mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ LLSimInfo* info = (*it).second;
+
+ if (info->mCurrentImage.isNull())
+ {
+ info->mCurrentImage = gImageList.getImage(info->mMapImageID[gWorldMap->mCurrentMap], MIPMAP_TRUE, FALSE);
+ }
+ if (info->mOverlayImage.isNull() && info->mMapImageID[2].notNull())
+ {
+ info->mOverlayImage = gImageList.getImage(info->mMapImageID[2], MIPMAP_TRUE, FALSE);
+ info->mOverlayImage->bind(0);
+ info->mOverlayImage->setClamp(TRUE, TRUE);
+ }
+
+ LLViewerImage* simimage = info->mCurrentImage;
+ LLViewerImage* overlayimage = info->mOverlayImage;
+
+ if (gMapScale < 8.f)
+ {
+ simimage->setBoostLevel(0);
+ if (overlayimage) overlayimage->setBoostLevel(0);
+ continue;
+ }
+
+ LLVector3d origin_global = from_region_handle(handle);
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+
+ // Find x and y position relative to camera's center.
+ LLVector3d rel_region_pos = origin_global - camera_global;
+ S32 relative_x = lltrunc((rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale);
+ S32 relative_y = lltrunc((rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale);
+
+ // When the view isn't panned, 0,0 = center of rectangle
+ F32 bottom = sPanY + half_height + relative_y;
+ F32 left = sPanX + half_width + relative_x;
+ F32 top = bottom + gMapScale ;
+ F32 right = left + gMapScale ;
+
+ // Switch to world map texture (if available for this region) if either:
+ // 1. Tiles are zoomed out small enough, or
+ // 2. Sim's texture has not been loaded yet
+ F32 map_scale_cutoff = SIM_MAP_SCALE;
+ if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) > 0)
+ {
+ map_scale_cutoff = SIM_NULL_MAP_SCALE;
+ }
+
+ info->mShowAgentLocations = (gMapScale >= SIM_MAP_AGENT_SCALE);
+
+ bool sim_visible =
+ (gMapScale >= map_scale_cutoff) &&
+ (simimage->getHasGLTexture());
+
+ if (sim_visible)
+ {
+ // Fade in
+ if (info->mAlpha < 0.0f)
+ info->mAlpha = 1.f; // don't fade initially
+ else
+ info->mAlpha = lerp(info->mAlpha, 1.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+ else
+ {
+ // Fade out
+ if (info->mAlpha < 0.0f)
+ info->mAlpha = 0.f; // don't fade initially
+ else
+ info->mAlpha = lerp(info->mAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ // discard regions that are outside the rectangle
+ // and discard small regions
+ if (top < 0.f ||
+ bottom > height ||
+ right < 0.f ||
+ left > width )
+ {
+ simimage->setBoostLevel(0);
+ if (overlayimage) overlayimage->setBoostLevel(0);
+ continue;
+ }
+
+ mVisibleRegions.push_back(handle);
+ // See if the agents need updating
+ if (current_time - info->mAgentsUpdateTime > AGENTS_UPDATE_TIME)
+ {
+ gWorldMap->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, info->mHandle);
+ info->mAgentsUpdateTime = current_time;
+ }
+
+ // Bias the priority escalation for images nearer
+ LLVector3d center_global = origin_global;
+ center_global.mdV[VX] += 128.0;
+ center_global.mdV[VY] += 128.0;
+
+ S32 draw_size = llround(gMapScale);
+ simimage->setBoostLevel(LLViewerImage::BOOST_MAP);
+ simimage->setKnownDrawSize(draw_size, draw_size);
+
+ if (overlayimage)
+ {
+ overlayimage->setBoostLevel(LLViewerImage::BOOST_MAP);
+ overlayimage->setKnownDrawSize(draw_size, draw_size);
+ }
+
+ LLTextureView::addDebugImage(simimage);
+
+ if (sim_visible && info->mAlpha > 0.001f)
+ {
+ // Draw using the texture. If we don't clamp we get artifact at
+ // the edge.
+ LLGLSUIDefault gls_ui;
+ LLViewerImage::bindTexture(simimage);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ F32 alpha = sim_alpha * info->mAlpha;
+ glColor4f(1.f, 1.0f, 1.0f, alpha);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex3f(left, top, 0.f);
+ glTexCoord2f(0.f, 0.f);
+ glVertex3f(left, bottom, 0.f);
+ glTexCoord2f(1.f, 0.f);
+ glVertex3f(right, bottom, 0.f);
+ glTexCoord2f(1.f, 1.f);
+ glVertex3f(right, top, 0.f);
+ glEnd();
+
+ if (gSavedSettings.getBOOL("MapShowLandForSale") && overlayimage && overlayimage->getHasGLTexture())
+ {
+ LLViewerImage::bindTexture(overlayimage);
+ glColor4f(1.f, 1.f, 1.f, alpha);
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex3f(left, top, -0.5f);
+ glTexCoord2f(0.f, 0.f);
+ glVertex3f(left, bottom, -0.5f);
+ glTexCoord2f(1.f, 0.f);
+ glVertex3f(right, bottom, -0.5f);
+ glTexCoord2f(1.f, 1.f);
+ glVertex3f(right, top, -0.5f);
+ glEnd();
+ }
+
+ if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) == 0)
+ {
+ // draw an alpha of 1 where the sims are visible (except NULL sims)
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glColorMask(FALSE, FALSE, FALSE, TRUE);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+
+ LLGLSNoTexture gls_no_texture;
+ glBegin(GL_QUADS);
+ glVertex2f(left, top);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glVertex2f(right, top);
+ glEnd();
+
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ }
+ }
+
+ if (info->mAccess == SIM_ACCESS_DOWN)
+ {
+ // Draw a transparent red square over down sims
+ glBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA);
+ glColor4f(0.2f, 0.0f, 0.0f, 0.4f);
+
+ LLGLSNoTexture gls_no_texture;
+ glBegin(GL_QUADS);
+ glVertex2f(left, top);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glVertex2f(right, top);
+ glEnd();
+ }
+
+ // If this is mature, and you are not, draw a line across it
+ if (info->mAccess != SIM_ACCESS_DOWN && info->mAccess > gAgent.mAccess)
+ {
+ glBlendFunc(GL_DST_ALPHA, GL_ZERO);
+
+ LLGLSNoTexture gls_no_texture;
+ glColor3f(1.f, 0.f, 0.f);
+ glBegin(GL_LINES);
+ glVertex2f(left, top);
+ glVertex2f(right, bottom);
+ glVertex2f(left, bottom);
+ glVertex2f(right, top);
+ glEnd();
+ }
+
+ // Draw the region name in the lower left corner
+ LLFontGL* font = LLFontGL::sSansSerifSmall;
+
+ char mesg[MAX_STRING];
+ if (gMapScale < sThresholdA)
+ {
+ mesg[0] = '\0';
+ }
+ else if (gMapScale < sThresholdB)
+ {
+ //sprintf(mesg, "%d", info->mAgents);
+ mesg[0] = '\0';
+ }
+ else
+ {
+ //sprintf(mesg, "%d / %s (%s)",
+ // info->mAgents,
+ // info->mName.c_str(),
+ // LLViewerRegion::accessToShortString(info->mAccess) );
+ if (info->mAccess == SIM_ACCESS_DOWN)
+ {
+ sprintf(mesg, "%s (Offline)", info->mName.c_str());
+ }
+ else
+ {
+ sprintf(mesg, "%s", info->mName.c_str());
+ }
+ }
+
+ if (mesg[0] != '\0')
+ {
+ font->renderUTF8(
+ mesg, 0,
+ llfloor(left + 3),
+ llfloor(bottom + 2),
+ LLColor4::white,
+ LLFontGL::LEFT,
+ LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+ }
+ }
+#endif
+
+
+#if 1
+ // Draw background rectangle
+ LLGLSUIDefault gls_ui;
+ {
+ LLGLSNoTexture gls_no_texture;
+ glAlphaFunc(GL_GEQUAL, 0.0f);
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
+ glColor4fv( mBackgroundColor.mV );
+ gl_rect_2d(0, height, width, 0);
+ }
+
+ glAlphaFunc(GL_GEQUAL, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Infohubs
+ // Draw under avatar so you can find yourself.
+ if (gSavedSettings.getBOOL("MapShowInfohubs")) //(gMapScale >= sThresholdB)
+ {
+ S32 infohub_icon_size = (S32)sInfohubImage->getWidth();
+ for (U32 infohub=0; infohub<gWorldMap->mInfohubs.size(); ++infohub)
+ {
+ LLItemInfo *infohub_info = &gWorldMap->mInfohubs[infohub];
+
+ pos_map = globalPosToView(infohub_info->mPosGlobal);
+ gl_draw_image(llround(pos_map.mV[VX])-infohub_icon_size/2,
+ llround(pos_map.mV[VY])-infohub_icon_size/2,
+ sInfohubImage,
+ LLColor4::white);
+ }
+ }
+
+ // Telehubs
+ // Draw under avatar so you can find yourself.
+ if (gSavedSettings.getBOOL("MapShowTelehubs")) //(gMapScale >= sThresholdB)
+ {
+ S32 telehub_icon_size = (S32)sTelehubImage->getWidth();
+ for (U32 telehub=0; telehub<gWorldMap->mTelehubs.size(); ++telehub)
+ {
+ LLItemInfo *telehub_info = &gWorldMap->mTelehubs[telehub];
+
+ pos_map = globalPosToView(telehub_info->mPosGlobal);
+ gl_draw_image(llround(pos_map.mV[VX])-telehub_icon_size/2,
+ llround(pos_map.mV[VY])-telehub_icon_size/2,
+ sTelehubImage,
+ LLColor4::white);
+ }
+ }
+
+ // Home
+ // Always Draw
+ if (1) //(gMapScale >= sThresholdB)
+ {
+ S32 home_icon_size = (S32)sHomeImage->getWidth();
+ if (gAgent.getHomePosGlobal(&pos_global))
+ {
+ pos_map = globalPosToView(pos_global);
+ gl_draw_image(llround(pos_map.mV[VX])-home_icon_size/2,
+ llround(pos_map.mV[VY])-home_icon_size/2,
+ sHomeImage,
+ LLColor4::white);
+ }
+ }
+
+ // Draw all these under the avatar, so you can find yourself
+
+ if (gSavedSettings.getBOOL("MapShowLandForSale"))
+ {
+ drawGenericItems(gWorldMap->mLandForSale, sForSaleImage);
+ }
+
+ if (gSavedSettings.getBOOL("MapShowClassifieds"))
+ {
+ drawGenericItems(gWorldMap->mClassifieds, sClassifiedsImage);
+ }
+
+ if (gSavedSettings.getBOOL("MapShowPopular"))
+ {
+ drawGenericItems(gWorldMap->mPopular, sPopularImage);
+ }
+
+ if (gSavedSettings.getBOOL("MapShowEvents"))
+ {
+ drawEvents();
+ }
+
+ // Your avatar.
+ // Draw avatar position, always, and underneath other avatars
+ pos_global = gAgent.getPositionGlobal();
+ pos_map = globalPosToView(pos_global);
+ gl_draw_image(llround(pos_map.mV[VX])-8,
+ llround(pos_map.mV[VY])-8,
+ sAvatarLargeImage,
+ LLColor4::white);
+
+ if (!pointInView(llround(pos_map.mV[VX]), llround(pos_map.mV[VY])))
+ {
+ drawTracking(pos_global,
+ lerp(LLColor4::yellow, LLColor4::orange, 0.4f),
+ TRUE,
+ "You are here",
+ "",
+ llround(LLFontGL::sSansSerifSmall->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking
+ }
+
+ drawFrustum();
+
+ // Draw icons for the avatars in each region.
+ // Draw after avatar so you can see nearby people.
+ if (gSavedSettings.getBOOL("MapShowPeople"))
+ {
+ drawAgents();
+ }
+
+ //-----------------------------------------------------------------------
+
+ // Always draw tracking information
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if ( LLTracker::TRACKING_AVATAR == tracking_status )
+ {
+ drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor, TRUE, LLTracker::getLabel(), "" );
+ }
+ else if ( LLTracker::TRACKING_LANDMARK == tracking_status
+ || LLTracker::TRACKING_LOCATION == tracking_status )
+ {
+ // While fetching landmarks, will have 0,0,0 location for a while,
+ // so don't draw. JC
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ if (!pos_global.isExactlyZero())
+ {
+ drawTracking( pos_global, gTrackColor, TRUE, LLTracker::getLabel(), LLTracker::getToolTip() );
+ }
+ }
+ else if (gWorldMap->mIsTrackingUnknownLocation)
+ {
+ if (gWorldMap->mInvalidLocation)
+ {
+ // We know this location to be invalid
+ LLColor4 loading_color(0.0, 0.5, 1.0, 1.0);
+ drawTracking( gWorldMap->mUnknownLocation, loading_color, TRUE, "Invalid Location", "");
+ }
+ else
+ {
+ double value = fmod(current_time, 2);
+ value = 0.5 + 0.5*cos(value * 3.14159f);
+ LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
+ drawTracking( gWorldMap->mUnknownLocation, loading_color, TRUE, "Loading...", "");
+ }
+ }
+#endif
+
+ // turn off the scissor
+ LLGLDisable no_scissor(GL_SCISSOR_TEST);
+
+ updateDirections();
+
+ LLView::draw();
+
+ updateVisibleBlocks();
+}
+
+void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, LLPointer<LLViewerImage> image)
+{
+ LLWorldMap::item_info_list_t::const_iterator e;
+ for (e = items.begin(); e != items.end(); ++e)
+ {
+ const LLItemInfo& info = *e;
+
+ drawGenericItem(info, image);
+ }
+}
+
+void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLPointer<LLViewerImage> image)
+{
+ S32 half_width = image->getWidth()/2;
+ S32 half_height = image->getHeight()/2;
+
+ LLVector3 pos_map = globalPosToView( item.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - half_width,
+ llround(pos_map.mV[VY]) - half_height,
+ image,
+ LLColor4::white);
+}
+
+void LLWorldMapView::drawAgents()
+{
+ //S32 half_width = sPopularImage->getWidth()/2;
+ //S32 half_height = sPopularImage->getHeight()/2;
+
+ F32 agents_scale = (gMapScale * 0.9f) / 256.f;
+
+ for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
+ {
+ U64 handle = *iter;
+ LLSimInfo* siminfo = gWorldMap->simInfoFromHandle(handle);
+ if (siminfo && (siminfo->mAccess == SIM_ACCESS_DOWN))
+ {
+ continue;
+ }
+ LLWorldMap::agent_list_map_t::iterator countsiter = gWorldMap->mAgentLocationsMap.find(handle);
+ if (siminfo && siminfo->mShowAgentLocations && countsiter != gWorldMap->mAgentLocationsMap.end())
+ {
+ // Show Individual agents
+ LLWorldMap::item_info_list_t& agentcounts = countsiter->second;
+ S32 sim_agent_count = 0;
+ for (LLWorldMap::item_info_list_t::iterator iter = agentcounts.begin();
+ iter != agentcounts.end(); ++iter)
+ {
+ const LLItemInfo& info = *iter;
+ LLVector3 pos_map = globalPosToView( info.mPosGlobal );
+ S32 agent_count = info.mExtra;
+ if (agent_count > 0)
+ {
+ sim_agent_count += agent_count;
+ F32 y = 0;
+ for (S32 cur_agent = 0; cur_agent < agent_count; cur_agent++)
+ {
+ gl_draw_image(llround(pos_map.mV[VX]) - 4,
+ llround(pos_map.mV[VY] + y) - 4,
+ sAvatarSmallImage,
+ LLColor4::white );
+ // move up a bit
+ y += 3.f;
+ }
+ }
+ }
+ gWorldMap->mNumAgents[handle] = sim_agent_count; // override mNumAgents for this sim
+ }
+ else
+ {
+ // Show agent 'stack' at center of sim
+ S32 num_agents = gWorldMap->mNumAgents[handle];
+ if (num_agents > 0)
+ {
+ LLVector3d region_pos = from_region_handle(handle);
+ region_pos[VX] += REGION_WIDTH_METERS * .5f;
+ region_pos[VY] += REGION_WIDTH_METERS * .5f;
+ LLVector3 pos_map = globalPosToView(region_pos);
+ // Reduce the stack size as you zoom out - always display at lease one agent where there is one or more
+ S32 agent_count = (S32)(((num_agents-1) * agents_scale + (num_agents-1) * 0.1f)+.1f) + 1;
+ S32 y = 0;
+ S32 cur_agent;
+ for (cur_agent = 0; cur_agent < agent_count; cur_agent++)
+ {
+ gl_draw_image(
+ llround(pos_map.mV[VX]) - 4,
+ llround(pos_map.mV[VY]) + y - 4,
+ sAvatarSmallImage,
+ LLColor4::white );
+ // move up a bit
+ y += 3;
+ }
+ }
+ }
+ }
+}
+
+
+void LLWorldMapView::drawEvents()
+{
+ BOOL show_mature = gSavedSettings.getBOOL("ShowMatureEvents");
+
+ // Non-selected events
+ // Draw under avatar so you can find yourself.
+ LLWorldMap::item_info_list_t::const_iterator e;
+ for (e = gWorldMap->mPGEvents.begin(); e != gWorldMap->mPGEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (!event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventImage->getHeight()/2,
+ sEventImage,
+ LLColor4::white);
+ }
+ }
+ if (show_mature)
+ {
+ for (e = gWorldMap->mMatureEvents.begin(); e != gWorldMap->mMatureEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (!event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventMatureImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventMatureImage->getHeight()/2,
+ sEventMatureImage,
+ LLColor4::white);
+ }
+ }
+ }
+
+ // Selected events
+ // Draw under avatar so you can find yourself.
+ for (e = gWorldMap->mPGEvents.begin(); e != gWorldMap->mPGEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventImage->getHeight()/2,
+ sEventImage,
+ LLColor4::white);
+
+ //drawIconName(
+ // pos_map.mV[VX],
+ // pos_map.mV[VY],
+ // gEventColor,
+ // event.mToolTip.c_str(),
+ // event.mName.c_str() );
+ }
+ }
+ if (show_mature)
+ {
+ for (e = gWorldMap->mMatureEvents.begin(); e != gWorldMap->mMatureEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventMatureImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventMatureImage->getHeight()/2,
+ sEventMatureImage,
+ LLColor4::white);
+
+ //drawIconName(
+ // pos_map.mV[VX],
+ // pos_map.mV[VY],
+ // gEventColor,
+ // event.mToolTip.c_str(),
+ // event.mName.c_str() );
+ }
+ }
+ }
+}
+
+void LLWorldMapView::drawDots()
+{
+
+}
+
+
+void LLWorldMapView::drawFrustum()
+{
+ // Draw frustum
+ F32 meters_to_pixels = gMapScale/ REGION_WIDTH_METERS;
+
+ F32 horiz_fov = gCamera->getView() * gCamera->getAspect();
+ F32 far_clip_meters = gCamera->getFar();
+ F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
+
+ F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
+ F32 half_width_pixels = half_width_meters * meters_to_pixels;
+
+ F32 ctr_x = mRect.getWidth() * 0.5f + sPanX;
+ F32 ctr_y = mRect.getHeight() * 0.5f + sPanY;
+
+ LLGLSNoTexture gls_no_texture;
+
+ // Since we don't rotate the map, we have to rotate the frustum.
+ glPushMatrix();
+ glTranslatef( ctr_x, ctr_y, 0 );
+ glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
+
+ // Draw triangle with more alpha in far pixels to make it
+ // fade out in distance.
+ glBegin( GL_TRIANGLES );
+ glColor4f(1.f, 1.f, 1.f, 0.25f);
+ glVertex2f( 0, 0 );
+
+ glColor4f(1.f, 1.f, 1.f, 0.02f);
+ glVertex2f( -half_width_pixels, far_clip_pixels );
+
+ glColor4f(1.f, 1.f, 1.f, 0.02f);
+ glVertex2f( half_width_pixels, far_clip_pixels );
+ glEnd();
+ glPopMatrix();
+}
+
+
+LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos )
+{
+ LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal();
+ LLVector3 pos_local;
+ pos_local.setVec(relative_pos_global); // convert to floats from doubles
+
+ pos_local.mV[VX] *= sPixelsPerMeter;
+ pos_local.mV[VY] *= sPixelsPerMeter;
+ // leave Z component in meters
+
+
+ pos_local.mV[VX] += mRect.getWidth() / 2 + sPanX;
+ pos_local.mV[VY] += mRect.getHeight() / 2 + sPanY;
+
+ return pos_local;
+}
+
+
+void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& color,
+ BOOL draw_arrow, LLString label, LLString tooltip, S32 vert_offset )
+{
+ LLVector3 pos_local = globalPosToView( pos_global );
+ S32 x = llround( pos_local.mV[VX] );
+ S32 y = llround( pos_local.mV[VY] );
+ LLFontGL* font = LLFontGL::sSansSerifSmall;
+ S32 text_x = x;
+ S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight());
+
+ BOOL is_in_window = true;
+
+ if( x < 0
+ || y < 0
+ || x >= mRect.getWidth()
+ || y >= mRect.getHeight() )
+ {
+ if (draw_arrow)
+ {
+ drawTrackingCircle( mRect, x, y, color, 3, 15 );
+ drawTrackingArrow( mRect, x, y, color );
+ text_x = sTrackingArrowX;
+ text_y = sTrackingArrowY;
+ }
+ is_in_window = false;
+ }
+ else if (LLTracker::getTrackingStatus() == LLTracker::TRACKING_LOCATION &&
+ LLTracker::getTrackedLocationType() != LLTracker::LOCATION_NOTHING)
+ {
+ drawTrackingCircle( mRect, x, y, color, 3, 15 );
+ }
+ else
+ {
+ // Draw, but without relative Z
+ gl_draw_image(x - sTrackCircleImage->getWidth()/2,
+ y - sTrackCircleImage->getHeight()/2,
+ sTrackCircleImage,
+ color);
+ }
+
+ // clamp text position to on-screen
+ const S32 TEXT_PADDING = DEFAULT_TRACKING_ARROW_SIZE + 2;
+ S32 half_text_width = llfloor(font->getWidthF32(label) * 0.5f);
+ text_x = llclamp(text_x, half_text_width + TEXT_PADDING, mRect.getWidth() - half_text_width - TEXT_PADDING);
+ text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, mRect.getHeight() - llround(font->getLineHeight()) - TEXT_PADDING - vert_offset);
+
+ if (label != "")
+ {
+ font->renderUTF8(
+ label, 0,
+ text_x,
+ text_y,
+ LLColor4::white, LLFontGL::HCENTER,
+ LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
+
+ if (tooltip != "")
+ {
+ text_y -= (S32)font->getLineHeight();
+
+ font->renderUTF8(
+ tooltip, 0,
+ text_x,
+ text_y,
+ LLColor4::white, LLFontGL::HCENTER,
+ LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
+ }
+ }
+}
+
+// If you change this, then you need to change LLTracker::getTrackedPositionGlobal() as well
+LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y )
+{
+ x -= llfloor((mRect.getWidth() / 2 + sPanX));
+ y -= llfloor((mRect.getHeight() / 2 + sPanY));
+
+ LLVector3 pos_local( (F32)x, (F32)y, 0.f );
+
+ pos_local *= ( REGION_WIDTH_METERS / gMapScale );
+
+ LLVector3d pos_global;
+ pos_global.setVec( pos_local );
+ pos_global += gAgent.getCameraPositionGlobal();
+ if(gAgent.isGodlike())
+ {
+ pos_global.mdV[VZ] = GODLY_TELEPORT_HEIGHT; // Godly height should always be 200.
+ }
+ else
+ {
+ pos_global.mdV[VZ] = gAgent.getPositionAgent().mV[VZ]; // Want agent's height, not camera's
+ }
+
+ return pos_global;
+}
+
+
+BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen )
+{
+ if( !getVisible() || !pointInView( x, y ) )
+ {
+ return FALSE;
+ }
+
+ LLVector3d pos_global = viewPosToGlobal(x, y);
+
+ LLSimInfo* info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if (info)
+ {
+ LLViewerRegion *region = gAgent.getRegion();
+
+ std::string message =
+ llformat("%s (%s)",
+ info->mName.c_str(),
+ LLViewerRegion::accessToString(info->mAccess));
+
+ if (info->mAccess != SIM_ACCESS_DOWN)
+ {
+ S32 agent_count = gWorldMap->mNumAgents[info->mHandle];
+ if (region && region->getHandle() == info->mHandle)
+ {
+ ++agent_count; // Bump by 1 if we're here
+ }
+
+ // We may not have an agent count when the map is really
+ // zoomed out, so don't display anything about the count. JC
+ if (agent_count > 0)
+ {
+ message += llformat("\n%d ", agent_count);
+
+ if (agent_count == 1)
+ {
+ message += "person";
+ }
+ else
+ {
+ message += "people";
+ }
+ }
+ }
+ msg.assign( message );
+
+ // Optionally show region flags
+ std::string region_flags = LLViewerRegion::regionFlagsToString(info->mRegionFlags);
+
+ if (!region_flags.empty())
+ {
+ msg += '\n';
+ msg += region_flags;
+ }
+
+ S32 SLOP = 4;
+ localPointToScreen(
+ x - SLOP, y - SLOP,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
+ sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
+ }
+ return TRUE;
+}
+
+// Pass relative Z of 0 to draw at same level.
+// static
+void LLWorldMapView::drawAvatar(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z,
+ F32 dot_radius)
+{
+ F32 left = x_pixels - dot_radius;
+ F32 right = x_pixels + dot_radius;
+ F32 center = (left + right) * 0.5f;
+ F32 top = y_pixels + dot_radius;
+ F32 bottom = y_pixels - dot_radius;
+
+ const F32 HEIGHT_THRESHOLD = 7.f;
+
+ if (relative_z > HEIGHT_THRESHOLD)
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(left, top);
+ glVertex2f(right, top);
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+ else if (relative_z > -HEIGHT_THRESHOLD)
+ {
+ gl_draw_image( llround(x_pixels) - sAvatarSmallImage->getWidth()/2,
+ llround(y_pixels) - sAvatarSmallImage->getHeight()/2,
+ sAvatarSmallImage,
+ color);
+ }
+ else
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+}
+
+// Pass relative Z of 0 to draw at same level.
+// static
+void LLWorldMapView::drawTrackingDot( F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z,
+ F32 dot_radius)
+{
+ F32 left = x_pixels - dot_radius;
+ F32 right = x_pixels + dot_radius;
+ F32 center = (left + right) * 0.5f;
+ F32 top = y_pixels + dot_radius;
+ F32 bottom = y_pixels - dot_radius;
+
+ const F32 HEIGHT_THRESHOLD = 7.f;
+
+ if (relative_z > HEIGHT_THRESHOLD)
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(left, top);
+ glVertex2f(right, top);
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+ else if (relative_z > -HEIGHT_THRESHOLD)
+ {
+ gl_draw_image( llround(x_pixels) - sAvatarSmallImage->getWidth()/2,
+ llround(y_pixels) - sAvatarSmallImage->getHeight()/2,
+ sTrackCircleImage,
+ color);
+ }
+ else
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+}
+
+
+// Pass relative Z of 0 to draw at same level.
+// static
+void LLWorldMapView::drawIconName(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ const std::string& first_line,
+ const std::string& second_line)
+{
+ const S32 VERT_PAD = 8;
+ S32 text_x = llround(x_pixels);
+ S32 text_y = llround(y_pixels
+ - BIG_DOT_RADIUS
+ - VERT_PAD);
+
+ // render text
+ LLFontGL::sSansSerif->renderUTF8(first_line, 0,
+ text_x,
+ text_y,
+ color,
+ LLFontGL::HCENTER,
+ LLFontGL::TOP,
+ LLFontGL::DROP_SHADOW);
+
+ text_y -= llround(LLFontGL::sSansSerif->getLineHeight());
+
+ // render text
+ LLFontGL::sSansSerif->renderUTF8(second_line, 0,
+ text_x,
+ text_y,
+ color,
+ LLFontGL::HCENTER,
+ LLFontGL::TOP,
+ LLFontGL::DROP_SHADOW);
+}
+
+
+//static
+void LLWorldMapView::drawTrackingCircle( const LLRect& rect, S32 x, S32 y, const LLColor4& color, S32 min_thickness, S32 overlap )
+{
+ F32 start_theta = 0.f;
+ F32 end_theta = F_TWO_PI;
+ F32 x_delta = 0.f;
+ F32 y_delta = 0.f;
+
+ if (x < 0)
+ {
+ x_delta = 0.f - (F32)x;
+ start_theta = F_PI + F_PI_BY_TWO;
+ end_theta = F_TWO_PI + F_PI_BY_TWO;
+ }
+ else if (x > rect.getWidth())
+ {
+ x_delta = (F32)(x - rect.getWidth());
+ start_theta = F_PI_BY_TWO;
+ end_theta = F_PI + F_PI_BY_TWO;
+ }
+
+ if (y < 0)
+ {
+ y_delta = 0.f - (F32)y;
+ if (x < 0)
+ {
+ start_theta = 0.f;
+ end_theta = F_PI_BY_TWO;
+ }
+ else if (x > rect.getWidth())
+ {
+ start_theta = F_PI_BY_TWO;
+ end_theta = F_PI;
+ }
+ else
+ {
+ start_theta = 0.f;
+ end_theta = F_PI;
+ }
+ }
+ else if (y > rect.getHeight())
+ {
+ y_delta = (F32)(y - rect.getHeight());
+ if (x < 0)
+ {
+ start_theta = F_PI + F_PI_BY_TWO;
+ end_theta = F_TWO_PI;
+ }
+ else if (x > rect.getWidth())
+ {
+ start_theta = F_PI;
+ end_theta = F_PI + F_PI_BY_TWO;
+ }
+ else
+ {
+ start_theta = F_PI;
+ end_theta = F_TWO_PI;
+ }
+ }
+
+ F32 distance = sqrtf(x_delta * x_delta + y_delta * y_delta);
+
+ distance = llmax(0.1f, distance);
+
+ F32 outer_radius = distance + (1.f + (9.f * sqrtf(x_delta * y_delta) / distance)) * (F32)overlap;
+ F32 inner_radius = outer_radius - (F32)min_thickness;
+
+ F32 angle_adjust_x = asin(x_delta / outer_radius);
+ F32 angle_adjust_y = asin(y_delta / outer_radius);
+
+ if (angle_adjust_x)
+ {
+ if (angle_adjust_y)
+ {
+ F32 angle_adjust = llmin(angle_adjust_x, angle_adjust_y);
+ start_theta += angle_adjust;
+ end_theta -= angle_adjust;
+ }
+ else
+ {
+ start_theta += angle_adjust_x;
+ end_theta -= angle_adjust_x;
+ }
+ }
+ else if (angle_adjust_y)
+ {
+ start_theta += angle_adjust_y;
+ end_theta -= angle_adjust_y;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef((F32)x, (F32)y, 0.f);
+ gl_washer_segment_2d(inner_radius, outer_radius, start_theta, end_theta, 40, color, color);
+ glPopMatrix();
+
+}
+
+// static
+void LLWorldMapView::drawTrackingArrow(const LLRect& rect, S32 x, S32 y,
+ const LLColor4& color,
+ S32 arrow_size)
+{
+ F32 x_center = (F32)rect.getWidth() / 2.f;
+ F32 y_center = (F32)rect.getHeight() / 2.f;
+
+ F32 x_clamped = (F32)llclamp( x, 0, rect.getWidth() - arrow_size );
+ F32 y_clamped = (F32)llclamp( y, 0, rect.getHeight() - arrow_size );
+
+ F32 slope = (F32)(y - y_center) / (F32)(x - x_center);
+ F32 window_ratio = (F32)rect.getHeight() / (F32)rect.getWidth();
+
+ if (llabs(slope) > window_ratio && y_clamped != (F32)y)
+ {
+ // clamp by y
+ x_clamped = (y_clamped - y_center) / slope + x_center;
+ // adjust for arrow size
+ x_clamped = llclamp(x_clamped , 0.f, (F32)(rect.getWidth() - arrow_size) );
+ }
+ else if (x_clamped != (F32)x)
+ {
+ // clamp by x
+ y_clamped = (x_clamped - x_center) * slope + y_center;
+ // adjust for arrow size
+ y_clamped = llclamp( y_clamped, 0.f, (F32)(rect.getHeight() - arrow_size) );
+ }
+
+ //FIXME: deal with non-square window properly
+ S32 half_arrow_size = (S32) (0.5f * arrow_size);
+
+ F32 angle = atan2( y + half_arrow_size - y_center, x + half_arrow_size - x_center);
+
+ sTrackingArrowX = llfloor(x_clamped);
+ sTrackingArrowY = llfloor(y_clamped);
+
+ gl_draw_scaled_rotated_image(
+ sTrackingArrowX,
+ sTrackingArrowY,
+ arrow_size, arrow_size,
+ RAD_TO_DEG * angle,
+ sTrackArrowImage,
+ color);
+}
+
+void LLWorldMapView::setDirectionPos( LLTextBox* text_box, F32 rotation )
+{
+ // Rotation is in radians.
+ // Rotation of 0 means x = 1, y = 0 on the unit circle.
+
+
+ F32 map_half_height = mRect.getHeight() * 0.5f;
+ F32 map_half_width = mRect.getWidth() * 0.5f;
+ F32 text_half_height = text_box->getRect().getHeight() * 0.5f;
+ F32 text_half_width = text_box->getRect().getWidth() * 0.5f;
+ F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width );
+
+ text_box->setOrigin(
+ llround(map_half_width - text_half_width + radius * cos( rotation )),
+ llround(map_half_height - text_half_height + radius * sin( rotation )) );
+}
+
+
+void LLWorldMapView::updateDirections()
+{
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+
+ S32 text_height = mTextBoxNorth->getRect().getHeight();
+ S32 text_width = mTextBoxNorth->getRect().getWidth();
+
+ const S32 PAD = 2;
+ S32 top = height - text_height - PAD;
+ S32 left = PAD*2;
+ S32 bottom = PAD;
+ S32 right = width - text_width - PAD;
+ S32 center_x = width/2 - text_width/2;
+ S32 center_y = height/2 - text_height/2;
+
+ mTextBoxNorth->setOrigin( center_x, top );
+ mTextBoxEast->setOrigin( right, center_y );
+ mTextBoxSouth->setOrigin( center_x, bottom );
+ mTextBoxWest->setOrigin( left, center_y );
+
+ // These have wider text boxes
+ text_width = mTextBoxNorthWest->getRect().getWidth();
+ right = width - text_width - PAD;
+
+ mTextBoxNorthWest->setOrigin(left, top);
+ mTextBoxNorthEast->setOrigin(right, top);
+ mTextBoxSouthWest->setOrigin(left, bottom);
+ mTextBoxSouthEast->setOrigin(right, bottom);
+
+// S32 hint_width = mTextBoxScrollHint->getRect().getWidth();
+// mTextBoxScrollHint->setOrigin( width - hint_width - text_width - 2 * PAD,
+// PAD * 2 + text_height );
+}
+
+
+void LLWorldMapView::reshape( S32 width, S32 height, BOOL called_from_parent )
+{
+ LLView::reshape( width, height, called_from_parent );
+}
+
+bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track)
+{
+ LLVector3 pos_view = globalPosToView(item.mPosGlobal);
+ S32 item_x = llround(pos_view.mV[VX]);
+ S32 item_y = llround(pos_view.mV[VY]);
+
+ if (x < item_x - BIG_DOT_RADIUS) return false;
+ if (x > item_x + BIG_DOT_RADIUS) return false;
+ if (y < item_y - BIG_DOT_RADIUS) return false;
+ if (y > item_y + BIG_DOT_RADIUS) return false;
+
+ LLSimInfo* sim_info = gWorldMap->simInfoFromHandle(item.mRegionHandle);
+ if (sim_info)
+ {
+ if (track)
+ {
+ gFloaterWorldMap->trackLocation(item.mPosGlobal);
+ }
+ }
+
+ if (track)
+ {
+ gFloaterWorldMap->trackGenericItem(item);
+ }
+
+ item.mSelected = TRUE;
+ *id = item.mID;
+
+ return true;
+}
+
+// Handle a click, which might be on a dot
+void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask,
+ S32* hit_type,
+ LLUUID* id)
+{
+ LLVector3d pos_global = viewPosToGlobal(x, y);
+
+ // *HACK: Adjust Z values automatically for liaisons & gods so
+ // we swoop down when they click on the map. Sadly, the P2P
+ // branch does not pay attention to this value; however, the
+ // Distributed Messaging branch honors kt.
+ if(gAgent.isGodlike())
+ {
+ pos_global.mdV[VZ] = 200.0;
+ }
+
+ *hit_type = 0; // hit nothing
+
+ gWorldMap->mIsTrackingUnknownLocation = FALSE;
+ gWorldMap->mIsTrackingDoubleClick = FALSE;
+ gWorldMap->mIsTrackingCommit = FALSE;
+
+ LLWorldMap::item_info_list_t::iterator it;
+
+ // clear old selected stuff
+ for (it = gWorldMap->mPGEvents.begin(); it != gWorldMap->mPGEvents.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mMatureEvents.begin(); it != gWorldMap->mMatureEvents.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mPopular.begin(); it != gWorldMap->mPopular.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mLandForSale.begin(); it != gWorldMap->mLandForSale.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mClassifieds.begin(); it != gWorldMap->mClassifieds.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+
+ // Select event you clicked on
+ if (gSavedSettings.getBOOL("MapShowEvents"))
+ {
+ for (it = gWorldMap->mPGEvents.begin(); it != gWorldMap->mPGEvents.end(); ++it)
+ {
+ LLItemInfo& event = *it;
+
+ if (checkItemHit(x, y, event, id, false))
+ {
+ *hit_type = MAP_ITEM_PG_EVENT;
+ mItemPicked = TRUE;
+ gFloaterWorldMap->trackEvent(event);
+ return;
+ }
+ }
+ if (gSavedSettings.getBOOL("ShowMatureEvents"))
+ {
+ for (it = gWorldMap->mMatureEvents.begin(); it != gWorldMap->mMatureEvents.end(); ++it)
+ {
+ LLItemInfo& event = *it;
+
+ if (checkItemHit(x, y, event, id, false))
+ {
+ *hit_type = MAP_ITEM_MATURE_EVENT;
+ mItemPicked = TRUE;
+ gFloaterWorldMap->trackEvent(event);
+ return;
+ }
+ }
+ }
+ }
+
+ if (gSavedSettings.getBOOL("MapShowPopular"))
+ {
+ for (it = gWorldMap->mPopular.begin(); it != gWorldMap->mPopular.end(); ++it)
+ {
+ LLItemInfo& popular = *it;
+
+ if (checkItemHit(x, y, popular, id, true))
+ {
+ *hit_type = MAP_ITEM_POPULAR;
+ mItemPicked = TRUE;
+ return;
+ }
+ }
+ }
+
+ if (gSavedSettings.getBOOL("MapShowLandForSale"))
+ {
+ for (it = gWorldMap->mLandForSale.begin(); it != gWorldMap->mLandForSale.end(); ++it)
+ {
+ LLItemInfo& land = *it;
+
+ if (checkItemHit(x, y, land, id, true))
+ {
+ *hit_type = MAP_ITEM_LAND_FOR_SALE;
+ mItemPicked = TRUE;
+ return;
+ }
+ }
+ }
+
+ if (gSavedSettings.getBOOL("MapShowClassifieds"))
+ {
+ for (it = gWorldMap->mClassifieds.begin(); it != gWorldMap->mClassifieds.end(); ++it)
+ {
+ LLItemInfo& classified = *it;
+
+ if (checkItemHit(x, y, classified, id, true))
+ {
+ *hit_type = MAP_ITEM_CLASSIFIED;
+ mItemPicked = TRUE;
+ return;
+ }
+ }
+ }
+
+ // If we get here, we haven't clicked on an icon
+
+ gFloaterWorldMap->trackLocation(pos_global);
+ mItemPicked = FALSE;
+
+ *id = LLUUID::null;
+ return;
+}
+
+
+BOOL outside_slop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy);
+}
+
+BOOL LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ gFocusMgr.setMouseCapture( this, NULL );
+
+ mMouseDownPanX = llround(sPanX);
+ mMouseDownPanY = llround(sPanY);
+ mMouseDownX = x;
+ mMouseDownY = y;
+ sHandledLastClick = TRUE;
+ return TRUE;
+}
+
+BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ if (this == gFocusMgr.getMouseCapture())
+ {
+ if (mPanning)
+ {
+ // restore mouse cursor
+ S32 local_x, local_y;
+ local_x = mMouseDownX + llfloor(sPanX - mMouseDownPanX);
+ local_y = mMouseDownY + llfloor(sPanY - mMouseDownPanY);
+ LLRect clip_rect = mRect;
+ clip_rect.stretch(-8);
+ clip_rect.clipPointToRect(mMouseDownX, mMouseDownY, local_x, local_y);
+ LLUI::setCursorPositionLocal(this, local_x, local_y);
+
+ // finish the pan
+ mPanning = FALSE;
+
+ mMouseDownX = 0;
+ mMouseDownY = 0;
+ }
+ else
+ {
+ // ignore whether we hit an event or not
+ S32 hit_type;
+ LLUUID id;
+ handleClick(x, y, mask, &hit_type, &id);
+ }
+ gViewerWindow->showCursor();
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLWorldMapView::updateBlock(S32 block_x, S32 block_y)
+{
+ S32 offset = block_x | (block_y * MAP_BLOCK_RES);
+ if (!gWorldMap->mMapBlockLoaded[gWorldMap->mCurrentMap][offset])
+ {
+// llinfos << "Loading Block (" << block_x << "," << block_y << ")" << llendl;
+ gWorldMap->sendMapBlockRequest(block_x << 3, block_y << 3, (block_x << 3) + 7, (block_y << 3) + 7);
+ gWorldMap->mMapBlockLoaded[gWorldMap->mCurrentMap][offset] = TRUE;
+ }
+}
+
+void LLWorldMapView::updateVisibleBlocks()
+{
+ if (gMapScale < SIM_MAP_SCALE)
+ {
+ // We don't care what is loaded if we're zoomed out
+ return;
+ }
+ // check if we've loaded the 9 potentially visible zones
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+
+ // Convert pan to sim coordinates
+ S32 world_center_x = S32((-sPanX / gMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
+ S32 world_center_y = S32((-sPanY / gMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
+
+ // Find the corresponding 8x8 block
+ S32 world_block_x = world_center_x >> 3;
+ S32 world_block_y = world_center_y >> 3;
+
+ for (S32 block_x = llmax(world_block_x-1, 0); block_x <= llmin(world_block_x+1, MAP_BLOCK_RES-1); ++block_x)
+ {
+ for (S32 block_y = llmax(world_block_y-1, 0); block_y <= llmin(world_block_y+1, MAP_BLOCK_RES-1); ++block_y)
+ {
+ updateBlock(block_x, block_y);
+ }
+ }
+}
+
+BOOL LLWorldMapView::handleHover( S32 x, S32 y, MASK mask )
+{
+ if (this == gFocusMgr.getMouseCapture())
+ {
+ if (mPanning || outside_slop(x, y, mMouseDownX, mMouseDownY))
+ {
+ // just started panning, so hide cursor
+ if (!mPanning)
+ {
+ mPanning = TRUE;
+ gViewerWindow->hideCursor();
+ }
+
+ F32 delta_x = (F32)(gViewerWindow->getCurrentMouseDX());
+ F32 delta_y = (F32)(gViewerWindow->getCurrentMouseDY());
+
+ // Set pan to value at start of drag + offset
+ sPanX += delta_x;
+ sPanY += delta_y;
+ sTargetPanX = sPanX;
+ sTargetPanY = sPanY;
+
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ // doesn't matter, cursor should be hidden
+ gViewerWindow->setCursor(UI_CURSOR_CROSS );
+ return TRUE;
+ }
+ else
+ {
+ // While we're waiting for data from the tracker, we're busy. JC
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ if (LLTracker::isTracking(NULL)
+ && pos_global.isExactlyZero())
+ {
+ gViewerWindow->setCursor( UI_CURSOR_WAIT );
+ }
+ else
+ {
+ gViewerWindow->setCursor( UI_CURSOR_CROSS );
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLWorldMapView" << llendl;
+ return TRUE;
+ }
+}
+
+
+BOOL LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ if( sHandledLastClick )
+ {
+ S32 hit_type;
+ LLUUID id;
+ handleClick(x, y, mask, &hit_type, &id);
+
+ switch (hit_type)
+ {
+ case MAP_ITEM_PG_EVENT:
+ case MAP_ITEM_MATURE_EVENT:
+ {
+ gFloaterWorldMap->close();
+ // This is an ungainly hack
+ char uuid_str[38];
+ S32 event_id;
+ id.toString(uuid_str);
+ sscanf(&uuid_str[28], "%X", &event_id);
+ LLFloaterDirectory::showEvents(event_id);
+ break;
+ }
+ case MAP_ITEM_POPULAR:
+ {
+ gFloaterWorldMap->close();
+ LLFloaterDirectory::showPopular(id);
+ break;
+ }
+ case MAP_ITEM_LAND_FOR_SALE:
+ {
+ gFloaterWorldMap->close();
+ LLFloaterDirectory::showLandForSale(id);
+ break;
+ }
+ case MAP_ITEM_CLASSIFIED:
+ {
+ gFloaterWorldMap->close();
+ LLFloaterDirectory::showClassified(id);
+ break;
+ }
+ default:
+ {
+ if (gWorldMap->mIsTrackingUnknownLocation)
+ {
+ gWorldMap->mIsTrackingDoubleClick = TRUE;
+ }
+ else
+ {
+ // Teleport if we got a valid location
+ LLVector3d pos_global = viewPosToGlobal(x,y);
+ LLSimInfo* sim_info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if (sim_info && sim_info->mAccess != SIM_ACCESS_DOWN)
+ {
+ gAgent.teleportViaLocation( pos_global );
+ }
+ }
+ }
+ };
+
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/indra/newview/llworldmapview.h b/indra/newview/llworldmapview.h
new file mode 100644
index 0000000000..7cf0f655a1
--- /dev/null
+++ b/indra/newview/llworldmapview.h
@@ -0,0 +1,173 @@
+/**
+ * @file llworldmapview.h
+ * @brief LLWorldMapView class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Global map of the world.
+
+#ifndef LL_LLWORLDMAPVIEW_H
+#define LL_LLWORLDMAPVIEW_H
+
+#include "llpanel.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4color.h"
+#include "llviewerimage.h"
+#include "llmapimagetype.h"
+#include "llworldmap.h"
+
+class LLItemInfo;
+
+const S32 DEFAULT_TRACKING_ARROW_SIZE = 16;
+
+class LLColor4;
+class LLColor4U;
+class LLCoordGL;
+class LLViewerImage;
+class LLTextBox;
+
+#define SIM_NULL_MAP_SCALE 2 // width in pixels, where we start drawing "null" sims
+#define SIM_MAP_AGENT_SCALE 20 // width in pixels, where we start drawing agents
+#define SIM_MAP_SCALE 90 // width in pixels, where we start drawing sim tiles
+
+#define AGENTS_UPDATE_TIME 60.0 // in seconds
+#define AGENT_COUNTS_UPDATE_TIME 60.0 // in seconds
+
+class LLWorldMapView : public LLPanel
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+
+ LLWorldMapView(const std::string& name, const LLRect& rect );
+ virtual ~LLWorldMapView();
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen );
+
+ bool checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track);
+ void handleClick(S32 x, S32 y, MASK mask, S32* hit_type, LLUUID* id);
+
+ // Scale and pan are shared across all instances.
+ static void setScale( F32 scale );
+ static void translatePan( S32 delta_x, S32 delta_y );
+ static void setPan( S32 x, S32 y, BOOL snap = TRUE );
+
+ LLVector3 globalPosToView(const LLVector3d& global_pos);
+ LLVector3d viewPosToGlobal(S32 x,S32 y);
+
+ virtual void draw();
+ void drawGenericItems(const LLWorldMap::item_info_list_t& items, LLPointer<LLViewerImage> image);
+ void drawGenericItem(const LLItemInfo& item, LLPointer<LLViewerImage> image);
+ void drawAgents();
+ void drawEvents();
+ void drawDots();
+ void drawFrustum();
+
+ static void cleanupTextures();
+
+ // Draw the tracking indicator, doing the right thing if it's outside
+ // the view area.
+ void drawTracking( const LLVector3d& pos_global,
+ const LLColor4& color,
+ BOOL draw_arrow = TRUE, LLString label = "", LLString tooltip = "", S32 vert_offset = 0);
+ static void drawTrackingArrow(const LLRect& view_rect, S32 x, S32 y,
+ const LLColor4& color,
+ S32 arrow_size = DEFAULT_TRACKING_ARROW_SIZE);
+ static void drawTrackingDot(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z = 0.f,
+ F32 dot_radius = 3.f);
+
+ static void drawTrackingCircle( const LLRect& rect, S32 x, S32 y,
+ const LLColor4& color,
+ S32 min_thickness,
+ S32 overlap );
+ static void drawAvatar( F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z = 0.f,
+ F32 dot_radius = 3.f);
+ static void drawIconName(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ const std::string& first_line,
+ const std::string& second_line);
+
+ // Prevents accidental double clicks
+ static void clearLastClick() { sHandledLastClick = FALSE; }
+
+ // if the view changes, download additional sim info as needed
+ void updateBlock(S32 block_x, S32 block_y);
+ void updateVisibleBlocks();
+
+protected:
+ void setDirectionPos( LLTextBox* text_box, F32 rotation );
+ void updateDirections();
+
+public:
+ LLColor4 mBackgroundColor;
+
+ static LLPointer<LLViewerImage> sAvatarYouSmallImage;
+ static LLPointer<LLViewerImage> sAvatarSmallImage;
+ static LLPointer<LLViewerImage> sAvatarLargeImage;
+ static LLPointer<LLViewerImage> sTelehubImage;
+ static LLPointer<LLViewerImage> sInfohubImage;
+ static LLPointer<LLViewerImage> sHomeImage;
+ static LLPointer<LLViewerImage> sEventImage;
+ static LLPointer<LLViewerImage> sEventMatureImage;
+ static LLPointer<LLViewerImage> sTrackCircleImage;
+ static LLPointer<LLViewerImage> sTrackArrowImage;
+ static LLPointer<LLViewerImage> sClassifiedsImage;
+ static LLPointer<LLViewerImage> sPopularImage;
+ static LLPointer<LLViewerImage> sForSaleImage;
+
+ static F32 sThresholdA;
+ static F32 sThresholdB;
+ static F32 sPixelsPerMeter; // world meters to map pixels
+
+ BOOL mItemPicked;
+
+ static F32 sPanX; // in pixels
+ static F32 sPanY; // in pixels
+ static F32 sTargetPanX; // in pixels
+ static F32 sTargetPanY; // in pixels
+ static S32 sTrackingArrowX;
+ static S32 sTrackingArrowY;
+
+ // Are we mid-pan from a user drag?
+ BOOL mPanning;
+ S32 mMouseDownPanX; // value at start of drag
+ S32 mMouseDownPanY; // value at start of drag
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+
+ LLTextBox* mTextBoxEast;
+ LLTextBox* mTextBoxNorth;
+ LLTextBox* mTextBoxWest;
+ LLTextBox* mTextBoxSouth;
+
+ LLTextBox* mTextBoxSouthEast;
+ LLTextBox* mTextBoxNorthEast;
+ LLTextBox* mTextBoxNorthWest;
+ LLTextBox* mTextBoxSouthWest;
+ LLTextBox* mTextBoxScrollHint;
+
+ static BOOL sHandledLastClick;
+ S32 mSelectIDStart;
+ F64 mAgentCountsUpdateTime;
+
+ typedef std::vector<U64> handle_list_t;
+ handle_list_t mVisibleRegions; // set every frame
+};
+
+#endif
diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp
new file mode 100644
index 0000000000..9cbee095f2
--- /dev/null
+++ b/indra/newview/llxmlrpctransaction.cpp
@@ -0,0 +1,577 @@
+/**
+ * @file llxmlrpctransaction.cpp
+ * @brief LLXMLRPCTransaction and related class implementations
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llxmlrpctransaction.h"
+
+#include "llviewercontrol.h"
+
+// Have to include these last to avoid queue redefinition!
+#include <curl/curl.h>
+#include <xmlrpc-epi/xmlrpc.h>
+
+#include "viewer.h"
+
+LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const
+{
+ return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id));
+}
+
+std::string LLXMLRPCValue::asString() const
+{
+ const char* s = XMLRPC_GetValueString(mV);
+ return s ? s : "";
+}
+
+int LLXMLRPCValue::asInt() const { return XMLRPC_GetValueInt(mV); }
+bool LLXMLRPCValue::asBool() const { return XMLRPC_GetValueBoolean(mV) != 0; }
+double LLXMLRPCValue::asDouble() const { return XMLRPC_GetValueDouble(mV); }
+
+LLXMLRPCValue LLXMLRPCValue::rewind()
+{
+ return LLXMLRPCValue(XMLRPC_VectorRewind(mV));
+}
+
+LLXMLRPCValue LLXMLRPCValue::next()
+{
+ return LLXMLRPCValue(XMLRPC_VectorNext(mV));
+}
+
+bool LLXMLRPCValue::isValid() const
+{
+ return mV != NULL;
+}
+
+LLXMLRPCValue LLXMLRPCValue::createArray()
+{
+ return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_array));
+}
+
+LLXMLRPCValue LLXMLRPCValue::createStruct()
+{
+ return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_struct));
+}
+
+
+void LLXMLRPCValue::append(LLXMLRPCValue& v)
+{
+ XMLRPC_AddValueToVector(mV, v.mV);
+}
+
+void LLXMLRPCValue::appendString(const std::string& v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(NULL, v.c_str(), 0));
+}
+
+void LLXMLRPCValue::appendInt(int v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(NULL, v));
+}
+
+void LLXMLRPCValue::appendBool(bool v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(NULL, v));
+}
+
+void LLXMLRPCValue::appendDouble(double v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(NULL, v));
+}
+
+
+void LLXMLRPCValue::append(const char* id, LLXMLRPCValue& v)
+{
+ XMLRPC_SetValueID(v.mV, id, 0);
+ XMLRPC_AddValueToVector(mV, v.mV);
+}
+
+void LLXMLRPCValue::appendString(const char* id, const std::string& v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(id, v.c_str(), 0));
+}
+
+void LLXMLRPCValue::appendInt(const char* id, int v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(id, v));
+}
+
+void LLXMLRPCValue::appendBool(const char* id, bool v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(id, v));
+}
+
+void LLXMLRPCValue::appendDouble(const char* id, double v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(id, v));
+}
+
+void LLXMLRPCValue::free()
+{
+ XMLRPC_CleanupValue(mV);
+ mV = NULL;
+}
+
+XMLRPC_VALUE LLXMLRPCValue::getValue() const
+{
+ return mV;
+}
+
+
+class LLXMLRPCTransaction::Impl
+{
+public:
+ typedef LLXMLRPCTransaction::Status Status;
+
+ CURL* mCurl;
+ CURLM* mCurlMulti;
+
+ Status mStatus;
+ CURLcode mCurlCode;
+ std::string mStatusMessage;
+ std::string mStatusURI;
+
+ char mCurlErrorBuffer[CURL_ERROR_SIZE];
+
+ std::string mURI;
+ char* mRequestText;
+ int mRequestTextSize;
+
+ std::string mProxyAddress;
+ struct curl_slist* mHeaders;
+
+ std::string mResponseText;
+ XMLRPC_REQUEST mResponse;
+
+ Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip);
+ Impl(const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip);
+ ~Impl();
+
+ bool process();
+
+ void setStatus(Status code,
+ const std::string& message = "", const std::string& uri = "");
+ void setCurlStatus(CURLcode);
+
+private:
+ void init(XMLRPC_REQUEST request, bool useGzip);
+
+ static size_t curlDownloadCallback(
+ void* data, size_t size, size_t nmemb, void* user_data);
+};
+
+LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
+ XMLRPC_REQUEST request, bool useGzip)
+ : mCurl(0), mCurlMulti(0),
+ mStatus(LLXMLRPCTransaction::StatusNotStarted),
+ mURI(uri),
+ mRequestText(0), mHeaders(0),
+ mResponse(0)
+{
+ init(request, useGzip);
+}
+
+
+LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip)
+ : mCurl(0), mCurlMulti(0),
+ mStatus(LLXMLRPCTransaction::StatusNotStarted),
+ mURI(uri),
+ mRequestText(0), mHeaders(0),
+ mResponse(0)
+{
+ XMLRPC_REQUEST request = XMLRPC_RequestNew();
+ XMLRPC_RequestSetMethodName(request, method.c_str());
+ XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+ XMLRPC_RequestSetData(request, params.getValue());
+
+ init(request, useGzip);
+}
+
+
+
+
+void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)
+{
+ mCurl = curl_easy_init();
+
+ if (gSavedSettings.getBOOL("BrowserProxyEnabled"))
+ {
+ mProxyAddress = gSavedSettings.getString("BrowserProxyAddress");
+ S32 port = gSavedSettings.getS32 ( "BrowserProxyPort" );
+
+ // tell curl about the settings
+ curl_easy_setopt(mCurl, CURLOPT_PROXY, mProxyAddress.c_str());
+ curl_easy_setopt(mCurl, CURLOPT_PROXYPORT, (long)port);
+ curl_easy_setopt(mCurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+ };
+
+// curl_easy_setopt(mCurl, CURLOPT_VERBOSE, 1); // usefull for debugging
+ curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &curlDownloadCallback);
+ curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this);
+ curl_easy_setopt(mCurl, CURLOPT_ERRORBUFFER, &mCurlErrorBuffer);
+ curl_easy_setopt(mCurl, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str());
+ curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, gVerifySSLCert);
+
+ /* Setting the DNS cache timeout to -1 disables it completely.
+ This might help with bug #503 */
+ curl_easy_setopt(mCurl, CURLOPT_DNS_CACHE_TIMEOUT, -1);
+
+ mHeaders = curl_slist_append(mHeaders, "Content-Type: text/xml");
+ curl_easy_setopt(mCurl, CURLOPT_URL, mURI.c_str());
+ curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaders);
+ if (useGzip)
+ {
+ curl_easy_setopt(mCurl, CURLOPT_ENCODING, "");
+ }
+
+ mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize);
+ if (mRequestText)
+ {
+ curl_easy_setopt(mCurl, CURLOPT_POSTFIELDS, mRequestText);
+ curl_easy_setopt(mCurl, CURLOPT_POSTFIELDSIZE, mRequestTextSize);
+ }
+ else
+ {
+ setStatus(StatusOtherError);
+ }
+
+ mCurlMulti = curl_multi_init();
+ curl_multi_add_handle(mCurlMulti, mCurl);
+}
+
+
+LLXMLRPCTransaction::Impl::~Impl()
+{
+ if (mResponse)
+ {
+ XMLRPC_RequestFree(mResponse, 1);
+ }
+
+ if (mHeaders)
+ {
+ curl_slist_free_all(mHeaders);
+ }
+
+ if (mRequestText)
+ {
+ XMLRPC_Free(mRequestText);
+ }
+
+ if (mCurl)
+ {
+ if (mCurlMulti)
+ {
+ curl_multi_remove_handle(mCurlMulti, mCurl);
+ }
+ curl_easy_cleanup(mCurl);
+ }
+
+ if (mCurlMulti)
+ {
+ curl_multi_cleanup(mCurlMulti);
+ }
+
+}
+
+bool LLXMLRPCTransaction::Impl::process()
+{
+ switch(mStatus)
+ {
+ case LLXMLRPCTransaction::StatusComplete:
+ case LLXMLRPCTransaction::StatusCURLError:
+ case LLXMLRPCTransaction::StatusXMLRPCError:
+ case LLXMLRPCTransaction::StatusOtherError:
+ {
+ return true;
+ }
+
+ case LLXMLRPCTransaction::StatusNotStarted:
+ {
+ setStatus(LLXMLRPCTransaction::StatusStarted);
+ break;
+ }
+
+ default:
+ {
+ // continue onward
+ }
+ }
+
+ const F32 MAX_PROCESSING_TIME = 0.05f;
+ LLTimer timer;
+ int count;
+
+ while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(mCurlMulti, &count))
+ {
+ if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME)
+ {
+ return false;
+ }
+ }
+
+ while(CURLMsg* curl_msg = curl_multi_info_read(mCurlMulti, &count))
+ {
+ if (CURLMSG_DONE == curl_msg->msg)
+ {
+ if (curl_msg->data.result != CURLE_OK)
+ {
+ setCurlStatus(curl_msg->data.result);
+ llalerts << "LLXMLRPCTransaction CURL error "
+ << mCurlCode << ": " << mCurlErrorBuffer << llendl;
+ llalerts << "LLXMLRPCTransaction request URI: "
+ << mURI << llendl;
+
+ return true;
+ }
+
+ setStatus(LLXMLRPCTransaction::StatusComplete);
+
+ mResponse = XMLRPC_REQUEST_FromXML(
+ mResponseText.data(), mResponseText.size(), NULL);
+
+ bool hasError = false;
+ bool hasFault = false;
+ int faultCode = 0;
+ std::string faultString;
+
+ LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse));
+ if (error.isValid())
+ {
+ hasError = true;
+ faultCode = error["faultCode"].asInt();
+ faultString = error["faultString"].asString();
+ }
+ else if (XMLRPC_ResponseIsFault(mResponse))
+ {
+ hasFault = true;
+ faultCode = XMLRPC_GetResponseFaultCode(mResponse);
+ faultString = XMLRPC_GetResponseFaultString(mResponse);
+ }
+
+ if (hasError || hasFault)
+ {
+ setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
+
+ llalerts << "LLXMLRPCTransaction XMLRPC "
+ << (hasError ? "error " : "fault ")
+ << faultCode << ": "
+ << faultString << llendl;
+ llalerts << "LLXMLRPCTransaction request URI: "
+ << mURI << llendl;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void LLXMLRPCTransaction::Impl::setStatus(Status status,
+ const std::string& message, const std::string& uri)
+{
+ mStatus = status;
+ mStatusMessage = message;
+ mStatusURI = uri;
+
+ if (mStatusMessage.empty())
+ {
+ switch (mStatus)
+ {
+ case StatusNotStarted:
+ mStatusMessage = "(not started)";
+ break;
+
+ case StatusStarted:
+ mStatusMessage = "(waiting for server response)";
+ break;
+
+ case StatusDownloading:
+ mStatusMessage = "(reading server response)";
+ break;
+
+ case StatusComplete:
+ mStatusMessage = "(done)";
+ break;
+
+ default:
+ // Usually this means that there's a problem with the login server,
+ // not with the client. Direct user to status page. JC
+ mStatusMessage =
+ "Despite our best efforts, something unexpected has gone wrong. \n"
+ " \n"
+ "Please check www.secondlife.com/status and the Second Life \n"
+ "Announcements forum to see if there is a known problem with \n"
+ "the service.";
+ mStatusURI = "http://secondlife.com/status/";
+ /*
+ mStatusMessage =
+ "Despite our best efforts, something unexpected has gone wrong.\n"
+ "Please go to the Support section of the SecondLife.com web site\n"
+ "and report the problem. If possible, include your SecondLife.log\n"
+ "file from:\n"
+#if LL_WINDOWS
+ "C:\\Documents and Settings\\<name>\\Application Data\\SecondLife\\logs\n"
+#elif LL_DARWIN
+ "~/Library/Application Support/SecondLife/logs\n"
+#elif LL_LINUX
+ "~/.secondlife/logs\n"
+#else
+#error "Need platform here."
+#endif
+ "Thank you.";
+ mStatusURI = "http://secondlife.com/community/support.php";
+ */
+ }
+ }
+}
+
+void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code)
+{
+ std::string message;
+ std::string uri = "http://secondlife.com/community/support.php";
+
+ switch (code)
+ {
+ case CURLE_COULDNT_RESOLVE_HOST:
+ message =
+ "DNS could not resolve the host name.\n"
+ "Please verify that you can connect to the www.secondlife.com\n"
+ "web site. If you can, but continue to receive this error,\n"
+ "please go to the support section and report this problem.";
+ break;
+
+ case CURLE_SSL_PEER_CERTIFICATE:
+ message =
+ "The login server couldn't verify itself via SSL.\n"
+ "If you continue to receive this error, please go\n"
+ "to the Support section of the SecondLife.com web site\n"
+ "and report the problem.";
+ break;
+
+ case CURLE_SSL_CACERT:
+ case CURLE_SSL_CONNECT_ERROR:
+ message =
+ "Often this means that your computer\'s clock is set incorrectly.\n"
+ "Please go to Control Panels and make sure the time and date\n"
+ "are set correctly.\n"
+ "\n"
+ "If you continue to receive this error, please go\n"
+ "to the Support section of the SecondLife.com web site\n"
+ "and report the problem.";
+ break;
+
+ default:
+ break;
+ }
+
+ mCurlCode = code;
+ setStatus(StatusCURLError, message, uri);
+}
+
+size_t LLXMLRPCTransaction::Impl::curlDownloadCallback(
+ void* data, size_t size, size_t nmemb, void* user_data)
+{
+ Impl& impl(*(Impl*)user_data);
+
+ size_t n = size * nmemb;
+
+ impl.mResponseText.append((const char*)data, n);
+
+ if (impl.mStatus == LLXMLRPCTransaction::StatusStarted)
+ {
+ impl.setStatus(LLXMLRPCTransaction::StatusDownloading);
+ }
+
+ return n;
+}
+
+
+LLXMLRPCTransaction::LLXMLRPCTransaction(
+ const std::string& uri, XMLRPC_REQUEST request, bool useGzip)
+: impl(* new Impl(uri, request, useGzip))
+{ }
+
+
+LLXMLRPCTransaction::LLXMLRPCTransaction(
+ const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip)
+: impl(* new Impl(uri, method, params, useGzip))
+{ }
+
+LLXMLRPCTransaction::~LLXMLRPCTransaction()
+{
+ delete &impl;
+}
+
+bool LLXMLRPCTransaction::process()
+{
+ return impl.process();
+}
+
+LLXMLRPCTransaction::Status LLXMLRPCTransaction::status(int* curlCode)
+{
+ if (curlCode)
+ {
+ *curlCode =
+ (impl.mStatus == StatusCURLError)
+ ? impl.mCurlCode
+ : CURLE_OK;
+ }
+
+ return impl.mStatus;
+}
+
+std::string LLXMLRPCTransaction::statusMessage()
+{
+ return impl.mStatusMessage;
+}
+
+std::string LLXMLRPCTransaction::statusURI()
+{
+ return impl.mStatusURI;
+}
+
+XMLRPC_REQUEST LLXMLRPCTransaction::response()
+{
+ return impl.mResponse;
+}
+
+LLXMLRPCValue LLXMLRPCTransaction::responseValue()
+{
+ return LLXMLRPCValue(XMLRPC_RequestGetData(impl.mResponse));
+}
+
+
+F64 LLXMLRPCTransaction::transferRate()
+{
+ if (!impl.mCurl || impl.mStatus != StatusComplete)
+ {
+ return 0.0L;
+ }
+
+ double size_bytes = 0.0;
+ double time_seconds = 0.0;
+ double rate_bytes_per_sec = 0.0;
+
+ curl_easy_getinfo(impl.mCurl, CURLINFO_SIZE_DOWNLOAD, &size_bytes);
+ curl_easy_getinfo(impl.mCurl, CURLINFO_TOTAL_TIME, &time_seconds);
+ curl_easy_getinfo(impl.mCurl, CURLINFO_SPEED_DOWNLOAD, &rate_bytes_per_sec);
+
+ double rate_bits_per_sec = rate_bytes_per_sec * 8.0;
+
+ llinfos << "Buffer size: " << impl.mResponseText.size() << " B" << llendl;
+ llinfos << "Transfer size: " << size_bytes << " B" << llendl;
+ llinfos << "Transfer time: " << time_seconds << " s" << llendl;
+ llinfos << "Transfer rate: " << rate_bits_per_sec/1000.0 << " Kb/s" << llendl;
+
+ return rate_bits_per_sec;
+}
diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h
new file mode 100644
index 0000000000..1ec8d600d6
--- /dev/null
+++ b/indra/newview/llxmlrpctransaction.h
@@ -0,0 +1,115 @@
+/**
+ * @file llxmlrpctransaction.h
+ * @brief LLXMLRPCTransaction and related class header file
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLXMLRPCTRANSACTION_H
+#define LLXMLRPCTRANSACTION_H
+
+#include <string>
+
+typedef struct _xmlrpc_request* XMLRPC_REQUEST;
+typedef struct _xmlrpc_value* XMLRPC_VALUE;
+ // foward decl of types from xmlrpc.h (this usage is type safe)
+
+class LLXMLRPCValue
+ // a c++ wrapper around XMLRPC_VALUE
+{
+public:
+ LLXMLRPCValue() : mV(NULL) { }
+ LLXMLRPCValue(XMLRPC_VALUE value) : mV(value) { }
+
+ bool isValid() const;
+
+ std::string asString() const;
+ int asInt() const;
+ bool asBool() const;
+ double asDouble() const;
+
+ LLXMLRPCValue operator[](const char*) const;
+
+ LLXMLRPCValue rewind();
+ LLXMLRPCValue next();
+
+ static LLXMLRPCValue createArray();
+ static LLXMLRPCValue createStruct();
+
+ void append(LLXMLRPCValue&);
+ void appendString(const std::string&);
+ void appendInt(int);
+ void appendBool(bool);
+ void appendDouble(double);
+ void appendValue(LLXMLRPCValue&);
+
+ void append(const char*, LLXMLRPCValue&);
+ void appendString(const char*, const std::string&);
+ void appendInt(const char*, int);
+ void appendBool(const char*, bool);
+ void appendDouble(const char*, double);
+ void appendValue(const char*, LLXMLRPCValue&);
+
+ void free();
+ // only call this on the top level created value
+
+ XMLRPC_VALUE getValue() const;
+
+private:
+ XMLRPC_VALUE mV;
+};
+
+
+class LLXMLRPCTransaction
+ // an asynchronous request and respones via XML-RPC
+{
+public:
+ LLXMLRPCTransaction(const std::string& uri,
+ XMLRPC_REQUEST request, bool useGzip = true);
+ // does not take ownership of the request object
+ // request can be freed as soon as the transaction is constructed
+
+ LLXMLRPCTransaction(const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip = true);
+ // *does* take control of the request value, you must not free it
+
+ ~LLXMLRPCTransaction();
+
+ typedef enum {
+ StatusNotStarted,
+ StatusStarted,
+ StatusDownloading,
+ StatusComplete,
+ StatusCURLError,
+ StatusXMLRPCError,
+ StatusOtherError
+ } Status;
+
+ bool process();
+ // run the request a little, returns true when done
+
+ Status status(int* curlCode);
+ // return status, and extended CURL code, if code isn't null
+ std::string statusMessage();
+ // return a message string, suitable for showing the user
+ std::string statusURI();
+ // return a URI for the user with more information
+ // can be empty
+
+ XMLRPC_REQUEST response();
+ LLXMLRPCValue responseValue();
+ // only valid if StatusComplete, otherwise NULL
+ // retains ownership of the result object, don't free it
+
+ F64 transferRate();
+ // only valid if StsatusComplete, otherwise 0.0
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+
+#endif // LLXMLRPCTRANSACTION_H
diff --git a/indra/newview/macmain.h b/indra/newview/macmain.h
new file mode 100644
index 0000000000..112d9b87ee
--- /dev/null
+++ b/indra/newview/macmain.h
@@ -0,0 +1,26 @@
+/**
+ * @file macmain.h
+ * @brief Main Mac viewer defines
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/* main.h */
+
+#define rMenuBar 128 /* menu bar */
+
+#define mApple 128 /* Apple menu */
+#define iAbout 1
+
+#define mFile 129 /* File menu */
+#define iNew 1
+#define iClose 4
+#define iQuitSeparator 10
+#define iQuit 11
+
+#define mEdit 130 /* Edit menu */
+
+#define kAboutBox 200 /* Dialog resource for About box */
+
+#define kSleepTime 32767
diff --git a/indra/newview/macutil_Prefix.h b/indra/newview/macutil_Prefix.h
new file mode 100644
index 0000000000..8674a01c66
--- /dev/null
+++ b/indra/newview/macutil_Prefix.h
@@ -0,0 +1,18 @@
+/**
+ * @file macutil_Prefix.h
+ * @brief Precompiled prefix file
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ *
+ * Precompiled prefix file used for
+ * AutoUpdater
+ * crashreporter
+ *
+ */
+
+#include <Carbon/Carbon.h>
+
diff --git a/indra/newview/macview.r b/indra/newview/macview.r
new file mode 100644
index 0000000000..8124232cf1
--- /dev/null
+++ b/indra/newview/macview.r
@@ -0,0 +1,123 @@
+/* main.r */
+
+#include <Carbon/Carbon.r>
+#include "macmain.h"
+
+resource 'MBAR' (rMenuBar, preload) {
+ { /* array MenuArray: 3 elements */
+ /* [1] */
+ 128,
+ /* [2] */
+ 129,
+ /* [3] */
+ 130
+ }
+};
+
+resource 'MENU' (mApple, preload) {
+ 128,
+ textMenuProc,
+ 0x7FFFFFFD,
+ enabled,
+ apple,
+ { /* array: 2 elements */
+ /* [1] */
+ "About Second Life", noIcon, noKey, noMark, plain,
+ /* [2] */
+ "-", noIcon, noKey, noMark, plain
+ }
+};
+
+resource 'MENU' (mFile, preload) {
+ 129,
+ textMenuProc,
+ 0x1400,
+ enabled,
+ "File",
+ { /* array: 11 elements */
+ /* [1] */
+ "New", noIcon, "N", noMark, plain,
+ /* [2] */
+ "Open", noIcon, "O", noMark, plain,
+ /* [3] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [4] */
+ "Close", noIcon, "W", noMark, plain,
+ /* [5] */
+ "Save", noIcon, "S", noMark, plain,
+ /* [6] */
+ "Save AsÉ", noIcon, noKey, noMark, plain,
+ /* [7] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [8] */
+ "Page SetupÉ", noIcon, noKey, noMark, plain,
+ /* [9] */
+ "PrintÉ", noIcon, noKey, noMark, plain,
+ /* [10] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [11] */
+ "Quit", noIcon, "Q", noMark, plain
+ }
+};
+
+resource 'MENU' (mEdit, preload) {
+ 130,
+ textMenuProc,
+ 0x0,
+ enabled,
+ "Edit",
+ { /* array: 6 elements */
+ /* [1] */
+ "Undo", noIcon, "Z", noMark, plain,
+ /* [2] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [3] */
+ "Cut", noIcon, "X", noMark, plain,
+ /* [4] */
+ "Copy", noIcon, "C", noMark, plain,
+ /* [5] */
+ "Paste", noIcon, "V", noMark, plain,
+ /* [6] */
+ "Clear", noIcon, noKey, noMark, plain
+ }
+};
+
+resource 'DITL' (kAboutBox) {
+ { /* array DITLarray: 3 elements */
+ /* [1] */
+ {16, 21, 38, 208},
+ StaticText {
+ disabled,
+ "Hello hello hello..."
+ },
+ /* [2] */
+ {116, 287, 136, 345},
+ Button {
+ enabled,
+ "OK"
+ },
+ /* [3] */
+ {54, 139, 74, 197},
+ Button {
+ enabled,
+ "OK"
+ }
+ }
+};
+
+resource 'ALRT' (kAboutBox) {
+ {40, 40, 139, 280},
+ 200,
+ { /* array: 4 elements */
+ /* [1] */
+ OK, visible, sound1,
+ /* [2] */
+ OK, visible, sound1,
+ /* [3] */
+ OK, visible, sound1,
+ /* [4] */
+ OK, visible, sound1
+ },
+ noAutoCenter
+};
+
diff --git a/indra/newview/macview_Prefix.h b/indra/newview/macview_Prefix.h
new file mode 100644
index 0000000000..60097bd422
--- /dev/null
+++ b/indra/newview/macview_Prefix.h
@@ -0,0 +1,221 @@
+/**
+ * @file macview_Prefix.h
+ * @brief Prefix header for all source files of the 'newview' target in the 'newview' project.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// MBW -- This doesn't work. There are some conflicts between things in Carbon.h and some of the linden source.
+//#include <Carbon/Carbon.h>
+
+////////////////// From llagent.cpp
+#include "llpreprocessor.h"
+#include "stdtypes.h"
+#include "stdenums.h"
+
+#include "llagent.h"
+
+#include "llcoordframe.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llcriticaldamp.h"
+#include "llfocusmgr.h"
+#include "llparcel.h"
+#include "llpermissions.h"
+#include "llregionhandle.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "message.h"
+#include "qmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "vmath.h"
+//#include "llteleportflags.h"
+
+#include "llbox.h"
+#include "llbutton.h"
+#include "llcameraview.h"
+#include "llconsole.h"
+#include "lldrawable.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterdirectory.h"
+#include "llfloatergroups.h"
+#include "llfloatermap.h"
+#include "llfloaterworldmap.h"
+#include "llfloatermute.h"
+#include "llconversation.h"
+#include "llfloatertools.h"
+#include "llhudeffectlookat.h"
+#include "llhudmanager.h"
+#include "llinventoryview.h"
+#include "lljoystickbutton.h"
+#include "llmenugl.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llsphere.h"
+#include "llstatusbar.h"
+#include "lltalkview.h"
+#include "lltool.h"
+#include "lltoolfocus.h"
+#include "lltoolcomp.h" // for gToolGun
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolview.h"
+#include "llui.h" // for make_ui_sound
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoground.h"
+#include "llvosky.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+/////////////////// From llfloater.cpp
+#include "llbutton.h"
+#include "lldraghandle.h"
+#include "llfocusmgr.h"
+#include "llresizebar.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llui.h"
+#include "llviewborder.h"
+#include "llvieweruictrlfactory.h"
+
+
+/////////////////// From lldrawpool.cpp
+#include "llface.h"
+#include "llcontrol.h"
+#include "pipeline.h"
+
+#include "llviewerobjectlist.h" // For debug listing.
+
+//extern LLPipeline gPipeline;
+
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpooltree.h"
+#include "lldrawpooltreenew.h"
+#include "lldrawpoolterrain.h"
+#include "lldrawpoolsky.h"
+#include "lldrawpoolwater.h"
+#include "lldrawpoolground.h"
+#include "lldrawpoolbump.h"
+#include "llvotreenew.h"
+
+/////////////////// From llface.cpp
+#include "llgl.h"
+#include "llviewerimagelist.h"
+#include "llsky.h"
+#include "llvosky.h"
+#include "llcontrol.h"
+#include "v3color.h"
+#include "pipeline.h"
+#include "llvolume.h"
+#include "llviewercamera.h"
+#include "lllightconstants.h"
+
+#include "llvovolume.h"
+#include "m3math.h"
+#include "lldrawpoolbump.h"
+
+
+
+/////////////////// From llpanel.cpp
+#include "llpanel.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "lltimer.h"
+
+#include "llmenugl.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llkeyboard.h"
+#include "llviewerwindow.h"
+#include "llcontrol.h"
+#include "lluictrl.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewborder.h"
+#include "llviewerimagelist.h"
+#include "llbutton.h"
+#include "llfocusmgr.h"
+
+
+
+/////////////////// From llvovolume.cpp
+#include "llvovolume.h"
+#include "llviewerimagelist.h"
+
+#include "llcontrol.h"
+
+#include "object_flags.h"
+
+#include "material_codes.h"
+#include "llagent.h"
+#include "llworld.h"
+#include "llviewerregion.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llvolumemgr.h"
+#include "llsky.h"
+
+#include "pipeline.h"
+#include "llmaterialtable.h"
+#include "message.h"
+#include "llviewertextureanim.h"
+#include "llviewercamera.h"
+#include "lldrawpoolbump.h"
+
+
+/////////////////// From llagentpilot.cpp
+#include "llagentpilot.h"
+#include "llagent.h"
+#include "llframestats.h"
+#include "viewer.h"
+#include "llcontrol.h"
+
+
+/////////////////// From llloginview.cpp
+#include "llloginview.h"
+
+#include "indra_constants.h" // for key and mask constants
+#include "llfontgl.h"
+#include "v4color.h"
+#include "llwindow_impl.h" // shell_open()
+
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llcontrol.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "llui.h"
+//#include "lluiconstants.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h" // for handle_preferences()
+#include "llviewerwindow.h" // to link into child list
+#include "llfocusmgr.h"
+#include "llmd5.h"
+#include "llversion.h"
+#include "viewer.h"
+
diff --git a/indra/newview/noise.cpp b/indra/newview/noise.cpp
new file mode 100644
index 0000000000..d2e837a5a7
--- /dev/null
+++ b/indra/newview/noise.cpp
@@ -0,0 +1,66 @@
+/**
+ * @file noise.cpp
+ * @brief Perlin noise routines for procedural textures, etc
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "noise.h"
+
+#include "llrand.h"
+
+// static
+#define B 0x100
+S32 p[B + B + 2];
+F32 g3[B + B + 2][3];
+F32 g2[B + B + 2][2];
+F32 g1[B + B + 2];
+S32 gNoiseStart = 1;
+
+
+F32 noise2(F32 *vec)
+{
+ U8 bx0, bx1, by0, by1;
+ U32 b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, *q, sx, sy, a, b, u, v;
+ S32 i, j;
+
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+
+ fast_setup(*vec, bx0, bx1, rx0, rx1);
+ fast_setup(*(vec + 1), by0, by1, ry0, ry1);
+
+ i = *(p + bx0);
+ j = *(p + bx1);
+
+ b00 = *(p + i + by0);
+ b10 = *(p + j + by0);
+ b01 = *(p + i + by1);
+ b11 = *(p + j + by1);
+
+ sx = s_curve(rx0);
+ sy = s_curve(ry0);
+
+
+ q = *(g2 + b00);
+ u = fast_at2(rx0, ry0, q);
+ q = *(g2 + b10);
+ v = fast_at2(rx1, ry0, q);
+ a = lerp_m(sx, u, v);
+
+ q = *(g2 + b01);
+ u = fast_at2(rx0,ry1,q);
+ q = *(g2 + b11);
+ v = fast_at2(rx1,ry1,q);
+ b = lerp_m(sx, u, v);
+
+ return lerp_m(sy, a, b);
+}
+
diff --git a/indra/newview/noise.h b/indra/newview/noise.h
new file mode 100644
index 0000000000..c436450e41
--- /dev/null
+++ b/indra/newview/noise.h
@@ -0,0 +1,334 @@
+/**
+ * @file noise.h
+ * @brief Perlin noise routines for procedural textures, etc
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_NOISE_H
+#define LL_NOISE_H
+
+#include "llmath.h"
+
+F32 turbulence2(F32 *v, F32 freq);
+F32 turbulence3(float *v, float freq);
+F32 clouds3(float *v, float freq);
+F32 noise2(float *vec);
+F32 noise3(float *vec);
+
+inline F32 bias(F32 a, F32 b)
+{
+ return (F32)pow(a, (F32)(log(b) / log(0.5f)));
+}
+
+inline F32 gain(F32 a, F32 b)
+{
+ F32 p = (F32) (log(1.f - b) / log(0.5f));
+
+ if (a < .001f)
+ return 0.f;
+ else if (a > .999f)
+ return 1.f;
+ if (a < 0.5f)
+ return (F32)(pow(2 * a, p) / 2.f);
+ else
+ return (F32)(1.f - pow(2 * (1.f - a), p) / 2.f);
+}
+
+inline F32 turbulence2(F32 *v, F32 freq)
+{
+ F32 t, vec[2];
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) {
+ vec[0] = freq * v[0];
+ vec[1] = freq * v[1];
+ t += noise2(vec)/freq;
+ }
+ return t;
+}
+
+inline F32 turbulence3(F32 *v, F32 freq)
+{
+ F32 t, vec[3];
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) {
+ vec[0] = freq * v[0];
+ vec[1] = freq * v[1];
+ vec[2] = freq * v[2];
+ t += noise3(vec)/freq;
+// t += fabs(noise3(vec)) / freq; // Like snow - bubbly at low frequencies
+// t += sqrt(fabs(noise3(vec))) / freq; // Better at low freq
+// t += (noise3(vec)*noise3(vec)) / freq;
+ }
+ return t;
+}
+
+inline F32 clouds3(F32 *v, F32 freq)
+{
+ F32 t, vec[3];
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) {
+ vec[0] = freq * v[0];
+ vec[1] = freq * v[1];
+ vec[2] = freq * v[2];
+ //t += noise3(vec)/freq;
+// t += fabs(noise3(vec)) / freq; // Like snow - bubbly at low frequencies
+// t += sqrt(fabs(noise3(vec))) / freq; // Better at low freq
+ t += (noise3(vec)*noise3(vec)) / freq;
+ }
+ return t;
+}
+
+/* noise functions over 1, 2, and 3 dimensions */
+
+#define B 0x100
+#define BM 0xff
+
+#define N 0x1000
+#define NF32 (4096.f)
+#define NP 12 /* 2^N */
+#define NM 0xfff
+
+extern S32 p[B + B + 2];
+extern F32 g3[B + B + 2][3];
+extern F32 g2[B + B + 2][2];
+extern F32 g1[B + B + 2];
+extern S32 gNoiseStart;
+
+static void init(void);
+
+#define s_curve(t) ( t * t * (3.f - 2.f * t) )
+
+#define lerp_m(t, a, b) ( a + t * (b - a) )
+
+#define setup_noise(i,b0,b1,r0,r1)\
+ t = vec[i] + N;\
+ b0 = (lltrunc(t)) & BM;\
+ b1 = (b0+1) & BM;\
+ r0 = t - lltrunc(t);\
+ r1 = r0 - 1.f;
+
+
+inline void fast_setup(F32 vec, U8 &b0, U8 &b1, F32 &r0, F32 &r1)
+{
+ S32 t_S32;
+
+ r1 = vec + NF32;
+ t_S32 = lltrunc(r1);
+ b0 = (U8)t_S32;
+ b1 = b0 + 1;
+ r0 = r1 - t_S32;
+ r1 = r0 - 1.f;
+}
+
+inline F32 noise1(const F32 arg)
+{
+ int bx0, bx1;
+ F32 rx0, rx1, sx, t, u, v, vec[1];
+
+ vec[0] = arg;
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+ setup_noise(0, bx0,bx1, rx0,rx1);
+
+ sx = s_curve(rx0);
+
+ u = rx0 * g1[ p[ bx0 ] ];
+ v = rx1 * g1[ p[ bx1 ] ];
+
+ return lerp_m(sx, u, v);
+}
+
+inline F32 fast_at2(F32 rx, F32 ry, F32 *q)
+{
+ return rx * (*q) + ry * (*(q + 1));
+}
+
+
+
+inline F32 fast_at3(F32 rx, F32 ry, F32 rz, F32 *q)
+{
+ return rx * (*q) + ry * (*(q + 1)) + rz * (*(q + 2));
+}
+
+
+
+inline F32 noise3(F32 *vec)
+{
+ U8 bx0, bx1, by0, by1, bz0, bz1;
+ S32 b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
+ S32 i, j;
+
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+ fast_setup(*vec, bx0,bx1, rx0,rx1);
+ fast_setup(*(vec + 1), by0,by1, ry0,ry1);
+ fast_setup(*(vec + 2), bz0,bz1, rz0,rz1);
+
+ i = p[ bx0 ];
+ j = p[ bx1 ];
+
+ b00 = p[ i + by0 ];
+ b10 = p[ j + by0 ];
+ b01 = p[ i + by1 ];
+ b11 = p[ j + by1 ];
+
+ t = s_curve(rx0);
+ sy = s_curve(ry0);
+ sz = s_curve(rz0);
+
+ q = g3[ b00 + bz0 ];
+ u = fast_at3(rx0,ry0,rz0,q);
+ q = g3[ b10 + bz0 ];
+ v = fast_at3(rx1,ry0,rz0,q);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz0 ];
+ u = fast_at3(rx0,ry1,rz0,q);
+ q = g3[ b11 + bz0 ];
+ v = fast_at3(rx1,ry1,rz0,q);
+ b = lerp_m(t, u, v);
+
+ c = lerp_m(sy, a, b);
+
+ q = g3[ b00 + bz1 ];
+ u = fast_at3(rx0,ry0,rz1,q);
+ q = g3[ b10 + bz1 ];
+ v = fast_at3(rx1,ry0,rz1,q);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz1 ];
+ u = fast_at3(rx0,ry1,rz1,q);
+ q = g3[ b11 + bz1 ];
+ v = fast_at3(rx1,ry1,rz1,q);
+ b = lerp_m(t, u, v);
+
+ d = lerp_m(sy, a, b);
+
+ return lerp_m(sz, c, d);
+}
+
+
+/*
+F32 noise3(F32 *vec)
+{
+ int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
+ S32 i, j;
+
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+ setup_noise(0, bx0,bx1, rx0,rx1);
+ setup_noise(1, by0,by1, ry0,ry1);
+ setup_noise(2, bz0,bz1, rz0,rz1);
+
+ i = p[ bx0 ];
+ j = p[ bx1 ];
+
+ b00 = p[ i + by0 ];
+ b10 = p[ j + by0 ];
+ b01 = p[ i + by1 ];
+ b11 = p[ j + by1 ];
+
+ t = s_curve(rx0);
+ sy = s_curve(ry0);
+ sz = s_curve(rz0);
+
+#define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )
+
+ q = g3[ b00 + bz0 ] ; u = at3(rx0,ry0,rz0);
+ q = g3[ b10 + bz0 ] ; v = at3(rx1,ry0,rz0);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz0 ] ; u = at3(rx0,ry1,rz0);
+ q = g3[ b11 + bz0 ] ; v = at3(rx1,ry1,rz0);
+ b = lerp_m(t, u, v);
+
+ c = lerp_m(sy, a, b);
+
+ q = g3[ b00 + bz1 ] ; u = at3(rx0,ry0,rz1);
+ q = g3[ b10 + bz1 ] ; v = at3(rx1,ry0,rz1);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz1 ] ; u = at3(rx0,ry1,rz1);
+ q = g3[ b11 + bz1 ] ; v = at3(rx1,ry1,rz1);
+ b = lerp_m(t, u, v);
+
+ d = lerp_m(sy, a, b);
+
+ return lerp_m(sz, c, d);
+}
+*/
+
+static void normalize2(F32 v[2])
+{
+ F32 s;
+
+ s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1]);
+ v[0] = v[0] * s;
+ v[1] = v[1] * s;
+}
+
+static void normalize3(F32 v[3])
+{
+ F32 s;
+
+ s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ v[0] = v[0] * s;
+ v[1] = v[1] * s;
+ v[2] = v[2] * s;
+}
+
+static void init(void)
+{
+ int i, j, k;
+
+ for (i = 0 ; i < B ; i++) {
+ p[i] = i;
+
+ g1[i] = (F32)((rand() % (B + B)) - B) / B;
+
+ for (j = 0 ; j < 2 ; j++)
+ g2[i][j] = (F32)((rand() % (B + B)) - B) / B;
+ normalize2(g2[i]);
+
+ for (j = 0 ; j < 3 ; j++)
+ g3[i][j] = (F32)((rand() % (B + B)) - B) / B;
+ normalize3(g3[i]);
+ }
+
+ while (--i) {
+ k = p[i];
+ p[i] = p[j = rand() % B];
+ p[j] = k;
+ }
+
+ for (i = 0 ; i < B + 2 ; i++) {
+ p[B + i] = p[i];
+ g1[B + i] = g1[i];
+ for (j = 0 ; j < 2 ; j++)
+ g2[B + i][j] = g2[i][j];
+ for (j = 0 ; j < 3 ; j++)
+ g3[B + i][j] = g3[i][j];
+ }
+}
+
+#undef B
+#undef BM
+#undef N
+#undef NF32
+#undef NP
+#undef NM
+
+#endif // LL_NOISE_
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
new file mode 100644
index 0000000000..8bc9007310
--- /dev/null
+++ b/indra/newview/pipeline.cpp
@@ -0,0 +1,5316 @@
+/**
+ * @file pipeline.cpp
+ * @brief Rendering pipeline.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "pipeline.h"
+
+// library includes
+#include "audioengine.h" // For MAX_BUFFERS for debugging.
+#include "imageids.h"
+#include "llagpmempool.h"
+#include "llerror.h"
+#include "llviewercontrol.h"
+#include "llfasttimer.h"
+#include "llfontgl.h"
+#include "llmemory.h"
+#include "llnamevalue.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "material_codes.h"
+#include "timing.h"
+#include "v3color.h"
+#include "llui.h"
+
+// newview includes
+#include "llagent.h"
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolground.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpooltree.h"
+#include "lldrawpoolhud.h"
+#include "lldrawpoolwater.h"
+#include "llface.h"
+#include "llfeaturemanager.h"
+#include "llfloatertelehub.h"
+#include "llframestats.h"
+#include "llgldbg.h"
+#include "llhudmanager.h"
+#include "lllightconstants.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "lltracker.h"
+#include "lltool.h"
+#include "lltoolmgr.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h" // for audio debugging.
+#include "llviewerwindow.h" // For getSpinAxis
+#include "llvoavatar.h"
+#include "llvoground.h"
+#include "llvosky.h"
+#include "llvotree.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llagpmempoolarb.h"
+#include "llagparray.inl"
+
+#ifdef _DEBUG
+// Debug indices is disabled for now for debug performance - djs 4/24/02
+//#define DEBUG_INDICES
+#else
+//#define DEBUG_INDICES
+#endif
+
+const F32 BACKLIGHT_DAY_MAGNITUDE_AVATAR = 0.2f;
+const F32 BACKLIGHT_NIGHT_MAGNITUDE_AVATAR = 0.1f;
+const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f;
+const F32 BACKLIGHT_NIGHT_MAGNITUDE_OBJECT = 0.08f;
+const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40;
+const S32 MAX_OFFSCREEN_GEOMETRY_CHANGES_PER_FRAME = 10;
+
+// Guess on the number of visible objects in the scene, used to
+// pre-size std::vector and other arrays. JC
+const S32 ESTIMATED_VISIBLE_OBJECT_COUNT = 8192;
+
+// If the sum of the X + Y + Z scale of an object exceeds this number,
+// it will be considered a potential occluder. For instance,
+// a box of size 6 x 6 x 1 has sum 13, which might be an occluder. JC
+const F32 OCCLUDE_SCALE_SUM_THRESHOLD = 8.f;
+
+// Max number of occluders to search for. JC
+const S32 MAX_OCCLUDER_COUNT = 2;
+
+extern S32 gBoxFrame;
+extern BOOL gRenderLightGlows;
+extern BOOL gHideSelectedObjects;
+
+
+BOOL gAvatarBacklight = FALSE;
+
+F32 gMinObjectDistance = MIN_NEAR_PLANE;
+S32 gTrivialAccepts = 0;
+
+BOOL gRenderForSelect = FALSE;
+
+BOOL gUsePickAlpha = TRUE;
+F32 gPickAlphaThreshold = 0.f;
+F32 gPickAlphaTargetThreshold = 0.f;
+
+//glsl parameter tables
+const char* LLPipeline::sReservedAttribs[] =
+{
+ "materialColor",
+ "specularColor",
+ "binormal"
+};
+
+U32 LLPipeline::sReservedAttribCount = LLPipeline::GLSL_END_RESERVED_ATTRIBS;
+
+const char* LLPipeline::sAvatarAttribs[] =
+{
+ "weight",
+ "clothing",
+ "gWindDir",
+ "gSinWaveParams",
+ "gGravity"
+};
+
+U32 LLPipeline::sAvatarAttribCount = sizeof(LLPipeline::sAvatarAttribs)/sizeof(char*);
+
+const char* LLPipeline::sAvatarUniforms[] =
+{
+ "matrixPalette"
+};
+
+U32 LLPipeline::sAvatarUniformCount = 1;
+
+const char* LLPipeline::sReservedUniforms[] =
+{
+ "diffuseMap",
+ "specularMap",
+ "bumpMap",
+ "environmentMap",
+ "scatterMap"
+};
+
+U32 LLPipeline::sReservedUniformCount = LLPipeline::GLSL_END_RESERVED_UNIFORMS;
+
+const char* LLPipeline::sTerrainUniforms[] =
+{
+ "detail0",
+ "detail1",
+ "alphaRamp"
+};
+
+U32 LLPipeline::sTerrainUniformCount = sizeof(LLPipeline::sTerrainUniforms)/sizeof(char*);
+
+const char* LLPipeline::sWaterUniforms[] =
+{
+ "screenTex",
+ "eyeVec",
+ "time",
+ "d1",
+ "d2",
+ "lightDir",
+ "specular",
+ "lightExp",
+ "fbScale",
+ "refScale"
+};
+
+U32 LLPipeline::sWaterUniformCount = sizeof(LLPipeline::sWaterUniforms)/sizeof(char*);
+
+// the SSE variable is dependent on software blending being enabled.
+
+//----------------------------------------
+
+void stamp(F32 x, F32 y, F32 xs, F32 ys)
+{
+ glBegin(GL_QUADS);
+ glTexCoord2f(0,0);
+ glVertex3f(x, y, 0.0f);
+ glTexCoord2f(1,0);
+ glVertex3f(x+xs,y, 0.0f);
+ glTexCoord2f(1,1);
+ glVertex3f(x+xs,y+ys,0.0f);
+ glTexCoord2f(0,1);
+ glVertex3f(x, y+ys,0.0f);
+ glEnd();
+}
+
+
+
+//----------------------------------------
+
+S32 LLPipeline::sCompiles = 0;
+S32 LLPipeline::sAGPMaxPoolSize = 1 << 25; // 32MB
+BOOL LLPipeline::sRenderPhysicalBeacons = FALSE;
+BOOL LLPipeline::sRenderScriptedBeacons = FALSE;
+BOOL LLPipeline::sRenderParticleBeacons = FALSE;
+BOOL LLPipeline::sRenderSoundBeacons = FALSE;
+
+LLPipeline::LLPipeline() :
+ mVertexShadersEnabled(FALSE),
+ mVertexShadersLoaded(0),
+ mLastRebuildPool(NULL),
+ mAlphaPool(NULL),
+ mSkyPool(NULL),
+ mStarsPool(NULL),
+ mCloudsPool(NULL),
+ mTerrainPool(NULL),
+ mWaterPool(NULL),
+ mGroundPool(NULL),
+ mHUDPool(NULL),
+ mAGPMemPool(NULL),
+ mGlobalFence(0),
+ mBufferIndex(0),
+ mBufferCount(kMaxBufferCount),
+ mUseOcclusionCulling(FALSE),
+ mLightMask(0),
+ mLightMovingMask(0)
+{
+ for(S32 i = 0; i < kMaxBufferCount; i++)
+ {
+ mBufferMemory[i] = NULL;
+ }
+ for (S32 i = 0; i < kMaxBufferCount; i++)
+ {
+ mBufferFence[i] = 0;
+ }
+}
+
+void LLPipeline::init()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ stop_glerror();
+
+ mAGPBound = FALSE;
+ mObjectPartition = new LLSpatialPartition;
+ mTrianglesDrawnStat.reset();
+ resetFrameStats();
+
+ mRenderTypeMask = 0xffffffff; // All render types start on
+ mRenderDebugFeatureMask = 0xffffffff; // All debugging features on
+ mRenderFeatureMask = 0; // All features start off
+ mRenderDebugMask = 0; // All debug starts off
+
+ mBackfaceCull = TRUE;
+
+ // Disable AGP initially.
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP;
+
+ stop_glerror();
+
+ // Enable features
+
+ mUseVBO = gSavedSettings.getBOOL("RenderUseVBO");
+
+ // Allocate the shared buffers for software skinning
+ for(S32 i=0; i < mBufferCount; i++)
+ {
+ mBufferMemory[i] = new LLAGPArray<U8>;
+ mBufferMemory[i]->reserve_block(AVATAR_VERTEX_BYTES*AVATAR_BUFFER_ELEMENTS);
+ }
+
+ if (gFeatureManagerp->isFeatureAvailable("RenderAGP"))
+ {
+ setUseAGP(gSavedSettings.getBOOL("RenderUseAGP") && gGLManager.mHasAnyAGP);
+ }
+ else
+ {
+ setUseAGP(FALSE);
+ }
+
+ stop_glerror();
+
+ for(S32 i=0; i < mBufferCount; i++)
+ {
+ if (!mBufferMemory[i]->isAGP() && usingAGP())
+ {
+ llwarns << "pipeline buffer memory is non-AGP when AGP available!" << llendl;
+ }
+ }
+
+ setShaders();
+}
+
+void LLPipeline::LLScatterShader::init(GLhandleARB shader, int map_stage)
+{
+ glUseProgramObjectARB(shader);
+ glUniform1iARB(glGetUniformLocationARB(shader, "scatterMap"), map_stage);
+ glUseProgramObjectARB(0);
+}
+
+LLPipeline::~LLPipeline()
+{
+
+}
+
+void LLPipeline::cleanup()
+{
+ for(pool_set_t::iterator iter = mPools.begin();
+ iter != mPools.end(); )
+ {
+ pool_set_t::iterator curiter = iter++;
+ LLDrawPool* poolp = *curiter;
+ if (poolp->mReferences.empty())
+ {
+ mPools.erase(curiter);
+ removeFromQuickLookup( poolp );
+ delete poolp;
+ }
+ }
+
+ if (!mSimplePools.empty())
+ {
+ llwarns << "Simple Pools not cleaned up" << llendl;
+ }
+ if (!mTerrainPools.empty())
+ {
+ llwarns << "Terrain Pools not cleaned up" << llendl;
+ }
+ if (!mTreePools.empty())
+ {
+ llwarns << "Tree Pools not cleaned up" << llendl;
+ }
+ if (!mTreeNewPools.empty())
+ {
+ llwarns << "TreeNew Pools not cleaned up" << llendl;
+ }
+ if (!mBumpPools.empty())
+ {
+ llwarns << "Bump Pools not cleaned up" << llendl;
+ }
+ delete mAlphaPool;
+ mAlphaPool = NULL;
+ delete mSkyPool;
+ mSkyPool = NULL;
+ delete mStarsPool;
+ mStarsPool = NULL;
+ delete mCloudsPool;
+ mCloudsPool = NULL;
+ delete mTerrainPool;
+ mTerrainPool = NULL;
+ delete mWaterPool;
+ mWaterPool = NULL;
+ delete mGroundPool;
+ mGroundPool = NULL;
+ delete mHUDPool;
+ mHUDPool = NULL;
+
+ mBloomImagep = NULL;
+ mBloomImage2p = NULL;
+ mFaceSelectImagep = NULL;
+ mAlphaSizzleImagep = NULL;
+
+ for(S32 i=0; i < mBufferCount; i++)
+ {
+ delete mBufferMemory[i];
+ mBufferMemory[i] = NULL;
+ }
+
+ delete mObjectPartition;
+ mObjectPartition = NULL;
+
+ if (mAGPMemPool && mGlobalFence)
+ {
+ mAGPMemPool->deleteFence(mGlobalFence);
+ mGlobalFence = 0;
+ }
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+}
+
+//============================================================================
+
+BOOL LLPipeline::initAGP()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ mAGPMemPool = LLAGPMemPool::createPool(sAGPMaxPoolSize, mUseVBO);
+
+ if (!mAGPMemPool)
+ {
+ llinfos << "Warning! Couldn't allocate AGP memory!" << llendl;
+ llinfos << "Disabling AGP!" << llendl;
+ mAGPMemPool = NULL;
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
+ return FALSE;
+ }
+ else if (!mAGPMemPool->getSize())
+ {
+ llinfos << "Warning! Unable to allocate AGP memory! Disabling AGP" << llendl;
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
+ return FALSE;
+ }
+ else
+ {
+ llinfos << "Allocated " << mAGPMemPool->getSize() << " bytes of AGP memory" << llendl;
+ mAGPMemPool->bind();
+
+ if (mAGPMemPool->getSize() < MIN_AGP_SIZE)
+ {
+ llwarns << "Not enough AGP memory!" << llendl;
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
+ return FALSE;
+ }
+
+
+ if (mAGPMemPool)
+ {
+ // Create the fence that we use for global synchronization.
+ mGlobalFence = mAGPMemPool->createFence();
+ }
+ return TRUE;
+ }
+
+}
+
+void LLPipeline::cleanupAGP()
+{
+ int i;
+ for(i=0; i < mBufferCount; i++)
+ {
+ mBufferMemory[i]->deleteFence(mBufferFence[i]);
+ mBufferMemory[i]->setUseAGP(FALSE);
+ }
+
+ flushAGPMemory();
+ if (mAGPMemPool && mGlobalFence)
+ {
+ mAGPMemPool->deleteFence(mGlobalFence);
+ mGlobalFence = 0;
+ }
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+}
+
+BOOL LLPipeline::usingAGP() const
+{
+ return (mRenderFeatureMask & RENDER_FEATURE_AGP) ? TRUE : FALSE;
+}
+
+void LLPipeline::setUseAGP(const BOOL use_agp)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (use_agp == usingAGP())
+ {
+ return;
+ }
+ else if (use_agp)
+ {
+ mRenderFeatureMask |= RENDER_FEATURE_AGP;
+ initAGP();
+
+ // Forces us to allocate an AGP memory block immediately.
+ int i;
+ for(i=0; i < mBufferCount; i++)
+ {
+ mBufferMemory[i]->setUseAGP(use_agp);
+ mBufferMemory[i]->realloc(mBufferMemory[i]->getMax());
+ mBufferFence[i] = mBufferMemory[i]->createFence();
+ }
+
+ // Must be done AFTER you initialize AGP
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->setUseAGP(use_agp);
+ }
+ }
+ else
+ {
+ unbindAGP();
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP;
+
+ // Must be done BEFORE you blow away AGP
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->setUseAGP(use_agp);
+ }
+
+ int i;
+ for(i=0; i < mBufferCount; i++)
+ {
+ if (mBufferMemory[i])
+ {
+ mBufferMemory[i]->setUseAGP(use_agp);
+ mBufferMemory[i]->deleteFence(mBufferFence[i]);
+ mBufferFence[i] = 0;
+ }
+ else
+ {
+ llerrs << "setUseAGP without buffer memory" << llendl;
+ }
+ }
+
+ cleanupAGP();
+ }
+
+}
+
+//============================================================================
+
+void LLPipeline::destroyGL()
+{
+ setUseAGP(FALSE);
+ stop_glerror();
+ unloadShaders();
+ mHighlightFaces.reset();
+}
+
+void LLPipeline::restoreGL()
+{
+ if (mVertexShadersEnabled)
+ {
+ setShaders();
+ }
+
+ if (mObjectPartition)
+ {
+ mObjectPartition->restoreGL();
+ }
+}
+
+//============================================================================
+// Load Shader
+
+static LLString get_object_log(GLhandleARB ret)
+{
+ LLString res;
+
+ //get log length
+ GLint length;
+ glGetObjectParameterivARB(ret, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
+ if (length > 0)
+ {
+ //the log could be any size, so allocate appropriately
+ GLcharARB* log = new GLcharARB[length];
+ glGetInfoLogARB(ret, length, &length, log);
+ res = LLString(log);
+ delete[] log;
+ }
+ return res;
+}
+
+void LLPipeline::dumpObjectLog(GLhandleARB ret, BOOL warns)
+{
+ LLString log = get_object_log(ret);
+ if (warns)
+ {
+ llwarns << log << llendl;
+ }
+ else
+ {
+ llinfos << log << llendl;
+ }
+}
+
+GLhandleARB LLPipeline::loadShader(const LLString& filename, S32 cls, GLenum type)
+{
+ GLenum error;
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR entering loadShader(): " << error << llendl;
+ }
+
+ llinfos << "Loading shader file: " << filename << llendl;
+
+ if (filename.empty())
+ {
+ return 0;
+ }
+
+
+ //read in from file
+ FILE* file = NULL;
+
+ S32 try_gpu_class = mVertexShaderLevel[cls];
+ S32 gpu_class;
+
+ //find the most relevant file
+ for (gpu_class = try_gpu_class; gpu_class > 0; gpu_class--)
+ { //search from the current gpu class down to class 1 to find the most relevant shader
+ std::stringstream fname;
+ fname << gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/class");
+ fname << gpu_class << "/" << filename;
+
+ llinfos << "Looking in " << fname.str().c_str() << llendl;
+ file = fopen(fname.str().c_str(), "r");
+ if (file)
+ {
+ break; // done
+ }
+ }
+
+ if (file == NULL)
+ {
+ llinfos << "GLSL Shader file not found: " << filename << llendl;
+ return 0;
+ }
+
+ //we can't have any lines longer than 1024 characters
+ //or any shaders longer than 1024 lines... deal - DaveP
+ GLcharARB buff[1024];
+ GLcharARB* text[1024];
+ GLuint count = 0;
+
+ //copy file into memory
+ while(fgets(buff, 1024, file) != NULL)
+ {
+ text[count++] = strdup(buff);
+ }
+ fclose(file);
+
+ //create shader object
+ GLhandleARB ret = glCreateShaderObjectARB(type);
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR in glCreateShaderObjectARB: " << error << llendl;
+ }
+ else
+ {
+ //load source
+ glShaderSourceARB(ret, count, (const GLcharARB**) text, NULL);
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR in glShaderSourceARB: " << error << llendl;
+ }
+ else
+ {
+ //compile source
+ glCompileShaderARB(ret);
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR in glCompileShaderARB: " << error << llendl;
+ }
+ }
+ }
+ //free memory
+ for (GLuint i = 0; i < count; i++)
+ {
+ free(text[i]);
+ }
+ if (error == GL_NO_ERROR)
+ {
+ //check for errors
+ GLint success = GL_TRUE;
+ glGetObjectParameterivARB(ret, GL_OBJECT_COMPILE_STATUS_ARB, &success);
+ error = glGetError();
+ if (error != GL_NO_ERROR || success == GL_FALSE)
+ {
+ //an error occured, print log
+ llwarns << "GLSL Compilation Error: (" << error << ") in " << filename << llendl;
+ dumpObjectLog(ret);
+ ret = 0;
+ }
+ }
+ else
+ {
+ ret = 0;
+ }
+ stop_glerror();
+
+ //successfully loaded, save results
+#if 1 // 1.9.1
+ if (ret)
+ {
+ mVertexShaderLevel[cls] = try_gpu_class;
+ }
+ else
+ {
+ if (mVertexShaderLevel[cls] > 1)
+ {
+ mVertexShaderLevel[cls] = mVertexShaderLevel[cls] - 1;
+ ret = loadShader(filename,cls,type);
+ if (ret && mMaxVertexShaderLevel[cls] > mVertexShaderLevel[cls])
+ {
+ mMaxVertexShaderLevel[cls] = mVertexShaderLevel[cls];
+ }
+ }
+ }
+#else
+ if (ret)
+ {
+ S32 max = -1;
+ /*if (try_gpu_class == mMaxVertexShaderLevel[cls])
+ {
+ max = gpu_class;
+ }*/
+ saveVertexShaderLevel(cls,try_gpu_class,max);
+ }
+ else
+ {
+ if (mVertexShaderLevel[cls] > 1)
+ {
+ mVertexShaderLevel[cls] = mVertexShaderLevel[cls] - 1;
+ ret = loadShader(f,cls,type);
+ if (ret && mMaxVertexShaderLevel[cls] > mVertexShaderLevel[cls])
+ {
+ saveVertexShaderLevel(cls, mVertexShaderLevel[cls], mVertexShaderLevel[cls]);
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+BOOL LLPipeline::linkProgramObject(GLhandleARB obj, BOOL suppress_errors)
+{
+ //check for errors
+ glLinkProgramARB(obj);
+ GLint success = GL_TRUE;
+ glGetObjectParameterivARB(obj, GL_OBJECT_LINK_STATUS_ARB, &success);
+ if (!suppress_errors && success == GL_FALSE)
+ {
+ //an error occured, print log
+ llwarns << "GLSL Linker Error:" << llendl;
+ }
+
+ LLString log = get_object_log(obj);
+ LLString::toLower(log);
+ if (log.find("software") != LLString::npos)
+ {
+ llwarns << "GLSL Linker: Running in Software:" << llendl;
+ success = GL_FALSE;
+ suppress_errors = FALSE;
+ }
+ if (!suppress_errors)
+ {
+ dumpObjectLog(obj, !success);
+ }
+
+ return success;
+}
+
+BOOL LLPipeline::validateProgramObject(GLhandleARB obj)
+{
+ //check program validity against current GL
+ glValidateProgramARB(obj);
+ GLint success = GL_TRUE;
+ glGetObjectParameterivARB(obj, GL_OBJECT_VALIDATE_STATUS_ARB, &success);
+ if (success == GL_FALSE)
+ {
+ llwarns << "GLSL program not valid: " << llendl;
+ dumpObjectLog(obj);
+ }
+ else
+ {
+ dumpObjectLog(obj, FALSE);
+ }
+
+ return success;
+}
+
+//============================================================================
+// Shader Management
+
+void LLPipeline::setShaders()
+{
+ if (gViewerWindow)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_WAIT);
+ }
+
+ // Lighting
+ setLightingDetail(-1);
+
+ // Shaders
+ for (S32 i=0; i<SHADER_COUNT; i++)
+ {
+ mVertexShaderLevel[i] = 0;
+ mMaxVertexShaderLevel[i] = 0;
+ }
+ if (canUseVertexShaders())
+ {
+ S32 light_class = 2;
+ S32 env_class = 2;
+ if (getLightingDetail() == 0)
+ {
+ light_class = 1;
+ }
+ // Load lighting shaders
+ mVertexShaderLevel[SHADER_LIGHTING] = light_class;
+ mMaxVertexShaderLevel[SHADER_LIGHTING] = light_class;
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = env_class;
+ mMaxVertexShaderLevel[SHADER_ENVIRONMENT] = env_class;
+ BOOL loaded = loadShadersLighting();
+ if (loaded)
+ {
+ mVertexShadersEnabled = TRUE;
+ mVertexShadersLoaded = 1;
+
+ // Load all shaders to set max levels
+ loadShadersEnvironment();
+
+ // Load max avatar shaders to set the max level
+ mVertexShaderLevel[SHADER_AVATAR] = 3;
+ mMaxVertexShaderLevel[SHADER_AVATAR] = 3;
+ loadShadersAvatar();
+
+ // Load shaders to correct levels
+ if (!gSavedSettings.getBOOL("RenderRippleWater"))
+ {
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ loadShadersEnvironment(); // unloads
+ }
+
+#if LL_DARWIN // force avatar shaders off for mac
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ mMaxVertexShaderLevel[SHADER_AVATAR] = 0;
+#else
+ if (gSavedSettings.getBOOL("RenderAvatarVP"))
+ {
+ S32 avatar = gSavedSettings.getS32("RenderAvatarMode");
+ S32 avatar_class = 1 + avatar;
+ // Set the actual level
+ mVertexShaderLevel[SHADER_AVATAR] = avatar_class;
+ loadShadersAvatar();
+ if (mVertexShaderLevel[SHADER_AVATAR] != avatar_class)
+ {
+ if (mVertexShaderLevel[SHADER_AVATAR] == 0)
+ {
+ gSavedSettings.setBOOL("RenderAvatarVP", FALSE);
+ }
+ avatar = llmax(mVertexShaderLevel[SHADER_AVATAR]-1,0);
+ gSavedSettings.setS32("RenderAvatarMode", avatar);
+ }
+ }
+ else
+ {
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ gSavedSettings.setS32("RenderAvatarMode", 0);
+ loadShadersAvatar(); // unloads
+ }
+#endif
+ }
+ else
+ {
+ mVertexShadersEnabled = FALSE;
+ mVertexShadersLoaded = 0;
+ }
+ }
+ if (gViewerWindow)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ }
+}
+
+BOOL LLPipeline::canUseVertexShaders()
+{
+ if (!gGLManager.mHasVertexShader ||
+ !gGLManager.mHasFragmentShader ||
+ !gFeatureManagerp->isFeatureAvailable("VertexShaderEnable") ||
+ mVertexShadersLoaded == -1)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+void LLPipeline::unloadShaders()
+{
+ mObjectSimpleProgram.unload();
+ mObjectBumpProgram.unload();
+ mObjectAlphaProgram.unload();
+ mWaterProgram.unload();
+ mTerrainProgram.unload();
+ mGroundProgram.unload();
+ mAvatarProgram.unload();
+ mAvatarEyeballProgram.unload();
+ mAvatarPickProgram.unload();
+ mHighlightProgram.unload();
+
+ mVertexShaderLevel[SHADER_LIGHTING] = 0;
+ mVertexShaderLevel[SHADER_OBJECT] = 0;
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ mVertexShaderLevel[SHADER_INTERFACE] = 0;
+
+ mLightVertex = mLightFragment = mScatterVertex = mScatterFragment = 0;
+ mVertexShadersLoaded = 0;
+}
+
+#if 0 // 1.9.2
+// Any time shader options change
+BOOL LLPipeline::loadShaders()
+{
+ unloadShaders();
+
+ if (!canUseVertexShaders())
+ {
+ return FALSE;
+ }
+
+ S32 light_class = mMaxVertexShaderLevel[SHADER_LIGHTING];
+ if (getLightingDetail() == 0)
+ {
+ light_class = 1; // Use minimum lighting shader
+ }
+ else if (getLightingDetail() == 1)
+ {
+ light_class = 2; // Use medium lighting shader
+ }
+ mVertexShaderLevel[SHADER_LIGHTING] = light_class;
+ mVertexShaderLevel[SHADER_OBJECT] = llmin(mMaxVertexShaderLevel[SHADER_OBJECT], gSavedSettings.getS32("VertexShaderLevelObject"));
+ mVertexShaderLevel[SHADER_AVATAR] = llmin(mMaxVertexShaderLevel[SHADER_AVATAR], gSavedSettings.getS32("VertexShaderLevelAvatar"));
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = llmin(mMaxVertexShaderLevel[SHADER_ENVIRONMENT], gSavedSettings.getS32("VertexShaderLevelEnvironment"));
+ mVertexShaderLevel[SHADER_INTERFACE] = mMaxVertexShaderLevel[SHADER_INTERFACE];
+
+ BOOL loaded = loadShadersLighting();
+ if (loaded)
+ {
+ loadShadersEnvironment(); // Must load this before object/avatar for scatter
+ loadShadersObject();
+ loadShadersAvatar();
+ loadShadersInterface();
+ mVertexShadersLoaded = 1;
+ }
+ else
+ {
+ unloadShaders();
+ mVertexShadersEnabled = FALSE;
+ mVertexShadersLoaded = 0; //-1; // -1 = failed
+ setLightingDetail(-1);
+ }
+
+ return loaded;
+}
+#endif
+
+BOOL LLPipeline::loadShadersLighting()
+{
+ // Load light dependency shaders first
+ // All of these have to load for any shaders to function
+
+ std::string lightvertex = "lighting/lightV.glsl";
+ //get default light function implementation
+ mLightVertex = loadShader(lightvertex, SHADER_LIGHTING, GL_VERTEX_SHADER_ARB);
+ if( !mLightVertex )
+ {
+ llwarns << "Failed to load " << lightvertex << llendl;
+ return FALSE;
+ }
+
+ std::string lightfragment = "lighting/lightF.glsl";
+ mLightFragment = loadShader(lightfragment, SHADER_LIGHTING, GL_FRAGMENT_SHADER_ARB);
+ if ( !mLightFragment )
+ {
+ llwarns << "Failed to load " << lightfragment << llendl;
+ return FALSE;
+ }
+
+ // NOTE: Scatter shaders use the ENVIRONMENT detail level
+
+ std::string scattervertex = "environment/scatterV.glsl";
+ mScatterVertex = loadShader(scattervertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB);
+ if ( !mScatterVertex )
+ {
+ llwarns << "Failed to load " << scattervertex << llendl;
+ return FALSE;
+ }
+
+ std::string scatterfragment = "environment/scatterF.glsl";
+ mScatterFragment = loadShader(scatterfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB);
+ if ( !mScatterFragment )
+ {
+ llwarns << "Failed to load " << scatterfragment << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersEnvironment()
+{
+ GLhandleARB baseObjects[] =
+ {
+ mLightFragment,
+ mLightVertex,
+ mScatterFragment,
+ mScatterVertex
+ };
+ S32 baseCount = 4;
+
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_ENVIRONMENT] == 0)
+ {
+ mWaterProgram.unload();
+ mGroundProgram.unload();
+ mTerrainProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load water vertex shader
+ std::string waterfragment = "environment/waterF.glsl";
+ std::string watervertex = "environment/waterV.glsl";
+ mWaterProgram.mProgramObject = glCreateProgramObjectARB();
+ mWaterProgram.attachObjects(baseObjects, baseCount);
+ mWaterProgram.attachObject(loadShader(watervertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB));
+ mWaterProgram.attachObject(loadShader(waterfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB));
+
+ success = mWaterProgram.mapAttributes();
+ if (success)
+ {
+ success = mWaterProgram.mapUniforms(sWaterUniforms, sWaterUniformCount);
+ }
+ if (!success)
+ {
+ llwarns << "Failed to load " << watervertex << llendl;
+ }
+ }
+ if (success)
+ {
+ //load ground vertex shader
+ std::string groundvertex = "environment/groundV.glsl";
+ std::string groundfragment = "environment/groundF.glsl";
+ mGroundProgram.mProgramObject = glCreateProgramObjectARB();
+ mGroundProgram.attachObjects(baseObjects, baseCount);
+ mGroundProgram.attachObject(loadShader(groundvertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB));
+ mGroundProgram.attachObject(loadShader(groundfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB));
+
+ success = mGroundProgram.mapAttributes();
+ if (success)
+ {
+ success = mGroundProgram.mapUniforms();
+ }
+ if (!success)
+ {
+ llwarns << "Failed to load " << groundvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load terrain vertex shader
+ std::string terrainvertex = "environment/terrainV.glsl";
+ std::string terrainfragment = "environment/terrainF.glsl";
+ mTerrainProgram.mProgramObject = glCreateProgramObjectARB();
+ mTerrainProgram.attachObjects(baseObjects, baseCount);
+ mTerrainProgram.attachObject(loadShader(terrainvertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB));
+ mTerrainProgram.attachObject(loadShader(terrainfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB));
+ success = mTerrainProgram.mapAttributes();
+ if (success)
+ {
+ success = mTerrainProgram.mapUniforms(sTerrainUniforms, sTerrainUniformCount);
+ }
+ if (!success)
+ {
+ llwarns << "Failed to load " << terrainvertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ mMaxVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ return FALSE;
+ }
+
+ if (gWorldPointer)
+ {
+ gWorldPointer->updateWaterObjects();
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersObject()
+{
+ GLhandleARB baseObjects[] =
+ {
+ mLightFragment,
+ mLightVertex,
+ mScatterFragment,
+ mScatterVertex
+ };
+ S32 baseCount = 4;
+
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_OBJECT] == 0)
+ {
+ mObjectSimpleProgram.unload();
+ mObjectBumpProgram.unload();
+ mObjectAlphaProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load object (volume/tree) vertex shader
+ std::string simplevertex = "objects/simpleV.glsl";
+ std::string simplefragment = "objects/simpleF.glsl";
+ mObjectSimpleProgram.mProgramObject = glCreateProgramObjectARB();
+ mObjectSimpleProgram.attachObjects(baseObjects, baseCount);
+ mObjectSimpleProgram.attachObject(loadShader(simplevertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB));
+ mObjectSimpleProgram.attachObject(loadShader(simplefragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB));
+ success = mObjectSimpleProgram.mapAttributes();
+ if (success)
+ {
+ success = mObjectSimpleProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << simplevertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load object bumpy vertex shader
+ std::string bumpshinyvertex = "objects/bumpshinyV.glsl";
+ std::string bumpshinyfragment = "objects/bumpshinyF.glsl";
+ mObjectBumpProgram.mProgramObject = glCreateProgramObjectARB();
+ mObjectBumpProgram.attachObjects(baseObjects, baseCount);
+ mObjectBumpProgram.attachObject(loadShader(bumpshinyvertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB));
+ mObjectBumpProgram.attachObject(loadShader(bumpshinyfragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB));
+ success = mObjectBumpProgram.mapAttributes();
+ if (success)
+ {
+ success = mObjectBumpProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << bumpshinyvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load object alpha vertex shader
+ std::string alphavertex = "objects/alphaV.glsl";
+ std::string alphafragment = "objects/alphaF.glsl";
+ mObjectAlphaProgram.mProgramObject = glCreateProgramObjectARB();
+ mObjectAlphaProgram.attachObjects(baseObjects, baseCount);
+ mObjectAlphaProgram.attachObject(loadShader(alphavertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB));
+ mObjectAlphaProgram.attachObject(loadShader(alphafragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB));
+
+ success = mObjectAlphaProgram.mapAttributes();
+ if (success)
+ {
+ success = mObjectAlphaProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << alphavertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_OBJECT] = 0;
+ mMaxVertexShaderLevel[SHADER_OBJECT] = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersAvatar()
+{
+ GLhandleARB baseObjects[] =
+ {
+ mLightFragment,
+ mLightVertex,
+ mScatterFragment,
+ mScatterVertex
+ };
+ S32 baseCount = 4;
+
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_AVATAR] == 0)
+ {
+ mAvatarProgram.unload();
+ mAvatarEyeballProgram.unload();
+ mAvatarPickProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load specular (eyeball) vertex program
+ std::string eyeballvertex = "avatar/eyeballV.glsl";
+ std::string eyeballfragment = "avatar/eyeballF.glsl";
+ mAvatarEyeballProgram.mProgramObject = glCreateProgramObjectARB();
+ mAvatarEyeballProgram.attachObjects(baseObjects, baseCount);
+ mAvatarEyeballProgram.attachObject(loadShader(eyeballvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB));
+ mAvatarEyeballProgram.attachObject(loadShader(eyeballfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB));
+ success = mAvatarEyeballProgram.mapAttributes();
+ if (success)
+ {
+ success = mAvatarEyeballProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << eyeballvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ mAvatarSkinVertex = loadShader("avatar/avatarSkinV.glsl", SHADER_AVATAR, GL_VERTEX_SHADER_ARB);
+ //load avatar vertex shader
+ std::string avatarvertex = "avatar/avatarV.glsl";
+ std::string avatarfragment = "avatar/avatarF.glsl";
+
+ mAvatarProgram.mProgramObject = glCreateProgramObjectARB();
+ mAvatarProgram.attachObjects(baseObjects, baseCount);
+ mAvatarProgram.attachObject(mAvatarSkinVertex);
+ mAvatarProgram.attachObject(loadShader(avatarvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB));
+ mAvatarProgram.attachObject(loadShader(avatarfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB));
+
+ success = mAvatarProgram.mapAttributes(sAvatarAttribs, sAvatarAttribCount);
+ if (success)
+ {
+ success = mAvatarProgram.mapUniforms(sAvatarUniforms, sAvatarUniformCount);
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << avatarvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load avatar picking shader
+ std::string pickvertex = "avatar/pickAvatarV.glsl";
+ std::string pickfragment = "avatar/pickAvatarF.glsl";
+ mAvatarPickProgram.mProgramObject = glCreateProgramObjectARB();
+ mAvatarPickProgram.attachObject(loadShader(pickvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB));
+ mAvatarPickProgram.attachObject(loadShader(pickfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB));
+ mAvatarPickProgram.attachObject(mAvatarSkinVertex);
+
+ success = mAvatarPickProgram.mapAttributes(sAvatarAttribs, sAvatarAttribCount);
+ if (success)
+ {
+ success = mAvatarPickProgram.mapUniforms(sAvatarUniforms, sAvatarUniformCount);
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << pickvertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ mMaxVertexShaderLevel[SHADER_AVATAR] = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersInterface()
+{
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_INTERFACE] == 0)
+ {
+ mHighlightProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load highlighting shader
+ std::string highlightvertex = "interface/highlightV.glsl";
+ std::string highlightfragment = "interface/highlightF.glsl";
+ mHighlightProgram.mProgramObject = glCreateProgramObjectARB();
+ mHighlightProgram.attachObject(loadShader(highlightvertex, SHADER_INTERFACE, GL_VERTEX_SHADER_ARB));
+ mHighlightProgram.attachObject(loadShader(highlightfragment, SHADER_INTERFACE, GL_FRAGMENT_SHADER_ARB));
+
+ success = mHighlightProgram.mapAttributes();
+ if (success)
+ {
+ success = mHighlightProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << highlightvertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_INTERFACE] = 0;
+ mMaxVertexShaderLevel[SHADER_INTERFACE] = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//============================================================================
+
+void LLPipeline::enableShadows(const BOOL enable_shadows)
+{
+ //should probably do something here to wrangle shadows....
+}
+
+S32 LLPipeline::getMaxLightingDetail() const
+{
+ if (mVertexShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS)
+ {
+ return 3;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+S32 LLPipeline::setLightingDetail(S32 level)
+{
+ if (level < 0)
+ {
+ level = gSavedSettings.getS32("RenderLightingDetail");
+ }
+ level = llclamp(level, 0, getMaxLightingDetail());
+ if (level != mLightingDetail)
+ {
+ gSavedSettings.setS32("RenderLightingDetail", level);
+ if (level >= 2)
+ {
+ gObjectList.relightAllObjects();
+ }
+ mLightingDetail = level;
+
+ if (mVertexShadersLoaded == 1)
+ {
+ gPipeline.setShaders();
+ }
+ }
+ return mLightingDetail;
+}
+
+LLAGPMemBlock *LLPipeline::allocAGPFromPool(const S32 bytes, const U32 target)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (!mAGPMemPool)
+ {
+ llwarns << "Attempting to allocate AGP memory when AGP disabled!" << llendl;
+ return NULL;
+ }
+ else
+ {
+ if (mUseVBO)
+ {
+ return ((LLAGPMemPoolARB*) mAGPMemPool)->allocBlock(bytes, target);
+ }
+ else
+ {
+ return mAGPMemPool->allocBlock(bytes);
+ }
+ }
+}
+
+
+void LLPipeline::unbindAGP()
+{
+ if (mAGPMemPool && mAGPBound)
+ {
+ mAGPMemPool->disable();
+ mAGPBound = FALSE;
+ }
+}
+
+void LLPipeline::bindAGP()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (mAGPMemPool && !mAGPBound && usingAGP())
+ {
+ mAGPMemPool->enable();
+ mAGPBound = TRUE;
+ }
+}
+
+U8* LLPipeline::bufferGetScratchMemory(void)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ return(mBufferMemory[mBufferIndex]->getScratchMemory());
+}
+
+void LLPipeline::bufferWaitFence(void)
+{
+ mBufferMemory[mBufferIndex]->waitFence(mBufferFence[mBufferIndex]);
+}
+
+void LLPipeline::bufferSendFence(void)
+{
+ mBufferMemory[mBufferIndex]->sendFence(mBufferFence[mBufferIndex]);
+}
+
+void LLPipeline::bufferRotate(void)
+{
+ mBufferIndex++;
+ if(mBufferIndex >= mBufferCount)
+ mBufferIndex = 0;
+}
+
+// Called when a texture changes # of channels (rare, may cause faces to move to alpha pool)
+void LLPipeline::dirtyPoolObjectTextures(const LLViewerImage *texturep)
+{
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->dirtyTexture(texturep);
+ }
+}
+
+LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerImage *tex0)
+{
+ LLDrawPool *poolp = NULL;
+ switch( type )
+ {
+ case LLDrawPool::POOL_SIMPLE:
+ poolp = get_if_there(mSimplePools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_TREE:
+ poolp = get_if_there(mTreePools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_TREE_NEW:
+ poolp = get_if_there(mTreeNewPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_TERRAIN:
+ poolp = get_if_there(mTerrainPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_BUMP:
+ poolp = get_if_there(mBumpPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_MEDIA:
+ poolp = get_if_there(mMediaPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_ALPHA:
+ poolp = mAlphaPool;
+ break;
+
+ case LLDrawPool::POOL_AVATAR:
+ break; // Do nothing
+
+ case LLDrawPool::POOL_SKY:
+ poolp = mSkyPool;
+ break;
+
+ case LLDrawPool::POOL_STARS:
+ poolp = mStarsPool;
+ break;
+
+ case LLDrawPool::POOL_CLOUDS:
+ poolp = mCloudsPool;
+ break;
+
+ case LLDrawPool::POOL_WATER:
+ poolp = mWaterPool;
+ break;
+
+ case LLDrawPool::POOL_GROUND:
+ poolp = mGroundPool;
+ break;
+
+ case LLDrawPool::POOL_HUD:
+ poolp = mHUDPool;
+ break;
+
+ default:
+ llassert(0);
+ llerrs << "Invalid Pool Type in LLPipeline::findPool() type=" << type << llendl;
+ break;
+ }
+
+ return poolp;
+}
+
+
+LLDrawPool *LLPipeline::getPool(const U32 type, LLViewerImage *tex0)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ LLDrawPool *poolp = findPool(type, tex0);
+ if (poolp)
+ {
+ return poolp;
+ }
+
+ LLDrawPool *new_poolp = LLDrawPool::createPool(type, tex0);
+ addPool( new_poolp );
+
+ return new_poolp;
+}
+
+
+// static
+LLDrawPool* LLPipeline::getPoolFromTE(const LLTextureEntry* te, LLViewerImage* imagep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ bool alpha = te->getColor().mV[3] < 0.999f;
+ if (imagep)
+ {
+ alpha = alpha || (imagep->getComponents() == 4) || (imagep->getComponents() == 2);
+ }
+
+ if (te->getMediaFlags() == LLTextureEntry::MF_WEB_PAGE)
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_MEDIA, imagep);
+ }
+ else if (alpha)
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ }
+ else if ((te->getBumpmap() || te->getShiny()))
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_BUMP, imagep);
+ }
+ else
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep);
+ }
+}
+
+
+void LLPipeline::addPool(LLDrawPool *new_poolp)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ mPools.insert(new_poolp);
+ addToQuickLookup( new_poolp );
+}
+
+void LLPipeline::allocDrawable(LLViewerObject *vobj)
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+ LLDrawable *drawable = new LLDrawable();
+ vobj->mDrawable = drawable;
+
+ drawable->mVObjp = vobj;
+
+ //encompass completely sheared objects by taking
+ //the most extreme point possible (<1,1,0.5>)
+ drawable->setRadius(LLVector3(1,1,0.5f).scaleVec(vobj->getScale()).magVec());
+ if (vobj->isOrphaned())
+ {
+ drawable->setState(LLDrawable::FORCE_INVISIBLE);
+ }
+ drawable->updateXform(TRUE);
+}
+
+
+void LLPipeline::unlinkDrawable(LLDrawable *drawablep)
+{
+ LLFastTimer t(LLFastTimer::FTM_PIPELINE);
+
+ // Based on flags, remove the drawable from the queues that it's on.
+ if (drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.erase(drawablep);
+ }
+
+ if (drawablep->getSpatialGroup())
+ {
+ if (!drawablep->getSpatialGroup()->mSpatialPartition->remove(drawablep, drawablep->getSpatialGroup()))
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Couldn't remove object from spatial group!" << llendl;
+#else
+ llerrs << "Couldn't remove object from spatial group!" << llendl;
+#endif
+ }
+ }
+
+ mLights.erase(drawablep);
+}
+
+U32 LLPipeline::addObject(LLViewerObject *vobj)
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+ if (gNoRender)
+ {
+ return 0;
+ }
+
+ LLDrawable *drawablep = vobj->createDrawable(this);
+
+ llassert(drawablep);
+
+ //mCompleteSet.put(drawable);
+ //gResyncObjects = TRUE;
+
+ if (vobj->getParent())
+ {
+ vobj->setDrawableParent(((LLViewerObject*)vobj->getParent())->mDrawable); // LLPipeline::addObject 1
+ }
+ else
+ {
+ vobj->setDrawableParent(NULL); // LLPipeline::addObject 2
+ }
+
+
+ if ((!drawablep->getVOVolume()) &&
+ (vobj->getPCode() != LLViewerObject::LL_VO_SKY) &&
+ (vobj->getPCode() != LLViewerObject::LL_VO_STARS) &&
+ (vobj->getPCode() != LLViewerObject::LL_VO_GROUND))
+ {
+ drawablep->getSpatialPartition()->put(drawablep);
+ if (!drawablep->getSpatialGroup())
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Failure adding drawable to object partition!" << llendl;
+#else
+ llerrs << "Failure adding drawable to object partition!" << llendl;
+#endif
+ }
+ }
+ else
+ {
+ markMoved(drawablep);
+ }
+
+ markMaterialed(drawablep);
+ markRebuild(drawablep, LLDrawable::REBUILD_ALL, TRUE);
+
+ return 1;
+}
+
+
+void LLPipeline::resetFrameStats()
+{
+ sCompiles = 0;
+ mVerticesRelit = 0;
+ mLightingChanges = 0;
+ mGeometryChanges = 0;
+ mNumVisibleFaces = 0;
+}
+
+//external functions for asynchronous updating
+void LLPipeline::updateMoveDampedAsync(LLDrawable* drawablep)
+{
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return;
+ }
+ if (!drawablep)
+ {
+ llerrs << "updateMove called with NULL drawablep" << llendl;
+ }
+ if (drawablep->isState(LLDrawable::EARLY_MOVE))
+ {
+ return;
+ }
+ // update drawable now
+ drawablep->clearState(LLDrawable::MOVE_UNDAMPED); // force to DAMPED
+ drawablep->updateMove(); // returns done
+ drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame
+ // Put on move list so that EARLY_MOVE gets cleared
+ if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.insert(drawablep);
+ drawablep->setState(LLDrawable::ON_MOVE_LIST);
+ }
+}
+
+void LLPipeline::updateMoveNormalAsync(LLDrawable* drawablep)
+{
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return;
+ }
+ if (!drawablep)
+ {
+ llerrs << "updateMove called with NULL drawablep" << llendl;
+ }
+ if (drawablep->isState(LLDrawable::EARLY_MOVE))
+ {
+ return;
+ }
+ // update drawable now
+ drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED
+ drawablep->updateMove();
+ drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame
+ // Put on move list so that EARLY_MOVE gets cleared
+ if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.insert(drawablep);
+ drawablep->setState(LLDrawable::ON_MOVE_LIST);
+ }
+}
+
+void LLPipeline::updateMove()
+{
+ mObjectPartition->mOctree->validate();
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE);
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return;
+ }
+
+ mMoveChangesStat.addValue((F32)mMovedList.size());
+
+ for (LLDrawable::drawable_set_t::iterator iter = mMovedList.begin();
+ iter != mMovedList.end(); )
+ {
+ LLDrawable::drawable_set_t::iterator curiter = iter++;
+ LLDrawable *drawablep = *curiter;
+ BOOL done = TRUE;
+ if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE)))
+ {
+ done = drawablep->updateMove();
+ }
+ drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED);
+ if (done)
+ {
+ mMovedList.erase(curiter);
+ drawablep->clearState(LLDrawable::ON_MOVE_LIST);
+ }
+ }
+
+ for (LLDrawable::drawable_set_t::iterator iter = mActiveQ.begin();
+ iter != mActiveQ.end(); )
+ {
+ LLDrawable::drawable_set_t::iterator curiter = iter++;
+ LLDrawable* drawablep = *curiter;
+ if (drawablep && !drawablep->isDead())
+ {
+ if (drawablep->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES &&
+ (!drawablep->getParent() || !drawablep->getParent()->isActive()))
+ {
+ drawablep->makeStatic(); // removes drawable and its children from mActiveQ
+ iter = mActiveQ.upper_bound(drawablep); // next valid entry
+ }
+ }
+ else
+ {
+ mActiveQ.erase(curiter);
+ }
+ }
+
+ for (LLDrawable::drawable_set_t::iterator iter = mRetexturedList.begin();
+ iter != mRetexturedList.end(); ++iter)
+ {
+ LLDrawable* drawablep = *iter;
+ if (drawablep && !drawablep->isDead())
+ {
+ drawablep->updateTexture();
+ }
+ }
+ mRetexturedList.clear();
+
+ for (LLDrawable::drawable_set_t::iterator iter = mRematerialedList.begin();
+ iter != mRematerialedList.end(); ++iter)
+ {
+ LLDrawable* drawablep = *iter;
+ if (drawablep && !drawablep->isDead())
+ {
+ drawablep->updateMaterial();
+ }
+ }
+ mRematerialedList.clear();
+
+ if (mObjectPartition->mOctree)
+ {
+ //balance octree
+ LLFastTimer ot(LLFastTimer::FTM_OCTREE_BALANCE);
+ mObjectPartition->mOctree->validate();
+ mObjectPartition->mOctree->balance();
+ mObjectPartition->mOctree->validate();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Culling and occlusion testing
+/////////////////////////////////////////////////////////////////////////////
+
+void LLPipeline::updateCull()
+{
+ LLFastTimer t(LLFastTimer::FTM_CULL);
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ LLDrawable::incrementVisible();
+ mVisibleList.resize(0);
+ mVisibleList.reserve(ESTIMATED_VISIBLE_OBJECT_COUNT);
+
+ gTrivialAccepts = 0;
+
+ if (mObjectPartition)
+ {
+ if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery)
+ {
+ mObjectPartition->processOcclusion(gCamera);
+ stop_glerror();
+ }
+ mObjectPartition->cull(*gCamera);
+ }
+
+ // Hack for avatars - warning - this is really FRAGILE! - djs 05/06/02
+ LLVOAvatar::updateAllAvatarVisiblity();
+
+ // If there are any other hacks here, make sure to add them to the
+ // standard pick code.
+
+ gMinObjectDistance = llclamp(gMinObjectDistance, MIN_NEAR_PLANE, MAX_NEAR_PLANE);
+
+ F32 water_height = gAgent.getRegion()->getWaterHeight();
+ F32 camera_height = gAgent.getCameraPositionAgent().mV[2];
+ if (fabs(camera_height - water_height) < 2.f)
+ {
+ gMinObjectDistance = MIN_NEAR_PLANE;
+ }
+
+ gCamera->setNear(gMinObjectDistance);
+
+ // Disable near clip stuff for now...
+
+ // now push it back out to max value
+ gMinObjectDistance = MIN_NEAR_PLANE;
+
+ if (gSky.mVOSkyp.notNull() && gSky.mVOSkyp->mDrawable.notNull())
+ {
+ // Hack for sky - always visible.
+ gSky.mVOSkyp->mDrawable->setVisible(*gCamera);
+ mVisibleList.push_back(gSky.mVOSkyp->mDrawable);
+ gSky.updateCull();
+ stop_glerror();
+ }
+ else
+ {
+ llinfos << "No sky drawable!" << llendl;
+ }
+
+ if (gSky.mVOGroundp.notNull() && gSky.mVOGroundp->mDrawable.notNull())
+ {
+ gSky.mVOGroundp->mDrawable->setVisible(*gCamera);
+ mVisibleList.push_back(gSky.mVOGroundp->mDrawable);
+ }
+
+ // add all HUD attachments
+ LLVOAvatar* my_avatarp = gAgent.getAvatarObject();
+ if (my_avatarp && my_avatarp->hasHUDAttachment())
+ {
+ for (LLViewerJointAttachment* attachmentp = my_avatarp->mAttachmentPoints.getFirstData();
+ attachmentp;
+ attachmentp = my_avatarp->mAttachmentPoints.getNextData())
+ {
+ if (attachmentp->getIsHUDAttachment() && attachmentp->getObject(0))
+ {
+ LLViewerObject* objectp = attachmentp->getObject(0);
+ markVisible(objectp->mDrawable);
+ objectp->mDrawable->updateDistance(*gCamera);
+ for (S32 i = 0; i < (S32)objectp->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = objectp->mChildList[i];
+ if (childp->mDrawable.notNull())
+ {
+ markVisible(childp->mDrawable);
+ childp->mDrawable->updateDistance(*gCamera);
+ }
+ }
+ }
+ }
+ }
+}
+
+void LLPipeline::markNotCulled(LLDrawable* drawablep, LLCamera& camera)
+{
+ if (drawablep->isVisible())
+ {
+ return;
+ }
+
+ // Tricky render mode to hide selected objects, but we definitely
+ // don't want to do any unnecessary pointer dereferences. JC
+ if (gHideSelectedObjects)
+ {
+ if (drawablep->getVObj() && drawablep->getVObj()->isSelected())
+ {
+ return;
+ }
+ }
+
+ if (drawablep && (hasRenderType(drawablep->mRenderType)))
+ {
+ if (!drawablep->isState(LLDrawable::INVISIBLE|LLDrawable::FORCE_INVISIBLE))
+ {
+ mVisibleList.push_back(drawablep);
+ drawablep->setVisible(camera, NULL, FALSE);
+ }
+ else if (drawablep->isState(LLDrawable::CLEAR_INVISIBLE))
+ {
+ // clear invisible flag here to avoid single frame glitch
+ drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE);
+ }
+ }
+}
+
+void LLPipeline::doOcclusion()
+{
+ if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery)
+ {
+ mObjectPartition->doOcclusion(gCamera);
+ }
+}
+
+
+BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority)
+{
+ BOOL update_complete = drawablep->updateGeometry(priority);
+ if (update_complete)
+ {
+ drawablep->setState(LLDrawable::BUILT);
+ mGeometryChanges++;
+ }
+ return update_complete;
+}
+
+void LLPipeline::updateGeom(F32 max_dtime)
+{
+ LLTimer update_timer;
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ LLPointer<LLDrawable> drawablep;
+
+ LLFastTimer t(LLFastTimer::FTM_GEO_UPDATE);
+
+ // notify various object types to reset internal cost metrics, etc.
+ // for now, only LLVOVolume does this to throttle LOD changes
+ LLVOVolume::preUpdateGeom();
+
+ // Iterate through all drawables on the priority build queue,
+ for (LLDrawable::drawable_set_t::iterator iter = mBuildQ1.begin();
+ iter != mBuildQ1.end();)
+ {
+ LLDrawable::drawable_set_t::iterator curiter = iter++;
+ LLDrawable* drawablep = *curiter;
+ BOOL update_complete = TRUE;
+ if (drawablep && !drawablep->isDead())
+ {
+ update_complete = updateDrawableGeom(drawablep, TRUE);
+ }
+ if (update_complete)
+ {
+ drawablep->clearState(LLDrawable::IN_REBUILD_Q1);
+ mBuildQ1.erase(curiter);
+ }
+ }
+
+ // Iterate through some drawables on the non-priority build queue
+ S32 min_count = 16;
+ if (mBuildQ2.size() > 1000)
+ {
+ min_count = mBuildQ2.size();
+ }
+ else
+ {
+ mBuildQ2.sort(LLDrawable::CompareDistanceGreaterVisibleFirst());
+ }
+
+ S32 count = 0;
+
+ max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, max_dtime);
+
+ for (LLDrawable::drawable_list_t::iterator iter = mBuildQ2.begin();
+ iter != mBuildQ2.end(); )
+ {
+ LLDrawable::drawable_list_t::iterator curiter = iter++;
+ LLDrawable* drawablep = *curiter;
+ BOOL update_complete = TRUE;
+ if (drawablep && !drawablep->isDead())
+ {
+ update_complete = updateDrawableGeom(drawablep, FALSE);
+ count++;
+ }
+ if (update_complete)
+ {
+ drawablep->clearState(LLDrawable::IN_REBUILD_Q2);
+ mBuildQ2.erase(curiter);
+ }
+ if ((update_timer.getElapsedTimeF32() >= max_dtime) && count > min_count)
+ {
+ break;
+ }
+ }
+}
+
+void LLPipeline::markVisible(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if(!drawablep || drawablep->isDead())
+ {
+ llwarns << "LLPipeline::markVisible called with NULL drawablep" << llendl;
+ return;
+ }
+ if (!drawablep->isVisible())
+ {
+ drawablep->setVisible(*gCamera);
+ mVisibleList.push_back(drawablep);
+ }
+}
+
+void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep)
+ {
+ llerrs << "Sending null drawable to moved list!" << llendl;
+ return;
+ }
+
+ if (drawablep->isDead())
+ {
+ llwarns << "Marking NULL or dead drawable moved!" << llendl;
+ return;
+ }
+
+ if (drawablep->getParent())
+ {
+ //ensure that parent drawables are moved first
+ markMoved(drawablep->getParent(), damped_motion);
+ }
+
+
+ if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.insert(drawablep);
+ drawablep->setState(LLDrawable::ON_MOVE_LIST);
+ }
+ if (damped_motion == FALSE)
+ {
+ drawablep->setState(LLDrawable::MOVE_UNDAMPED); // UNDAMPED trumps DAMPED
+ }
+ else if (drawablep->isState(LLDrawable::MOVE_UNDAMPED))
+ {
+ drawablep->clearState(LLDrawable::MOVE_UNDAMPED);
+ }
+}
+
+void LLPipeline::markShift(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep || drawablep->isDead())
+ {
+ return;
+ }
+
+ if (!drawablep->isState(LLDrawable::ON_SHIFT_LIST))
+ {
+ drawablep->getVObj()->setChanged(LLXform::SHIFTED | LLXform::SILHOUETTE);
+ if (drawablep->getParent())
+ {
+ markShift(drawablep->getParent());
+ }
+ mShiftList.push_back(drawablep);
+ drawablep->setState(LLDrawable::ON_SHIFT_LIST);
+ }
+}
+
+void LLPipeline::shiftObjects(const LLVector3 &offset)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ for (LLDrawable::drawable_vector_t::iterator iter = mShiftList.begin();
+ iter != mShiftList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+ drawablep->shiftPos(offset);
+ drawablep->clearState(LLDrawable::ON_SHIFT_LIST);
+ }
+ mShiftList.resize(0);
+
+ mObjectPartition->shift(offset);
+}
+
+void LLPipeline::markTextured(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep->isDead())
+ {
+ mRetexturedList.insert(drawablep);
+ }
+}
+
+void LLPipeline::markMaterialed(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep->isDead())
+ {
+ mRematerialedList.insert(drawablep);
+ }
+}
+
+void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag, BOOL priority)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (drawablep && !drawablep->isDead())
+ {
+ if (!drawablep->isState(LLDrawable::BUILT))
+ {
+ priority = TRUE;
+ }
+ if (priority)
+ {
+ mBuildQ1.insert(drawablep);
+ drawablep->setState(LLDrawable::IN_REBUILD_Q1); // flag is not needed, just for debugging
+ }
+ else if (!drawablep->isState(LLDrawable::IN_REBUILD_Q2))
+ {
+ mBuildQ2.push_back(drawablep);
+ drawablep->setState(LLDrawable::IN_REBUILD_Q2); // need flag here because it is just a list
+ }
+ if (flag & LLDrawable::REBUILD_VOLUME)
+ {
+ drawablep->getVObj()->setChanged(LLXform::SILHOUETTE);
+ }
+ drawablep->setState(flag);
+ if ((flag & LLDrawable::REBUILD_LIGHTING) && drawablep->getLit())
+ {
+ if (drawablep->isLight())
+ {
+ drawablep->clearState(LLDrawable::LIGHTING_BUILT);
+ }
+ else
+ {
+ drawablep->clearState(LLDrawable::LIGHTING_BUILT);
+ }
+ }
+ }
+}
+
+void LLPipeline::markRelight(LLDrawable *drawablep, const BOOL priority)
+{
+ if (getLightingDetail() >= 2)
+ {
+ markRebuild(drawablep, LLDrawable::REBUILD_LIGHTING, FALSE);
+ }
+}
+
+void LLPipeline::stateSort()
+{
+ LLFastTimer ftm(LLFastTimer::FTM_STATESORT);
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin();
+ iter != mVisibleList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+
+ if (!drawablep->isActive())
+ {
+ drawablep->updateDistance(*gCamera);
+ }
+
+ /*
+ if (!drawablep->isState(LLDrawable::BUILT))
+ {
+ // This geometry hasn't been rebuilt but it's visible, make sure it gets put on the rebuild list.
+ llerrs << "Visible object " << drawablep << ":" << drawablep->getVObj()->getPCodeString();
+ llcont << " visible but not built, put on rebuild" << llendl;
+ markRebuild(drawablep);
+ continue;
+ }
+ */
+
+ for (LLDrawable::face_list_t::iterator iter = drawablep->mFaces.begin();
+ iter != drawablep->mFaces.end(); iter++)
+ {
+ LLFace* facep = *iter;
+ if (facep->hasGeometry())
+ {
+ facep->getPool()->enqueue(facep);
+ }
+ }
+
+ if (sRenderPhysicalBeacons)
+ {
+ // Only show the beacon on the root object.
+ LLViewerObject *vobj = drawablep->getVObj();
+ if (vobj
+ && !vobj->isAvatar()
+ && !vobj->getParent()
+ && vobj->usePhysics())
+ {
+ gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(0.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ if (sRenderScriptedBeacons)
+ {
+ // Only show the beacon on the root object.
+ LLViewerObject *vobj = drawablep->getVObj();
+ if (vobj
+ && !vobj->isAvatar()
+ && !vobj->getParent()
+ && vobj->flagScripted())
+ {
+ gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ if (sRenderParticleBeacons)
+ {
+ // Look for attachments, objects, etc.
+ LLViewerObject *vobj = drawablep->getVObj();
+ if (vobj
+ && vobj->isParticleSource())
+ {
+ LLColor4 light_blue(0.5f, 0.5f, 1.f, 0.5f);
+ gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", light_blue, LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ // Draw physical objects in red.
+ if (gHUDManager->getShowPhysical())
+ {
+ LLViewerObject *vobj;
+ vobj = drawablep->getVObj();
+ if (vobj && !vobj->isAvatar())
+ {
+ if (!vobj->isAvatar() &&
+ (vobj->usePhysics() || vobj->flagHandleTouch()))
+ {
+ if (!drawablep->isVisible())
+ {
+ // Skip objects that aren't visible.
+ continue;
+ }
+ S32 face_id;
+ for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++)
+ {
+ mHighlightFaces.put(drawablep->getFace(face_id) );
+ }
+ }
+ }
+ }
+
+ mNumVisibleFaces += drawablep->getNumFaces();
+ }
+
+ // If god mode, also show audio cues
+ if (sRenderSoundBeacons && gAudiop)
+ {
+ // Update all of our audio sources, clean up dead ones.
+ LLAudioEngine::source_map::iterator iter;
+ for (iter = gAudiop->mAllSources.begin(); iter != gAudiop->mAllSources.end(); ++iter)
+ {
+ LLAudioSource *sourcep = iter->second;
+
+ LLVector3d pos_global = sourcep->getPositionGlobal();
+ LLVector3 pos = gAgent.getPosAgentFromGlobal(pos_global);
+ //pos += LLVector3(0.f, 0.f, 0.2f);
+ gObjectList.addDebugBeacon(pos, "", LLColor4(1.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ // If managing your telehub, draw beacons at telehub and currently selected spawnpoint.
+ if (LLFloaterTelehub::renderBeacons())
+ {
+ LLFloaterTelehub::addBeacons();
+ }
+
+ mSelectedFaces.reset();
+
+ // Draw face highlights for selected faces.
+ if (gSelectMgr->getTEMode())
+ {
+ LLViewerObject *vobjp;
+ S32 te;
+ gSelectMgr->getFirstTE(&vobjp,&te);
+
+ while (vobjp)
+ {
+ LLDrawable *drawablep = vobjp->mDrawable;
+ if (!drawablep || drawablep->isDead() || (!vobjp->isHUDAttachment() && !drawablep->isVisible()))
+ {
+ llwarns << "Dead drawable on selected face list!" << llendl;
+ }
+ else
+ {
+ LLVOVolume *volp = drawablep->getVOVolume();
+ if (volp)
+ {
+ if (volp->getAllTEsSame())
+ {
+ SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
+ faceinfo->mFacep = drawablep->getFace(vobjp->getFaceIndexOffset());
+ faceinfo->mTE = te;
+ }
+ else
+ {
+ // This is somewhat inefficient, but works correctly.
+ S32 face_id;
+ for (face_id = 0; face_id < vobjp->getVolume()->getNumFaces(); face_id++)
+ {
+ LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset());
+ if (te == facep->getTEOffset())
+ {
+ SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
+ faceinfo->mFacep = facep;
+ faceinfo->mTE = -1;
+ }
+ }
+ }
+ }
+ else
+ {
+ // This is somewhat inefficient, but works correctly.
+ S32 face_id;
+ for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++)
+ {
+ LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset());
+ if (te == facep->getTEOffset())
+ {
+ SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
+ faceinfo->mFacep = facep;
+ faceinfo->mTE = -1;
+ }
+ }
+ }
+ }
+ gSelectMgr->getNextTE(&vobjp,&te);
+ }
+ }
+}
+
+
+static void render_hud_elements()
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_UI);
+ gPipeline.disableLights();
+
+ gPipeline.renderDebug();
+
+ LLGLDisable fog(GL_FOG);
+ LLGLSUIDefault gls_ui;
+
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
+ {
+ gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD bersion in render_ui_3d()
+
+ // Draw the tracking overlays
+ LLTracker::render3D();
+
+ // Show the property lines
+ if (gWorldp)
+ {
+ gWorldp->renderPropertyLines();
+ }
+ if (gParcelMgr)
+ {
+ gParcelMgr->render();
+ gParcelMgr->renderParcelCollision();
+ }
+
+ // Render debugging beacons.
+ gObjectList.renderObjectBeacons();
+ LLHUDObject::renderAll();
+ gObjectList.resetObjectBeacons();
+ }
+ else if (gForceRenderLandFence)
+ {
+ // This is only set when not rendering the UI, for parcel snapshots
+ gParcelMgr->render();
+ }
+
+}
+
+void LLPipeline::renderHighlights()
+{
+ // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD)
+ // Render highlighted faces.
+ LLColor4 color(1.f, 1.f, 1.f, 0.5f);
+ LLGLEnable color_mat(GL_COLOR_MATERIAL);
+ disableLights();
+
+ if ((mVertexShaderLevel[SHADER_INTERFACE] > 0))
+ {
+ mHighlightProgram.bind();
+ gPipeline.mHighlightProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,1,0,0,0.5f);
+ }
+
+ if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED))
+ {
+ // Make sure the selection image gets downloaded and decoded
+ if (!mFaceSelectImagep)
+ {
+ mFaceSelectImagep = gImageList.getImage(IMG_FACE_SELECT);
+ }
+ mFaceSelectImagep->addTextureStats((F32)MAX_IMAGE_AREA);
+
+ for (S32 i = 0; i < mSelectedFaces.count(); i++)
+ {
+ LLFace *facep = mSelectedFaces[i].mFacep;
+ if (!facep || facep->getDrawable()->isDead())
+ {
+ llerrs << "Bad face on selection" << llendl;
+ }
+
+ LLDrawPool* poolp = facep->getPool();
+
+ if (!poolp->canUseAGP())
+ {
+ unbindAGP();
+ }
+ else if (usingAGP())
+ {
+ bindAGP();
+ }
+
+ if (mSelectedFaces[i].mTE == -1)
+ {
+ // Yes, I KNOW this is stupid...
+ poolp->renderFaceSelected(facep, mFaceSelectImagep, color);
+ }
+ else
+ {
+ LLVOVolume *volp = (LLVOVolume *)facep->getViewerObject();
+ // Do the special coalesced face mode.
+ S32 j;
+ S32 offset = 0;
+ S32 count = volp->getVolume()->getVolumeFace(0).mIndices.size();
+ for (j = 0; j <= mSelectedFaces[i].mTE; j++)
+ {
+ count = volp->getVolume()->getVolumeFace(j).mIndices.size();
+ if (j < mSelectedFaces[i].mTE)
+ {
+ offset += count;
+ }
+ }
+
+ poolp->renderFaceSelected(facep, mFaceSelectImagep, color, offset, count);
+ }
+ }
+ }
+
+ if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED))
+ {
+ // Paint 'em red!
+ color.setVec(1.f, 0.f, 0.f, 0.5f);
+ for (S32 i = 0; i < mHighlightFaces.count(); i++)
+ {
+ LLFace* facep = mHighlightFaces[i];
+ LLDrawPool* poolp = facep->getPool();
+ if (!poolp->canUseAGP())
+ {
+ unbindAGP();
+ }
+ else if (usingAGP())
+ {
+ bindAGP();
+ }
+
+ poolp->renderFaceSelected(facep, LLViewerImage::sNullImagep, color);
+ }
+ }
+
+ // Contains a list of the faces of objects that are physical or
+ // have touch-handlers.
+ mHighlightFaces.reset();
+
+ if (mVertexShaderLevel[SHADER_INTERFACE] > 0)
+ {
+ mHighlightProgram.unbind();
+ }
+}
+
+void LLPipeline::renderGeom()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ LLFastTimer t(LLFastTimer::FTM_RENDER_GEOMETRY);
+
+ if (!mAlphaSizzleImagep)
+ {
+ mAlphaSizzleImagep = gImageList.getImage(LLUUID(gViewerArt.getString("alpha_sizzle.tga")), MIPMAP_TRUE, TRUE);
+ }
+
+ ///////////////////////////////////////////
+ //
+ // Sync and verify GL state
+ //
+ //
+
+ stop_glerror();
+ gFrameStats.start(LLFrameStats::RENDER_SYNC);
+
+ // Do verification of GL state
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+#endif
+ if (mRenderDebugMask & RENDER_DEBUG_VERIFY)
+ {
+ if (!verify())
+ {
+ llerrs << "Pipeline verification failed!" << llendl;
+ }
+ }
+
+ if (mAGPMemPool)
+ {
+ mAGPMemPool->waitFence(mGlobalFence);
+ }
+
+ unbindAGP();
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (hasRenderType(poolp->getType()))
+ {
+ poolp->prerender();
+ poolp->syncAGP();
+ }
+ }
+
+ gFrameStats.start(LLFrameStats::RENDER_GEOM);
+
+ // Initialize lots of GL state to "safe" values
+ mTrianglesDrawn = 0;
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLGLSPipeline gls_pipeline;
+
+ LLGLState gls_color_material(GL_COLOR_MATERIAL, mLightingDetail < 2);
+ LLGLState normalize(GL_NORMALIZE, TRUE);
+
+ // Toggle backface culling for debugging
+ LLGLEnable cull_face(mBackfaceCull ? GL_CULL_FACE : 0);
+ // Set fog
+ LLGLEnable fog_enable(hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG) ? GL_FOG : 0);
+
+
+ LLViewerImage::sDefaultImagep->bind(0);
+ LLViewerImage::sDefaultImagep->setClamp(FALSE, FALSE);
+
+ //////////////////////////////////////////////
+ //
+ // Actually render all of the geometry
+ //
+ //
+
+ stop_glerror();
+ BOOL non_agp = FALSE;
+ BOOL did_hud_elements = FALSE;
+
+ U32 cur_type = 0;
+
+ S32 skipped_vertices = 0;
+ {
+ LLFastTimer t(LLFastTimer::FTM_POOLS);
+ BOOL occlude = TRUE;
+
+ calcNearbyLights();
+
+ pool_set_t::iterator iter1 = mPools.begin();
+ while ( iter1 != mPools.end() )
+ {
+ LLDrawPool *poolp = *iter1;
+
+ cur_type = poolp->getType();
+
+ if (cur_type >= LLDrawPool::POOL_TREE && occlude)
+ { //all the occluders have been drawn, do occlusion queries
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+ doOcclusion();
+ occlude = FALSE;
+ }
+
+ if (cur_type >= LLDrawPool::POOL_HUD && !did_hud_elements)
+ {
+ renderHighlights();
+ // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD)
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+ render_hud_elements();
+ did_hud_elements = TRUE;
+ }
+
+ pool_set_t::iterator iter2 = iter1;
+ if (hasRenderType(poolp->getType()))
+ {
+ LLFastTimer t(LLFastTimer::FTM_POOLRENDER);
+
+ setupHWLights(poolp);
+
+ if (mVertexShadersEnabled && poolp->getVertexShaderLevel() == 0)
+ {
+ glUseProgramObjectARB(0);
+ }
+ else if (mVertexShadersEnabled)
+ {
+ mMaterialIndex = mSpecularIndex = 0;
+ switch(cur_type)
+ {
+ case LLDrawPool::POOL_SKY:
+ case LLDrawPool::POOL_STARS:
+ case LLDrawPool::POOL_CLOUDS:
+ glUseProgramObjectARB(0);
+ break;
+ case LLDrawPool::POOL_TERRAIN:
+ mTerrainProgram.bind();
+ break;
+ case LLDrawPool::POOL_GROUND:
+ mGroundProgram.bind();
+ break;
+ case LLDrawPool::POOL_TREE:
+ case LLDrawPool::POOL_TREE_NEW:
+ case LLDrawPool::POOL_SIMPLE:
+ case LLDrawPool::POOL_MEDIA:
+ mObjectSimpleProgram.bind();
+ break;
+ case LLDrawPool::POOL_BUMP:
+ mObjectBumpProgram.bind();
+ break;
+ case LLDrawPool::POOL_AVATAR:
+ glUseProgramObjectARB(0);
+ break;
+ case LLDrawPool::POOL_WATER:
+ glUseProgramObjectARB(0);
+ break;
+ case LLDrawPool::POOL_ALPHA:
+ mObjectAlphaProgram.bind();
+ break;
+ case LLDrawPool::POOL_HUD:
+ default:
+ glUseProgramObjectARB(0);
+ break;
+ }
+ }
+
+ for( S32 i = 0; i < poolp->getNumPasses(); i++ )
+ {
+ poolp->beginRenderPass(i);
+ for (iter2 = iter1; iter2 != mPools.end(); iter2++)
+ {
+ LLDrawPool *p = *iter2;
+ if (p->getType() != cur_type)
+ {
+ break;
+ }
+ if (p->getType() != LLDrawPool::POOL_AVATAR
+ && p->getType() != LLDrawPool::POOL_ALPHA
+ && p->getType() != LLDrawPool::POOL_HUD
+ && (!p->getIndexCount() || !p->getVertexCount()))
+ {
+ continue;
+ }
+
+ if (p->canUseAGP() && usingAGP())
+ {
+ bindAGP();
+ }
+ else
+ {
+ //llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl;
+ unbindAGP();
+ non_agp = TRUE;
+ }
+
+ p->resetTrianglesDrawn();
+ p->render(i);
+ mTrianglesDrawn += p->getTrianglesDrawn();
+ skipped_vertices += p->mSkippedVertices;
+ p->mSkippedVertices = 0;
+ }
+ poolp->endRenderPass(i);
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+ LLGLState::checkClientArrays();
+#endif
+ }
+ }
+ else
+ {
+ // Skip all pools of this type
+ for (iter2 = iter1; iter2 != mPools.end(); iter2++)
+ {
+ LLDrawPool *p = *iter2;
+ if (p->getType() != cur_type)
+ {
+ break;
+ }
+ }
+ }
+ iter1 = iter2;
+ stop_glerror();
+ }
+
+ if (occlude)
+ {
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+ doOcclusion();
+ }
+ }
+ stop_glerror();
+
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+
+ if (!did_hud_elements)
+ {
+ renderHighlights();
+ render_hud_elements();
+ }
+
+ static S32 agp_mix_count = 0;
+ if (non_agp && usingAGP())
+ {
+ if (0 == agp_mix_count % 16)
+ {
+ lldebugs << "Mixing AGP and non-AGP pools, slow!" << llendl;
+ }
+ agp_mix_count++;
+ }
+ else
+ {
+ agp_mix_count = 0;
+ }
+
+ // Contains a list of the faces of objects that are physical or
+ // have touch-handlers.
+ mHighlightFaces.reset();
+
+ // This wait is in case we try to do multiple renders of a frame,
+ // I don't know what happens when we send a fence multiple times without
+ // checking it.
+ if (mAGPMemPool)
+ {
+ mAGPMemPool->waitFence(mGlobalFence);
+ mAGPMemPool->sendFence(mGlobalFence);
+ }
+}
+
+void LLPipeline::renderDebug()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ // Disable all client state
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ // Debug stuff.
+ mObjectPartition->renderDebug();
+
+ if (mRenderDebugMask & LLPipeline::RENDER_DEBUG_LIGHT_TRACE)
+ {
+ LLGLSNoTexture no_texture;
+
+ LLVector3 pos, pos1;
+
+ for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin();
+ iter != mVisibleList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+ for (LLDrawable::drawable_set_t::iterator iter = drawablep->mLightSet.begin();
+ iter != drawablep->mLightSet.end(); iter++)
+ {
+ LLDrawable *targetp = *iter;
+ if (targetp->isDead() || !targetp->getVObj()->getNumTEs())
+ {
+ continue;
+ }
+ else
+ {
+ if (targetp->getTextureEntry(0))
+ {
+ if (drawablep->getVObj()->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+ {
+ glColor4f(0.f, 1.f, 0.f, 1.f);
+ gObjectList.addDebugBeacon(drawablep->getPositionAgent(), "TC");
+ }
+ else
+ {
+ glColor4fv (targetp->getTextureEntry(0)->getColor().mV);
+ }
+ glBegin(GL_LINES);
+ glVertex3fv(targetp->getPositionAgent().mV);
+ glVertex3fv(drawablep->getPositionAgent().mV);
+ glEnd();
+ }
+ }
+ }
+ }
+ }
+
+ mCompilesStat.addValue(sCompiles);
+ mLightingChangesStat.addValue(mLightingChanges);
+ mGeometryChangesStat.addValue(mGeometryChanges);
+ mTrianglesDrawnStat.addValue(mTrianglesDrawn/1000.f);
+ mVerticesRelitStat.addValue(mVerticesRelit);
+ mNumVisibleFacesStat.addValue(mNumVisibleFaces);
+ mNumVisibleDrawablesStat.addValue((S32)mVisibleList.size());
+
+ if (gRenderLightGlows)
+ {
+ displaySSBB();
+ }
+
+ /*if (mRenderDebugMask & RENDER_DEBUG_BBOXES)
+ {
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+
+ for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin(); iter != mVisibleList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+ LLVector3 min, max;
+
+ if (drawablep->getVObj() && drawablep->getVObj()->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+ {
+ // Render drawable bbox
+ drawablep->getBounds(min, max);
+ glColor4f(0.f, 1.f, 0.f, 0.25f);
+ render_bbox(min, max);
+
+ // Render object bbox
+ LLVector3 scale = drawablep->getVObj()->getScale();
+ LLVector3 pos = drawablep->getVObj()->getPositionAgent();
+ min = pos - scale * 0.5f;
+ max = pos + scale * 0.5f;
+ glColor4f(1.f, 0.f, 0.f, 0.25f);
+ render_bbox(min, max);
+ }
+ }
+ }*/
+
+ /*
+ // Debugging code for parcel sound.
+ F32 x, y;
+
+ LLGLSNoTexture gls_no_texture;
+
+ glBegin(GL_POINTS);
+ if (gAgent.getRegion())
+ {
+ // Draw the composition layer for the region that I'm in.
+ for (x = 0; x <= 260; x++)
+ {
+ for (y = 0; y <= 260; y++)
+ {
+ if (gParcelMgr->isSoundLocal(gAgent.getRegion()->getOriginGlobal() + LLVector3d(x, y, 0.f)))
+ {
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.f, 0.f, 1.f, 1.f);
+ }
+
+ glVertex3f(x, y, gAgent.getRegion()->getLandHeightRegion(LLVector3(x, y, 0.f)));
+ }
+ }
+ }
+ glEnd();
+ */
+
+ if (mRenderDebugMask & RENDER_DEBUG_COMPOSITION)
+ {
+ // Debug composition layers
+ F32 x, y;
+
+ LLGLSNoTexture gls_no_texture;
+
+ glBegin(GL_POINTS);
+ if (gAgent.getRegion())
+ {
+ // Draw the composition layer for the region that I'm in.
+ for (x = 0; x <= 260; x++)
+ {
+ for (y = 0; y <= 260; y++)
+ {
+ if ((x > 255) || (y > 255))
+ {
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.f, 0.f, 1.f, 1.f);
+ }
+ F32 z = gAgent.getRegion()->getCompositionXY((S32)x, (S32)y);
+ z *= 5.f;
+ z += 50.f;
+ glVertex3f(x, y, z);
+ }
+ }
+ }
+ glEnd();
+ }
+
+ if (mRenderDebugMask & RENDER_DEBUG_AGP_MEM)
+ {
+ displayAGP();
+ }
+
+ if (mRenderDebugMask & RENDER_DEBUG_POOLS)
+ {
+ displayPools();
+ }
+
+// if (mRenderDebugMask & RENDER_DEBUG_QUEUES)
+// {
+// displayQueues();
+// }
+
+ if (mRenderDebugMask & RENDER_DEBUG_MAP)
+ {
+ displayMap();
+ }
+}
+
+
+
+
+BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max)
+{
+ min.setVec(1000,1000);
+ max.setVec(-1000,-1000);
+
+ if (box.mMatrix[3][3] <= 0.0f) return FALSE;
+
+ const F32 vec[8][3] = {
+ { -0.5f,-0.5f,-0.5f },
+ { -0.5f,-0.5f,+0.5f },
+ { -0.5f,+0.5f,-0.5f },
+ { -0.5f,+0.5f,+0.5f },
+ { +0.5f,-0.5f,-0.5f },
+ { +0.5f,-0.5f,+0.5f },
+ { +0.5f,+0.5f,-0.5f },
+ { +0.5f,+0.5f,+0.5f } };
+
+ LLVector4 v;
+
+ for (S32 i=0;i<8;i++)
+ {
+ v.setVec(vec[i][0],vec[i][1],vec[i][2],1);
+ v = v * box;
+ F32 iw = 1.0f / v.mV[3];
+ v.mV[0] *= iw;
+ v.mV[1] *= iw;
+
+ min.mV[0] = llmin(min.mV[0],v.mV[0]);
+ max.mV[0] = llmax(max.mV[0],v.mV[0]);
+
+ min.mV[1] = llmin(min.mV[1],v.mV[1]);
+ max.mV[1] = llmax(max.mV[1],v.mV[1]);
+ }
+
+ /*
+ min.mV[0] = max.mV[0] = box.mMatrix[3][0];
+ min.mV[1] = max.mV[1] = box.mMatrix[3][1];
+ F32 iw = 1.0f / box.mMatrix[3][3];
+
+ F32 f0 = (fabs(box.mMatrix[0][0])+fabs(box.mMatrix[1][0])+fabs(box.mMatrix[2][0])) * 0.5f;
+ F32 f1 = (fabs(box.mMatrix[0][1])+fabs(box.mMatrix[1][1])+fabs(box.mMatrix[2][1])) * 0.5f;
+ F32 f2 = (fabs(box.mMatrix[0][2])+fabs(box.mMatrix[1][2])+fabs(box.mMatrix[2][2])) * 0.5f;
+
+ min.mV[0] -= f0;
+ min.mV[1] -= f1;
+
+ max.mV[0] += f0;
+ max.mV[1] += f1;
+
+ min.mV[0] *= iw;
+ min.mV[1] *= iw;
+
+ max.mV[0] *= iw;
+ max.mV[1] *= iw;
+ */
+ return TRUE;
+}
+
+void LLPipeline::displaySSBB()
+{
+ LLMatrix4 proj;
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ LLMatrix4 camera;
+ LLMatrix4 comb;
+
+ gCamera->getMatrixToLocal(camera);
+
+ if (!mBloomImagep)
+ {
+ mBloomImagep = gImageList.getImage(IMG_BLOOM1);
+ }
+
+ // don't write to depth buffer with light glows so that chat bubbles can pop through
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ LLViewerImage::bindTexture(mBloomImagep);
+
+ glGetFloatv(GL_PROJECTION_MATRIX,(float*)proj.mMatrix);
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ //glScalef(0.25,0.25,0.25);
+
+ S32 sizex = gViewerWindow->getWindowWidth() / 2;
+ S32 sizey = gViewerWindow->getWindowHeight() / 2;
+
+ F32 aspect = (float)sizey / (float)sizex;
+
+ for (LLDrawable::drawable_set_t::iterator iter = mLights.begin();
+ iter != mLights.end(); iter++)
+ {
+ LLDrawable *lightp = *iter;
+ if (lightp->isDead())
+ {
+ continue;
+ }
+
+ LLMatrix4 mat = lightp->mXform.getWorldMatrix();
+
+ mat *= camera;
+ mat *= cfr;
+ mat *= proj;
+
+ U8 color[64];
+
+ LLVector2 min,max;
+ if (mat.mMatrix[3][3] < 160 && compute_min_max(mat,min,max))
+ {
+ F32 cx = (max.mV[0] + min.mV[0]) * 0.5f;
+ F32 cy = (max.mV[1] + min.mV[1]) * 0.5f;
+ F32 sx = (max.mV[0] - min.mV[0]) * 2.0f;
+ F32 sy = (max.mV[1] - min.mV[1]) * 2.0f;
+ S32 x = (S32)(cx * (F32)sizex) + sizex;
+ S32 y = (S32)(cy * (F32)sizey) + sizey;
+
+ if (cx > -1 && cx < 1 && cy > -1 && cy < 1)
+ {
+ glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]);
+
+ S32 total = 0;
+ for (S32 i=0;i<64;i++)
+ {
+ total += color[i];
+ }
+ total /= 64;
+
+ sx = (sy = (sx + sy) * 0.5f * ((float)total/255.0f)) * aspect;
+
+
+ if (total > 60)
+ {
+ color[3+32] = total >> 1;
+ glBegin(GL_QUADS);
+ glColor4ubv(&color[32]);
+ glTexCoord2f(0,0);
+ glVertex3f(cx-sx,cy-sy,0);
+ glTexCoord2f(1,0);
+ glVertex3f(cx+sx,cy-sy,0);
+ glTexCoord2f(1,1);
+ glVertex3f(cx+sx,cy+sy,0);
+ glTexCoord2f(0,1);
+ glVertex3f(cx-sx,cy+sy,0);
+ glEnd();
+ }
+ }
+ }
+
+ }
+
+ // sun
+ {
+ LLVector4 sdir(gSky.getSunDirection() * 10000.0f + gAgent.getPositionAgent());
+ sdir.mV[3] = 1.0f;
+ sdir = sdir * camera;
+ sdir = sdir * cfr;
+ sdir = sdir * proj; // todo: preconcat
+
+ sdir.mV[0] /= sdir.mV[3];
+ sdir.mV[1] /= sdir.mV[3];
+
+ U8 color[64];
+
+ if (sdir.mV[3] > 0)
+ {
+ F32 cx = sdir.mV[0];
+ F32 cy = sdir.mV[1];
+ F32 sx, sy;
+ S32 x = (S32)(cx * (F32)sizex) + sizex;
+ S32 y = (S32)(cy * (F32)sizey) + sizey;
+
+ if (cx > -1 && cx < 1 && cy > -1 && cy < 1)
+ {
+ glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]);
+
+ S32 total = 0;
+ for (S32 i=0;i<64;i++)
+ {
+ total += color[i];
+ }
+ total >>= 7;
+
+ sx = (sy = ((float)total/255.0f)) * aspect;
+
+ const F32 fix = -0.1f;
+
+ color[32] = (U8)(color[32] * 0.5f + 255 * 0.5f);
+ color[33] = (U8)(color[33] * 0.5f + 255 * 0.5f);
+ color[34] = (U8)(color[34] * 0.5f + 255 * 0.5f);
+
+ if (total > 80)
+ {
+ color[32+3] = (U8)total;
+ glBegin(GL_QUADS);
+ glColor4ubv(&color[32]);
+ glTexCoord2f(0,0);
+ glVertex3f(cx-sx,cy-sy+fix,0);
+ glTexCoord2f(1,0);
+ glVertex3f(cx+sx,cy-sy+fix,0);
+ glTexCoord2f(1,1);
+ glVertex3f(cx+sx,cy+sy+fix,0);
+ glTexCoord2f(0,1);
+ glVertex3f(cx-sx,cy+sy+fix,0);
+ glEnd();
+ }
+ }
+ }
+ }
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+void LLPipeline::displayMap()
+{
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glTranslatef(-1,-1,0);
+ glScalef(2,2,0);
+
+ glColor4f(0,0,0,0.5);
+ glBegin(GL_QUADS);
+ glVertex3f(0,0,0);
+ glVertex3f(1,0,0);
+ glVertex3f(1,1,0);
+ glVertex3f(0,1,0);
+ glEnd();
+
+ static F32 totalW = 1.5f;
+ static F32 offset = 0.5f;
+ static F32 scale = 1.0f;
+
+ S32 mousex = gViewerWindow->getCurrentMouseX();
+ S32 mousey = gViewerWindow->getCurrentMouseY();
+ S32 w = gViewerWindow->getWindowWidth();
+ S32 h = gViewerWindow->getWindowHeight();
+
+ if (mousex < 20 && offset > 0) offset -= (20 - mousex) * 0.02f;
+ if (mousex > (w - 20)) offset += (20 - (w - mousex)) * 0.02f;
+ if (offset > (totalW-1)) offset = (totalW-1);
+
+ if (mousey < 20 && scale > 0.1) scale -= (20 - mousey) * 0.001f;
+ if (mousey > (h - 20) && scale < 1.0f) scale += (20 - (h - mousey)) * 0.001f;
+
+ glScalef(scale*scale,scale,1);
+ glTranslatef(-offset,0,0);
+ //totalW = mStaticTree->render2D(0,0.8f/scale);
+ //mDynamicTree->render2D(0,0.4f/scale);
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+void LLPipeline::renderForSelect()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ //for each drawpool
+
+ glMatrixMode(GL_MODELVIEW);
+
+ LLGLSDefault gls_default;
+ LLGLSObjectSelect gls_object_select;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ disableLights();
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+ glDisableClientState( GL_NORMAL_ARRAY );
+ glDisableClientState( GL_TEXTURE_COORD_ARRAY );
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+ LLGLState::checkClientArrays();
+ U32 last_type = 0;
+#endif
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (poolp->canUseAGP() && usingAGP())
+ {
+ bindAGP();
+ }
+ else
+ {
+ //llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl;
+ unbindAGP();
+ }
+ poolp->renderForSelect();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ if (poolp->getType() != last_type)
+ {
+ last_type = poolp->getType();
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+ LLGLState::checkClientArrays();
+ }
+#endif
+ }
+
+ // Disable all of the client state
+ glDisableClientState( GL_VERTEX_ARRAY );
+
+ if (mAGPMemPool)
+ {
+ mAGPMemPool->waitFence(mGlobalFence);
+ mAGPMemPool->sendFence(mGlobalFence);
+ }
+}
+
+void LLPipeline::renderFaceForUVSelect(LLFace* facep)
+{
+ if (facep) facep->renderSelectedUV();
+}
+
+void LLPipeline::rebuildPools()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ S32 max_count = mPools.size();
+ S32 num_rebuilds = 0;
+ pool_set_t::iterator iter1 = mPools.upper_bound(mLastRebuildPool);
+ while(max_count > 0 && mPools.size() > 0) // && num_rebuilds < MAX_REBUILDS)
+ {
+ if (iter1 == mPools.end())
+ {
+ iter1 = mPools.begin();
+ }
+ LLDrawPool* poolp = *iter1;
+ num_rebuilds += poolp->rebuild();
+ if (poolp->mReferences.empty())
+ {
+ mPools.erase(iter1++);
+ removeFromQuickLookup( poolp );
+ if (poolp == mLastRebuildPool)
+ {
+ mLastRebuildPool = NULL;
+ }
+ delete poolp;
+ }
+ else
+ {
+ mLastRebuildPool = poolp;
+ iter1++;
+ }
+ max_count--;
+ }
+
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->rebuildHUD();
+ }
+}
+
+void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp )
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ switch( new_poolp->getType() )
+ {
+ case LLDrawPool::POOL_SIMPLE:
+ mSimplePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_TREE:
+ mTreePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_TREE_NEW:
+ mTreeNewPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_TERRAIN:
+ mTerrainPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_BUMP:
+ mBumpPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_MEDIA:
+ mMediaPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_ALPHA:
+ if( mAlphaPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl;
+ }
+ else
+ {
+ mAlphaPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_AVATAR:
+ break; // Do nothing
+
+ case LLDrawPool::POOL_SKY:
+ if( mSkyPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Sky pool" << llendl;
+ }
+ else
+ {
+ mSkyPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_STARS:
+ if( mStarsPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Stars pool" << llendl;
+ }
+ else
+ {
+ mStarsPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_CLOUDS:
+ if( mCloudsPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Clouds pool" << llendl;
+ }
+ else
+ {
+ mCloudsPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_WATER:
+ if( mWaterPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Water pool" << llendl;
+ }
+ else
+ {
+ mWaterPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_GROUND:
+ if( mGroundPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Ground Pool" << llendl;
+ }
+ else
+ {
+ mGroundPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_HUD:
+ if( mHUDPool )
+ {
+ llerrs << "LLPipeline::addPool(): Duplicate HUD Pool!" << llendl;
+ }
+ mHUDPool = new_poolp;
+ break;
+
+ default:
+ llassert(0);
+ llwarns << "Invalid Pool Type in LLPipeline::addPool()" << llendl;
+ break;
+ }
+}
+
+void LLPipeline::removePool( LLDrawPool* poolp )
+{
+ removeFromQuickLookup(poolp);
+ mPools.erase(poolp);
+ delete poolp;
+}
+
+void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp )
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ switch( poolp->getType() )
+ {
+ case LLDrawPool::POOL_SIMPLE:
+ #ifdef _DEBUG
+ {
+ BOOL found = mSimplePools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mSimplePools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_TREE:
+ #ifdef _DEBUG
+ {
+ BOOL found = mTreePools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mTreePools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_TREE_NEW:
+ #ifdef _DEBUG
+ {
+ BOOL found = mTreeNewPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mTreeNewPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_TERRAIN:
+ #ifdef _DEBUG
+ {
+ BOOL found = mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_BUMP:
+ #ifdef _DEBUG
+ {
+ BOOL found = mBumpPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mBumpPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_MEDIA:
+ #ifdef _DEBUG
+ {
+ BOOL found = mMediaPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mMediaPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_ALPHA:
+ llassert( poolp == mAlphaPool );
+ mAlphaPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_AVATAR:
+ break; // Do nothing
+
+ case LLDrawPool::POOL_SKY:
+ llassert( poolp == mSkyPool );
+ mSkyPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_STARS:
+ llassert( poolp == mStarsPool );
+ mStarsPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_CLOUDS:
+ llassert( poolp == mCloudsPool );
+ mCloudsPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_WATER:
+ llassert( poolp == mWaterPool );
+ mWaterPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_GROUND:
+ llassert( poolp == mGroundPool );
+ mGroundPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_HUD:
+ llassert( poolp == mHUDPool );
+ mHUDPool = NULL;
+ break;
+
+ default:
+ llassert(0);
+ llwarns << "Invalid Pool Type in LLPipeline::removeFromQuickLookup() type=" << poolp->getType() << llendl;
+ break;
+ }
+}
+
+void LLPipeline::flushAGPMemory()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->flushAGP();
+ }
+}
+
+void LLPipeline::resetDrawOrders()
+{
+ // Iterate through all of the draw pools and rebuild them.
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->resetDrawOrders();
+ }
+}
+
+//-------------------------------
+
+
+LLViewerObject *LLPipeline::nearestObjectAt(F32 yaw, F32 pitch)
+{
+ // Stub to find nearest Object at given yaw and pitch
+
+ /*
+ LLEdge *vd = NULL;
+
+ if (vd)
+ {
+ return (LLViewerObject*)vd->mDrawablep->getVObj();
+ }
+ */
+
+ return NULL;
+}
+
+void LLPipeline::printPools()
+{
+ /*
+ // Iterate through all of the draw pools and rebuild them.
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (pool->mTexturep[0])
+ {
+ llinfos << "Op pool " << pool->mTexturep[0]->mID << llendflush;
+ }
+ else
+ {
+ llinfos << "Opaque pool NULL" << llendflush;
+ }
+ llinfos << " Vertices: \t" << pool->getVertexCount() << llendl;
+ }
+
+
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ llinfos << "Al pool " << pool;
+ llcont << " Vertices: \t" << pool->getVertexCount() << llendl;
+ }
+
+ pool = mHighlightPools.getFirst();
+ llinfos << "Si pool " << pool;
+ llcont << " Vertices: \t" << pool->getVertexCount() << llendl;
+ */
+}
+
+
+void LLPipeline::displayPools()
+{
+ // Needs to be fixed to handle chained pools - djs
+ LLUI::setLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glScalef(2,2,1);
+ glTranslatef(-0.5f,-0.5f,0);
+
+ F32 x = 0.0f, y = 0.05f;
+ F32 xs = 0.01f, ys = 0.01f;
+ F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
+
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLGLSTexture gls_texture;
+ LLDrawPool *poolp = *iter;
+ if (poolp->getDebugTexture())
+ {
+ poolp->getDebugTexture()->bind();
+ glColor4f(1,1,1,1);
+ stamp(x,y,xs*4,ys*4);
+ }
+
+ LLGLSNoTexture no_texture;
+
+ F32 a = 1.f - (F32)poolp->mRebuildTime / (F32)poolp->mRebuildFreq;
+ glColor4f(a,a,a,1);
+ stamp(x,y,xs,ys);
+
+ x = (xs + xs2) * 4.0f;
+
+ F32 h = ys * 0.5f;
+
+ S32 total = 0;
+
+ for (std::vector<LLFace*>::iterator iter = poolp->mReferences.begin();
+ iter != poolp->mReferences.end(); iter++)
+ {
+ LLFace *face = *iter;
+ F32 w = xs;
+
+ if (!face || !face->getDrawable())
+ {
+ w = 16 / 3000.0f;
+
+ stamp(x,y,w,h);
+
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h + ys2;
+ }
+ else
+ {
+ if (w) x += w + xs2;
+ }
+
+ continue;
+ }
+
+ if (face->getDrawable()->isVisible())
+ {
+ if (face->isState(LLFace::BACKLIST))
+ {
+ glColor4f(1,0,1,1);
+ }
+ else if (total > poolp->getMaxVertices())
+ {
+ glColor4f(1,0,0,1);
+ }
+ else
+ {
+ glColor4f(0,1,0,1);
+ total += face->getGeomCount();
+ }
+ }
+ else
+ {
+ if (face->isState(LLFace::BACKLIST))
+ {
+ glColor4f(0,0,1,1);
+ }
+ else
+ {
+ glColor4f(1,1,0,1);
+ }
+ }
+
+ w = face->getGeomCount() / 3000.0f;
+
+ stamp(x,y,w,h);
+
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h + ys2;
+ }
+ else
+ {
+ if (w) x += w + xs2;
+ }
+ }
+
+ y += ys + ys2;
+ x = 0;
+ }
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+}
+
+static F32 xs = 0.01f, ys = 0.01f;
+static F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
+static F32 winx, winy;
+
+void displayDrawable(F32 &x, F32 &y, LLDrawable *drawable, F32 alpha = 0.5f)
+{
+ F32 w = 0;
+ F32 h = ys * 0.5f;
+
+ if (drawable && !drawable->isDead())
+ {
+ for (S32 f=0;f < drawable->getNumFaces(); f++)
+ {
+ w += drawable->getFace(f)->getGeomCount() / 30000.0f;
+ }
+ w+=xs;
+ glColor4f(1,1,0, alpha);
+ }
+ else
+ {
+ w = 0.01f;
+ glColor4f(0,0,0,alpha);
+ }
+
+// const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+
+// U8 pcode = drawable->getVObj()->getPCode();
+
+ //char *string = (char*)LLPrimitive::pCodeToString(pcode);
+ //if (pcode == 0x3e) string = "terrain";
+ //else if (pcode == 0x2e) string = "cloud";
+ //string[3] = 0;
+
+ stamp(x * winx,y * winy,w * winx,h * winy);
+
+ /*
+ glColor4f(0,0,0,1);
+ font->render(string,x*winx+1,y*winy+1);
+ LLGLSNoTexture no_texture;
+ */
+
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h + ys2;
+ }
+ else
+ {
+ if (w) x += w + xs2;
+ }
+
+}
+
+#if 0 // No longer up date
+
+void displayQueue(F32 &x, F32 &y, LLDynamicArray<LLDrawable*>& processed, LLDynamicQueuePtr<LLPointer<LLDrawable> >& remaining)
+{
+ S32 i;
+ for (i=0;i<processed.count();i++)
+ {
+ displayDrawable(x,y,processed[i],1);
+ }
+
+ x += xs * 2;
+
+ S32 count = remaining.count();
+ for (i=0;i<count;i++)
+ {
+ LLDrawable* drawablep = remaining[(i + remaining.getFirst()) % remaining.getMax()];
+ if (drawablep && !drawablep->isDead())
+ {
+ displayDrawable(x,y,drawable,0.5);
+ }
+ }
+
+ y += ys * 4;
+ x = (xs + xs2) * 4.0f;
+
+}
+
+void LLPipeline::displayQueues()
+{
+ LLUI::setLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glScalef(2,2,1);
+ glTranslatef(-0.5f,-0.5f,0);
+
+ winx = (F32)gViewerWindow->getWindowWidth();
+ winy = (F32)gViewerWindow->getWindowHeight();
+
+ glScalef(1.0f/winx,1.0f/winy,1);
+
+ F32 x = (xs + xs2) * 4.0f;
+ F32 y = 0.1f;
+
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ font->renderUTF8("Build1", 0,0,(S32)(y*winy),LLColor4(1,1,1,1));
+ displayQueue(x,y, gBuildProcessed, mBuildQ1);
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+}
+
+#endif
+
+// static
+void render_bbox(const LLVector3 &min, const LLVector3 &max)
+{
+ S32 i;
+ LLVector3 verticesp[16];
+
+ verticesp[0].setVec(min.mV[0],min.mV[1],max.mV[2]);
+ verticesp[1].setVec(min.mV[0],min.mV[1],min.mV[2]);
+ verticesp[2].setVec(min.mV[0],max.mV[1],min.mV[2]);
+ verticesp[3].setVec(min.mV[0],max.mV[1],max.mV[2]);
+ verticesp[4].setVec(max.mV[0],max.mV[1],max.mV[2]);
+ verticesp[5].setVec(max.mV[0],max.mV[1],min.mV[2]);
+ verticesp[6].setVec(max.mV[0],min.mV[1],min.mV[2]);
+ verticesp[7].setVec(max.mV[0],min.mV[1],max.mV[2]);
+ verticesp[8 ] = verticesp[0];
+ verticesp[9 ] = verticesp[1];
+ verticesp[10] = verticesp[6];
+ verticesp[11] = verticesp[7];
+ verticesp[12] = verticesp[4];
+ verticesp[13] = verticesp[5];
+ verticesp[14] = verticesp[2];
+ verticesp[15] = verticesp[3];
+
+ LLGLSNoTexture gls_no_texture;
+ {
+ LLUI::setLineWidth(1.f);
+ glBegin(GL_LINE_LOOP);
+ for (i = 0; i < 16; i++)
+ {
+ glVertex3fv(verticesp[i].mV);
+ }
+ glEnd();
+ }
+ {
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINE_LOOP);
+ for (i = 0; i < 16; i++)
+ {
+ glVertex3fv(verticesp[i].mV);
+ }
+ glEnd();
+ }
+ LLUI::setLineWidth(1.0f);
+}
+
+//============================================================================
+// Once-per-frame setup of hardware lights,
+// including sun/moon, avatar backlight, and up to 6 local lights
+
+void LLPipeline::setupAvatarLights(BOOL for_edit)
+{
+ const LLColor4 black(0,0,0,1);
+
+ if (for_edit)
+ {
+ LLColor4 diffuse(0.8f, 0.8f, 0.8f, 0.f);
+ LLVector4 light_pos_cam(-8.f, 0.25f, 10.f, 0.f); // w==0 => directional light
+ LLMatrix4 camera_mat = gCamera->getModelview();
+ LLMatrix4 camera_rot(camera_mat.getMat3());
+ camera_rot.invert();
+ LLVector4 light_pos = light_pos_cam * camera_rot;
+
+ light_pos.normVec();
+
+ mHWLightColors[1] = diffuse;
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse.mV);
+ glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV);
+ glLightfv(GL_LIGHT1, GL_POSITION, light_pos.mV);
+ glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f);
+ glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f);
+ }
+ else if (gAvatarBacklight) // Always true (unless overridden in a devs .ini)
+ {
+ LLVector3 opposite_pos = -1.f * mSunDir;
+ LLVector3 orthog_light_pos = mSunDir % LLVector3::z_axis;
+ LLVector4 backlight_pos = LLVector4(lerp(opposite_pos, orthog_light_pos, 0.3f), 0.0f);
+ backlight_pos.normVec();
+
+ LLColor4 light_diffuse = mSunDiffuse * mSunShadowFactor;
+ LLColor4 backlight_diffuse(1.f - light_diffuse.mV[VRED], 1.f - light_diffuse.mV[VGREEN], 1.f - light_diffuse.mV[VBLUE], 1.f);
+ F32 max_component = 0.001f;
+ for (S32 i = 0; i < 3; i++)
+ {
+ if (backlight_diffuse.mV[i] > max_component)
+ {
+ max_component = backlight_diffuse.mV[i];
+ }
+ }
+ F32 backlight_mag;
+ if (gSky.getSunDirection().mV[2] >= NIGHTTIME_ELEVATION_COS)
+ {
+ backlight_mag = BACKLIGHT_DAY_MAGNITUDE_OBJECT;
+ }
+ else
+ {
+ backlight_mag = BACKLIGHT_NIGHT_MAGNITUDE_OBJECT;
+ }
+ backlight_diffuse *= backlight_mag / max_component;
+
+ mHWLightColors[1] = backlight_diffuse;
+ glLightfv(GL_LIGHT1, GL_POSITION, backlight_pos.mV); // this is just sun/moon direction
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, backlight_diffuse.mV);
+ glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV);
+ glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f);
+ glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f);
+ }
+ else
+ {
+ mHWLightColors[1] = black;
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, black.mV);
+ glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV);
+ }
+}
+
+static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_dist)
+{
+ F32 inten = light->getLightIntensity();
+ if (inten < .001f)
+ {
+ return max_dist;
+ }
+ F32 radius = light->getLightRadius();
+ BOOL selected = light->isSelected();
+ LLVector3 dpos = light->getRenderPosition() - cam_pos;
+ F32 dist2 = dpos.magVecSquared();
+ if (!selected && dist2 > (max_dist + radius)*(max_dist + radius))
+ {
+ return max_dist;
+ }
+ F32 dist = fsqrtf(dist2);
+ dist *= 1.f / inten;
+ dist -= radius;
+ if (selected)
+ {
+ dist -= 10000.f; // selected lights get highest priority
+ }
+ if (light->mDrawable.notNull() && light->mDrawable->isState(LLDrawable::ACTIVE))
+ {
+ // moving lights get a little higher priority (too much causes artifacts)
+ dist -= light->getLightRadius()*0.25f;
+ }
+ return dist;
+}
+
+void LLPipeline::calcNearbyLights()
+{
+ if (mLightingDetail >= 1)
+ {
+ // mNearbyLight (and all light_set_t's) are sorted such that
+ // begin() == the closest light and rbegin() == the farthest light
+ const S32 MAX_LOCAL_LIGHTS = 6;
+// LLVector3 cam_pos = gAgent.getCameraPositionAgent();
+ LLVector3 cam_pos = gAgent.getPositionAgent();
+
+ F32 max_dist = LIGHT_MAX_RADIUS * 4.f; // ignore enitrely lights > 4 * max light rad
+
+ // UPDATE THE EXISTING NEARBY LIGHTS
+ light_set_t cur_nearby_lights;
+ for (light_set_t::iterator iter = mNearbyLights.begin();
+ iter != mNearbyLights.end(); iter++)
+ {
+ const Light* light = &(*iter);
+ LLDrawable* drawable = light->drawable;
+ LLVOVolume* volight = drawable->getVOVolume();
+ if (!volight || !drawable->isState(LLDrawable::LIGHT))
+ {
+ drawable->clearState(LLDrawable::NEARBY_LIGHT);
+ continue;
+ }
+ if (light->fade <= -LIGHT_FADE_TIME)
+ {
+ drawable->clearState(LLDrawable::NEARBY_LIGHT);
+ }
+ else
+ {
+ F32 dist = calc_light_dist(volight, cam_pos, max_dist);
+ cur_nearby_lights.insert(Light(drawable, dist, light->fade));
+ }
+ }
+ mNearbyLights = cur_nearby_lights;
+
+ // FIND NEW LIGHTS THAT ARE IN RANGE
+ light_set_t new_nearby_lights;
+ for (LLDrawable::drawable_set_t::iterator iter = mLights.begin();
+ iter != mLights.end(); ++iter)
+ {
+ LLDrawable* drawable = *iter;
+ LLVOVolume* light = drawable->getVOVolume();
+ if (!light || drawable->isState(LLDrawable::NEARBY_LIGHT))
+ {
+ continue;
+ }
+ if (light->isHUDAttachment())
+ {
+ continue; // no lighting from HUD objects
+ }
+ F32 dist = calc_light_dist(light, cam_pos, max_dist);
+ if (dist >= max_dist)
+ {
+ continue;
+ }
+ new_nearby_lights.insert(Light(drawable, dist, 0.f));
+ if (new_nearby_lights.size() > (U32)MAX_LOCAL_LIGHTS)
+ {
+ new_nearby_lights.erase(--new_nearby_lights.end());
+ const Light& last = *new_nearby_lights.rbegin();
+ max_dist = last.dist;
+ }
+ }
+
+ // INSERT ANY NEW LIGHTS
+ for (light_set_t::iterator iter = new_nearby_lights.begin();
+ iter != new_nearby_lights.end(); iter++)
+ {
+ const Light* light = &(*iter);
+ if (mNearbyLights.size() < (U32)MAX_LOCAL_LIGHTS)
+ {
+ mNearbyLights.insert(*light);
+ ((LLDrawable*) light->drawable)->setState(LLDrawable::NEARBY_LIGHT);
+ }
+ else
+ {
+ // crazy cast so that we can overwrite the fade value
+ // even though gcc enforces sets as const
+ // (fade value doesn't affect sort so this is safe)
+ Light* farthest_light = ((Light*) (&(*(mNearbyLights.rbegin()))));
+ if (light->dist < farthest_light->dist)
+ {
+ if (farthest_light->fade >= 0.f)
+ {
+ farthest_light->fade = -gFrameIntervalSeconds;
+ }
+ }
+ else
+ {
+ break; // none of the other lights are closer
+ }
+ }
+ }
+
+ }
+}
+
+void LLPipeline::setupHWLights(LLDrawPool* pool)
+{
+ const LLColor4 black(0,0,0,1);
+
+ setLightingDetail(-1); // update
+
+ // Ambient
+ LLColor4 ambient = gSky.getTotalAmbientColor();
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV);
+
+ // Light 0 = Sun or Moon (All objects)
+ {
+ mSunShadowFactor = 1.f; // no shadowing by defailt
+ if (gSky.getSunDirection().mV[2] >= NIGHTTIME_ELEVATION_COS)
+ {
+ mSunDir.setVec(gSky.getSunDirection());
+ mSunDiffuse.setVec(gSky.getSunDiffuseColor());
+ }
+ else
+ {
+ mSunDir.setVec(gSky.getMoonDirection());
+ mSunDiffuse.setVec(gSky.getMoonDiffuseColor() * 1.5f);
+ }
+
+ F32 max_color = llmax(mSunDiffuse.mV[0], mSunDiffuse.mV[1], mSunDiffuse.mV[2]);
+ if (max_color > 1.f)
+ {
+ mSunDiffuse *= 1.f/max_color;
+ }
+ mSunDiffuse.clamp();
+
+ LLVector4 light_pos(mSunDir, 0.0f);
+ LLColor4 light_diffuse = mSunDiffuse * mSunShadowFactor;
+ mHWLightColors[0] = light_diffuse;
+ glLightfv(GL_LIGHT0, GL_POSITION, light_pos.mV); // this is just sun/moon direction
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse.mV);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, black.mV);
+ glLightf (GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f);
+ glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 180.0f);
+ }
+
+ // Light 1 = Backlight (for avatars)
+ // (set by enableLightsAvatar)
+
+ S32 cur_light = 2;
+
+ // Nearby lights = LIGHT 2-7
+
+ mLightMovingMask = 0;
+
+ if (mLightingDetail >= 1)
+ {
+ for (light_set_t::iterator iter = mNearbyLights.begin();
+ iter != mNearbyLights.end(); ++iter)
+ {
+ LLDrawable* drawable = iter->drawable;
+ LLVOVolume* light = drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ if (drawable->isState(LLDrawable::ACTIVE))
+ {
+ mLightMovingMask |= (1<<cur_light);
+ }
+
+ LLColor4 light_color = light->getLightColor();
+ light_color.mV[3] = 0.0f;
+
+ F32 fade = iter->fade;
+ if (fade < LIGHT_FADE_TIME)
+ {
+ // fade in/out light
+ if (fade >= 0.f)
+ {
+ fade = fade / LIGHT_FADE_TIME;
+ ((Light*) (&(*iter)))->fade += gFrameIntervalSeconds;
+ }
+ else
+ {
+ fade = 1.f + fade / LIGHT_FADE_TIME;
+ ((Light*) (&(*iter)))->fade -= gFrameIntervalSeconds;
+ }
+ fade = llclamp(fade,0.f,1.f);
+ light_color *= fade;
+ }
+
+ LLVector3 light_pos(light->getRenderPosition());
+ LLVector4 light_pos_gl(light_pos, 1.0f);
+
+ F32 light_radius = llmax(light->getLightRadius(), 0.001f);
+ F32 atten, quad;
+
+#if 0 //1.9.1
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ atten = light_radius;
+ quad = llmax(light->getLightFalloff(), 0.0001f);
+ }
+ else
+#endif
+ {
+ F32 x = (3.f * (1.f + light->getLightFalloff()));
+ atten = x / (light_radius); // % of brightness at radius
+ quad = 0.0f;
+ }
+ mHWLightColors[cur_light] = light_color;
+ S32 gllight = GL_LIGHT0+cur_light;
+ glLightfv(gllight, GL_POSITION, light_pos_gl.mV);
+ glLightfv(gllight, GL_DIFFUSE, light_color.mV);
+ glLightfv(gllight, GL_AMBIENT, black.mV);
+ glLightfv(gllight, GL_SPECULAR, black.mV);
+ glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f);
+ glLightf (gllight, GL_LINEAR_ATTENUATION, atten);
+ glLightf (gllight, GL_QUADRATIC_ATTENUATION, quad);
+ glLightf (gllight, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (gllight, GL_SPOT_CUTOFF, 180.0f);
+ cur_light++;
+ if (cur_light >= 8)
+ {
+ break; // safety
+ }
+ }
+ }
+ for ( ; cur_light < 8 ; cur_light++)
+ {
+ mHWLightColors[cur_light] = black;
+ S32 gllight = GL_LIGHT0+cur_light;
+ glLightfv(gllight, GL_DIFFUSE, black.mV);
+ glLightfv(gllight, GL_AMBIENT, black.mV);
+ glLightfv(gllight, GL_SPECULAR, black.mV);
+ }
+
+ // Init GL state
+ glDisable(GL_LIGHTING);
+ for (S32 gllight=GL_LIGHT0; gllight<=GL_LIGHT7; gllight++)
+ {
+ glDisable(gllight);
+ }
+ mLightMask = 0;
+}
+
+void LLPipeline::enableLights(U32 mask, F32 shadow_factor)
+{
+ if (mLightingDetail == 0)
+ {
+ mask &= 0xf003; // sun and backlight only (and fullbright bit)
+ }
+ if (mLightMask != mask)
+ {
+ if (!mLightMask)
+ {
+ glEnable(GL_LIGHTING);
+ }
+ if (mask)
+ {
+ for (S32 i=0; i<8; i++)
+ {
+ if (mask & (1<<i))
+ {
+ glEnable(GL_LIGHT0 + i);
+ glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, mHWLightColors[i].mV);
+ }
+ else
+ {
+ glDisable(GL_LIGHT0 + i);
+ glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, LLColor4::black.mV);
+ }
+ }
+ }
+ else
+ {
+ glDisable(GL_LIGHTING);
+ }
+ mLightMask = mask;
+ LLColor4 ambient = gSky.getTotalAmbientColor();
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV);
+ }
+}
+
+void LLPipeline::enableLightsStatic(F32 shadow_factor)
+{
+ U32 mask = 0x01; // Sun
+ if (mLightingDetail >= 2)
+ {
+ mask |= mLightMovingMask; // Hardware moving lights
+ glColor4f(0.f, 0.f, 0.f, 1.0f); // no local lighting by default
+ }
+ else
+ {
+ mask |= 0xff & (~2); // Hardware local lights
+ }
+ enableLights(mask, shadow_factor);
+}
+
+void LLPipeline::enableLightsDynamic(F32 shadow_factor)
+{
+ U32 mask = 0xff & (~2); // Local lights
+ enableLights(mask, shadow_factor);
+ if (mLightingDetail >= 2)
+ {
+ glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default
+ }
+}
+
+void LLPipeline::enableLightsAvatar(F32 shadow_factor)
+{
+ U32 mask = 0xff; // All lights
+ setupAvatarLights(FALSE);
+ enableLights(mask, shadow_factor);
+}
+
+void LLPipeline::enableLightsAvatarEdit(const LLColor4& color)
+{
+ U32 mask = 0x2002; // Avatar backlight only, set ambient
+ setupAvatarLights(TRUE);
+ enableLights(mask, 1.0f);
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV);
+}
+
+void LLPipeline::enableLightsFullbright(const LLColor4& color)
+{
+ U32 mask = 0x1000; // Non-0 mask, set ambient
+ enableLights(mask, 1.f);
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV);
+ if (mLightingDetail >= 2)
+ {
+ glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default
+ }
+}
+
+void LLPipeline::disableLights()
+{
+ enableLights(0, 0.f); // no lighting (full bright)
+ glColor4f(1.f, 1.f, 1.f, 1.f); // lighting color = white by default
+}
+
+// Call *after*s etting up lights
+void LLPipeline::setAmbient(const LLColor4& ambient)
+{
+ mLightMask |= 0x4000; // tweak mask so that ambient will get reset
+ LLColor4 amb = ambient + gSky.getTotalAmbientColor();
+ amb.clamp();
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,amb.mV);
+}
+
+//============================================================================
+
+class LLMenuItemGL;
+class LLInvFVBridge;
+struct cat_folder_pair;
+class LLVOBranch;
+class LLVOLeaf;
+class Foo;
+
+template<> char* LLAGPArray<U8>::sTypeName = "U8 [AGP]";
+template<> char* LLAGPArray<U32>::sTypeName = "U32 [AGP]";
+template<> char* LLAGPArray<F32>::sTypeName = "F32 [AGP]";
+template<> char* LLAGPArray<LLColor4>::sTypeName = "LLColor4 [AGP]";
+template<> char* LLAGPArray<LLColor4U>::sTypeName = "LLColor4U [AGP]";
+template<> char* LLAGPArray<LLVector4>::sTypeName = "LLVector4 [AGP]";
+template<> char* LLAGPArray<LLVector3>::sTypeName = "LLVector3 [AGP]";
+template<> char* LLAGPArray<LLVector2>::sTypeName = "LLVector2 [AGP]";
+template<> char* LLAGPArray<LLFace*>::sTypeName = "LLFace* [AGP]";
+
+void scale_stamp(const F32 x, const F32 y, const F32 xs, const F32 ys)
+{
+ stamp(0.25f + 0.5f*x,
+ 0.5f + 0.45f*y,
+ 0.5f*xs,
+ 0.45f*ys);
+}
+
+void drawBars(const F32 begin, const F32 end, const F32 height = 1.f)
+{
+ if (begin >= 0 && end <=1)
+ {
+ F32 lines = 40.0f;
+ S32 ibegin = (S32)(begin * lines);
+ S32 iend = (S32)(end * lines);
+ F32 fbegin = begin * lines - ibegin;
+ F32 fend = end * lines - iend;
+
+ F32 line_height = height/lines;
+
+ if (iend == ibegin)
+ {
+ scale_stamp(fbegin, (F32)ibegin/lines,fend-fbegin, line_height);
+ }
+ else
+ {
+ // Beginning row
+ scale_stamp(fbegin, (F32)ibegin/lines, 1.0f-fbegin, line_height);
+
+ // End row
+ scale_stamp(0.0, (F32)iend/lines, fend, line_height);
+
+ // Middle rows
+ for (S32 l = (ibegin+1); l < iend; l++)
+ {
+ scale_stamp(0.0f, (F32)l/lines, 1.0f, line_height);
+ }
+ }
+ }
+}
+
+void LLPipeline::displayAGP()
+{
+ LLUI::setLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_alpha;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glScalef(2,2,1);
+ glTranslatef(-0.5f,-0.5f,0);
+
+ glColor4f(0,0,0,0.5f);
+ scale_stamp(0,0,1,1);
+
+ F32 x = 0.0f, y = 0.05f;
+ F32 xs = 0.015f, ys = 0.015f;
+ F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
+ F32 w = xs+xs2, h = ys + ys2;
+ S32 i=0;
+
+ F32 agp_size = 1.f;
+ if (mAGPMemPool)
+ {
+ agp_size = (F32)mAGPMemPool->getSize();
+ }
+
+ x = (xs + xs2) * 4.0f;
+
+ static float c = 0.0f;
+ c += 0.0001f;
+
+ LLAGPMemBlock *blockp = mBufferMemory[mBufferIndex]->getAGPMemBlock();
+
+ pool_set_t::iterator iter = mPools.begin();
+ LLDrawPool *poolp = *iter;
+
+ // Dump the shared AGP buffer
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mMemory.count()/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,used, 0.5f);
+
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end, 0.25f);
+ }
+
+ S32 used_bytes = 0;
+ S32 total_bytes = 0;
+ while (iter != mPools.end())
+ {
+ poolp = *iter++;
+
+ BOOL synced = FALSE;
+ i++;
+ total_bytes += poolp->mMemory.getMax();
+ used_bytes += poolp->mMemory.count();
+ LLViewerImage *texturep = poolp->getDebugTexture();
+
+
+ if (poolp->mMemory.mSynced)
+ {
+ poolp->mMemory.mSynced = FALSE;
+ synced = TRUE;
+ }
+
+ LLAGPMemBlock *blockp = poolp->mMemory.getAGPMemBlock();
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mMemory.count()/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(1.f, 0.75f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 0.75f, 0.75f, 1.f);
+ drawBars(begin,used, 0.5f);
+
+ glColor3fv(poolp->getDebugColor().mV);
+ drawBars(begin,end, 0.25f);
+
+ if (synced)
+ {
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 1.f, 1.f, 0.4f);
+ drawBars(begin,end);
+ }
+ }
+
+ synced = FALSE;
+ if (poolp->mWeights.mSynced)
+ {
+ poolp->mWeights.mSynced = FALSE;
+ synced = TRUE;
+ }
+ blockp = poolp->mWeights.getAGPMemBlock();
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mWeights.count()*sizeof(float)/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.0f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(0.0, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.0, 0.f, 0.75f, 1.f);
+ drawBars(begin,used, 0.5f);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor3fv(poolp->getDebugColor().mV);
+ drawBars(begin,end, 0.25f);
+
+ if (synced)
+ {
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 1.f, 1.f, 0.4f);
+ drawBars(begin,end);
+ }
+ }
+
+ synced = FALSE;
+ if (poolp->mClothingWeights.mSynced)
+ {
+ poolp->mClothingWeights.mSynced = FALSE;
+ synced = TRUE;
+ }
+ blockp = poolp->mClothingWeights.getAGPMemBlock();
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mClothingWeights.count()*sizeof(LLVector4)/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.75f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(0.75f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.75f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,used, 0.5f);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor3fv(poolp->getDebugColor().mV);
+ drawBars(begin,end, 0.25f);
+
+ if (synced)
+ {
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 1.f, 1.f, 0.5f);
+ drawBars(begin,end);
+ }
+ }
+
+ //
+ // Stamps on bottom of screen
+ //
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ stamp(x,y,xs,ys);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor3fv(poolp->getDebugColor().mV);
+ stamp(x,y,xs, ys*0.25f);
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h;
+ }
+ else
+ {
+ x += w + xs2;
+ }
+ }
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+}
+
+void LLPipeline::findReferences(LLDrawable *drawablep)
+{
+ if (std::find(mVisibleList.begin(), mVisibleList.end(), drawablep) != mVisibleList.end())
+ {
+ llinfos << "In mVisibleList" << llendl;
+ }
+ if (mLights.find(drawablep) != mLights.end())
+ {
+ llinfos << "In mLights" << llendl;
+ }
+ if (mMovedList.find(drawablep) != mMovedList.end())
+ {
+ llinfos << "In mMovedList" << llendl;
+ }
+ if (std::find(mShiftList.begin(), mShiftList.end(), drawablep) != mShiftList.end())
+ {
+ llinfos << "In mShiftList" << llendl;
+ }
+ if (mRetexturedList.find(drawablep) != mRetexturedList.end())
+ {
+ llinfos << "In mRetexturedList" << llendl;
+ }
+ if (mRematerialedList.find(drawablep) != mRematerialedList.end())
+ {
+ llinfos << "In mRematerialedList" << llendl;
+ }
+
+ if (mActiveQ.find(drawablep) != mActiveQ.end())
+ {
+ llinfos << "In mActiveQ" << llendl;
+ }
+ if (mBuildQ1.find(drawablep) != mBuildQ1.end())
+ {
+ llinfos << "In mBuildQ2" << llendl;
+ }
+ if (std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep) != mBuildQ2.end())
+ {
+ llinfos << "In mBuildQ2" << llendl;
+ }
+
+ S32 count;
+ /*
+ count = mStaticTree->count(drawablep);
+ if (count)
+ {
+ llinfos << "In mStaticTree: " << count << " references" << llendl;
+ }
+
+ count = mDynamicTree->count(drawablep);
+ if (count)
+ {
+ llinfos << "In mStaticTree: " << count << " references" << llendl;
+ }
+ */
+ count = gObjectList.findReferences(drawablep);
+ if (count)
+ {
+ llinfos << "In other drawables: " << count << " references" << llendl;
+ }
+}
+
+BOOL LLPipeline::verify()
+{
+ BOOL ok = TRUE;
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (!poolp->verify())
+ {
+ ok = FALSE;
+ }
+ }
+
+ if (!ok)
+ {
+ llwarns << "Pipeline verify failed!" << llendl;
+ }
+ return ok;
+}
+
+S32 LLPipeline::getAGPMemUsage()
+{
+ if (mAGPMemPool)
+ {
+ return mAGPMemPool->getSize();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+S32 LLPipeline::getMemUsage(const BOOL print)
+{
+ S32 mem_usage = 0;
+
+ if (mAGPMemPool)
+ {
+ S32 agp_usage = 0;
+ agp_usage = mAGPMemPool->getSize();
+ if (print)
+ {
+ llinfos << "AGP Mem: " << agp_usage << llendl;
+ llinfos << "AGP Mem used: " << mAGPMemPool->getTotalAllocated() << llendl;
+ }
+ mem_usage += agp_usage;
+ }
+
+
+ S32 pool_usage = 0;
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ pool_usage += poolp->getMemUsage(print);
+ }
+
+ if (print)
+ {
+ llinfos << "Pool Mem: " << pool_usage << llendl;
+ }
+
+ mem_usage += pool_usage;
+
+ return mem_usage;
+
+}
+
+//////////////////////////////
+//
+// Collision detection
+//
+//
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A method to compute a ray-AABB intersection.
+ * Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990
+ * Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
+ * Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only)
+ *
+ * Hence this version is faster as well as more robust than the original one.
+ *
+ * Should work provided:
+ * 1) the integer representation of 0.0f is 0x00000000
+ * 2) the sign bit of the float is the most significant one
+ *
+ * Report bugs: p.terdiman@codercorner.com
+ *
+ * \param aabb [in] the axis-aligned bounding box
+ * \param origin [in] ray origin
+ * \param dir [in] ray direction
+ * \param coord [out] impact coordinates
+ * \return true if ray intersects AABB
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//#define RAYAABB_EPSILON 0.00001f
+#define IR(x) ((U32&)x)
+
+bool LLRayAABB(const LLVector3 &center, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon)
+{
+ BOOL Inside = TRUE;
+ LLVector3 MinB = center - size;
+ LLVector3 MaxB = center + size;
+ LLVector3 MaxT;
+ MaxT.mV[VX]=MaxT.mV[VY]=MaxT.mV[VZ]=-1.0f;
+
+ // Find candidate planes.
+ for(U32 i=0;i<3;i++)
+ {
+ if(origin.mV[i] < MinB.mV[i])
+ {
+ coord.mV[i] = MinB.mV[i];
+ Inside = FALSE;
+
+ // Calculate T distances to candidate planes
+ if(IR(dir.mV[i])) MaxT.mV[i] = (MinB.mV[i] - origin.mV[i]) / dir.mV[i];
+ }
+ else if(origin.mV[i] > MaxB.mV[i])
+ {
+ coord.mV[i] = MaxB.mV[i];
+ Inside = FALSE;
+
+ // Calculate T distances to candidate planes
+ if(IR(dir.mV[i])) MaxT.mV[i] = (MaxB.mV[i] - origin.mV[i]) / dir.mV[i];
+ }
+ }
+
+ // Ray origin inside bounding box
+ if(Inside)
+ {
+ coord = origin;
+ return true;
+ }
+
+ // Get largest of the maxT's for final choice of intersection
+ U32 WhichPlane = 0;
+ if(MaxT.mV[1] > MaxT.mV[WhichPlane]) WhichPlane = 1;
+ if(MaxT.mV[2] > MaxT.mV[WhichPlane]) WhichPlane = 2;
+
+ // Check final candidate actually inside box
+ if(IR(MaxT.mV[WhichPlane])&0x80000000) return false;
+
+ for(U32 i=0;i<3;i++)
+ {
+ if(i!=WhichPlane)
+ {
+ coord.mV[i] = origin.mV[i] + MaxT.mV[WhichPlane] * dir.mV[i];
+ if (epsilon > 0)
+ {
+ if(coord.mV[i] < MinB.mV[i] - epsilon || coord.mV[i] > MaxB.mV[i] + epsilon) return false;
+ }
+ else
+ {
+ if(coord.mV[i] < MinB.mV[i] || coord.mV[i] > MaxB.mV[i]) return false;
+ }
+ }
+ }
+ return true; // ray hits box
+}
+
+//////////////////////////////
+//
+// Macros, functions, and inline methods from other classes
+//
+//
+
+void LLPipeline::setLight(LLDrawable *drawablep, BOOL is_light)
+{
+ if (drawablep)
+ {
+ if (is_light)
+ {
+ mLights.insert(drawablep);
+ drawablep->setState(LLDrawable::LIGHT);
+ }
+ else
+ {
+ mLights.erase(drawablep);
+ drawablep->clearState(LLDrawable::LIGHT);
+ }
+ markRelight(drawablep);
+ }
+}
+
+void LLPipeline::setActive(LLDrawable *drawablep, BOOL active)
+{
+ if (active)
+ {
+ mActiveQ.insert(drawablep);
+ }
+ else
+ {
+ mActiveQ.erase(drawablep);
+ }
+}
+
+//static
+void LLPipeline::toggleRenderType(void* data)
+{
+ S32 type = (S32)(intptr_t)data;
+ U32 bit = (1<<type);
+ if (gPipeline.hasRenderType(type))
+ {
+ llinfos << "Toggling render type mask " << std::hex << bit << " off" << std::dec << llendl;
+ }
+ else
+ {
+ llinfos << "Toggling render type mask " << std::hex << bit << " on" << std::dec << llendl;
+ }
+ gPipeline.mRenderTypeMask ^= bit;
+}
+
+//static
+BOOL LLPipeline::toggleRenderTypeControl(void* data)
+{
+ S32 type = (S32)(intptr_t)data;
+ return gPipeline.hasRenderType(type);
+}
+
+// Allows UI items labeled "Hide foo" instead of "Show foo"
+//static
+BOOL LLPipeline::toggleRenderTypeControlNegated(void* data)
+{
+ S32 type = (S32)(intptr_t)data;
+ return !gPipeline.hasRenderType(type);
+}
+
+//static
+void LLPipeline::toggleRenderDebug(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ if (gPipeline.hasRenderDebugMask(bit))
+ {
+ llinfos << "Toggling render debug mask " << std::hex << bit << " off" << std::dec << llendl;
+ }
+ else
+ {
+ llinfos << "Toggling render debug mask " << std::hex << bit << " on" << std::dec << llendl;
+ }
+ gPipeline.mRenderDebugMask ^= bit;
+}
+
+
+//static
+BOOL LLPipeline::toggleRenderDebugControl(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ return gPipeline.hasRenderDebugMask(bit);
+}
+
+//static
+void LLPipeline::toggleRenderDebugFeature(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ if (gPipeline.hasRenderDebugFeatureMask(bit))
+ {
+ llinfos << "Toggling render debug feature mask " << std::hex << bit << " off" << std::dec << llendl;
+ }
+ else
+ {
+ llinfos << "Toggling render debug feature mask " << std::hex << bit << " on" << std::dec << llendl;
+ }
+ gPipeline.mRenderDebugFeatureMask ^= bit;
+}
+
+
+//static
+BOOL LLPipeline::toggleRenderDebugFeatureControl(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ return gPipeline.hasRenderDebugFeatureMask(bit);
+}
+
+// static
+void LLPipeline::toggleRenderScriptedBeacons(void*)
+{
+ sRenderScriptedBeacons = !sRenderScriptedBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderScriptedBeacons(void*)
+{
+ return sRenderScriptedBeacons;
+}
+
+// static
+void LLPipeline::toggleRenderPhysicalBeacons(void*)
+{
+ sRenderPhysicalBeacons = !sRenderPhysicalBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderPhysicalBeacons(void*)
+{
+ return sRenderPhysicalBeacons;
+}
+
+// static
+void LLPipeline::toggleRenderParticleBeacons(void*)
+{
+ sRenderParticleBeacons = !sRenderParticleBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderParticleBeacons(void*)
+{
+ return sRenderParticleBeacons;
+}
+
+// static
+void LLPipeline::toggleRenderSoundBeacons(void*)
+{
+ sRenderSoundBeacons = !sRenderSoundBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderSoundBeacons(void*)
+{
+ return sRenderSoundBeacons;
+}
+
+//===============================
+// LLGLSL Shader implementation
+//===============================
+LLGLSLShader::LLGLSLShader()
+: mProgramObject(0)
+{ }
+
+void LLGLSLShader::unload()
+{
+ stop_glerror();
+ mAttribute.clear();
+ mTexture.clear();
+ mUniform.clear();
+
+ if (mProgramObject)
+ {
+ GLhandleARB obj[1024];
+ GLsizei count;
+
+ glGetAttachedObjectsARB(mProgramObject, 1024, &count, obj);
+ for (GLsizei i = 0; i < count; i++)
+ {
+ glDeleteObjectARB(obj[i]);
+ }
+
+ glDeleteObjectARB(mProgramObject);
+
+ mProgramObject = 0;
+ }
+
+ //hack to make apple not complain
+ glGetError();
+
+ stop_glerror();
+}
+
+void LLGLSLShader::attachObject(GLhandleARB object)
+{
+ if (object != 0)
+ {
+ stop_glerror();
+ glAttachObjectARB(mProgramObject, object);
+ stop_glerror();
+ }
+ else
+ {
+ llwarns << "Attempting to attach non existing shader object. " << llendl;
+ }
+}
+
+void LLGLSLShader::attachObjects(GLhandleARB* objects, S32 count)
+{
+ for (S32 i = 0; i < count; i++)
+ {
+ attachObject(objects[i]);
+ }
+}
+
+BOOL LLGLSLShader::mapAttributes(const char** attrib_names, S32 count)
+{
+ //link the program
+ BOOL res = link();
+
+ mAttribute.clear();
+ mAttribute.resize(LLPipeline::sReservedAttribCount + count, -1);
+
+ if (res)
+ { //read back channel locations
+
+ //read back reserved channels first
+ for (S32 i = 0; i < (S32) LLPipeline::sReservedAttribCount; i++)
+ {
+ const char* name = LLPipeline::sReservedAttribs[i];
+ S32 index = glGetAttribLocationARB(mProgramObject, name);
+ if (index != -1)
+ {
+ mAttribute[i] = index;
+ llinfos << "Attribute " << name << " assigned to channel " << index << llendl;
+ }
+ }
+
+ for (S32 i = 0; i < count; i++)
+ {
+ const char* name = attrib_names[i];
+ S32 index = glGetAttribLocationARB(mProgramObject, name);
+ if (index != -1)
+ {
+ mAttribute[LLPipeline::sReservedAttribCount + i] = index;
+ llinfos << "Attribute " << name << " assigned to channel " << index << llendl;
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLGLSLShader::mapUniform(GLint index, const char** uniform_names, S32 count)
+{
+ if (index == -1)
+ {
+ return;
+ }
+
+ GLenum type;
+ GLsizei length;
+ GLint size;
+ char name[1024];
+ name[0] = 0;
+
+ glGetActiveUniformARB(mProgramObject, index, 1024, &length, &size, &type, name);
+
+ //find the index of this uniform
+ for (S32 i = 0; i < (S32) LLPipeline::sReservedUniformCount; i++)
+ {
+ if (mUniform[i] == -1 && !strncmp(LLPipeline::sReservedUniforms[i],name, strlen(LLPipeline::sReservedUniforms[i])))
+ {
+ //found it
+ S32 location = glGetUniformLocationARB(mProgramObject, name);
+ mUniform[i] = location;
+ llinfos << "Uniform " << name << " is at location " << location << llendl;
+ mTexture[i] = mapUniformTextureChannel(location, type);
+ return;
+ }
+ }
+
+ for (S32 i = 0; i < count; i++)
+ {
+ if (mUniform[i+LLPipeline::sReservedUniformCount] == -1 &&
+ !strncmp(uniform_names[i],name, strlen(uniform_names[i])))
+ {
+ //found it
+ S32 location = glGetUniformLocationARB(mProgramObject, name);
+ mUniform[i+LLPipeline::sReservedUniformCount] = location;
+ llinfos << "Uniform " << name << " is at location " << location << " stored in index " <<
+ (i+LLPipeline::sReservedUniformCount) << llendl;
+ mTexture[i+LLPipeline::sReservedUniformCount] = mapUniformTextureChannel(location, type);
+ return;
+ }
+ }
+
+ //llinfos << "Unknown uniform: " << name << llendl;
+ }
+
+GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type)
+{
+ if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB)
+ { //this here is a texture
+ glUniform1iARB(location, mActiveTextureChannels);
+ llinfos << "Assigned to texture channel " << mActiveTextureChannels << llendl;
+ return mActiveTextureChannels++;
+ }
+ return -1;
+}
+
+BOOL LLGLSLShader::mapUniforms(const char** uniform_names, S32 count)
+{
+ BOOL res = TRUE;
+
+ mActiveTextureChannels = 0;
+ mUniform.clear();
+ mTexture.clear();
+
+ //initialize arrays
+ mUniform.resize(count + LLPipeline::sReservedUniformCount, -1);
+ mTexture.resize(count + LLPipeline::sReservedUniformCount, -1);
+
+
+
+ bind();
+
+ //get the number of active uniforms
+ GLint activeCount;
+ glGetObjectParameterivARB(mProgramObject, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &activeCount);
+
+ for (S32 i = 0; i < activeCount; i++)
+ {
+ mapUniform(i, uniform_names, count);
+ }
+
+ unbind();
+
+ return res;
+}
+
+BOOL LLGLSLShader::link(BOOL suppress_errors)
+{
+ return gPipeline.linkProgramObject(mProgramObject, suppress_errors);
+}
+
+void LLGLSLShader::bind()
+{
+ glUseProgramObjectARB(mProgramObject);
+ if (mAttribute.size() > 0)
+ {
+ gPipeline.mMaterialIndex = mAttribute[0];
+ }
+}
+
+void LLGLSLShader::unbind()
+{
+ for (U32 i = 0; i < mAttribute.size(); ++i)
+ {
+ vertexAttrib4f(i, 0,0,0,1);
+ }
+ glUseProgramObjectARB(0);
+}
+
+S32 LLGLSLShader::enableTexture(S32 uniform, S32 mode)
+{
+ if (uniform < 0 || uniform >= (S32)mTexture.size())
+ {
+ llerrs << "LLGLSLShader::enableTexture: uniform out of range: " << uniform << llendl;
+ }
+ S32 index = mTexture[uniform];
+ if (index != -1)
+ {
+ glActiveTextureARB(GL_TEXTURE0_ARB+index);
+ glEnable(mode);
+ }
+ return index;
+}
+
+S32 LLGLSLShader::disableTexture(S32 uniform, S32 mode)
+{
+ S32 index = mTexture[uniform];
+ if (index != -1)
+ {
+ glActiveTextureARB(GL_TEXTURE0_ARB+index);
+ glDisable(mode);
+ }
+ return index;
+}
+
+void LLGLSLShader::vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ if (mAttribute[index] > 0)
+ {
+ glVertexAttrib4fARB(mAttribute[index], x, y, z, w);
+ }
+}
+
+void LLGLSLShader::vertexAttrib4fv(U32 index, GLfloat* v)
+{
+ if (mAttribute[index] > 0)
+ {
+ glVertexAttrib4fvARB(mAttribute[index], v);
+ }
+}
+
+LLViewerObject* LLPipeline::pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision)
+{
+ LLDrawable* drawable = mObjectPartition->pickDrawable(start, end, collision);
+ return drawable ? drawable->getVObj() : NULL;
+}
+
+
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
new file mode 100644
index 0000000000..4dada886cb
--- /dev/null
+++ b/indra/newview/pipeline.h
@@ -0,0 +1,659 @@
+/**
+ * @file pipeline.h
+ * @brief Rendering pipeline definitions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PIPELINE_H
+#define LL_PIPELINE_H
+
+#include "llagparray.h"
+#include "lldarrayptr.h"
+#include "lldqueueptr.h"
+#include "llstat.h"
+#include "lllinkedqueue.h"
+#include "llskiplist.h"
+#include "lldrawpool.h"
+#include "llspatialpartition.h"
+#include "m4math.h"
+#include "llmemory.h"
+#include "lldrawpool.h"
+#include "llgl.h"
+
+class LLViewerImage;
+class LLDrawable;
+class LLEdge;
+class LLFace;
+class LLViewerObject;
+class LLAgent;
+class LLDisplayPrimitive;
+class LLTextureEntry;
+class LLRenderFunc;
+class LLAGPMemPool;
+class LLAGPMemBlock;
+
+typedef enum e_avatar_skinning_method
+{
+ SKIN_METHOD_SOFTWARE,
+ SKIN_METHOD_VERTEX_PROGRAM
+} EAvatarSkinningMethod;
+
+BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max); // Shouldn't be defined here!
+bool LLRayAABB(const LLVector3 &center, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon = 0);
+BOOL LLLineSegmentAABB(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
+
+class LLGLSLShader
+{
+public:
+ LLGLSLShader();
+
+ void unload();
+ void attachObject(GLhandleARB object);
+ void attachObjects(GLhandleARB* objects = NULL, S32 count = 0);
+ BOOL mapAttributes(const char** attrib_names = NULL, S32 count = 0);
+ BOOL mapUniforms(const char** uniform_names = NULL, S32 count = 0);
+ void mapUniform(GLint index, const char** uniform_names = NULL, S32 count = 0);
+ void vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+ void vertexAttrib4fv(U32 index, GLfloat* v);
+
+ GLint mapUniformTextureChannel(GLint location, GLenum type);
+
+
+ //enable/disable texture channel for specified uniform
+ //if given texture uniform is active in the shader,
+ //the corresponding channel will be active upon return
+ //returns channel texture is enabled in from [0-MAX)
+ S32 enableTexture(S32 uniform, S32 mode = GL_TEXTURE_2D);
+ S32 disableTexture(S32 uniform, S32 mode = GL_TEXTURE_2D);
+
+ BOOL link(BOOL suppress_errors = FALSE);
+ void bind();
+ void unbind();
+
+
+ GLhandleARB mProgramObject;
+ std::vector<GLint> mAttribute;
+ std::vector<GLint> mUniform;
+ std::vector<GLint> mTexture;
+ S32 mActiveTextureChannels;
+};
+
+class LLPipeline
+{
+public:
+ LLPipeline();
+ ~LLPipeline();
+
+ void destroyGL();
+ void restoreGL();
+
+ void init();
+ void cleanup();
+
+ /// @brief Get a draw pool from pool type (POOL_SIMPLE, POOL_MEDIA) and texture.
+ /// @return Draw pool, or NULL if not found.
+ LLDrawPool *findPool(const U32 pool_type, LLViewerImage *tex0 = NULL);
+
+ /// @brief Get a draw pool for faces of the appropriate type and texture. Create if necessary.
+ /// @return Always returns a draw pool.
+ LLDrawPool *getPool(const U32 pool_type, LLViewerImage *tex0 = NULL);
+
+ /// @brief Figures out draw pool type from texture entry. Creates pool if necessary.
+ static LLDrawPool* getPoolFromTE(const LLTextureEntry* te, LLViewerImage* te_image);
+
+ void addPool(LLDrawPool *poolp); // Only to be used by LLDrawPool classes for splitting pools!
+ void removePool( LLDrawPool* poolp );
+
+ void allocDrawable(LLViewerObject *obj);
+
+ void unlinkDrawable(LLDrawable*);
+
+ // Object related methods
+ void markVisible(LLDrawable *drawablep);
+ void doOcclusion();
+ void markNotCulled(LLDrawable* drawablep, LLCamera& camera);
+ void markMoved(LLDrawable *drawablep, BOOL damped_motion = FALSE);
+ void markShift(LLDrawable *drawablep);
+ void markTextured(LLDrawable *drawablep);
+ void markMaterialed(LLDrawable *drawablep);
+ void markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag = LLDrawable::REBUILD_ALL, BOOL priority = FALSE);
+ void markRelight(LLDrawable *drawablep, const BOOL now = FALSE);
+
+ //get the object between start and end that's closest to start. Return the point of collision in collision.
+ LLViewerObject* pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision);
+
+ void dirtyPoolObjectTextures(const LLViewerImage *texture); // Something about this texture has changed. Dirty it.
+
+ void resetDrawOrders();
+
+ U32 addObject(LLViewerObject *obj);
+
+ BOOL usingAGP() const;
+ void setUseAGP(const BOOL use_agp); // Attempt to use AGP;
+
+ void enableShadows(const BOOL enable_shadows);
+
+// void setLocalLighting(const BOOL local_lighting);
+// BOOL isLocalLightingEnabled() const;
+ S32 setLightingDetail(S32 level);
+ S32 getLightingDetail() const { return mLightingDetail; }
+ S32 getMaxLightingDetail() const;
+
+ void setUseVertexShaders(BOOL use_shaders);
+ BOOL getUseVertexShaders() const { return mVertexShadersEnabled; }
+ BOOL canUseVertexShaders();
+ BOOL setVertexShaderLevel(S32 type, S32 level);
+ S32 getVertexShaderLevel(S32 type) const { return mVertexShaderLevel[type]; }
+ S32 getMaxVertexShaderLevel(S32 type) const { return mMaxVertexShaderLevel[type]; }
+
+ void setShaders();
+
+ void dumpObjectLog(GLhandleARB ret, BOOL warns = TRUE);
+ BOOL linkProgramObject(GLhandleARB obj, BOOL suppress_errors = FALSE);
+ BOOL validateProgramObject(GLhandleARB obj);
+ GLhandleARB loadShader(const LLString& filename, S32 cls, GLenum type);
+
+ void setUseOcclusionCulling(BOOL b) { mUseOcclusionCulling = b; }
+ BOOL getUseOcclusionCulling() const { return mUseOcclusionCulling; }
+
+ BOOL initAGP();
+ void cleanupAGP();
+ LLAGPMemBlock *allocAGPFromPool(const S32 bytes, const U32 target); // Static flag is ignored for now.
+ void flushAGPMemory(); // Clear all AGP memory blocks (to pack & reduce fragmentation)
+
+ // phases
+ void resetFrameStats();
+
+ void updateMoveDampedAsync(LLDrawable* drawablep);
+ void updateMoveNormalAsync(LLDrawable* drawablep);
+ void updateMove();
+ void updateCull();
+ void updateGeom(F32 max_dtime);
+
+ void stateSort();
+
+ void renderGeom();
+ void renderHighlights();
+ void renderDebug();
+
+ void renderForSelect();
+ void renderFaceForUVSelect(LLFace* facep);
+ void rebuildPools(); // Rebuild pools
+
+ // bindAGP and unbindAGP are used to bind AGP memory.
+ // AGP should never be bound if you're writing/copying data to AGP.
+ // bindAGP will do the correct thing if AGP rendering has been disabled globally.
+ void bindAGP();
+ void unbindAGP();
+ inline BOOL isAGPBound() const { return mAGPBound; }
+
+ void setupVisibility();
+ void computeVisibility();
+
+ LLViewerObject *nearestObjectAt(F32 yaw, F32 pitch); // CCW yaw from +X = 0 radians, pitch down from +Y = 0 radians, NULL if no object
+
+ void displayPools();
+ void displayAGP();
+ void displayMap();
+ void displaySSBB();
+ void displayQueues();
+ void printPools();
+
+ void findReferences(LLDrawable *drawablep); // Find the lists which have references to this object
+ BOOL verify(); // Verify that all data in the pipeline is "correct"
+
+ // just the AGP part
+ S32 getAGPMemUsage();
+
+ // all pools
+ S32 getMemUsage(const BOOL print = FALSE);
+
+ S32 getVisibleCount() const { return mVisibleList.size(); }
+ S32 getLightCount() const { return mLights.size(); }
+
+ void calcNearbyLights();
+ void setupHWLights(LLDrawPool* pool);
+ void setupAvatarLights(BOOL for_edit = FALSE);
+ void enableLights(U32 mask, F32 shadow_factor);
+ void enableLightsStatic(F32 shadow_factor);
+ void enableLightsDynamic(F32 shadow_factor);
+ void enableLightsAvatar(F32 shadow_factor);
+ void enableLightsAvatarEdit(const LLColor4& color);
+ void enableLightsFullbright(const LLColor4& color);
+ void disableLights();
+ void setAmbient(const LLColor4& ambient);
+
+ void shiftObjects(const LLVector3 &offset);
+
+ void setLight(LLDrawable *drawablep, BOOL is_light);
+ void setActive(LLDrawable *drawablep, BOOL active);
+
+ BOOL hasRenderType(const U32 type) const { return (type && (mRenderTypeMask & (1<<type))) ? TRUE : FALSE; }
+ BOOL hasRenderDebugFeatureMask(const U32 mask) const { return (mRenderDebugFeatureMask & mask) ? TRUE : FALSE; }
+ BOOL hasRenderFeatureMask(const U32 mask) const { return (mRenderFeatureMask & mask) ? TRUE : FALSE; }
+ BOOL hasRenderDebugMask(const U32 mask) const { return (mRenderDebugMask & mask) ? TRUE : FALSE; }
+
+ // Vertex buffer stuff?
+ U8* bufferGetScratchMemory(void);
+ void bufferWaitFence(void);
+ void bufferSendFence(void);
+ void bufferRotate(void);
+
+ // For UI control of render features
+ static void toggleRenderType(void* data);
+ static void toggleRenderDebug(void* data);
+ static void toggleRenderDebugFeature(void* data);
+ static BOOL toggleRenderTypeControl(void* data);
+ static BOOL toggleRenderTypeControlNegated(void* data);
+ static BOOL toggleRenderDebugControl(void* data);
+ static BOOL toggleRenderDebugFeatureControl(void* data);
+
+ static void toggleRenderParticleBeacons(void* data);
+ static BOOL getRenderParticleBeacons(void* data);
+
+ static void toggleRenderSoundBeacons(void* data);
+ static BOOL getRenderSoundBeacons(void* data);
+
+ static void toggleRenderPhysicalBeacons(void* data);
+ static BOOL getRenderPhysicalBeacons(void* data);
+
+ static void toggleRenderScriptedBeacons(void* data);
+ static BOOL getRenderScriptedBeacons(void* data);
+
+private:
+ void initShaders(BOOL force);
+ void unloadShaders();
+ BOOL loadShaders();
+ BOOL loadShadersLighting();
+ BOOL loadShadersObject();
+ BOOL loadShadersAvatar();
+ BOOL loadShadersEnvironment();
+ BOOL loadShadersInterface();
+ void saveVertexShaderLevel(S32 type, S32 level, S32 max);
+ void addToQuickLookup( LLDrawPool* new_poolp );
+ void removeFromQuickLookup( LLDrawPool* poolp );
+ BOOL updateDrawableGeom(LLDrawable* drawable, BOOL priority);
+
+public:
+ enum {GPU_CLASS_MAX = 3 };
+ enum EShaderClass
+ {
+ SHADER_LIGHTING,
+ SHADER_OBJECT,
+ SHADER_AVATAR,
+ SHADER_ENVIRONMENT,
+ SHADER_INTERFACE,
+ SHADER_COUNT
+ };
+ enum LLRenderTypeMask
+ {
+ // Following are pool types (some are also object types)
+ RENDER_TYPE_SKY = LLDrawPool::POOL_SKY,
+ RENDER_TYPE_STARS = LLDrawPool::POOL_STARS,
+ RENDER_TYPE_GROUND = LLDrawPool::POOL_GROUND,
+ RENDER_TYPE_TERRAIN = LLDrawPool::POOL_TERRAIN,
+ RENDER_TYPE_SIMPLE = LLDrawPool::POOL_SIMPLE,
+ RENDER_TYPE_MEDIA = LLDrawPool::POOL_MEDIA,
+ RENDER_TYPE_BUMP = LLDrawPool::POOL_BUMP,
+ RENDER_TYPE_AVATAR = LLDrawPool::POOL_AVATAR,
+ RENDER_TYPE_TREE = LLDrawPool::POOL_TREE,
+ RENDER_TYPE_TREE_NEW = LLDrawPool::POOL_TREE_NEW,
+ RENDER_TYPE_WATER = LLDrawPool::POOL_WATER,
+ RENDER_TYPE_CLOUDS = LLDrawPool::POOL_CLOUDS,
+ RENDER_TYPE_ALPHA = LLDrawPool::POOL_ALPHA,
+ RENDER_TYPE_HUD = LLDrawPool::POOL_HUD,
+
+ // Following are object types (only used in drawable mRenderType)
+ RENDER_TYPE_VOLUME,
+ RENDER_TYPE_GRASS,
+ RENDER_TYPE_PARTICLES,
+ };
+
+ enum LLRenderDebugFeatureMask
+ {
+ RENDER_DEBUG_FEATURE_UI = 0x0001,
+ RENDER_DEBUG_FEATURE_SELECTED = 0x0002,
+ RENDER_DEBUG_FEATURE_HIGHLIGHTED = 0x0004,
+ RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES = 0x0008,
+// RENDER_DEBUG_FEATURE_HW_LIGHTING = 0x0010,
+ RENDER_DEBUG_FEATURE_FLEXIBLE = 0x0010,
+ RENDER_DEBUG_FEATURE_FOG = 0x0020,
+ RENDER_DEBUG_FEATURE_PALETTE = 0x0040,
+ RENDER_DEBUG_FEATURE_FR_INFO = 0x0080,
+ RENDER_DEBUG_FEATURE_CHAIN_FACES = 0x0100
+ };
+
+ enum LLRenderFeatureMask
+ {
+ RENDER_FEATURE_AGP = 0x01,
+// RENDER_FEATURE_LOCAL_LIGHTING = 0x02,
+ RENDER_FEATURE_OBJECT_BUMP = 0x04,
+ RENDER_FEATURE_AVATAR_BUMP = 0x08,
+// RENDER_FEATURE_SHADOWS = 0x10,
+ RENDER_FEATURE_RIPPLE_WATER = 0X20
+ };
+
+ enum LLRenderDebugMask
+ {
+ RENDER_DEBUG_LIGHT_TRACE = 0x0001,
+ RENDER_DEBUG_POOLS = 0x0002,
+ RENDER_DEBUG_MAP = 0x0004,
+ RENDER_DEBUG_AGP_MEM = 0x0008,
+ RENDER_DEBUG_QUEUES = 0x0010,
+ RENDER_DEBUG_COMPOSITION = 0x0020,
+ RENDER_DEBUG_SSBB = 0x0040,
+ RENDER_DEBUG_VERIFY = 0x0080,
+ RENDER_DEBUG_SHADOW_MAP = 0x0100,
+ RENDER_DEBUG_BBOXES = 0x0200,
+ RENDER_DEBUG_OCTREE = 0x0400,
+ RENDER_DEBUG_FACE_CHAINS = 0x0800,
+ RENDER_DEBUG_OCCLUSION = 0x1000,
+ RENDER_DEBUG_POINTS = 0x2000,
+ RENDER_DEBUG_TEXTURE_PRIORITY = 0x4000,
+ };
+
+ LLPointer<LLViewerImage> mAlphaSizzleImagep;
+
+ LLSpatialPartition *mObjectPartition;
+
+ BOOL mBackfaceCull;
+ S32 mTrianglesDrawn;
+ LLStat mTrianglesDrawnStat;
+ LLStat mCompilesStat;
+ S32 mVerticesRelit;
+ LLStat mVerticesRelitStat;
+
+ S32 mLightingChanges;
+ LLStat mLightingChangesStat;
+ S32 mGeometryChanges;
+ LLStat mGeometryChangesStat;
+ LLStat mMoveChangesStat;
+
+ S32 mNumVisibleFaces;
+ LLStat mNumVisibleFacesStat;
+ LLStat mNumVisibleDrawablesStat;
+
+ static S32 sAGPMaxPoolSize;
+ static S32 sCompiles;
+
+ BOOL mUseVBO; // Use ARB vertex buffer objects, if available
+
+ class LLScatterShader
+ {
+ public:
+ static void init(GLhandleARB shader, int map_stage);
+ };
+
+ //utility shader objects (not shader programs)
+ GLhandleARB mLightVertex;
+ GLhandleARB mLightFragment;
+ GLhandleARB mScatterVertex;
+ GLhandleARB mScatterFragment;
+
+ //global (reserved slot) shader parameters
+ static const char* sReservedAttribs[];
+ static U32 sReservedAttribCount;
+
+ typedef enum
+ {
+ GLSL_MATERIAL_COLOR = 0,
+ GLSL_SPECULAR_COLOR,
+ GLSL_BINORMAL,
+ GLSL_END_RESERVED_ATTRIBS
+ } eGLSLReservedAttribs;
+
+ static const char* sReservedUniforms[];
+ static U32 sReservedUniformCount;
+
+ typedef enum
+ {
+ GLSL_DIFFUSE_MAP = 0,
+ GLSL_SPECULAR_MAP,
+ GLSL_BUMP_MAP,
+ GLSL_ENVIRONMENT_MAP,
+ GLSL_SCATTER_MAP,
+ GLSL_END_RESERVED_UNIFORMS
+ } eGLSLReservedUniforms;
+
+ //object shaders
+ LLGLSLShader mObjectSimpleProgram;
+ LLGLSLShader mObjectAlphaProgram;
+ LLGLSLShader mObjectBumpProgram;
+
+ //water parameters
+ static const char* sWaterUniforms[];
+ static U32 sWaterUniformCount;
+
+ typedef enum
+ {
+ GLSL_WATER_SCREENTEX = GLSL_END_RESERVED_UNIFORMS,
+ GLSL_WATER_EYEVEC,
+ GLSL_WATER_TIME,
+ GLSL_WATER_WAVE_DIR1,
+ GLSL_WATER_WAVE_DIR2,
+ GLSL_WATER_LIGHT_DIR,
+ GLSL_WATER_SPECULAR,
+ GLSL_WATER_SPECULAR_EXP,
+ GLSL_WATER_FBSCALE,
+ GLSL_WATER_REFSCALE
+ } eWaterUniforms;
+
+
+ //terrain parameters
+ static const char* sTerrainUniforms[];
+ static U32 sTerrainUniformCount;
+
+ typedef enum
+ {
+ GLSL_TERRAIN_DETAIL0 = GLSL_END_RESERVED_UNIFORMS,
+ GLSL_TERRAIN_DETAIL1,
+ GLSL_TERRAIN_ALPHARAMP
+ } eTerrainUniforms;
+
+ //environment shaders
+ LLGLSLShader mTerrainProgram;
+ LLGLSLShader mGroundProgram;
+ LLGLSLShader mWaterProgram;
+
+ //interface shaders
+ LLGLSLShader mHighlightProgram;
+
+ //avatar shader parameter tables
+ static const char* sAvatarAttribs[];
+ static U32 sAvatarAttribCount;
+
+ typedef enum
+ {
+ GLSL_AVATAR_WEIGHT = GLSL_END_RESERVED_ATTRIBS,
+ GLSL_AVATAR_CLOTHING,
+ GLSL_AVATAR_WIND,
+ GLSL_AVATAR_SINWAVE,
+ GLSL_AVATAR_GRAVITY
+ } eAvatarAttribs;
+
+ static const char* sAvatarUniforms[];
+ static U32 sAvatarUniformCount;
+
+ typedef enum
+ {
+ GLSL_AVATAR_MATRIX = GLSL_END_RESERVED_UNIFORMS
+ } eAvatarUniforms;
+
+ //avatar skinning utility shader object
+ GLhandleARB mAvatarSkinVertex;
+
+ //avatar shader handles
+ LLGLSLShader mAvatarProgram;
+ LLGLSLShader mAvatarEyeballProgram;
+ LLGLSLShader mAvatarPickProgram;
+
+ //current avatar shader parameter pointer
+ GLint mAvatarMatrixParam;
+ GLint mMaterialIndex;
+ GLint mSpecularIndex;
+
+ LLColor4 mSunDiffuse;
+ LLVector3 mSunDir;
+
+protected:
+ class SelectedFaceInfo
+ {
+ public:
+ LLFace *mFacep;
+ S32 mTE;
+ };
+
+ BOOL mVertexShadersEnabled;
+ S32 mVertexShadersLoaded; // 0 = no, 1 = yes, -1 = failed
+ S32 mVertexShaderLevel[SHADER_COUNT];
+ S32 mMaxVertexShaderLevel[SHADER_COUNT];
+
+ U32 mRenderTypeMask;
+ U32 mRenderFeatureMask;
+ U32 mRenderDebugFeatureMask;
+ U32 mRenderDebugMask;
+
+ /////////////////////////////////////////////
+ //
+ //
+ LLDrawable::drawable_vector_t mVisibleList;
+ LLDrawable::drawable_set_t mMovedList;
+
+ LLDrawable::drawable_vector_t mShiftList;
+
+ /////////////////////////////////////////////
+ //
+ //
+ struct Light
+ {
+ Light(LLDrawable* ptr, F32 d, F32 f = 0.0f)
+ : drawable(ptr),
+ dist(d),
+ fade(f)
+ {}
+ LLPointer<LLDrawable> drawable;
+ F32 dist;
+ F32 fade;
+ struct compare
+ {
+ bool operator()(const Light& a, const Light& b) const
+ {
+ if ( a.dist < b.dist )
+ return true;
+ else if ( a.dist > b.dist )
+ return false;
+ else
+ return a.drawable < b.drawable;
+ }
+ };
+ };
+ typedef std::set< Light, Light::compare > light_set_t;
+
+ LLDrawable::drawable_set_t mLights;
+ light_set_t mNearbyLights; // lights near camera
+ LLColor4 mHWLightColors[8];
+
+ /////////////////////////////////////////////
+ //
+ // Different queues of drawables being processed.
+ //
+ LLDrawable::drawable_set_t mBuildQ1; // priority
+ LLDrawable::drawable_list_t mBuildQ2; // non-priority
+
+ LLDrawable::drawable_set_t mActiveQ;
+
+ LLDrawable::drawable_set_t mRetexturedList;
+ LLDrawable::drawable_set_t mRematerialedList;
+
+ //////////////////////////////////////////////////
+ //
+ // Draw pools are responsible for storing all rendered data,
+ // and performing the actual rendering of objects.
+ //
+ struct compare_pools
+ {
+ bool operator()(const LLDrawPool* a, const LLDrawPool* b) const
+ {
+ if (!a)
+ return true;
+ else if (!b)
+ return false;
+ else
+ {
+ S32 atype = a->getType();
+ S32 btype = b->getType();
+ if (atype < btype)
+ return true;
+ else if (atype > btype)
+ return false;
+ else
+ return a->getId() < b->getId();
+ }
+ }
+ };
+ typedef std::set<LLDrawPool*, compare_pools > pool_set_t;
+ pool_set_t mPools;
+ LLDrawPool* mLastRebuildPool;
+
+ // For quick-lookups into mPools (mapped by texture pointer)
+ std::map<uintptr_t, LLDrawPool*> mSimplePools;
+ std::map<uintptr_t, LLDrawPool*> mTerrainPools;
+ std::map<uintptr_t, LLDrawPool*> mTreePools;
+ std::map<uintptr_t, LLDrawPool*> mTreeNewPools;
+ std::map<uintptr_t, LLDrawPool*> mBumpPools;
+ std::map<uintptr_t, LLDrawPool*> mMediaPools;
+ LLDrawPool* mAlphaPool;
+ LLDrawPool* mSkyPool;
+ LLDrawPool* mStarsPool;
+ LLDrawPool* mCloudsPool;
+ LLDrawPool* mTerrainPool;
+ LLDrawPool* mWaterPool;
+ LLDrawPool* mGroundPool;
+ LLDrawPool* mHUDPool;
+ // Note: no need to keep an quick-lookup to avatar pools, since there's only one per avatar
+
+
+ LLDynamicArray<LLFace*> mHighlightFaces; // highlight faces on physical objects
+ LLDynamicArray<SelectedFaceInfo> mSelectedFaces;
+
+ LLPointer<LLViewerImage> mFaceSelectImagep;
+ LLPointer<LLViewerImage> mBloomImagep;
+ LLPointer<LLViewerImage> mBloomImage2p;
+
+ BOOL mAGPBound;
+ LLAGPMemPool *mAGPMemPool;
+ U32 mGlobalFence;
+
+ // Round-robin AGP buffers for use by the software skinner
+ enum
+ {
+ kMaxBufferCount = 4
+ };
+
+ S32 mBufferIndex;
+ S32 mBufferCount;
+ LLAGPArray<U8> *mBufferMemory[kMaxBufferCount];
+ U32 mBufferFence[kMaxBufferCount];
+ BOOL mUseOcclusionCulling; // object-object occlusion culling
+ U32 mLightMask;
+ U32 mLightMovingMask;
+ S32 mLightingDetail;
+ F32 mSunShadowFactor;
+
+ static BOOL sRenderPhysicalBeacons;
+ static BOOL sRenderScriptedBeacons;
+ static BOOL sRenderParticleBeacons;
+ static BOOL sRenderSoundBeacons;
+};
+
+void render_bbox(const LLVector3 &min, const LLVector3 &max);
+
+extern LLPipeline gPipeline;
+extern BOOL gRenderForSelect;
+extern F32 gPickAlphaThreshold;
+extern F32 gPickAlphaTargetThreshold;
+extern BOOL gUsePickAlpha;
+
+#endif
diff --git a/indra/newview/res-sdl/arrow.BMP b/indra/newview/res-sdl/arrow.BMP
new file mode 100644
index 0000000000..a8f6da64b5
--- /dev/null
+++ b/indra/newview/res-sdl/arrow.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/arrowcop.BMP b/indra/newview/res-sdl/arrowcop.BMP
new file mode 100644
index 0000000000..1a26a0df34
--- /dev/null
+++ b/indra/newview/res-sdl/arrowcop.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/arrowcopmulti.BMP b/indra/newview/res-sdl/arrowcopmulti.BMP
new file mode 100644
index 0000000000..48f153cef6
--- /dev/null
+++ b/indra/newview/res-sdl/arrowcopmulti.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/arrowdrag.BMP b/indra/newview/res-sdl/arrowdrag.BMP
new file mode 100644
index 0000000000..cd868eec20
--- /dev/null
+++ b/indra/newview/res-sdl/arrowdrag.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/circleandline.BMP b/indra/newview/res-sdl/circleandline.BMP
new file mode 100644
index 0000000000..284ae8b7d5
--- /dev/null
+++ b/indra/newview/res-sdl/circleandline.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/cross.BMP b/indra/newview/res-sdl/cross.BMP
new file mode 100644
index 0000000000..0b4672d4d6
--- /dev/null
+++ b/indra/newview/res-sdl/cross.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/hand.BMP b/indra/newview/res-sdl/hand.BMP
new file mode 100644
index 0000000000..2a092fbb7f
--- /dev/null
+++ b/indra/newview/res-sdl/hand.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/ibeam.BMP b/indra/newview/res-sdl/ibeam.BMP
new file mode 100644
index 0000000000..820904a228
--- /dev/null
+++ b/indra/newview/res-sdl/ibeam.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/ll_icon.BMP b/indra/newview/res-sdl/ll_icon.BMP
new file mode 100644
index 0000000000..4a44aafbfa
--- /dev/null
+++ b/indra/newview/res-sdl/ll_icon.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrow.BMP b/indra/newview/res-sdl/llarrow.BMP
new file mode 100644
index 0000000000..a8f6da64b5
--- /dev/null
+++ b/indra/newview/res-sdl/llarrow.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrowdrag.BMP b/indra/newview/res-sdl/llarrowdrag.BMP
new file mode 100644
index 0000000000..cd868eec20
--- /dev/null
+++ b/indra/newview/res-sdl/llarrowdrag.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrowdragmulti.BMP b/indra/newview/res-sdl/llarrowdragmulti.BMP
new file mode 100644
index 0000000000..fb528bc92d
--- /dev/null
+++ b/indra/newview/res-sdl/llarrowdragmulti.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrowlocked.BMP b/indra/newview/res-sdl/llarrowlocked.BMP
new file mode 100644
index 0000000000..0aaa441ab1
--- /dev/null
+++ b/indra/newview/res-sdl/llarrowlocked.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llgrablocked.BMP b/indra/newview/res-sdl/llgrablocked.BMP
new file mode 100644
index 0000000000..847439670f
--- /dev/null
+++ b/indra/newview/res-sdl/llgrablocked.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llno.BMP b/indra/newview/res-sdl/llno.BMP
new file mode 100644
index 0000000000..284ae8b7d5
--- /dev/null
+++ b/indra/newview/res-sdl/llno.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llnolocked.BMP b/indra/newview/res-sdl/llnolocked.BMP
new file mode 100644
index 0000000000..61f0170cb3
--- /dev/null
+++ b/indra/newview/res-sdl/llnolocked.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolcamera.BMP b/indra/newview/res-sdl/lltoolcamera.BMP
new file mode 100644
index 0000000000..c961d7a49c
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolcamera.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolcreate.BMP b/indra/newview/res-sdl/lltoolcreate.BMP
new file mode 100644
index 0000000000..08a4a9322d
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolcreate.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolfocus.BMP b/indra/newview/res-sdl/lltoolfocus.BMP
new file mode 100644
index 0000000000..afb90a95e3
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolfocus.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolgrab.BMP b/indra/newview/res-sdl/lltoolgrab.BMP
new file mode 100644
index 0000000000..f2ac68bf3c
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolgrab.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolland.BMP b/indra/newview/res-sdl/lltoolland.BMP
new file mode 100644
index 0000000000..64e6365625
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolland.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolpan.BMP b/indra/newview/res-sdl/lltoolpan.BMP
new file mode 100644
index 0000000000..ffbef21ec7
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolpan.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolpipette.BMP b/indra/newview/res-sdl/lltoolpipette.BMP
new file mode 100644
index 0000000000..2d27118289
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolpipette.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolrotate.BMP b/indra/newview/res-sdl/lltoolrotate.BMP
new file mode 100644
index 0000000000..dd84673018
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolrotate.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolscale.BMP b/indra/newview/res-sdl/lltoolscale.BMP
new file mode 100644
index 0000000000..882515e5e3
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolscale.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltooltranslate.BMP b/indra/newview/res-sdl/lltooltranslate.BMP
new file mode 100644
index 0000000000..d084f6a026
--- /dev/null
+++ b/indra/newview/res-sdl/lltooltranslate.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolzoomin.BMP b/indra/newview/res-sdl/lltoolzoomin.BMP
new file mode 100644
index 0000000000..e4e46cc702
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolzoomin.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolzoomout.BMP b/indra/newview/res-sdl/lltoolzoomout.BMP
new file mode 100644
index 0000000000..7f958383ab
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolzoomout.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizenesw.BMP b/indra/newview/res-sdl/sizenesw.BMP
new file mode 100644
index 0000000000..559579f40e
--- /dev/null
+++ b/indra/newview/res-sdl/sizenesw.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizens.BMP b/indra/newview/res-sdl/sizens.BMP
new file mode 100644
index 0000000000..8373077dff
--- /dev/null
+++ b/indra/newview/res-sdl/sizens.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizenwse.BMP b/indra/newview/res-sdl/sizenwse.BMP
new file mode 100644
index 0000000000..6d069fa765
--- /dev/null
+++ b/indra/newview/res-sdl/sizenwse.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizewe.BMP b/indra/newview/res-sdl/sizewe.BMP
new file mode 100644
index 0000000000..878df453a4
--- /dev/null
+++ b/indra/newview/res-sdl/sizewe.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolbuy.BMP b/indra/newview/res-sdl/toolbuy.BMP
new file mode 100644
index 0000000000..a00bdb2c58
--- /dev/null
+++ b/indra/newview/res-sdl/toolbuy.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolopen.BMP b/indra/newview/res-sdl/toolopen.BMP
new file mode 100644
index 0000000000..93c7e44b28
--- /dev/null
+++ b/indra/newview/res-sdl/toolopen.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolpickobject.BMP b/indra/newview/res-sdl/toolpickobject.BMP
new file mode 100644
index 0000000000..25469fc3a8
--- /dev/null
+++ b/indra/newview/res-sdl/toolpickobject.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolpickobject2.BMP b/indra/newview/res-sdl/toolpickobject2.BMP
new file mode 100644
index 0000000000..09df69e675
--- /dev/null
+++ b/indra/newview/res-sdl/toolpickobject2.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolpickobject3.BMP b/indra/newview/res-sdl/toolpickobject3.BMP
new file mode 100644
index 0000000000..fc28698050
--- /dev/null
+++ b/indra/newview/res-sdl/toolpickobject3.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolsit.BMP b/indra/newview/res-sdl/toolsit.BMP
new file mode 100644
index 0000000000..ca911ece71
--- /dev/null
+++ b/indra/newview/res-sdl/toolsit.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/wait.BMP b/indra/newview/res-sdl/wait.BMP
new file mode 100644
index 0000000000..26dec59afe
--- /dev/null
+++ b/indra/newview/res-sdl/wait.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/working.BMP b/indra/newview/res-sdl/working.BMP
new file mode 100644
index 0000000000..26dec59afe
--- /dev/null
+++ b/indra/newview/res-sdl/working.BMP
Binary files differ
diff --git a/indra/newview/res/arrow.cur b/indra/newview/res/arrow.cur
new file mode 100644
index 0000000000..d11b68867f
--- /dev/null
+++ b/indra/newview/res/arrow.cur
Binary files differ
diff --git a/indra/newview/res/arrowcop.cur b/indra/newview/res/arrowcop.cur
new file mode 100644
index 0000000000..8b5902c13d
--- /dev/null
+++ b/indra/newview/res/arrowcop.cur
Binary files differ
diff --git a/indra/newview/res/arrowcopmulti.cur b/indra/newview/res/arrowcopmulti.cur
new file mode 100644
index 0000000000..2620522616
--- /dev/null
+++ b/indra/newview/res/arrowcopmulti.cur
Binary files differ
diff --git a/indra/newview/res/arrowdrag.cur b/indra/newview/res/arrowdrag.cur
new file mode 100644
index 0000000000..77b2d1a0fb
--- /dev/null
+++ b/indra/newview/res/arrowdrag.cur
Binary files differ
diff --git a/indra/newview/res/bitmap2.bmp b/indra/newview/res/bitmap2.bmp
new file mode 100644
index 0000000000..770b07558c
--- /dev/null
+++ b/indra/newview/res/bitmap2.bmp
Binary files differ
diff --git a/indra/newview/res/circleandline.cur b/indra/newview/res/circleandline.cur
new file mode 100644
index 0000000000..00050f5501
--- /dev/null
+++ b/indra/newview/res/circleandline.cur
Binary files differ
diff --git a/indra/newview/res/install_icon.BMP b/indra/newview/res/install_icon.BMP
new file mode 100644
index 0000000000..09df573870
--- /dev/null
+++ b/indra/newview/res/install_icon.BMP
Binary files differ
diff --git a/indra/newview/res/ll_icon.BMP b/indra/newview/res/ll_icon.BMP
new file mode 100644
index 0000000000..3a9964cd95
--- /dev/null
+++ b/indra/newview/res/ll_icon.BMP
Binary files differ
diff --git a/indra/newview/res/ll_icon.ico b/indra/newview/res/ll_icon.ico
new file mode 100644
index 0000000000..c35a3fa3a3
--- /dev/null
+++ b/indra/newview/res/ll_icon.ico
Binary files differ
diff --git a/indra/newview/res/llarrow.cur b/indra/newview/res/llarrow.cur
new file mode 100644
index 0000000000..8a922d7f3f
--- /dev/null
+++ b/indra/newview/res/llarrow.cur
Binary files differ
diff --git a/indra/newview/res/llarrowdrag.cur b/indra/newview/res/llarrowdrag.cur
new file mode 100644
index 0000000000..77b2d1a0fb
--- /dev/null
+++ b/indra/newview/res/llarrowdrag.cur
Binary files differ
diff --git a/indra/newview/res/llarrowdragmulti.cur b/indra/newview/res/llarrowdragmulti.cur
new file mode 100644
index 0000000000..f0853b899c
--- /dev/null
+++ b/indra/newview/res/llarrowdragmulti.cur
Binary files differ
diff --git a/indra/newview/res/llarrowlocked.cur b/indra/newview/res/llarrowlocked.cur
new file mode 100644
index 0000000000..5b81f147ef
--- /dev/null
+++ b/indra/newview/res/llarrowlocked.cur
Binary files differ
diff --git a/indra/newview/res/llgrablocked.cur b/indra/newview/res/llgrablocked.cur
new file mode 100644
index 0000000000..73dea154e6
--- /dev/null
+++ b/indra/newview/res/llgrablocked.cur
Binary files differ
diff --git a/indra/newview/res/llno.cur b/indra/newview/res/llno.cur
new file mode 100644
index 0000000000..00050f5501
--- /dev/null
+++ b/indra/newview/res/llno.cur
Binary files differ
diff --git a/indra/newview/res/llnolocked.cur b/indra/newview/res/llnolocked.cur
new file mode 100644
index 0000000000..dfa4a05509
--- /dev/null
+++ b/indra/newview/res/llnolocked.cur
Binary files differ
diff --git a/indra/newview/res/lltoolcamera.cur b/indra/newview/res/lltoolcamera.cur
new file mode 100644
index 0000000000..e0f19461c7
--- /dev/null
+++ b/indra/newview/res/lltoolcamera.cur
Binary files differ
diff --git a/indra/newview/res/lltoolcreate.cur b/indra/newview/res/lltoolcreate.cur
new file mode 100644
index 0000000000..716f328211
--- /dev/null
+++ b/indra/newview/res/lltoolcreate.cur
Binary files differ
diff --git a/indra/newview/res/lltoolfocus.cur b/indra/newview/res/lltoolfocus.cur
new file mode 100644
index 0000000000..7b38ad3af9
--- /dev/null
+++ b/indra/newview/res/lltoolfocus.cur
Binary files differ
diff --git a/indra/newview/res/lltoolgrab.cur b/indra/newview/res/lltoolgrab.cur
new file mode 100644
index 0000000000..4484001357
--- /dev/null
+++ b/indra/newview/res/lltoolgrab.cur
Binary files differ
diff --git a/indra/newview/res/lltoolland.cur b/indra/newview/res/lltoolland.cur
new file mode 100644
index 0000000000..e22ef6c73d
--- /dev/null
+++ b/indra/newview/res/lltoolland.cur
Binary files differ
diff --git a/indra/newview/res/lltoolpan.cur b/indra/newview/res/lltoolpan.cur
new file mode 100644
index 0000000000..0a348669b6
--- /dev/null
+++ b/indra/newview/res/lltoolpan.cur
Binary files differ
diff --git a/indra/newview/res/lltoolpipette.cur b/indra/newview/res/lltoolpipette.cur
new file mode 100644
index 0000000000..b4780967f9
--- /dev/null
+++ b/indra/newview/res/lltoolpipette.cur
Binary files differ
diff --git a/indra/newview/res/lltoolrotate.cur b/indra/newview/res/lltoolrotate.cur
new file mode 100644
index 0000000000..adbadaee84
--- /dev/null
+++ b/indra/newview/res/lltoolrotate.cur
Binary files differ
diff --git a/indra/newview/res/lltoolscale.cur b/indra/newview/res/lltoolscale.cur
new file mode 100644
index 0000000000..4b6d20bfad
--- /dev/null
+++ b/indra/newview/res/lltoolscale.cur
Binary files differ
diff --git a/indra/newview/res/lltooltranslate.cur b/indra/newview/res/lltooltranslate.cur
new file mode 100644
index 0000000000..2df80de0a6
--- /dev/null
+++ b/indra/newview/res/lltooltranslate.cur
Binary files differ
diff --git a/indra/newview/res/lltoolzoomin.cur b/indra/newview/res/lltoolzoomin.cur
new file mode 100644
index 0000000000..0b5d80101e
--- /dev/null
+++ b/indra/newview/res/lltoolzoomin.cur
Binary files differ
diff --git a/indra/newview/res/lltoolzoomout.cur b/indra/newview/res/lltoolzoomout.cur
new file mode 100644
index 0000000000..b33e68d1a6
--- /dev/null
+++ b/indra/newview/res/lltoolzoomout.cur
Binary files differ
diff --git a/indra/newview/res/loginbackground.bmp b/indra/newview/res/loginbackground.bmp
new file mode 100644
index 0000000000..288a0b0398
--- /dev/null
+++ b/indra/newview/res/loginbackground.bmp
Binary files differ
diff --git a/indra/newview/res/resource.h b/indra/newview/res/resource.h
new file mode 100644
index 0000000000..c7e1552dff
--- /dev/null
+++ b/indra/newview/res/resource.h
@@ -0,0 +1,159 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by newViewRes.rc
+//
+#define IDREMOVE 3
+#define IDI_LL_ICON 103
+#define IDC_GRABHAND 104
+#define IDR_MENU1 112
+#define IDD_DIALOG1 113
+#define IDI_INSTALL_ICON 133
+#define IDC_CURSOR1 134
+#define IDC_CURSOR2 136
+#define IDB_BITMAP1 138
+#define IDB_BITMAP2 140
+#define IDB_BITMAP3 144
+#define IDC_CURSOR3 147
+#define IDB_BITMAP4 148
+#define IDB_BITMAP5 149
+#define IDB_LOGIN 149
+#define IDC_CURSOR4 153
+#define IDC_CURSOR5 154
+#define IDC_RADIO_56 1000
+#define IDC_RADIO_128 1001
+#define IDC_RADIO_256 1002
+#define IDC_RADIO_CUSTOM 1003
+#define IDC_STATIC_LAND 1004
+#define IDC_EDIT_LAND 1005
+#define IDC_STATIC_WATER 1006
+#define IDC_EDIT_WATER 1007
+#define IDC_STATIC_WIND 1008
+#define IDC_EDIT_WIND 1009
+#define IDC_STATIC_CLOUD 1010
+#define IDC_EDIT_CLOUD 1011
+#define IDC_STATIC_TASK 1012
+#define IDC_EDIT_TASK 1013
+#define IDC_STATIC_OTHER 1014
+#define IDC_EDIT_OTHER 1015
+#define IDC_EDIT_USERNAME 1018
+#define IDC_EDIT_PASSWORD 1019
+#define IDC_EDIT_ACCOUNT 1020
+#define IDC_EDIT_GUEST 1021
+#define IDC_EDIT_LASTNAME 1021
+#define IDC_LABEL_TEXTURE 1028
+#define IDC_BUTTON_APPLY 1030
+#define IDAPPLY 1030
+#define IDC_BUTTON_GRAB 1031
+#define IDC_COMBO_TEXTURE 1032
+#define IDC_RADIO_POSITION 1033
+#define IDC_RADIO_SIZE 1034
+#define IDC_RADIO_ROTATION 1035
+#define IDC_STATIC_POS_X 1036
+#define IDC_STATIC_POS_Y 1037
+#define IDC_STATIC_POS_Z 1038
+#define IDC_EDIT_POS_X 1039
+#define IDC_EDIT_POS_Y 1040
+#define IDC_CHECK_AUTO_LOGIN 1040
+#define IDC_EDIT_POS_Z 1041
+#define IDC_STATIC_FILENAME 1041
+#define IDC_STATIC_POS_X2 1042
+#define IDC_EDIT_FILENAME 1042
+#define IDC_STATIC_POS_Y2 1043
+#define IDC_COMBO_FACE 1043
+#define IDC_STATIC_POS_Z2 1044
+#define IDC_RADIO_TEXTURE_LOW 1044
+#define IDC_COMBO_MATERIAL 1044
+#define IDC_EDIT_SIZE_X 1045
+#define IDC_RADIO_TEXTURE_MEDIUM 1045
+#define IDC_EDIT_SIZE_Y 1046
+#define IDC_RADIO_TEXTURE_HIGH 1046
+#define IDC_EDIT_SIZE_Z 1047
+#define IDC_STATIC_POS_X3 1048
+#define IDC_STATIC_POS_Y3 1049
+#define IDC_STATIC_POS_Z3 1050
+#define IDC_EDIT_ROT_X 1051
+#define IDC_EDIT_ROT_Y 1052
+#define IDC_EDIT_ROT_Z 1053
+#define IDC_CHECK_USE_PHYSICS 1065
+#define IDC_LIST1 1068
+#define IDC_LIST_USER 1068
+#define IDC_LIST_LOCATION 1068
+#define IDC_BUTTON_CHANGE 1070
+#define IDC_BUTTON_CLOSE 1071
+#define IDC_CHAT_SAY 1072
+#define IDC_STATIC_VERT_SCALE 1073
+#define IDC_STATIC_SCALE 1073
+#define IDC_EDIT_VERT_SCALE 1074
+#define IDC_STATIC_HORIZ_SCALE 1075
+#define IDC_STATIC_SCALE2 1075
+#define IDC_EDIT_HORIZ_SCALE 1076
+#define IDC_BUTTON_STRETCH 1077
+#define IDC_BUTTON_REPEAT 1078
+#define IDC_EDIT_VERT_OFFSET 1079
+#define IDC_EDIT_HORIZ_OFFSET 1080
+#define IDC_EDIT_ROTATION 1081
+#define IDC_STATIC_KBPS1 1082
+#define IDC_STATIC_KBPS2 1083
+#define IDC_BTN_INTERNAL 1083
+#define IDC_BUTTON_RESET 1083
+#define IDC_STATIC_KBPS3 1084
+#define IDC_BTN_EXTERNAL 1084
+#define IDC_STATIC_KBPS4 1085
+#define IDC_BTN_QUIT 1085
+#define IDC_STATIC_KBPS5 1086
+#define IDC_RESOURCE_TITLE 1086
+#define IDC_STATIC_KBPS6 1087
+#define IDC_RESOURCE_DESC 1087
+#define IDC_EDIT_SAY 1091
+#define IDC_BTN_NORMAL 1092
+#define IDC_BTN_WHISPER 1093
+#define IDC_BTN_SHOUT 1094
+#define IDC_STATIC_ACCOUNT 1102
+#define IDC_STATIC_PASSWORD 1103
+#define IDC_STATIC_GUEST 1104
+#define IDC_STATIC_SERVER 1104
+#define IDC_STATIC_SPACESERVER 1105
+#define IDC_STATIC_LASTNAME 1105
+#define IDC_CHECK_GUEST 1106
+#define IDC_SCRIPT_EDIT 1107
+#define IDC_LSLSYNTAX 1108
+#define IDDONE 1109
+#define IDC_LIST 1110
+#define IDC_EDIT_MESSAGE 1111
+#define IDC_BUTTON_REFRESH 1112
+#define IDC_STATIC_OFFSET 1113
+#define IDC_STATIC_ROTATION 1115
+#define IDC_STATIC_FILEPATH 1115
+#define IDC_STATIC_PX 1118
+#define IDC_STATIC_PY 1119
+#define IDC_STATIC_PZ 1120
+#define IDC_STATIC_SX 1121
+#define IDC_STATIC_SY 1122
+#define IDC_STATIC_SZ 1123
+#define IDC_STATIC_RX 1124
+#define IDC_STATIC_RY 1125
+#define IDC_STATIC_RZ 1126
+#define IDC_BTN_APPLY 1127
+#define IDC_COMBO_SERVER 1131
+#define IDC_STATIC_FIRSTNAME 1132
+#define IDC_EDIT_FIRSTNAME 1133
+#define IDC_REMEMBER_PASSWORD 1134
+#define IDC_REMEMBER_PASSWORD2 1135
+#define IDC_FULL_SCREEN 1135
+#define IDC_RADIO_HOME 1136
+#define IDC_RADIO_LAST 1137
+#define IDC_COMBO1 1138
+#define IDC_COMBO_FARM 1138
+#define ID_TESTMENU_TEST 40001
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 156
+#define _APS_NEXT_COMMAND_VALUE 40002
+#define _APS_NEXT_CONTROL_VALUE 1139
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/indra/newview/res/toolbuy.cur b/indra/newview/res/toolbuy.cur
new file mode 100644
index 0000000000..c61cd76bef
--- /dev/null
+++ b/indra/newview/res/toolbuy.cur
Binary files differ
diff --git a/indra/newview/res/toolopen.cur b/indra/newview/res/toolopen.cur
new file mode 100644
index 0000000000..8408f7c7eb
--- /dev/null
+++ b/indra/newview/res/toolopen.cur
Binary files differ
diff --git a/indra/newview/res/toolpickobject.cur b/indra/newview/res/toolpickobject.cur
new file mode 100644
index 0000000000..ba482b5445
--- /dev/null
+++ b/indra/newview/res/toolpickobject.cur
Binary files differ
diff --git a/indra/newview/res/toolpickobject2.cur b/indra/newview/res/toolpickobject2.cur
new file mode 100644
index 0000000000..b168f8d691
--- /dev/null
+++ b/indra/newview/res/toolpickobject2.cur
Binary files differ
diff --git a/indra/newview/res/toolpickobject3.cur b/indra/newview/res/toolpickobject3.cur
new file mode 100644
index 0000000000..457c3a6555
--- /dev/null
+++ b/indra/newview/res/toolpickobject3.cur
Binary files differ
diff --git a/indra/newview/res/toolpipette.cur b/indra/newview/res/toolpipette.cur
new file mode 100644
index 0000000000..b4780967f9
--- /dev/null
+++ b/indra/newview/res/toolpipette.cur
Binary files differ
diff --git a/indra/newview/res/toolsit.cur b/indra/newview/res/toolsit.cur
new file mode 100644
index 0000000000..60b0e773e5
--- /dev/null
+++ b/indra/newview/res/toolsit.cur
Binary files differ
diff --git a/indra/newview/res/uninstall_icon.BMP b/indra/newview/res/uninstall_icon.BMP
new file mode 100644
index 0000000000..562b56676a
--- /dev/null
+++ b/indra/newview/res/uninstall_icon.BMP
Binary files differ
diff --git a/indra/newview/secondlife.icns b/indra/newview/secondlife.icns
new file mode 100644
index 0000000000..31ce7b3250
--- /dev/null
+++ b/indra/newview/secondlife.icns
Binary files differ
diff --git a/indra/newview/skins/paths.xml b/indra/newview/skins/paths.xml
new file mode 100644
index 0000000000..fb6f4a133d
--- /dev/null
+++ b/indra/newview/skins/paths.xml
@@ -0,0 +1,4 @@
+<paths>
+ <directory>/skins/xui/en-us/</directory>
+ <directory>/skins/xui/[Language]/</directory>
+</paths> \ No newline at end of file